memprof 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/bin_api.h +12 -0
- data/ext/elf.c +52 -0
- data/ext/mach.c +46 -8
- data/ext/x86_64.c +1 -1
- data/ext/x86_64.h +83 -25
- data/memprof.gemspec +1 -1
- metadata +4 -11
data/ext/bin_api.h
CHANGED
@@ -39,6 +39,18 @@ bin_init();
|
|
39
39
|
void *
|
40
40
|
bin_find_symbol(const char *sym, size_t *size);
|
41
41
|
|
42
|
+
/*
|
43
|
+
* bin_find_symbol_name - find a symbol's name
|
44
|
+
*
|
45
|
+
* Given:
|
46
|
+
* - sym - a symbol address
|
47
|
+
*
|
48
|
+
* This function will search for the symbol sym and return its name if
|
49
|
+
* found, or NULL if the symbol could not be found.
|
50
|
+
*/
|
51
|
+
const char *
|
52
|
+
bin_find_symbol_name(void *sym);
|
53
|
+
|
42
54
|
/*
|
43
55
|
* bin_allocate_page - allocate a page suitable for trampolines
|
44
56
|
*
|
data/ext/elf.c
CHANGED
@@ -301,6 +301,58 @@ bin_find_symbol(const char *sym, size_t *size)
|
|
301
301
|
return do_bin_find_symbol(sym, size, ruby_info);
|
302
302
|
}
|
303
303
|
|
304
|
+
/*
|
305
|
+
* do_bin_find_symbol_name - internal symbol name lookup function.
|
306
|
+
*
|
307
|
+
* Given:
|
308
|
+
* - sym - the symbol address to look up
|
309
|
+
* - elf - an elf information structure
|
310
|
+
*
|
311
|
+
* This function will return the name of the symbol
|
312
|
+
* or NULL if nothing can be found.
|
313
|
+
*/
|
314
|
+
static const char *
|
315
|
+
do_bin_find_symbol_name(void *sym, struct elf_info *elf)
|
316
|
+
{
|
317
|
+
char *name = NULL;
|
318
|
+
void *ptr;
|
319
|
+
|
320
|
+
assert(sym != NULL);
|
321
|
+
assert(elf != NULL);
|
322
|
+
|
323
|
+
assert(elf->symtab_data != NULL);
|
324
|
+
assert(elf->symtab_data->d_buf != NULL);
|
325
|
+
|
326
|
+
ElfW(Sym) *esym = (ElfW(Sym)*) elf->symtab_data->d_buf;
|
327
|
+
ElfW(Sym) *lastsym = (ElfW(Sym)*) ((char*) elf->symtab_data->d_buf + elf->symtab_data->d_size);
|
328
|
+
|
329
|
+
assert(esym <= lastsym);
|
330
|
+
|
331
|
+
for (; esym < lastsym; esym++){
|
332
|
+
/* ignore weak/numeric/empty symbols */
|
333
|
+
if ((esym->st_value == 0) ||
|
334
|
+
(ELF32_ST_BIND(esym->st_info)== STB_WEAK) ||
|
335
|
+
(ELF32_ST_BIND(esym->st_info)== STB_NUM))
|
336
|
+
continue;
|
337
|
+
|
338
|
+
ptr = elf->base_addr + (void *)esym->st_value;
|
339
|
+
name = elf_strptr(elf->elf, elf->symtab_shdr.sh_link, (size_t)esym->st_name);
|
340
|
+
|
341
|
+
if (ptr == sym)
|
342
|
+
return name;
|
343
|
+
}
|
344
|
+
|
345
|
+
return NULL;
|
346
|
+
}
|
347
|
+
|
348
|
+
/*
|
349
|
+
* Do the same thing as in bin_find_symbol above, but compare addresses and return the string name.
|
350
|
+
*/
|
351
|
+
const char *
|
352
|
+
bin_find_symbol_name(void *sym) {
|
353
|
+
return do_bin_find_symbol_name(sym, ruby_info);
|
354
|
+
}
|
355
|
+
|
304
356
|
/*
|
305
357
|
* bin_update_image - update the ruby binary image in memory.
|
306
358
|
*
|
data/ext/mach.c
CHANGED
@@ -323,13 +323,6 @@ build_sorted_nlist_table(const struct mach_header_64 *hdr, uint32_t *nsyms, uint
|
|
323
323
|
|
324
324
|
void *
|
325
325
|
bin_find_symbol(const char *symbol, size_t *size) {
|
326
|
-
/* Correctly prefix the symbol with a '_' (whats a prettier way to do this?) */
|
327
|
-
size_t len = strlen(symbol);
|
328
|
-
char real_symbol[len + 2];
|
329
|
-
memcpy(real_symbol, "_", 1);
|
330
|
-
memcpy((real_symbol + 1), symbol, len);
|
331
|
-
memcpy((real_symbol + len + 1), "\0", 1);
|
332
|
-
|
333
326
|
void *ptr = NULL;
|
334
327
|
void *file = NULL;
|
335
328
|
|
@@ -354,7 +347,7 @@ bin_find_symbol(const char *symbol, size_t *size) {
|
|
354
347
|
const struct nlist_64 *nlist_entry = nlist_table[i];
|
355
348
|
const char *string = string_table + nlist_entry->n_un.n_strx;
|
356
349
|
|
357
|
-
if (strcmp(
|
350
|
+
if (string && strcmp(symbol, string+1) == 0) {
|
358
351
|
const uint64_t addr = nlist_entry->n_value;
|
359
352
|
/* Add the slide to get the *real* address in the process. */
|
360
353
|
ptr = (void*)(addr + _dyld_get_image_vmaddr_slide(index));
|
@@ -391,6 +384,51 @@ bin_find_symbol(const char *symbol, size_t *size) {
|
|
391
384
|
return ptr;
|
392
385
|
}
|
393
386
|
|
387
|
+
/*
|
388
|
+
* Do the same thing as in bin_find_symbol above, but compare addresses and return the string name.
|
389
|
+
*/
|
390
|
+
const char *
|
391
|
+
bin_find_symbol_name(void *symbol) {
|
392
|
+
const char *name = NULL;
|
393
|
+
void *ptr = NULL;
|
394
|
+
void *file = NULL;
|
395
|
+
|
396
|
+
uint32_t i, j, k;
|
397
|
+
uint32_t stroff, nsyms = 0;
|
398
|
+
int index = 0;
|
399
|
+
|
400
|
+
file = get_ruby_file_and_header_index(&index);
|
401
|
+
|
402
|
+
const struct mach_header_64 *hdr = (const struct mach_header_64*) file;
|
403
|
+
if (hdr->magic != MH_MAGIC_64)
|
404
|
+
errx(EX_SOFTWARE, "Magic for Ruby Mach-O file doesn't match");
|
405
|
+
|
406
|
+
const struct nlist_64 **nlist_table = build_sorted_nlist_table(hdr, &nsyms, &stroff);
|
407
|
+
/*
|
408
|
+
* The string names of symbols are stored separately from the symbol table.
|
409
|
+
* The symbol table entries contain a string 'index', which is an offset into this region.
|
410
|
+
*/
|
411
|
+
const char *string_table = (const char*)hdr + stroff;
|
412
|
+
|
413
|
+
for (i=0; i < nsyms; i++) {
|
414
|
+
const struct nlist_64 *nlist_entry = nlist_table[i];
|
415
|
+
const char *string = string_table + nlist_entry->n_un.n_strx;
|
416
|
+
|
417
|
+
const uint64_t addr = nlist_entry->n_value;
|
418
|
+
/* Add the slide to get the *real* address in the process. */
|
419
|
+
void *ptr = (void*)(addr + _dyld_get_image_vmaddr_slide(index));
|
420
|
+
|
421
|
+
if (ptr == symbol) {
|
422
|
+
name = string+1;
|
423
|
+
break;
|
424
|
+
}
|
425
|
+
}
|
426
|
+
|
427
|
+
free(nlist_table);
|
428
|
+
free(file);
|
429
|
+
return name;
|
430
|
+
}
|
431
|
+
|
394
432
|
/*
|
395
433
|
* I will explain bin_update_image with imaginary Ruby code:
|
396
434
|
*
|
data/ext/x86_64.c
CHANGED
@@ -168,7 +168,7 @@ arch_insert_inline_st2_tramp(void *addr, void *marker, void *trampoline, void *t
|
|
168
168
|
* This is to arrange for the new value in freelist to be in %rdi, and as such
|
169
169
|
* be the first argument to the C handler. As per the amd64 ABI.
|
170
170
|
*/
|
171
|
-
default_inline_st2_tramp.frame.rdi_source_displacement = marker - (void *)&(entry->frame.
|
171
|
+
default_inline_st2_tramp.frame.rdi_source_displacement = marker - (void *)&(entry->frame.align_rsp);
|
172
172
|
|
173
173
|
/* jmp back to the instruction after stage 1 trampoline was inserted
|
174
174
|
*
|
data/ext/x86_64.h
CHANGED
@@ -90,18 +90,40 @@ static struct tramp_st2_entry {
|
|
90
90
|
*
|
91
91
|
* This structure represents the assembly code:
|
92
92
|
*
|
93
|
-
* mov
|
94
|
-
*
|
95
|
-
*
|
96
|
-
*
|
97
|
-
*
|
98
|
-
*
|
99
|
-
*
|
100
|
-
*
|
101
|
-
*
|
102
|
-
*
|
103
|
-
*
|
104
|
-
*
|
93
|
+
* mov SOURCE_REGISTER,-0x3f90d4a7(%rip) # update freelist
|
94
|
+
*
|
95
|
+
* # save caller saved registers here
|
96
|
+
*
|
97
|
+
* push %rax
|
98
|
+
* push %rcx
|
99
|
+
* push %rdx
|
100
|
+
* push %rsi
|
101
|
+
* push %rdi
|
102
|
+
* push %r8
|
103
|
+
* push %r9
|
104
|
+
* push %r10
|
105
|
+
* push %r11
|
106
|
+
* push %rbp
|
107
|
+
* mov %rsp,%rbp
|
108
|
+
* mov -0x3f90d4bf(%rip),%rdi # move freelist into rdi as arg1
|
109
|
+
* and $0xfffffffffffffff0,%rsp # align stack pointer
|
110
|
+
* mov $0x7ffff65e74e0,%rcx # put handler function into position
|
111
|
+
* callq *%rcx # call handler
|
112
|
+
*
|
113
|
+
* # restore caller saved registers here
|
114
|
+
*
|
115
|
+
* leaveq
|
116
|
+
* pop %r11
|
117
|
+
* pop %r10
|
118
|
+
* pop %r9
|
119
|
+
* pop %r8
|
120
|
+
* pop %rdi
|
121
|
+
* pop %rsi
|
122
|
+
* pop %rdx
|
123
|
+
* pop %rcx
|
124
|
+
* pop %rax
|
125
|
+
*
|
126
|
+
* jmpq 0x433972 <gc_sweep+754> # jump back
|
105
127
|
*/
|
106
128
|
static struct inline_tramp_st2_entry {
|
107
129
|
unsigned char rex;
|
@@ -110,20 +132,42 @@ static struct inline_tramp_st2_entry {
|
|
110
132
|
uint32_t mov_displacement;
|
111
133
|
|
112
134
|
struct {
|
135
|
+
/*
|
136
|
+
* XXX xmm0-xmm7 are caller saved, too.
|
137
|
+
*/
|
138
|
+
unsigned char push_rax;
|
139
|
+
unsigned char push_rcx;
|
140
|
+
unsigned char push_rdx;
|
141
|
+
unsigned char push_rsi;
|
113
142
|
unsigned char push_rdi;
|
143
|
+
unsigned char push_r8[2];
|
144
|
+
unsigned char push_r9[2];
|
145
|
+
unsigned char push_r10[2];
|
146
|
+
unsigned char push_r11[2];
|
147
|
+
|
148
|
+
unsigned char push_rbp;
|
149
|
+
unsigned char mov_rsp_rbp[3];
|
150
|
+
|
114
151
|
unsigned char mov_rdi[3];
|
115
152
|
uint32_t rdi_source_displacement;
|
116
|
-
|
117
|
-
unsigned char push_rbp;
|
118
|
-
unsigned char save_rsp[3];
|
153
|
+
|
119
154
|
unsigned char align_rsp[4];
|
120
155
|
unsigned char mov[2];
|
121
156
|
void *addr;
|
122
157
|
unsigned char call[2];
|
158
|
+
|
123
159
|
unsigned char leave;
|
124
|
-
|
125
|
-
unsigned char
|
126
|
-
|
160
|
+
|
161
|
+
unsigned char pop_r11[2];
|
162
|
+
unsigned char pop_r10[2];
|
163
|
+
unsigned char pop_r9[2];
|
164
|
+
unsigned char pop_r8[2];
|
165
|
+
unsigned char pop_rdi;
|
166
|
+
unsigned char pop_rsi;
|
167
|
+
unsigned char pop_rdx;
|
168
|
+
unsigned char pop_rcx;
|
169
|
+
unsigned char pop_rax;
|
170
|
+
} __attribute__((__packed__)) frame;
|
127
171
|
|
128
172
|
unsigned char jmp;
|
129
173
|
uint32_t jmp_displacement;
|
@@ -134,19 +178,33 @@ static struct inline_tramp_st2_entry {
|
|
134
178
|
.mov_displacement = 0,
|
135
179
|
|
136
180
|
.frame = {
|
181
|
+
.push_rax = 0x50,
|
182
|
+
.push_rcx = 0x51,
|
183
|
+
.push_rdx = 0x52,
|
184
|
+
.push_rsi = 0x56,
|
137
185
|
.push_rdi = 0x57,
|
186
|
+
.push_r8 = {0x41, 0x50},
|
187
|
+
.push_r9 = {0x41, 0x51},
|
188
|
+
.push_r10 = {0x41, 0x52},
|
189
|
+
.push_r11 = {0x41, 0x53},
|
190
|
+
.push_rbp = 0x55,
|
191
|
+
.mov_rsp_rbp = {0x48, 0x89, 0xe5},
|
138
192
|
.mov_rdi = {0x48, 0x8b, 0x3d},
|
139
193
|
.rdi_source_displacement = 0,
|
140
|
-
.push_rbx = 0x53,
|
141
|
-
.push_rbp = 0x55,
|
142
|
-
.save_rsp = {0x48, 0x89, 0xe5},
|
143
194
|
.align_rsp = {0x48, 0x83, 0xe4, 0xf0},
|
144
|
-
.mov = {0x48,
|
195
|
+
.mov = {0x48, 0xb9},
|
145
196
|
.addr = 0,
|
146
|
-
.call = {0xff,
|
197
|
+
.call = {0xff, 0xd1},
|
147
198
|
.leave = 0xc9,
|
148
|
-
.
|
149
|
-
.
|
199
|
+
.pop_r11 = {0x41, 0x5b},
|
200
|
+
.pop_r10 = {0x41, 0x5a},
|
201
|
+
.pop_r9 = {0x41, 0x59},
|
202
|
+
.pop_r8 = {0x41, 0x58},
|
203
|
+
.pop_rdi = 0x5f,
|
204
|
+
.pop_rsi = 0x5e,
|
205
|
+
.pop_rdx = 0x5a,
|
206
|
+
.pop_rcx = 0x59,
|
207
|
+
.pop_rax = 0x58,
|
150
208
|
},
|
151
209
|
|
152
210
|
.jmp = 0xe9,
|
data/memprof.gemspec
CHANGED
metadata
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memprof
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
segments:
|
6
|
-
- 0
|
7
|
-
- 2
|
8
|
-
- 6
|
9
|
-
version: 0.2.6
|
4
|
+
version: 0.2.7
|
10
5
|
platform: ruby
|
11
6
|
authors:
|
12
7
|
- Joe Damato
|
@@ -66,20 +61,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
61
|
requirements:
|
67
62
|
- - ">="
|
68
63
|
- !ruby/object:Gem::Version
|
69
|
-
segments:
|
70
|
-
- 0
|
71
64
|
version: "0"
|
65
|
+
version:
|
72
66
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
67
|
requirements:
|
74
68
|
- - ">="
|
75
69
|
- !ruby/object:Gem::Version
|
76
|
-
segments:
|
77
|
-
- 0
|
78
70
|
version: "0"
|
71
|
+
version:
|
79
72
|
requirements: []
|
80
73
|
|
81
74
|
rubyforge_project:
|
82
|
-
rubygems_version: 1.3.
|
75
|
+
rubygems_version: 1.3.5
|
83
76
|
signing_key:
|
84
77
|
specification_version: 3
|
85
78
|
summary: Ruby Memory Profiler
|