Skip to content

Commit

Permalink
Get symbol addresses via objdump instead of libdw.
Browse files Browse the repository at this point in the history
Perf is comparable to libdw. Build is much simpler.
  • Loading branch information
adsr committed Nov 20, 2018
1 parent 3a3da22 commit 8c473d7
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 139 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "vendor/elfutils"]
path = vendor/elfutils
url = https://sourceware.org/git/elfutils.git
[submodule "vendor/termbox"]
path = vendor/termbox
url = https://github.com/nsf/termbox.git
36 changes: 7 additions & 29 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
phpspy_cflags:=-std=c99 -Wall -Wextra -pedantic -g -Ofast -pthread $(CFLAGS)
phpspy_libs:=$(LDLIBS)
phpspy_cflags:=-std=c99 -Wall -Wextra -pedantic -g -O3 $(CFLAGS)
phpspy_libs:=-pthread $(LDLIBS)
phpspy_ldflags:=$(LDFLAGS)
phpspy_includes:=-I. -I./vendor -Ivendor/termbox/src
phpspy_defines:=-DUSE_TERMBOX=1
phpspy_defines:=
phpspy_tests:=$(wildcard tests/test_*.sh)
phpspy_sources:=phpspy.c pgrep.c top.c addr_libdw.c event_fout.c

elfutils_subdirs:=lib libelf libebl libdwelf libdwfl libdw libcpu backends
elfutils_includes:=$(foreach subdir,$(elfutils_subdirs),-Ivendor/elfutils/$(subdir))
elfutils_includes:=$(elfutils_includes) -Ivendor/symlinks/libdwfl
elfutils_static_libs:=dw elf dwfl ebl dwelf
elfutils_ldflags:=$(foreach lib,$(elfutils_static_libs),-Lvendor/elfutils/lib$(lib))
elfutils_libs:=-Wl,-Bstatic
elfutils_libs:=$(elfutils_libs) $(foreach lib,$(elfutils_static_libs),-l$(lib))
elfutils_libs:=$(elfutils_libs) -Lvendor/elfutils/lib -leu
elfutils_libs:=$(elfutils_libs) -Lvendor/elfutils/backends -lebl_x86_64_pic
elfutils_libs:=$(elfutils_libs) -Wl,-Bdynamic
elfutils_libs:=$(elfutils_libs) -ldl -lz -llzma -lbz2
phpspy_sources:=phpspy.c pgrep.c top.c addr_objdump.c event_fout.c

termbox_inlcudes=-Ivendor/termbox/src/
termbox_libs:=-Wl,-Bstatic -Lvendor/termbox/build/src/ -ltermbox -Wl,-Bdynamic
Expand All @@ -28,7 +16,6 @@ php_path?=php
sinclude config.mk

has_pthread := $(shell $(LD) $(phpspy_ldflags) -lpthread -o/dev/null >/dev/null 2>&1 && echo :)
has_libdw := $(shell $(LD) $(phpspy_ldflags) -ldw -o/dev/null >/dev/null 2>&1 && echo :)
has_termbox := $(shell $(LD) $(phpspy_ldflags) -ltermbox -o/dev/null >/dev/null 2>&1 && echo :)
has_phpconf := $(shell command -v php-config >/dev/null 2>&1 && echo :)

Expand All @@ -42,16 +29,12 @@ endif

all: phpspy_static

phpspy_static: $(wildcard *.c *.h) vendor/elfutils/libdw/libdw.a vendor/termbox/build/src/libtermbox.a
$(CC) $(phpspy_cflags) $(phpspy_includes) $(elfutils_includes) $(termbox_inlcudes) $(phpspy_defines) $(phpspy_sources) -o phpspy $(phpspy_ldflags) $(elfutils_ldflags) $(phpspy_libs) $(elfutils_libs) $(termbox_libs)
phpspy_static: $(wildcard *.c *.h) vendor/termbox/build/src/libtermbox.a
$(CC) $(phpspy_cflags) $(phpspy_includes) $(termbox_inlcudes) $(phpspy_defines) $(phpspy_sources) -o phpspy $(phpspy_ldflags) $(phpspy_libs) $(termbox_libs)

phpspy_dynamic: $(wildcard *.c *.h)
@$(or $(has_libdw), $(error Need libdw. Hint: try `make phpspy_static`))
@$(or $(has_termbox), $(error Need libtermbox. Hint: try `make phpspy_static`))
$(CC) $(phpspy_cflags) $(phpspy_includes) $(phpspy_defines) $(phpspy_sources) -o phpspy $(phpspy_ldflags) $(phpspy_libs) -ldw -ltermbox

