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.
@@ -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