memprof 0.2.7 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -189,6 +189,8 @@ when 'i486'
189
189
  end
190
190
  add_define "_ARCH_#{arch}_"
191
191
 
192
+ add_define "_MEMPROF_DEBUG" if ENV['MEMPROF_DEBUG'] == '1'
193
+
192
194
  if is_elf or is_macho
193
195
  create_makefile('memprof')
194
196
  else
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
- int i;
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 build_sorted_nlist_table.
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 returns an array of pointers to nlist_64 entries in the symbol table
272
- * of the file buffer pointed to by the passed mach header, sorted by address, and sets the
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
- * !!! The pointer returned by this function must be freed !!!
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 const struct nlist_64 **
279
- build_sorted_nlist_table(const struct mach_header_64 *hdr, uint32_t *nsyms, uint32_t *stroff) {
280
- const struct nlist_64 **base;
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 *symbol_table = (const struct nlist_64*)((const char*)hdr + sc->symoff);
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
- /* TODO: qsort this table *IN PLACE* instead of making a copy of it. */
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
- base[j] = symbol_table + j;
319
+ new_symtbl[j] = file_symtbl + j;
295
320
 
296
- qsort(base, sc->nsyms, sizeof(struct nlist_64*), &nlist_cmp);
321
+ qsort(new_symtbl, sc->nsyms, sizeof(struct nlist_64*), &nlist_cmp);
297
322
 
298
- *nsyms = sc->nsyms;
299
- *stroff = sc->stroff;
300
- return base;
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
- * The workflow for bin_find_symbol is as follows:
310
- *
311
- * 1. Prefix the symbol with a "_", since Apple is weird
312
- * 2. Figure out what file we think contains the symbol table (either the executable, or libruby),
313
- * and read it into memory. We have to do this because the symbol table is not loaded
314
- * into the process as part of the normal image loading. While we're doing this,
315
- * figure out the dyld image index of the corresponding *in-process* image.
316
- * 3. Work up a copy of the symbol table, sorted by address.
317
- * 4. Iterate over the sorted table and find the symbol that matches our search name
318
- * 5. Obtain the rough (padded to 16 byte alignment) size of the symbol by subtracting it's
319
- * address from the address of the symbol *after* it.
320
- * 6. Calculate the *real* address of the symbol by adding the image's "slide"
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
- void *file = NULL;
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
- file = get_ruby_file_and_header_index(&index);
355
+ assert(mach_config.symbol_table != NULL);
356
+ assert(mach_config.symbol_count > 0);
334
357
 
335
- const struct mach_header_64 *hdr = (const struct mach_header_64*) file;
336
- if (hdr->magic != MH_MAGIC_64)
337
- errx(EX_SOFTWARE, "Magic for Ruby Mach-O file doesn't match");
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 + _dyld_get_image_vmaddr_slide(index));
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 = nlist_table[i + j];
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
- void *ptr = NULL;
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
- 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;
404
+ assert(mach_config.symbol_table != NULL);
405
+ assert(mach_config.symbol_count > 0);
412
406
 
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;
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 + _dyld_get_image_vmaddr_slide(index));
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
- /* mach-o is so cool it needs no initialization */
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
@@ -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 && *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. You are running dangerously low on RAM!\n");
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
- unsigned long num_entries;
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
- int i;
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 = 1;
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
- switch (nd_type(obj)) {
570
- case NODE_SCOPE:
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
- void
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
- char *heaps = *(char**)bin_find_symbol("heaps",0);
790
- int heaps_used = *(int*)bin_find_symbol("heaps_used",0);
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
- #ifndef sizeof__RVALUE
793
- size_t sizeof__RVALUE = bin_type_size("RVALUE");
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 * sizeof__heaps_slot) + offset__heaps_slot__slot);
831
- limit = *(int*)(heaps + (i * sizeof__heaps_slot) + offset__heaps_slot__limit);
832
- pend = p + (sizeof__RVALUE * limit);
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 += sizeof__RVALUE;
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
- create_tramp_table()
862
- {
863
- int i = 0;
864
- void *region = NULL;
865
- size_t tramp_sz = 0, inline_tramp_sz = 0;
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
- hook_freelist(int entry)
889
- {
890
- size_t sizes[FREELIST_INLINES], i = 0;
891
- void *freelist_inliners[FREELIST_INLINES];
892
- void *freelist = NULL;
893
- unsigned char *byte = NULL;
894
- int tramps_completed = 0;
895
-
896
- freelist_inliners[0] = bin_find_symbol("gc_sweep", &sizes[0]);
897
- /* sometimes gc_sweep gets inlined in garbage_collect */
898
- /* on REE, it gets inlined into garbage_collect_0 */
899
- if (!freelist_inliners[0])
900
- freelist_inliners[0] = bin_find_symbol("garbage_collect_0", &sizes[0]);
901
- if (!freelist_inliners[0])
902
- freelist_inliners[0] = bin_find_symbol("garbage_collect", &sizes[0]);
903
- if (!freelist_inliners[0]) {
904
- /* couldn't find anything containing gc_sweep. */
905
- errx(EX_SOFTWARE, "Couldn't find gc_sweep or garbage_collect!");
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
- freelist_inliners[1] = bin_find_symbol("finalize_list", &sizes[1]);
909
- if (!freelist_inliners[1])
910
- errx(EX_SOFTWARE, "Couldn't find finalize_list!");
911
-
912
- freelist_inliners[2] = bin_find_symbol("rb_gc_force_recycle", &sizes[2]);
913
- if (!freelist_inliners[2])
914
- errx(EX_SOFTWARE, "Couldn't find rb_gc_force_recycle!");
915
-
916
- freelist = bin_find_symbol("freelist", NULL);
917
- if (!freelist)
918
- errx(EX_SOFTWARE, "Couldn't find freelist!");
919
-
920
- /* start the search for places to insert the inline tramp */
921
-
922
- byte = freelist_inliners[i];
923
-
924
- while (i < FREELIST_INLINES) {
925
- if (arch_insert_inline_st2_tramp(byte, freelist, freelist_tramp,
926
- &inline_tramp_table[entry]) == 0) {
927
- /* insert occurred, so increment internal counters for the tramp table */
928
- entry++;
929
- inline_tramp_size++;
930
-
931
- /* add_freelist() only gets inlined *ONCE* into any of the 3 functions that we're scanning, */
932
- /* so move on to the next 'inliner' when after we tramp the first instruction we find. */
933
- /* REE's gc_sweep has 2 calls, but this gets optimized into a single inlining and a jmp to it */
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
- /* if we've looked at all the bytes in this function... */
942
- if (((void *)byte - freelist_inliners[i]) >= sizes[i]) {
943
- /* move on to the next function */
944
- i++;
945
- byte = freelist_inliners[i];
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
- byte++;
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 (tramps_completed != 3)
951
- errx(EX_SOFTWARE, "Inline add_freelist tramp insertion failed! Only inserted %d tramps.", tramps_completed);
952
- }
1064
+ if (memprof_config.classname == NULL)
1065
+ errors_printed += fprintf(stderr,
1066
+ "Failed to locate classname\n");
953
1067
 
954
- static void
955
- insert_tramp(const char *trampee, void *tramp)
956
- {
957
- void *trampee_addr = bin_find_symbol(trampee, NULL);
958
- int inline_ent = inline_tramp_size;
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
- ptr_to_rb_mark_table_add_filename = bin_find_symbol("rb_mark_table_add_filename", NULL);
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