vendor/elfutils/libdw/libdw.a: vendor/elfutils/configure.ac
cd vendor/elfutils && autoreconf -if && ./configure --enable-maintainer-mode && $(MAKE) SUBDIRS="$(elfutils_subdirs)"
$(CC) $(phpspy_cflags) $(phpspy_includes) $(phpspy_defines) $(phpspy_sources) -o phpspy $(phpspy_ldflags) $(phpspy_libs) -ltermbox

vendor/termbox/build/src/libtermbox.a: vendor/termbox/waf
cd vendor/termbox && ./waf configure && ./waf --targets=termbox_static
Expand All @@ -60,10 +43,6 @@ vendor/termbox/waf:
git submodule update --init --remote --recursive
cd vendor/termbox && git reset --hard

vendor/elfutils/configure.ac:
git submodule update --init --remote --recursive
cd vendor/elfutils && git reset --hard

test: phpspy_static $(phpspy_tests)
@total=0; \
pass=0; \
Expand All @@ -80,7 +59,6 @@ install: phpspy_static
install -D -v -m 755 phpspy $(DESTDIR)$(prefix)/bin/phpspy

clean:
cd vendor/elfutils && make clean
cd vendor/termbox && ./waf clean
rm -f phpspy

Expand Down
100 changes: 0 additions & 100 deletions addr_libdw.c

This file was deleted.

105 changes: 105 additions & 0 deletions addr_objdump.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "phpspy.h"

static int get_php_bin_path(pid_t pid, char *path);
static int get_php_base_addr(pid_t pid, char *path, uint64_t *raddr);
static int get_symbol_offset(char *path, const char *symbol, uint64_t *raddr);
static int popen_read_line(char *buf, size_t buf_size, char *cmd_fmt, ...);

int get_symbol_addr(addr_memo_t *memo, pid_t pid, const char *symbol, uint64_t *raddr) {
char *php_bin_path;
uint64_t *php_base_addr;
uint64_t addr_offset;
php_bin_path = memo->php_bin_path;
php_base_addr = &memo->php_base_addr;
if (*php_bin_path == '\0' && get_php_bin_path(pid, php_bin_path) != 0) {
return 1;
} else if (*php_base_addr == 0 && get_php_base_addr(pid, php_bin_path, php_base_addr) != 0) {
return 1;
} else if (get_symbol_offset(php_bin_path, symbol, &addr_offset) != 0) {
return 1;
}
*raddr = *php_base_addr + addr_offset;
return 0;
}

static int get_php_bin_path(pid_t pid, char *path) {
char buf[PHPSPY_STR_SIZE];
char *cmd_fmt = "awk '/libphp7/{print $NF; exit 0} END{exit 1}' /proc/%d/maps"
" || readlink -e /proc/%d/exe";
if (popen_read_line(buf, sizeof(buf), cmd_fmt, (int)pid, (int)pid) != 0) {
fprintf(stderr, "get_php_bin_path: Failed\n");
return 1;
}
strcpy(path, buf);
return 0;
}

static int get_php_base_addr(pid_t pid, char *path, uint64_t *raddr) {
/**
* This is very likely to be incorrect/incomplete. I thought the base
* address from `/proc/<pid>/maps` + the symbol address from `readelf` would
* lead to the actual memory address, but on at least one system I tested on
* this is not the case. On that system, working backwards from the address
* printed in `gdb`, it seems the missing piece was the 'virtual address' of
* the LOAD section in ELF headers. I suspect this may have to do with
* address relocation and/or a feature called 'prelinking', but not sure.
*/
char buf[PHPSPY_STR_SIZE];
uint64_t start_addr;
uint64_t virt_addr;
char *cmd_fmt = "grep -m1 ' %s$' /proc/%d/maps";
if (popen_read_line(buf, sizeof(buf), cmd_fmt, path, (int)pid) != 0) {
fprintf(stderr, "get_php_base_addr: Failed to get start_addr\n");
return 1;
}
start_addr = strtoull(buf, NULL, 16);
cmd_fmt = "objdump -p %s | awk '/LOAD/{print $5; exit}'";
if (popen_read_line(buf, sizeof(buf), cmd_fmt, path) != 0) {
fprintf(stderr, "get_php_base_addr: Failed to get virt_addr\n");
return 1;
}
virt_addr = strtoull(buf, NULL, 16);
*raddr = start_addr - virt_addr;
return 0;
}

static int get_symbol_offset(char *path, const char *symbol, uint64_t *raddr) {
char buf[PHPSPY_STR_SIZE];
char *cmd_fmt = "objdump -Tt %s | awk '/ %s$/{print $1; exit}'";
if (popen_read_line(buf, sizeof(buf), cmd_fmt, path, symbol) != 0) {
fprintf(stderr, "get_symbol_offset: Failed\n");
return 1;
}
*raddr = strtoull(buf, NULL, 16);
return 0;
}

