memprof 0.2.7 → 0.2.9
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/README +15 -6
- data/ext/bin_api.h +1 -9
- data/ext/elf.c +398 -81
- data/ext/extconf.rb +2 -0
- data/ext/mach.c +97 -86
- data/ext/memprof.c +267 -167
- data/ext/tramp.c +143 -0
- data/ext/tramp.h +23 -0
- data/ext/util.c +72 -0
- data/ext/util.h +40 -0
- data/ext/x86_64.c +2 -2
- data/lib/memprof/usr2.rb +1 -0
- data/memprof.gemspec +3 -3
- metadata +17 -5
data/ext/extconf.rb
CHANGED
data/ext/mach.c
CHANGED
@@ -2,15 +2,18 @@
|
|
2
2
|
|
3
3
|
#include "bin_api.h"
|
4
4
|
#include "arch.h"
|
5
|
+
#include "util.h"
|
5
6
|
|
7
|
+
#include <assert.h>
|
8
|
+
#include <dlfcn.h>
|
9
|
+
#include <err.h>
|
6
10
|
#include <limits.h>
|
11
|
+
#include <stdio.h>
|
12
|
+
#include <stdlib.h>
|
7
13
|
#include <string.h>
|
8
14
|
#include <sysexits.h>
|
9
15
|
#include <sys/mman.h>
|
10
|
-
#include <stdio.h>
|
11
16
|
#include <sys/stat.h>
|
12
|
-
#include <dlfcn.h>
|
13
|
-
#include <stdlib.h>
|
14
17
|
|
15
18
|
#include <mach-o/dyld.h>
|
16
19
|
#include <mach-o/getsect.h>
|
@@ -18,6 +21,17 @@
|
|
18
21
|
#include <mach-o/ldsyms.h>
|
19
22
|
#include <mach-o/nlist.h>
|
20
23
|
|
24
|
+
struct mach_config {
|
25
|
+
const struct nlist_64 **symbol_table;
|
26
|
+
const char *string_table;
|
27
|
+
uint32_t symbol_count;
|
28
|
+
uint32_t string_table_size;
|
29
|
+
intptr_t image_offset;
|
30
|
+
};
|
31
|
+
|
32
|
+
static struct mach_config mach_config;
|
33
|
+
extern struct memprof_config memprof_config;
|
34
|
+
|
21
35
|
/*
|
22
36
|
* The jmp instructions in the dyld stub table are 6 bytes,
|
23
37
|
* 2 bytes for the instruction and 4 bytes for the offset operand
|
@@ -193,14 +207,20 @@ update_bin_for_mach_header(const struct mach_header *header, intptr_t slide, voi
|
|
193
207
|
|
194
208
|
static int
|
195
209
|
find_dyld_image_index(const struct mach_header_64 *hdr) {
|
196
|
-
|
210
|
+
uint32_t i;
|
197
211
|
|
198
212
|
for (i = 0; i < _dyld_image_count(); i++) {
|
199
213
|
const struct mach_header_64 *tmphdr = (const struct mach_header_64*) _dyld_get_image_header(i);
|
200
214
|
if (hdr == tmphdr)
|
201
215
|
return i;
|
202
216
|
}
|
217
|
+
|
203
218
|
errx(EX_SOFTWARE, "Could not find image index");
|
219
|
+
|
220
|
+
/* this is to quiet a GCC warning. might be a bug because errx is marked
|
221
|
+
* __dead2/noreturn
|
222
|
+
*/
|
223
|
+
return -1;
|
204
224
|
}
|
205
225
|
|
206
226
|
/*
|
@@ -251,7 +271,7 @@ get_ruby_file_and_header_index(int *index) {
|
|
251
271
|
|
252
272
|
/*
|
253
273
|
* This function compares two nlist_64 structures by their n_value field (address, usually).
|
254
|
-
* It is used by qsort in
|
274
|
+
* It is used by qsort in extract_symbol_table.
|
255
275
|
*/
|
256
276
|
|
257
277
|
static int
|
@@ -268,16 +288,19 @@ nlist_cmp(const void *obj1, const void *obj2) {
|
|
268
288
|
}
|
269
289
|
|
270
290
|
/*
|
271
|
-
* This function
|
272
|
-
*
|
273
|
-
* passed uint32_t pointers nsyms and stroff to those fields found in the symtab_command structure.
|
291
|
+
* This function sets the passed pointers to a buffer containing the nlist_64 entries,
|
292
|
+
* a buffer containing the string data, the number of entries, and the size of the string buffer.
|
274
293
|
*
|
275
|
-
*
|
294
|
+
* The string names of symbols are stored separately from the symbol table.
|
295
|
+
* The symbol table entries contain a string 'index', which is an offset into this region.
|
296
|
+
*
|
297
|
+
* !!! This function allocates memory. symbol_table and string_table should be freed when no longer used !!!
|
276
298
|
*/
|
277
299
|
|
278
|
-
static
|
279
|
-
|
280
|
-
const struct nlist_64 **
|
300
|
+
static void
|
301
|
+
extract_symbol_table(const struct mach_header_64 *hdr, const struct nlist_64 ***symbol_table, const char **string_table, uint32_t *symbol_count, uint32_t *strsize) {
|
302
|
+
const struct nlist_64 **new_symtbl;
|
303
|
+
char *new_strtbl;
|
281
304
|
uint32_t i, j;
|
282
305
|
|
283
306
|
const char *lc = (const char*) hdr + sizeof(struct mach_header_64);
|
@@ -285,19 +308,23 @@ build_sorted_nlist_table(const struct mach_header_64 *hdr, uint32_t *nsyms, uint
|
|
285
308
|
for (i = 0; i < hdr->ncmds; i++) {
|
286
309
|
if (((const struct load_command*)lc)->cmd == LC_SYMTAB) {
|
287
310
|
const struct symtab_command *sc = (const struct symtab_command*) lc;
|
288
|
-
const struct nlist_64 *
|
311
|
+
const struct nlist_64 *file_symtbl = (const struct nlist_64*)((const char*)hdr + sc->symoff);
|
312
|
+
|
313
|
+
new_symtbl = malloc(sc->nsyms * sizeof(struct nlist_64*));
|
314
|
+
new_strtbl = malloc(sc->strsize);
|
289
315
|
|
290
|
-
|
291
|
-
base = malloc(sc->nsyms * sizeof(struct nlist_64*));
|
316
|
+
memcpy(new_strtbl, (char*)hdr + sc->stroff, sc->strsize);
|
292
317
|
|
293
318
|
for (j = 0; j < sc->nsyms; j++)
|
294
|
-
|
319
|
+
new_symtbl[j] = file_symtbl + j;
|
295
320
|
|
296
|
-
qsort(
|
321
|
+
qsort(new_symtbl, sc->nsyms, sizeof(struct nlist_64*), &nlist_cmp);
|
297
322
|
|
298
|
-
*
|
299
|
-
*
|
300
|
-
|
323
|
+
*symbol_table = new_symtbl;
|
324
|
+
*string_table = new_strtbl;
|
325
|
+
*symbol_count = sc->nsyms;
|
326
|
+
*strsize = sc->strsize;
|
327
|
+
return;
|
301
328
|
}
|
302
329
|
|
303
330
|
lc += ((const struct load_command*)lc)->cmdsize;
|
@@ -306,51 +333,36 @@ build_sorted_nlist_table(const struct mach_header_64 *hdr, uint32_t *nsyms, uint
|
|
306
333
|
}
|
307
334
|
|
308
335
|
/*
|
309
|
-
*
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
*
|
321
|
-
* (offset at which the image was loaded into the process) to the table entry's address.
|
336
|
+
* Return the string at the given offset into the symbol table's string buffer
|
337
|
+
*/
|
338
|
+
|
339
|
+
static inline const char*
|
340
|
+
get_symtab_string(uint32_t stroff) {
|
341
|
+
assert(mach_config.string_table != NULL);
|
342
|
+
assert(stroff < mach_config.string_table_size);
|
343
|
+
return mach_config.string_table + stroff;
|
344
|
+
}
|
345
|
+
|
346
|
+
/*
|
347
|
+
* Return the address and size of a symbol given it's name
|
322
348
|
*/
|
323
349
|
|
324
350
|
void *
|
325
351
|
bin_find_symbol(const char *symbol, size_t *size) {
|
326
352
|
void *ptr = NULL;
|
327
|
-
|
328
|
-
|
329
|
-
uint32_t i, j, k;
|
330
|
-
uint32_t stroff, nsyms = 0;
|
331
|
-
int index = 0;
|
353
|
+
uint32_t i, j;
|
332
354
|
|
333
|
-
|
355
|
+
assert(mach_config.symbol_table != NULL);
|
356
|
+
assert(mach_config.symbol_count > 0);
|
334
357
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
const struct nlist_64 **nlist_table = build_sorted_nlist_table(hdr, &nsyms, &stroff);
|
340
|
-
/*
|
341
|
-
* The string names of symbols are stored separately from the symbol table.
|
342
|
-
* The symbol table entries contain a string 'index', which is an offset into this region.
|
343
|
-
*/
|
344
|
-
const char *string_table = (const char*)hdr + stroff;
|
345
|
-
|
346
|
-
for (i=0; i < nsyms; i++) {
|
347
|
-
const struct nlist_64 *nlist_entry = nlist_table[i];
|
348
|
-
const char *string = string_table + nlist_entry->n_un.n_strx;
|
358
|
+
for (i=0; i < mach_config.symbol_count; i++) {
|
359
|
+
const struct nlist_64 *nlist_entry = mach_config.symbol_table[i];
|
360
|
+
const char *string = get_symtab_string(nlist_entry->n_un.n_strx);
|
349
361
|
|
350
362
|
if (string && strcmp(symbol, string+1) == 0) {
|
351
363
|
const uint64_t addr = nlist_entry->n_value;
|
352
364
|
/* Add the slide to get the *real* address in the process. */
|
353
|
-
ptr = (void*)(addr +
|
365
|
+
ptr = (void*)(addr + mach_config.image_offset);
|
354
366
|
|
355
367
|
if (size) {
|
356
368
|
const struct nlist_64 *next_entry = NULL;
|
@@ -362,7 +374,7 @@ bin_find_symbol(const char *symbol, size_t *size) {
|
|
362
374
|
*/
|
363
375
|
j = 1;
|
364
376
|
while (next_entry == NULL) {
|
365
|
-
const struct nlist_64 *tmp_entry =
|
377
|
+
const struct nlist_64 *tmp_entry = mach_config.symbol_table[i + j];
|
366
378
|
if (nlist_entry->n_value != tmp_entry->n_value)
|
367
379
|
next_entry = tmp_entry;
|
368
380
|
j++;
|
@@ -378,9 +390,6 @@ bin_find_symbol(const char *symbol, size_t *size) {
|
|
378
390
|
break;
|
379
391
|
}
|
380
392
|
}
|
381
|
-
|
382
|
-
free(nlist_table);
|
383
|
-
free(file);
|
384
393
|
return ptr;
|
385
394
|
}
|
386
395
|
|
@@ -390,33 +399,18 @@ bin_find_symbol(const char *symbol, size_t *size) {
|
|
390
399
|
const char *
|
391
400
|
bin_find_symbol_name(void *symbol) {
|
392
401
|
const char *name = NULL;
|
393
|
-
|
394
|
-
void *file = NULL;
|
395
|
-
|
396
|
-
uint32_t i, j, k;
|
397
|
-
uint32_t stroff, nsyms = 0;
|
398
|
-
int index = 0;
|
402
|
+
uint32_t i;
|
399
403
|
|
400
|
-
|
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;
|
404
|
+
assert(mach_config.symbol_table != NULL);
|
405
|
+
assert(mach_config.symbol_count > 0);
|
412
406
|
|
413
|
-
for (i=0; i <
|
414
|
-
const struct nlist_64 *nlist_entry =
|
415
|
-
const char *string =
|
407
|
+
for (i=0; i < mach_config.symbol_count; i++) {
|
408
|
+
const struct nlist_64 *nlist_entry = mach_config.symbol_table[i];
|
409
|
+
const char *string = get_symtab_string(nlist_entry->n_un.n_strx);
|
416
410
|
|
417
411
|
const uint64_t addr = nlist_entry->n_value;
|
418
412
|
/* Add the slide to get the *real* address in the process. */
|
419
|
-
void *ptr = (void*)(addr +
|
413
|
+
void *ptr = (void*)(addr + mach_config.image_offset);
|
420
414
|
|
421
415
|
if (ptr == symbol) {
|
422
416
|
name = string+1;
|
@@ -424,8 +418,6 @@ bin_find_symbol_name(void *symbol) {
|
|
424
418
|
}
|
425
419
|
}
|
426
420
|
|
427
|
-
free(nlist_table);
|
428
|
-
free(file);
|
429
421
|
return name;
|
430
422
|
}
|
431
423
|
|
@@ -471,12 +463,12 @@ bin_allocate_page()
|
|
471
463
|
void *ret = NULL;
|
472
464
|
size_t i = 0;
|
473
465
|
|
474
|
-
for (i = pagesize; i < INT_MAX - pagesize; i += pagesize) {
|
475
|
-
ret = mmap((void*)(NULL + i), pagesize, PROT_WRITE|PROT_READ|PROT_EXEC,
|
466
|
+
for (i = memprof_config.pagesize; i < INT_MAX - memprof_config.pagesize; i += memprof_config.pagesize) {
|
467
|
+
ret = mmap((void*)(NULL + i), memprof_config.pagesize, PROT_WRITE|PROT_READ|PROT_EXEC,
|
476
468
|
MAP_ANON|MAP_PRIVATE, -1, 0);
|
477
469
|
|
478
470
|
if (ret != MAP_FAILED) {
|
479
|
-
memset(ret, 0x90, pagesize);
|
471
|
+
memset(ret, 0x90, memprof_config.pagesize);
|
480
472
|
return ret;
|
481
473
|
}
|
482
474
|
}
|
@@ -498,6 +490,25 @@ bin_type_member_offset(const char *type, const char *member)
|
|
498
490
|
void
|
499
491
|
bin_init()
|
500
492
|
{
|
501
|
-
|
493
|
+
void *file = NULL;
|
494
|
+
int index = 0;
|
495
|
+
|
496
|
+
memset(&mach_config, 0, sizeof(struct mach_config));
|
497
|
+
|
498
|
+
file = get_ruby_file_and_header_index(&index);
|
499
|
+
|
500
|
+
const struct mach_header_64 *hdr = (const struct mach_header_64*) file;
|
501
|
+
if (hdr->magic != MH_MAGIC_64)
|
502
|
+
errx(EX_SOFTWARE, "Magic for Ruby Mach-O file doesn't match");
|
503
|
+
|
504
|
+
mach_config.image_offset = _dyld_get_image_vmaddr_slide(index);
|
505
|
+
|
506
|
+
extract_symbol_table(hdr, &mach_config.symbol_table, &mach_config.string_table, &mach_config.symbol_count, &mach_config.string_table_size);
|
507
|
+
|
508
|
+
assert(mach_config.symbol_table != NULL);
|
509
|
+
assert(mach_config.string_table != NULL);
|
510
|
+
assert(mach_config.symbol_count > 0);
|
511
|
+
|
512
|
+
free(file);
|
502
513
|
}
|
503
514
|
#endif
|
data/ext/memprof.c
CHANGED
@@ -4,17 +4,15 @@
|
|
4
4
|
#define _GNU_SOURCE
|
5
5
|
#endif
|
6
6
|
|
7
|
-
#include <err.h>
|
8
7
|
#include <fcntl.h>
|
9
8
|
#include <stddef.h>
|
10
9
|
#include <stdio.h>
|
11
10
|
#include <stdint.h>
|
12
11
|
#include <stdlib.h>
|
13
12
|
#include <unistd.h>
|
14
|
-
#include <sysexits.h>
|
15
|
-
#include <sys/mman.h>
|
16
|
-
#include <err.h>
|
17
13
|
#include <assert.h>
|
14
|
+
#include <err.h>
|
15
|
+
#include <sysexits.h>
|
18
16
|
|
19
17
|
#include <st.h>
|
20
18
|
#include <intern.h>
|
@@ -22,22 +20,10 @@
|
|
22
20
|
|
23
21
|
#include "arch.h"
|
24
22
|
#include "bin_api.h"
|
23
|
+
#include "tramp.h"
|
24
|
+
#include "util.h"
|
25
25
|
|
26
26
|
|
27
|
-
size_t pagesize;
|
28
|
-
|
29
|
-
/*
|
30
|
-
trampoline specific stuff
|
31
|
-
*/
|
32
|
-
struct tramp_st2_entry *tramp_table = NULL;
|
33
|
-
size_t tramp_size = 0;
|
34
|
-
|
35
|
-
/*
|
36
|
-
inline trampoline specific stuff
|
37
|
-
*/
|
38
|
-
size_t inline_tramp_size = 0;
|
39
|
-
struct inline_tramp_st2_entry *inline_tramp_table = NULL;
|
40
|
-
|
41
27
|
/*
|
42
28
|
* bleak_house stuff
|
43
29
|
*/
|
@@ -45,6 +31,8 @@ static VALUE eUnsupported;
|
|
45
31
|
static int track_objs = 0;
|
46
32
|
static st_table *objs = NULL;
|
47
33
|
|
34
|
+
struct memprof_config memprof_config;
|
35
|
+
|
48
36
|
struct obj_track {
|
49
37
|
VALUE obj;
|
50
38
|
char *source;
|
@@ -54,6 +42,7 @@ struct obj_track {
|
|
54
42
|
static VALUE gc_hook;
|
55
43
|
static void **ptr_to_rb_mark_table_add_filename = NULL;
|
56
44
|
static void (*rb_mark_table_add_filename)(char*);
|
45
|
+
static void (*rb_add_freelist)(VALUE);
|
57
46
|
|
58
47
|
static int
|
59
48
|
ree_sourcefile_mark_each(st_data_t key, st_data_t val, st_data_t arg)
|
@@ -106,7 +95,8 @@ newobj_tramp()
|
|
106
95
|
tracker = malloc(sizeof(*tracker));
|
107
96
|
|
108
97
|
if (tracker) {
|
109
|
-
if (ruby_current_node && ruby_current_node->nd_file &&
|
98
|
+
if (ruby_current_node && ruby_current_node->nd_file &&
|
99
|
+
*ruby_current_node->nd_file) {
|
110
100
|
tracker->source = ruby_current_node->nd_file;
|
111
101
|
tracker->line = nd_line(ruby_current_node);
|
112
102
|
} else if (ruby_sourcefile) {
|
@@ -122,15 +112,14 @@ newobj_tramp()
|
|
122
112
|
st_insert(objs, (st_data_t)ret, (st_data_t)tracker);
|
123
113
|
rb_gc_enable();
|
124
114
|
} else {
|
125
|
-
fprintf(stderr, "Warning, unable to allocate a tracker.
|
115
|
+
fprintf(stderr, "Warning, unable to allocate a tracker. "
|
116
|
+
"You are running dangerously low on RAM!\n");
|
126
117
|
}
|
127
118
|
}
|
128
119
|
|
129
120
|
return ret;
|
130
121
|
}
|
131
122
|
|
132
|
-
static void (*rb_add_freelist)(VALUE);
|
133
|
-
|
134
123
|
static void
|
135
124
|
freelist_tramp(unsigned long rval)
|
136
125
|
{
|
@@ -199,7 +188,7 @@ objs_tabulate(st_data_t key, st_data_t record, st_data_t arg)
|
|
199
188
|
|
200
189
|
struct results {
|
201
190
|
char **entries;
|
202
|
-
|
191
|
+
size_t num_entries;
|
203
192
|
};
|
204
193
|
|
205
194
|
static int
|
@@ -251,7 +240,7 @@ memprof_stats(int argc, VALUE *argv, VALUE self)
|
|
251
240
|
{
|
252
241
|
st_table *tmp_table;
|
253
242
|
struct results res;
|
254
|
-
|
243
|
+
size_t i;
|
255
244
|
VALUE str;
|
256
245
|
FILE *out = NULL;
|
257
246
|
|
@@ -318,7 +307,7 @@ memprof_track(int argc, VALUE *argv, VALUE self)
|
|
318
307
|
#include "env.h"
|
319
308
|
#include "re.h"
|
320
309
|
|
321
|
-
yajl_gen_status
|
310
|
+
static yajl_gen_status
|
322
311
|
yajl_gen_cstr(yajl_gen gen, const char * str)
|
323
312
|
{
|
324
313
|
if (!str || str[0] == 0)
|
@@ -327,7 +316,7 @@ yajl_gen_cstr(yajl_gen gen, const char * str)
|
|
327
316
|
return yajl_gen_string(gen, (unsigned char *)str, strlen(str));
|
328
317
|
}
|
329
318
|
|
330
|
-
yajl_gen_status
|
319
|
+
static yajl_gen_status
|
331
320
|
yajl_gen_format(yajl_gen gen, char *format, ...)
|
332
321
|
{
|
333
322
|
va_list args;
|
@@ -346,7 +335,7 @@ yajl_gen_format(yajl_gen gen, char *format, ...)
|
|
346
335
|
return ret;
|
347
336
|
}
|
348
337
|
|
349
|
-
yajl_gen_status
|
338
|
+
static yajl_gen_status
|
350
339
|
yajl_gen_value(yajl_gen gen, VALUE obj)
|
351
340
|
{
|
352
341
|
if (FIXNUM_P(obj))
|
@@ -392,7 +381,7 @@ each_ivar(st_data_t key, st_data_t record, st_data_t arg)
|
|
392
381
|
return ST_CONTINUE;
|
393
382
|
}
|
394
383
|
|
395
|
-
char *
|
384
|
+
static char *
|
396
385
|
nd_type_str(VALUE obj)
|
397
386
|
{
|
398
387
|
switch(nd_type(obj)) {
|
@@ -438,7 +427,7 @@ static VALUE (*rb_classname)(VALUE);
|
|
438
427
|
* print details on different types of nodes (nd_next, nd_lit, nd_nth, etc)
|
439
428
|
*/
|
440
429
|
|
441
|
-
void
|
430
|
+
static void
|
442
431
|
obj_dump(VALUE obj, yajl_gen gen)
|
443
432
|
{
|
444
433
|
int type;
|
@@ -529,7 +518,7 @@ obj_dump(VALUE obj, yajl_gen gen)
|
|
529
518
|
|
530
519
|
struct SCOPE *scope = (struct SCOPE *)obj;
|
531
520
|
if (scope->local_tbl) {
|
532
|
-
int i =
|
521
|
+
int i = 0;
|
533
522
|
int n = scope->local_tbl[0];
|
534
523
|
VALUE *list = &scope->local_vars[-1];
|
535
524
|
VALUE cur = *list++;
|
@@ -542,9 +531,13 @@ obj_dump(VALUE obj, yajl_gen gen)
|
|
542
531
|
yajl_gen_map_open(gen);
|
543
532
|
while (n--) {
|
544
533
|
cur = *list++;
|
534
|
+
i++;
|
535
|
+
|
536
|
+
if (!rb_is_local_id(scope->local_tbl[i]))
|
537
|
+
continue;
|
538
|
+
|
545
539
|
yajl_gen_cstr(gen, scope->local_tbl[i] == 95 ? "_" : rb_id2name(scope->local_tbl[i]));
|
546
540
|
yajl_gen_value(gen, cur);
|
547
|
-
i++;
|
548
541
|
}
|
549
542
|
yajl_gen_map_close(gen);
|
550
543
|
}
|
@@ -566,9 +559,84 @@ obj_dump(VALUE obj, yajl_gen gen)
|
|
566
559
|
yajl_gen_cstr(gen, "node_code");
|
567
560
|
yajl_gen_integer(gen, nd_type(obj));
|
568
561
|
|
569
|
-
|
570
|
-
|
562
|
+
#define PRINT_ID(sub) yajl_gen_format(gen, ":%s", rb_id2name(RNODE(obj)->sub.id));
|
563
|
+
#define PRINT_VAL(sub) yajl_gen_value(gen, RNODE(obj)->sub.value);
|
564
|
+
|
565
|
+
int nd_type = nd_type(obj);
|
566
|
+
yajl_gen_cstr(gen, "n1");
|
567
|
+
switch(nd_type) {
|
568
|
+
case NODE_LVAR:
|
569
|
+
case NODE_DVAR:
|
570
|
+
case NODE_IVAR:
|
571
|
+
case NODE_CVAR:
|
572
|
+
case NODE_GVAR:
|
573
|
+
case NODE_CONST:
|
574
|
+
case NODE_ATTRSET:
|
575
|
+
case NODE_LASGN:
|
576
|
+
case NODE_IASGN:
|
577
|
+
case NODE_DASGN:
|
578
|
+
case NODE_CVASGN:
|
579
|
+
case NODE_CVDECL:
|
580
|
+
case NODE_GASGN:
|
581
|
+
case NODE_DASGN_CURR:
|
582
|
+
case NODE_BLOCK_ARG:
|
583
|
+
PRINT_ID(u1);
|
571
584
|
break;
|
585
|
+
|
586
|
+
case NODE_SCOPE: {
|
587
|
+
ID *tbl = RNODE(obj)->nd_tbl;
|
588
|
+
yajl_gen_array_open(gen);
|
589
|
+
if (tbl) {
|
590
|
+
int size = tbl[0];
|
591
|
+
int i = 3;
|
592
|
+
|
593
|
+
for (; i < size+1; i++) {
|
594
|
+
yajl_gen_cstr(gen, tbl[i] == 95 ? "_" : rb_id2name(tbl[i]));
|
595
|
+
}
|
596
|
+
}
|
597
|
+
yajl_gen_array_close(gen);
|
598
|
+
break;
|
599
|
+
}
|
600
|
+
|
601
|
+
case NODE_CFUNC: {
|
602
|
+
const char *name = bin_find_symbol_name((void*)RNODE(obj)->u1.value);
|
603
|
+
yajl_gen_format(gen, "0x%x: %s", RNODE(obj)->u1.value, name ? name : "???");
|
604
|
+
break;
|
605
|
+
}
|
606
|
+
|
607
|
+
default:
|
608
|
+
PRINT_VAL(u1);
|
609
|
+
}
|
610
|
+
|
611
|
+
yajl_gen_cstr(gen, "n2");
|
612
|
+
switch(nd_type) {
|
613
|
+
case NODE_CALL:
|
614
|
+
case NODE_FBODY:
|
615
|
+
case NODE_DEFN:
|
616
|
+
case NODE_ATTRASGN:
|
617
|
+
case NODE_FCALL:
|
618
|
+
case NODE_VCALL:
|
619
|
+
case NODE_COLON2:
|
620
|
+
case NODE_COLON3:
|
621
|
+
PRINT_ID(u2);
|
622
|
+
break;
|
623
|
+
|
624
|
+
case NODE_NTH_REF:
|
625
|
+
yajl_gen_integer(gen, RNODE(obj)->u2.argc);
|
626
|
+
break;
|
627
|
+
|
628
|
+
default:
|
629
|
+
PRINT_VAL(u2);
|
630
|
+
}
|
631
|
+
|
632
|
+
yajl_gen_cstr(gen, "n3");
|
633
|
+
switch(nd_type) {
|
634
|
+
case NODE_ARGS:
|
635
|
+
yajl_gen_integer(gen, RNODE(obj)->u3.cnt);
|
636
|
+
break;
|
637
|
+
|
638
|
+
default:
|
639
|
+
PRINT_VAL(u3);
|
572
640
|
}
|
573
641
|
break;
|
574
642
|
|
@@ -737,7 +805,17 @@ objs_each_dump(st_data_t key, st_data_t record, st_data_t arg)
|
|
737
805
|
return ST_CONTINUE;
|
738
806
|
}
|
739
807
|
|
740
|
-
|
808
|
+
extern st_table *rb_global_tbl;
|
809
|
+
|
810
|
+
static int
|
811
|
+
globals_each_dump(st_data_t key, st_data_t record, st_data_t arg)
|
812
|
+
{
|
813
|
+
yajl_gen_cstr((yajl_gen)arg, rb_id2name((ID)key));
|
814
|
+
yajl_gen_value((yajl_gen)arg, rb_gvar_get((void*)record));
|
815
|
+
return ST_CONTINUE;
|
816
|
+
}
|
817
|
+
|
818
|
+
static void
|
741
819
|
json_print(void *ctx, const char * str, unsigned int len)
|
742
820
|
{
|
743
821
|
FILE *out = (FILE *)ctx;
|
@@ -786,28 +864,19 @@ memprof_dump(int argc, VALUE *argv, VALUE self)
|
|
786
864
|
static VALUE
|
787
865
|
memprof_dump_all(int argc, VALUE *argv, VALUE self)
|
788
866
|
{
|
789
|
-
|
790
|
-
|
867
|
+
if (memprof_config.heaps == NULL ||
|
868
|
+
memprof_config.heaps_used == NULL ||
|
869
|
+
memprof_config.sizeof_RVALUE == 0 ||
|
870
|
+
memprof_config.sizeof_heaps_slot == 0 ||
|
871
|
+
memprof_config.offset_heaps_slot_slot == -1 ||
|
872
|
+
memprof_config.offset_heaps_slot_limit == -1)
|
873
|
+
rb_raise(eUnsupported, "not enough config data to dump heap");
|
791
874
|
|
792
|
-
|
793
|
-
|
794
|
-
#endif
|
795
|
-
#ifndef sizeof__heaps_slot
|
796
|
-
size_t sizeof__heaps_slot = bin_type_size("heaps_slot");
|
797
|
-
#endif
|
798
|
-
#ifndef offset__heaps_slot__limit
|
799
|
-
int offset__heaps_slot__limit = bin_type_member_offset("heaps_slot", "limit");
|
800
|
-
#endif
|
801
|
-
#ifndef offset__heaps_slot__slot
|
802
|
-
int offset__heaps_slot__slot = bin_type_member_offset("heaps_slot", "slot");
|
803
|
-
#endif
|
875
|
+
char *heaps = *(char**)memprof_config.heaps;
|
876
|
+
int heaps_used = *(int*)memprof_config.heaps_used;
|
804
877
|
|
805
878
|
char *p, *pend;
|
806
879
|
int i, limit;
|
807
|
-
|
808
|
-
if (sizeof__RVALUE == 0 || sizeof__heaps_slot == 0)
|
809
|
-
rb_raise(eUnsupported, "could not find internal heap");
|
810
|
-
|
811
880
|
VALUE str;
|
812
881
|
FILE *out = NULL;
|
813
882
|
|
@@ -826,10 +895,26 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
|
|
826
895
|
|
827
896
|
//yajl_gen_array_open(gen);
|
828
897
|
|
898
|
+
yajl_gen_map_open(gen);
|
899
|
+
|
900
|
+
yajl_gen_cstr(gen, "_id");
|
901
|
+
yajl_gen_cstr(gen, "globals");
|
902
|
+
|
903
|
+
yajl_gen_cstr(gen, "type");
|
904
|
+
yajl_gen_cstr(gen, "globals");
|
905
|
+
|
906
|
+
yajl_gen_cstr(gen, "variables");
|
907
|
+
|
908
|
+
yajl_gen_map_open(gen);
|
909
|
+
st_foreach(rb_global_tbl, globals_each_dump, (st_data_t)gen);
|
910
|
+
yajl_gen_map_close(gen);
|
911
|
+
|
912
|
+
yajl_gen_map_close(gen);
|
913
|
+
|
829
914
|
for (i=0; i < heaps_used; i++) {
|
830
|
-
p = *(char**)(heaps + (i *
|
831
|
-
limit = *(int*)(heaps + (i *
|
832
|
-
pend = p + (
|
915
|
+
p = *(char**)(heaps + (i * memprof_config.sizeof_heaps_slot) + memprof_config.offset_heaps_slot_slot);
|
916
|
+
limit = *(int*)(heaps + (i * memprof_config.sizeof_heaps_slot) + memprof_config.offset_heaps_slot_limit);
|
917
|
+
pend = p + (memprof_config.sizeof_RVALUE * limit);
|
833
918
|
|
834
919
|
while (p < pend) {
|
835
920
|
if (RBASIC(p)->flags) {
|
@@ -841,7 +926,7 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
|
|
841
926
|
while(fputc('\n', out ? out : stdout) == EOF);
|
842
927
|
}
|
843
928
|
|
844
|
-
p +=
|
929
|
+
p += memprof_config.sizeof_RVALUE;
|
845
930
|
}
|
846
931
|
}
|
847
932
|
|
@@ -858,122 +943,134 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
|
|
858
943
|
}
|
859
944
|
|
860
945
|
static void
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
void *ent = arch_get_st2_tramp(&tramp_sz);
|
868
|
-
void *inline_ent = arch_get_inline_st2_tramp(&inline_tramp_sz);
|
869
|
-
|
870
|
-
if ((region = bin_allocate_page()) == MAP_FAILED)
|
871
|
-
errx(EX_SOFTWARE, "Failed to allocate memory for stage 1 trampolines.");
|
872
|
-
|
873
|
-
tramp_table = region;
|
874
|
-
inline_tramp_table = region + pagesize/2;
|
875
|
-
|
876
|
-
for (i = 0; i < (pagesize/2)/tramp_sz; i++) {
|
877
|
-
memcpy(tramp_table + i, ent, tramp_sz);
|
878
|
-
}
|
879
|
-
|
880
|
-
for (i = 0; i < (pagesize/2)/inline_tramp_sz; i++) {
|
881
|
-
memcpy(inline_tramp_table + i, inline_ent, inline_tramp_sz);
|
882
|
-
}
|
946
|
+
init_memprof_config_base() {
|
947
|
+
memset(&memprof_config, 0, sizeof(memprof_config));
|
948
|
+
memprof_config.offset_heaps_slot_limit = -1;
|
949
|
+
memprof_config.offset_heaps_slot_slot = -1;
|
950
|
+
memprof_config.pagesize = getpagesize();
|
951
|
+
assert(memprof_config.pagesize);
|
883
952
|
}
|
884
953
|
|
885
|
-
#define FREELIST_INLINES (3)
|
886
|
-
|
887
954
|
static void
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
955
|
+
init_memprof_config_extended() {
|
956
|
+
/* If we don't have add_freelist, find the functions it gets inlined into */
|
957
|
+
memprof_config.add_freelist = bin_find_symbol("add_freelist", NULL);
|
958
|
+
|
959
|
+
/*
|
960
|
+
* Sometimes gc_sweep gets inlined in garbage_collect
|
961
|
+
* (e.g., on REE it gets inlined into garbage_collect_0).
|
962
|
+
*/
|
963
|
+
if (memprof_config.add_freelist == NULL) {
|
964
|
+
memprof_config.gc_sweep = bin_find_symbol("gc_sweep",
|
965
|
+
&memprof_config.gc_sweep_size);
|
966
|
+
if (memprof_config.gc_sweep == NULL)
|
967
|
+
memprof_config.gc_sweep = bin_find_symbol("garbage_collect_0",
|
968
|
+
&memprof_config.gc_sweep_size);
|
969
|
+
if (memprof_config.gc_sweep == NULL)
|
970
|
+
memprof_config.gc_sweep = bin_find_symbol("garbage_collect",
|
971
|
+
&memprof_config.gc_sweep_size);
|
972
|
+
|
973
|
+
memprof_config.finalize_list = bin_find_symbol("finalize_list",
|
974
|
+
&memprof_config.finalize_list_size);
|
975
|
+
memprof_config.rb_gc_force_recycle = bin_find_symbol("rb_gc_force_recycle",
|
976
|
+
&memprof_config.rb_gc_force_recycle_size);
|
977
|
+
memprof_config.freelist = bin_find_symbol("freelist", NULL);
|
906
978
|
}
|
907
979
|
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
/* older patchlevels of 1.8.7 don't have an add_freelist(), but the instruction should be the same */
|
935
|
-
tramps_completed++;
|
936
|
-
i++;
|
937
|
-
byte = freelist_inliners[i];
|
938
|
-
continue;
|
939
|
-
}
|
980
|
+
memprof_config.classname = bin_find_symbol("classname", NULL);
|
981
|
+
memprof_config.rb_mark_table_add_filename = bin_find_symbol("rb_mark_table_add_filename", NULL);
|
982
|
+
|
983
|
+
/* Stuff for dumping the heap */
|
984
|
+
memprof_config.heaps = bin_find_symbol("heaps", NULL);
|
985
|
+
memprof_config.heaps_used = bin_find_symbol("heaps_used", NULL);
|
986
|
+
#ifdef sizeof__RVALUE
|
987
|
+
memprof_config.sizeof_RVALUE = sizeof__RVALUE;
|
988
|
+
#else
|
989
|
+
memprof_config.sizeof_RVALUE = bin_type_size("RVALUE");
|
990
|
+
#endif
|
991
|
+
#ifdef sizeof__heaps_slot
|
992
|
+
memprof_config.sizeof_heaps_slot = sizeof__heaps_slot;
|
993
|
+
#else
|
994
|
+
memprof_config.sizeof_heaps_slot = bin_type_size("heaps_slot");
|
995
|
+
#endif
|
996
|
+
#ifdef offset__heaps_slot__limit
|
997
|
+
memprof_config.offset_heaps_slot_limit = offset__heaps_slot__limit;
|
998
|
+
#else
|
999
|
+
memprof_config.offset_heaps_slot_limit = bin_type_member_offset("heaps_slot", "limit");
|
1000
|
+
#endif
|
1001
|
+
#ifdef offset__heaps_slot__slot
|
1002
|
+
memprof_config.offset_heaps_slot_slot = offset__heaps_slot__slot;
|
1003
|
+
#else
|
1004
|
+
memprof_config.offset_heaps_slot_slot = bin_type_member_offset("heaps_slot", "slot");
|
1005
|
+
#endif
|
940
1006
|
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
1007
|
+
int heap_errors_printed = 0;
|
1008
|
+
|
1009
|
+
if (memprof_config.heaps == NULL)
|
1010
|
+
heap_errors_printed += fprintf(stderr,
|
1011
|
+
"Failed to locate heaps\n");
|
1012
|
+
if (memprof_config.heaps_used == NULL)
|
1013
|
+
heap_errors_printed += fprintf(stderr,
|
1014
|
+
"Failed to locate heaps_used\n");
|
1015
|
+
if (memprof_config.sizeof_RVALUE == 0)
|
1016
|
+
heap_errors_printed += fprintf(stderr,
|
1017
|
+
"Failed to determine sizeof(RVALUE)\n");
|
1018
|
+
if (memprof_config.sizeof_heaps_slot == 0)
|
1019
|
+
heap_errors_printed += fprintf(stderr,
|
1020
|
+
"Failed to determine sizeof(heaps_slot)\n");
|
1021
|
+
if (memprof_config.offset_heaps_slot_limit == -1)
|
1022
|
+
heap_errors_printed += fprintf(stderr,
|
1023
|
+
"Failed to determine offset of heaps_slot->limit\n");
|
1024
|
+
if (memprof_config.offset_heaps_slot_slot == -1)
|
1025
|
+
heap_errors_printed += fprintf(stderr,
|
1026
|
+
"Failed to determine offset of heaps_slot->slot\n");
|
1027
|
+
|
1028
|
+
if (heap_errors_printed)
|
1029
|
+
fprintf(stderr, "You won't be able to dump your heap!\n");
|
1030
|
+
|
1031
|
+
int errors_printed = 0;
|
1032
|
+
|
1033
|
+
/* If we can't find add_freelist, we need to make sure we located the functions that it gets inlined into. */
|
1034
|
+
if (memprof_config.add_freelist == NULL) {
|
1035
|
+
if (memprof_config.gc_sweep == NULL) {
|
1036
|
+
errors_printed += fprintf(stderr,
|
1037
|
+
"Failed to locate add_freelist (it's probably inlined, but we couldn't find it there either!)\n");
|
1038
|
+
errors_printed += fprintf(stderr,
|
1039
|
+
"Failed to locate gc_sweep, garbage_collect_0, or garbage_collect\n");
|
946
1040
|
}
|
947
|
-
|
1041
|
+
if (memprof_config.gc_sweep_size == 0)
|
1042
|
+
errors_printed += fprintf(stderr,
|
1043
|
+
"Failed to determine the size of gc_sweep/garbage_collect_0/garbage_collect: %ld\n",
|
1044
|
+
memprof_config.gc_sweep_size);
|
1045
|
+
if (memprof_config.finalize_list == NULL)
|
1046
|
+
errors_printed += fprintf(stderr,
|
1047
|
+
"Failed to locate finalize_list\n");
|
1048
|
+
if (memprof_config.finalize_list_size == 0)
|
1049
|
+
errors_printed += fprintf(stderr,
|
1050
|
+
"Failed to determine the size of finalize_list: %ld\n",
|
1051
|
+
memprof_config.finalize_list_size);
|
1052
|
+
if (memprof_config.rb_gc_force_recycle == NULL)
|
1053
|
+
errors_printed += fprintf(stderr,
|
1054
|
+
"Failed to locate rb_gc_force_recycle\n");
|
1055
|
+
if (memprof_config.rb_gc_force_recycle_size == 0)
|
1056
|
+
errors_printed += fprintf(stderr,
|
1057
|
+
"Failed to determine the size of rb_gc_force_recycle: %ld\n",
|
1058
|
+
memprof_config.rb_gc_force_recycle_size);
|
1059
|
+
if (memprof_config.freelist == NULL)
|
1060
|
+
errors_printed += fprintf(stderr,
|
1061
|
+
"Failed to locate freelist\n");
|
948
1062
|
}
|
949
1063
|
|
950
|
-
if (
|
951
|
-
|
952
|
-
|
1064
|
+
if (memprof_config.classname == NULL)
|
1065
|
+
errors_printed += fprintf(stderr,
|
1066
|
+
"Failed to locate classname\n");
|
953
1067
|
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
if (trampee_addr == NULL) {
|
961
|
-
if (strcmp("add_freelist", trampee) == 0) {
|
962
|
-
/* XXX super hack */
|
963
|
-
inline_tramp_size++;
|
964
|
-
hook_freelist(inline_ent);
|
965
|
-
} else {
|
966
|
-
errx(EX_SOFTWARE, "Failed to locate required symbol %s", trampee);
|
967
|
-
}
|
968
|
-
} else {
|
969
|
-
if (strcmp("add_freelist", trampee) == 0) {
|
970
|
-
rb_add_freelist = trampee_addr;
|
971
|
-
}
|
972
|
-
|
973
|
-
tramp_table[tramp_size].addr = tramp;
|
974
|
-
if (bin_update_image(trampee, &tramp_table[tramp_size]) != 0)
|
975
|
-
errx(EX_SOFTWARE, "Failed to insert tramp for %s", trampee);
|
976
|
-
tramp_size++;
|
1068
|
+
if (errors_printed) {
|
1069
|
+
VALUE ruby_build_info = rb_eval_string("require 'rbconfig'; RUBY_DESCRIPTION + '\n' + RbConfig::CONFIG['CFLAGS'];");
|
1070
|
+
/* who knows what could happen */
|
1071
|
+
if (TYPE(ruby_build_info) == T_STRING)
|
1072
|
+
fprintf(stderr, "%s\n", StringValuePtr(ruby_build_info));
|
1073
|
+
errx(EX_SOFTWARE, "Memprof does not have enough data to run. Please email this output to bugs@memprof.com");
|
977
1074
|
}
|
978
1075
|
}
|
979
1076
|
|
@@ -990,21 +1087,24 @@ Init_memprof()
|
|
990
1087
|
rb_define_singleton_method(memprof, "dump", memprof_dump, -1);
|
991
1088
|
rb_define_singleton_method(memprof, "dump_all", memprof_dump_all, -1);
|
992
1089
|
|
993
|
-
pagesize = getpagesize();
|
994
1090
|
objs = st_init_numtable();
|
1091
|
+
init_memprof_config_base();
|
995
1092
|
bin_init();
|
1093
|
+
init_memprof_config_extended();
|
996
1094
|
create_tramp_table();
|
997
|
-
rb_add_freelist = NULL;
|
998
1095
|
|
999
1096
|
gc_hook = Data_Wrap_Struct(rb_cObject, sourcefile_marker, NULL, NULL);
|
1000
1097
|
rb_global_variable(&gc_hook);
|
1001
|
-
|
1098
|
+
|
1099
|
+
rb_classname = memprof_config.classname;
|
1100
|
+
rb_add_freelist = memprof_config.add_freelist;
|
1101
|
+
ptr_to_rb_mark_table_add_filename = memprof_config.rb_mark_table_add_filename;
|
1102
|
+
|
1103
|
+
assert(rb_classname);
|
1002
1104
|
|
1003
1105
|
insert_tramp("rb_newobj", newobj_tramp);
|
1004
1106
|
insert_tramp("add_freelist", freelist_tramp);
|
1005
1107
|
|
1006
|
-
rb_classname = bin_find_symbol("classname", 0);
|
1007
|
-
|
1008
1108
|
if (getenv("MEMPROF"))
|
1009
1109
|
track_objs = 1;
|
1010
1110
|
|