implement dump_all_known_symbols for linux · vmprof/vmprof-python@c88c051 · GitHub
Skip to content

Commit c88c051

Browse files
committed
implement dump_all_known_symbols for linux
1 parent dbac2eb commit c88c051

6 files changed

Lines changed: 179 additions & 31 deletions

File tree

setup.py

Lines changed: 13 additions & 4 deletions

src/_vmprof.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,16 @@ write_all_code_objects(PyObject *module, PyObject *noargs)
237237

238238

239239
static PyObject *
240-
sample_stack_now(PyObject *module, PyObject *noargs)
240+
sample_stack_now(PyObject *module, PyObject *args)
241241
{
242242
PyThreadState * tstate = NULL;
243+
244+
unsigned char write_to_log = 0;
245+
246+
if (!PyArg_ParseTuple(args, "|b", &write_to_log)) {
247+
return NULL;
248+
}
249+
243250
PyObject * list;
244251
int i;
245252
int entry_count;

src/symboltable.c

Lines changed: 134 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,13 @@
44
#include "_vmprof.h"
55

66
#ifdef _PY_TEST
7-
#define LOG printf
7+
#define LOG(...) printf(__VA_ARGS__)
88
#else
9-
#define LOG
9+
#define LOG(...)
1010
#endif
1111

12-
#ifdef __APPLE__
13-
14-
#include <mach-o/loader.h>
15-
#include <mach-o/nlist.h>
16-
#include <mach-o/stab.h>
17-
#include <mach-o/dyld.h>
18-
#include <mach-o/dyld_images.h>
19-
20-
void write_address_and_name(int fd, uint64_t e, const char * sym) {
12+
static
13+
void _write_address_and_name(int fd, uint64_t e, const char * sym) {
2114
struct str {
2215
long addr;
2316
long size;
@@ -33,6 +26,15 @@ void write_address_and_name(int fd, uint64_t e, const char * sym) {
3326
(void)write(fd, &s, sizeof(long)+sizeof(long)+s.size);
3427
}
3528

29+
#ifdef __APPLE__
30+
31+
#include <mach-o/loader.h>
32+
#include <mach-o/nlist.h>
33+
#include <mach-o/stab.h>
34+
#include <mach-o/dyld.h>
35+
#include <mach-o/dyld_images.h>
36+
37+
3638
void dump_all_known_symbols(int fd) {
3739
const struct mach_header_64 * hdr;
3840
const struct symtab_command *sc;
@@ -78,7 +80,7 @@ void dump_all_known_symbols(int fd) {
7880
}
7981
const char * sym = &strtbl[off];
8082
uint64_t e = entry->n_value;
81-
write_address_and_name(fd, e, sym);
83+
_write_address_and_name(fd, e, sym);
8284
}
8385
}
8486
}
@@ -87,13 +89,130 @@ void dump_all_known_symbols(int fd) {
8789
}
8890
}
8991
#elif defined(__unix__)
92+
93+
#include <link.h>
94+
95+
#include <stdio.h>
96+
#include <stdlib.h>
97+
#include <dlfcn.h>
98+
#include <string.h>
99+
#include <elf.h>
100+
#include <sys/auxv.h>
101+
#include <link.h>
102+
#include <sys/mman.h>
103+
#include <bits/wordsize.h>
104+
105+
#if __WORDSIZE == 64
106+
#define ELF_R_SYM ELF64_R_SYM
107+
#define ELF_ST_BIND ELF64_ST_BIND
108+
#define ELF_ST_TYPE ELF64_ST_TYPE
109+
#elif __WORDSIZE == 32
110+
#define ELF_R_SYM ELF32_R_SYM
111+
#define ELF_ST_BIND ELF32_ST_BIND
112+
#define ELF_ST_TYPE ELF32_ST_TYPE
113+
#else
114+
#error "unsupported word size"
115+
#endif
116+
117+
#define F_SYMTAB 0x00001
118+
#define F_STRTAB 0x00002
119+
#define F_RELA 0x00004
120+
121+
typedef struct _stab {
122+
int flags;
123+
char * strtab;
124+
ElfW(Xword) strtab_size;
125+
126+
ElfW(Sym) * symtab;
127+
ElfW(Xword) symtab_size;
128+
129+
ElfW(Rela) *rela;
130+
ElfW(Xword) rela_size;
131+
ElfW(Xword) rela_ent;
132+
} stab_t;
133+
134+
int _load_info_from(ElfW(Addr) base, stab_t * t, const ElfW(Phdr) * phdr) {
135+
ElfW(Dyn) *dyn;
136+
int result = 0;
137+
138+
for (dyn = (ElfW(Dyn) *)(base + phdr->p_vaddr); dyn->d_tag; dyn++) {
139+
if (dyn->d_tag == DT_SYMTAB) {
140+
t->symtab = (ElfW(Sym)*)dyn->d_un.d_ptr;
141+
result |= F_SYMTAB;
142+
} else if (dyn->d_tag == DT_SYMENT) {
143+
t->symtab_size = dyn->d_un.d_val;
144+
} else if (dyn->d_tag == DT_STRTAB) {
145+
t->strtab = (char *)dyn->d_un.d_ptr;
146+
result |= F_STRTAB;
147+
} else if (dyn->d_tag == DT_STRSZ) {
148+
t->strtab_size = dyn->d_un.d_val;
149+
} else if (dyn->d_tag == DT_RELA) {
150+
t->rela = (ElfW(Rela)*)dyn->d_un.d_ptr;
151+
result |= F_RELA;
152+
} else if (dyn->d_tag == DT_RELASZ) {
153+
t->rela_size = dyn->d_un.d_val;
154+
} else if (dyn->d_tag == DT_RELAENT) {
155+
t->rela_ent = dyn->d_un.d_val;
156+
}
157+
}
158+
t->flags = result;
159+
return ((F_SYMTAB | F_STRTAB) & result) != 0;
160+
}
161+
162+
static int _dump_symbols2(ElfW(Addr) base, stab_t * table, int fd) {
163+
ElfW(Rela) *rela;
164+
ElfW(Rela) *relaend;
165+
ElfW(Sym) * sym;
166+
167+
if (table->flags & F_RELA) {
168+
// yeah, go ahead, relocation could be found!!
169+
relaend = (ElfW(Rela)*)((char*)table->rela + table->rela_size);
170+
for (rela = table->rela; rela < relaend; rela++) {
171+
sym = &table->symtab[ELF_R_SYM(rela->r_info)];
172+
if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) {
173+
continue;
174+
}
175+
char * name = table->strtab + sym->st_name;
176+
if (strlen(name) <= 0) {
177+
continue;
178+
}
179+
uint64_t addr = base + rela->r_offset;
180+
//LOG("%s\n", name);
181+
_write_address_and_name(fd, addr, name);
182+
}
183+
}
184+
return 0;
185+
}
186+
187+
static int iter_shared_objects(struct dl_phdr_info *info, size_t size, void *data) {
188+
int fd = *((int*)data);
189+
uint16_t phentsize = getauxval(AT_PHENT);
190+
LOG("shared object %s\n", info->dlpi_name);
191+
stab_t table;
192+
int r;
193+
194+
int16_t phnum = info->dlpi_phnum;
195+
const ElfW(Phdr) * phdr = info->dlpi_phdr;
196+
ElfW(Addr) base = info->dlpi_addr;
197+
for (int i = 0; i < phnum; i++, (phdr = (ElfW(Phdr) *)((char *)phdr + phentsize))) {
198+
if (phdr->p_type != PT_DYNAMIC) {
199+
continue;
200+
}
201+
r = _load_info_from(base, &table, phdr);
202+
if (!r) {
203+
continue;
204+
}
205+
(void)_dump_symbols2(base, &table, fd);
206+
}
207+
return 0;
208+
}
209+
90210
void dump_all_known_symbols(int fd) {
91-
//write(fd, buf, size);
92-
xxx
211+
(void)dl_iterate_phdr(iter_shared_objects, (void*)&fd);
93212
}
94213
#else
95214
// other platforms than linux & mac os x
96215
void dump_all_known_symbols(int fd) {
97-
// oh, nothing to do!! not supported platform
216+
// oh, nothing to do!! a not supported platform
98217
}
99218
#endif