static int popen_read_line(char *buf, size_t buf_size, char *cmd_fmt, ...) {
FILE *fp;
char cmd[PHPSPY_STR_SIZE];
int buf_len;
va_list cmd_args;
va_start(cmd_args, cmd_fmt);
vsprintf(cmd, cmd_fmt, cmd_args);
va_end(cmd_args);
if (!(fp = popen(cmd, "r"))) {
perror("popen");
return 1;
}
if (fgets(buf, buf_size-1, fp) == NULL) {
fprintf(stderr, "popen_read_line: No stdout; cmd=%s\n", cmd);
pclose(fp);
return 1;
}
pclose(fp);
buf_len = strlen(buf);
while (buf_len > 0 && buf[buf_len-1] == '\n') {
--buf_len;
}
if (buf_len < 1) {
fprintf(stderr, "popen_read_line: Expected strlen(buf)>0; cmd=%s\n", cmd);
return 1;
}
buf[buf_len] = '\0';
return 0;
}
16 changes: 12 additions & 4 deletions phpspy.c
Original file line number Diff line number Diff line change
Expand Up @@ -372,22 +372,26 @@ static void redirect_child_stdio(int proc_fd, char *opt_path) {

static int find_addresses(trace_target_t *target) {
int rv;
addr_memo_t memo;

memset(&memo, 0, sizeof(addr_memo_t));

if (opt_executor_globals_addr != 0) {
target->executor_globals_addr = opt_executor_globals_addr;
} else {
try(rv, get_symbol_addr(target->pid, "executor_globals", &target->executor_globals_addr));
try(rv, get_symbol_addr(&memo, target->pid, "executor_globals", &target->executor_globals_addr));
}
if (opt_sapi_globals_addr != 0) {
target->sapi_globals_addr = opt_sapi_globals_addr;
} else if (opt_capture_req) {
try(rv, get_symbol_addr(target->pid, "sapi_globals", &target->sapi_globals_addr));
try(rv, get_symbol_addr(&memo, target->pid, "sapi_globals", &target->sapi_globals_addr));
}
if (0) {
/* TODO feature to print core_globals */
try(rv, get_symbol_addr(target->pid, "core_globals", &target->core_globals_addr));
try(rv, get_symbol_addr(&memo, target->pid, "core_globals", &target->core_globals_addr));
}
if (opt_capture_mem) {
try(rv, get_symbol_addr(target->pid, "alloc_globals", &target->alloc_globals_addr));
try(rv, get_symbol_addr(&memo, target->pid, "alloc_globals", &target->alloc_globals_addr));
}

/* TODO probably don't need zend_string_val_offset */
Expand Down Expand Up @@ -447,6 +451,10 @@ static int copy_proc_mem(trace_context_t *context, const char *what, void *raddr
int rv;
struct iovec local[1];
struct iovec remote[1];
if (raddr == NULL) {
fprintf(stderr, "copy_proc_mem: Not copying %s; raddr is NULL\n", what);
return 1;
}
local[0].iov_base = laddr;
local[0].iov_len = size;
remote[0].iov_base = raddr;
Expand Down
8 changes: 6 additions & 2 deletions phpspy.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
#include <php_structs_74.h>
#endif

#include <elfutils/libdwfl.h>
#include <uthash.h>

#define try(__rv, __call) do { if (((__rv) = (__call)) != 0) return (__rv); } while(0)
Expand Down Expand Up @@ -136,6 +135,11 @@ typedef struct trace_context_s {
char buf[PHPSPY_STR_SIZE];
} trace_context_t;

typedef struct addr_memo_s {
char php_bin_path[PHPSPY_STR_SIZE];
uint64_t php_base_addr;
} addr_memo_t;

extern char *opt_pgrep_args;
extern int done;
extern int opt_num_workers;
Expand All @@ -150,7 +154,7 @@ extern int main_pid(pid_t pid);
extern int main_top(int argc, char **argv);

extern void usage(FILE *fp, int exit_code);
extern int get_symbol_addr(pid_t pid, const char *symbol, uint64_t *raddr);
extern int get_symbol_addr(addr_memo_t *memo, pid_t pid, const char *symbol, uint64_t *raddr);
extern int event_handler_fout(struct trace_context_s *context, int event_type);

#endif
1 change: 0 additions & 1 deletion vendor/elfutils
Submodule elfutils deleted from 52b6b2

0 comments on commit 8c473d7

Please sign in to comment.