src/symboltable.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
*
77
* # encoded as a mapping
88
* addr = read_word(fd); name = read_string(fd)
9+
*
10+
* A) It is not allowed to have two addresses (virtual ones only valid
11+
* in the curent process) in this mapping to point to several symbols.
12+
* B) No duplicates are logged
913
*/
1014
void dump_all_known_symbols(int fd);

vmprof/test/test_c_symboltable.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# trick: compile with _CFFI_USE_EMBEDDING=1 which will not define Py_LIMITED_API
1818
stack_ffi.set_source("vmprof.test._test_symboltable", source, include_dirs=['src'],
1919
define_macros=[('_CFFI_USE_EMBEDDING',1),('_PY_TEST',1)], libraries=libs,
20-
extra_compile_args=['-g', '-O0'])
20+
extra_compile_args=['-g'])
2121

2222
sample = None
2323

@@ -41,15 +41,28 @@ def test_dump_all_known_symbols(self, tmpdir):
4141
while True:
4242
assert fd.read(1) == MARKER_NATIVE_SYMBOLS
4343
addr = read_word(fd)
44-
string = read_string(fd)
44+
string = read_string(fd).decode('utf-8')
4545
addrs.append((addr, string))
4646
if fd.tell() >= length:
4747
break
48-
assert addrs >= 100 # usually we have many many more!!
49-
symbols_to_be_found = ['_PyString_FromString', '_dump_all_known_symbols']
48+
assert len(addrs) >= 100 # usually we have many many more!!
49+
symbols_to_be_found = ['PyObject_Call', 'dump_all_known_symbols']
50+
duplicates = []
51+
names = set()
5052
for addr, name in addrs:
51-
if name in symbols_to_be_found:
52-
i = symbols_to_be_found.index(name)
53-
del symbols_to_be_found[i]
53+
assert len(name) > 0
54+
for i,sym in enumerate(symbols_to_be_found):
55+
if name in sym:
56+
del symbols_to_be_found[i]
57+
break
58+
if (addr,name) in names:
59+
duplicates.append((addr,name))
60+
else:
61+
names.add((addr, name))
5462
assert len(symbols_to_be_found) == 0
63+
# property B) see header symboltable.h
64+
assert len(duplicates) == 0
65+
addrs = [addr for addr,name in names]
66+
# property A), see header symboltable.h
67+
assert len(addrs) == len(set(addrs))
5568

vmprof/test/test_native.py

Lines changed: 0 additions & 4 deletions

0 commit comments

Comments
 (0)