memprof 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/mach.c CHANGED
@@ -18,21 +18,25 @@
18
18
  #include <mach-o/ldsyms.h>
19
19
  #include <mach-o/nlist.h>
20
20
 
21
- // The jmp instructions in the dyld stub table are 6 bytes,
22
- // 2 bytes for the instruction and 4 bytes for the offset operand
23
- //
24
- // This jmp does not jump to the offset operand, but instead
25
- // looks up an absolute address stored at the offset and jumps to that.
26
- // Offset is the offset from the address of the _next_ instruction sequence.
27
- //
28
- // We need to deference the address at this offset to find the real
29
- // target of the dyld stub entry.
21
+ /*
22
+ * The jmp instructions in the dyld stub table are 6 bytes,
23
+ * 2 bytes for the instruction and 4 bytes for the offset operand
24
+ *
25
+ * This jmp does not jump to the offset operand, but instead
26
+ * looks up an absolute address stored at the offset and jumps to that.
27
+ * Offset is the offset from the address of the _next_ instruction sequence.
28
+ *
29
+ * We need to deference the address at this offset to find the real
30
+ * target of the dyld stub entry.
31
+ */
30
32
 
31
33
  struct dyld_stub_entry {
32
34
  unsigned char jmp[2];
33
35
  uint32_t offset;
34
36
  } __attribute((__packed__));
35
37
 
38
+ /* Return the jmp target of a stub entry */
39
+
36
40
  static inline void*
37
41
  get_dyld_stub_target(struct dyld_stub_entry *entry) {
38
42
  // If the instructions match up, then dereference the address at the offset
@@ -42,14 +46,23 @@ get_dyld_stub_target(struct dyld_stub_entry *entry) {
42
46
  return NULL;
43
47
  }
44
48
 
49
+ /* Set the jmp target of a stub entry */
50
+
45
51
  static inline void
46
52
  set_dyld_stub_target(struct dyld_stub_entry *entry, void *addr) {
47
53
  *((void**)((void*)(entry + 1) + entry->offset)) = addr;
48
54
  }
49
55
 
50
- static void
56
+ /*
57
+ * Search all entries in a stub table for the stub that corresponds to trampee_addr,
58
+ * and overwrite to point at our trampoline code.
59
+ * Returns 0 if any tramps were successfully inserted.
60
+ */
61
+
62
+ static int
51
63
  update_dyld_stub_table(void *table, uint64_t len, void *trampee_addr, struct tramp_st2_entry *tramp)
52
64
  {
65
+ int ret = -1;
53
66
  struct dyld_stub_entry *entry = (struct dyld_stub_entry*) table;
54
67
  void *max_addr = table + len;
55
68
 
@@ -57,17 +70,20 @@ update_dyld_stub_table(void *table, uint64_t len, void *trampee_addr, struct tra
57
70
  void *target = get_dyld_stub_target(entry);
58
71
  if (trampee_addr == target) {
59
72
  set_dyld_stub_target(entry, tramp->addr);
73
+ ret = 0;
60
74
  }
61
75
  }
76
+ return ret;
62
77
  }
63
78
 
64
-
65
- // This function tells us if the passed stub table address
66
- // is something that we should try to update (by looking at it's filename)
79
+ /*
80
+ * This function tells us if the passed stub table address
81
+ * is something that we should try to update (by looking at it's filename)
82
+ * Only try to update dyld stub entries in files that match "libruby.dylib" or "*.bundle" (other C gems)
83
+ */
67
84
 
68
85
  static inline int
69
86
  should_update_stub_table(void *addr) {
70
- // Only try to update dyld stub entries in files that match "libruby.dylib" or "*.bundle" (other C gems)
71
87
  Dl_info info;
72
88
 
73
89
  if (dladdr(addr, &info)) {
@@ -88,54 +104,94 @@ should_update_stub_table(void *addr) {
88
104
  return 0;
89
105
  }
90
106
 
91
- static void
107
+ /*
108
+ * Attempts to update all necessary code in a given 'section' of a Mach-O image, to redirect
109
+ * the given function to the trampoline. This function takes care of both normal calls as well as
110
+ * shared library cases.
111
+ * Returns 0 if any tramps were successfully inserted.
112
+ */
113
+
114
+ static int
92
115
  update_mach_section(const struct mach_header *header, const struct section_64 *sect, intptr_t slide, void *trampee_addr, struct tramp_st2_entry *tramp) {
116
+ int ret = -1;
93
117
  uint64_t len = 0;
118
+ /*
119
+ * We should be able to calculate this information from the section_64 struct ourselves,
120
+ * but I encountered problems going that route, so using this helper function is fine.
121
+ *
122
+ * The segment "__TEXT" means "executable code and other read-only data." Segments have "sections", like "__text", "__const", etc.
123
+ * We want "__text" for normal function calls, and "__symbol_stub" (with variations like "__symbol_stub1") for shared lib stubs.
124
+ */
94
125
  void *section = getsectdatafromheader_64((const struct mach_header_64*)header, "__TEXT", sect->sectname, &len) + slide;
95
126
 
96
127
  if (strncmp(sect->sectname, "__symbol_stub", 13) == 0) {
97
- if (should_update_stub_table(section))
98
- update_dyld_stub_table(section, sect->size, trampee_addr, tramp);
128
+ if (should_update_stub_table(section)) {
129
+ if (update_dyld_stub_table(section, sect->size, trampee_addr, tramp) == 0) {
130
+ ret = 0;
131
+ }
132
+ }
133
+ return ret;
99
134
  }
100
135
 
136
+ /* TODO: check the filename just like we do above for stub sections. No reason to look at unrelated files. */
101
137
  if (strcmp(sect->sectname, "__text") == 0) {
102
138
  size_t count = 0;
103
139
  for(; count < len; section++, count++) {
104
- if (arch_insert_st1_tramp(section, trampee_addr, tramp)) {
105
- // printf("tramped %p for %s\n", byte, trampee);
140
+ if (arch_insert_st1_tramp(section, trampee_addr, tramp) == 0) {
141
+ ret = 0;
106
142
  }
107
143
  }
108
144
  }
145
+ return ret;
109
146
  }
110
147
 
111
- static void
148
+ /*
149
+ * For a given Mach-O image, iterates over all segments and their sections, passing
150
+ * the sections to update_mach_section for potential tramping.
151
+ * Returns 0 if any tramps were successfully inserted.
152
+ */
153
+
154
+ static int
112
155
  update_bin_for_mach_header(const struct mach_header *header, intptr_t slide, void *trampee_addr, struct tramp_st2_entry *tramp) {
156
+ int ret = -1;
113
157
  int i, j;
114
158
  int lc_count = header->ncmds;
115
159
 
116
- // this as a char* because we need to step it forward by an arbitrary number of bytes
160
+ /* Load commands start immediately after the Mach header.
161
+ * This as a char* because we need to step it forward by an arbitrary number of bytes,
162
+ * specified in the 'cmdsize' field of each load command.
163
+ */
117
164
  const char *lc = ((const char*) header) + sizeof(struct mach_header_64);
118
165
 
119
- // Check all the load commands in the object to see if they are segment commands
166
+ /* Check all the load commands in the object to see if they are segment commands */
120
167
  for (i = 0; i < lc_count; i++, lc += ((struct load_command*)lc)->cmdsize) {
121
168
  if (((struct load_command*)lc)->cmd == LC_SEGMENT_64) {
169
+ /* If it's a segment command, we want to iterate over each of it's sections. */
122
170
  const struct segment_command_64 *seg = (const struct segment_command_64 *) lc;
123
171
  const struct section_64 * sect = (const struct section_64*)(lc + sizeof(struct segment_command_64));
172
+ /* Sections start immediately after the segment_command, and are included in the segment's "cmdsize" */
124
173
  int section_count = (seg->cmdsize - sizeof(struct segment_command_64)) / sizeof(struct section_64);
125
174
 
175
+ /* Attempt to tramp the sections */
126
176
  for (j=0; j < section_count; j++, sect++) {
127
- update_mach_section(header, sect, slide, trampee_addr, tramp);
177
+ if (update_mach_section(header, sect, slide, trampee_addr, tramp) == 0) {
178
+ ret = 0;
179
+ }
128
180
  }
129
181
  }
130
182
  }
183
+ return ret;
131
184
  }
132
185
 
186
+ /* This function takes a pointer to an *in process* mach_header_64
187
+ * and returns it's image index, which is required to specify the image
188
+ * in many dyld functions.
189
+ *
190
+ * This will NOT work for mach objects read manually from a file, since
191
+ * that's just arbitrary data and the dyld system knows nothing about it.
192
+ */
133
193
 
134
- // This function takes a pointer to a loaded *in memory* mach_header_64
135
- // and returns it's image index. This will NOT work for mach objects read
136
- // from a file.
137
-
138
- int
194
+ static int
139
195
  find_dyld_image_index(const struct mach_header_64 *hdr) {
140
196
  int i;
141
197
 
@@ -147,16 +203,20 @@ find_dyld_image_index(const struct mach_header_64 *hdr) {
147
203
  errx(EX_SOFTWARE, "Could not find image index");
148
204
  }
149
205
 
150
-
151
- // This function returns a buffer containing the file that is presumed
152
- // to be either the Ruby executable or libruby. (Wherever rb_newobj is found.)
153
- //
154
- // The passed pointer index is set to the image index for the associated
155
- // in-memory mach image.
156
- //
157
- // !!! The pointer returned by this function must be freed !!!
158
-
159
- void *
206
+ /*
207
+ * This function returns a buffer containing the file that is presumed
208
+ * to be either the Ruby executable or libruby. (Wherever rb_newobj is found.)
209
+ *
210
+ * The passed pointer index is set to the dyld image index for the associated
211
+ * in-process mach image.
212
+ *
213
+ * The reason that we read in the file is because the symbol table is not loaded
214
+ * into memory with everything else at load time (at least not anywhere I can find).
215
+ *
216
+ * !!! The pointer returned by this function must be freed !!!
217
+ */
218
+
219
+ static void *
160
220
  get_ruby_file_and_header_index(int *index) {
161
221
  void *ptr = NULL;
162
222
  void *buf = NULL;
@@ -189,11 +249,12 @@ get_ruby_file_and_header_index(int *index) {
189
249
  return buf;
190
250
  }
191
251
 
252
+ /*
253
+ * 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.
255
+ */
192
256
 
193
- // This function compares two nlist_64 structures by their n_value field (address)
194
- // used by qsort in build_sorted_nlist_table
195
-
196
- int
257
+ static int
197
258
  nlist_cmp(const void *obj1, const void *obj2) {
198
259
  const struct nlist_64 *nlist1 = *(const struct nlist_64**) obj1;
199
260
  const struct nlist_64 *nlist2 = *(const struct nlist_64**) obj2;
@@ -206,14 +267,15 @@ nlist_cmp(const void *obj1, const void *obj2) {
206
267
  return 1;
207
268
  }
208
269
 
270
+ /*
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.
274
+ *
275
+ * !!! The pointer returned by this function must be freed !!!
276
+ */
209
277
 
210
- // This function returns an array of pointers to nlist_64 entries in the symbol table
211
- // of the file pointed to by the passed mach header, sorted by address, and sets the
212
- // passed uint32_t pointers nsyms and stroff to those fields found in the symtab_command structure.
213
- //
214
- // !!! The pointer returned by this function must be freed !!!
215
-
216
- const struct nlist_64 **
278
+ static const struct nlist_64 **
217
279
  build_sorted_nlist_table(const struct mach_header_64 *hdr, uint32_t *nsyms, uint32_t *stroff) {
218
280
  const struct nlist_64 **base;
219
281
  uint32_t i, j;
@@ -225,6 +287,7 @@ build_sorted_nlist_table(const struct mach_header_64 *hdr, uint32_t *nsyms, uint
225
287
  const struct symtab_command *sc = (const struct symtab_command*) lc;
226
288
  const struct nlist_64 *symbol_table = (const struct nlist_64*)((const char*)hdr + sc->symoff);
227
289
 
290
+ /* TODO: qsort this table *IN PLACE* instead of making a copy of it. */
228
291
  base = malloc(sc->nsyms * sizeof(struct nlist_64*));
229
292
 
230
293
  for (j = 0; j < sc->nsyms; j++)
@@ -242,9 +305,25 @@ build_sorted_nlist_table(const struct mach_header_64 *hdr, uint32_t *nsyms, uint
242
305
  errx(EX_SOFTWARE, "Unable to find LC_SYMTAB");
243
306
  }
244
307
 
308
+ /*
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.
322
+ */
323
+
245
324
  void *
246
- bin_find_symbol(char *symbol, size_t *size) {
247
- // Correctly prefix the symbol with a '_' (whats a prettier way to do this?)
325
+ bin_find_symbol(const char *symbol, size_t *size) {
326
+ /* Correctly prefix the symbol with a '_' (whats a prettier way to do this?) */
248
327
  size_t len = strlen(symbol);
249
328
  char real_symbol[len + 2];
250
329
  memcpy(real_symbol, "_", 1);
@@ -265,6 +344,10 @@ bin_find_symbol(char *symbol, size_t *size) {
265
344
  errx(EX_SOFTWARE, "Magic for Ruby Mach-O file doesn't match");
266
345
 
267
346
  const struct nlist_64 **nlist_table = build_sorted_nlist_table(hdr, &nsyms, &stroff);
347
+ /*
348
+ * The string names of symbols are stored separately from the symbol table.
349
+ * The symbol table entries contain a string 'index', which is an offset into this region.
350
+ */
268
351
  const char *string_table = (const char*)hdr + stroff;
269
352
 
270
353
  for (i=0; i < nsyms; i++) {
@@ -273,11 +356,17 @@ bin_find_symbol(char *symbol, size_t *size) {
273
356
 
274
357
  if (strcmp(real_symbol, string) == 0) {
275
358
  const uint64_t addr = nlist_entry->n_value;
359
+ /* Add the slide to get the *real* address in the process. */
276
360
  ptr = (void*)(addr + _dyld_get_image_vmaddr_slide(index));
277
361
 
278
362
  if (size) {
279
363
  const struct nlist_64 *next_entry = NULL;
280
364
 
365
+ /*
366
+ * There can be multiple entries in the symbol table with the same n_value (address).
367
+ * This means that the 'next' one isn't always good enough. We have to make sure it's
368
+ * really a different symbol.
369
+ */
281
370
  j = 1;
282
371
  while (next_entry == NULL) {
283
372
  const struct nlist_64 *tmp_entry = nlist_table[i + j];
@@ -286,6 +375,11 @@ bin_find_symbol(char *symbol, size_t *size) {
286
375
  j++;
287
376
  }
288
377
 
378
+ /*
379
+ * Subtract our address from the address of the next symbol to get it's rough size.
380
+ * My observation is that the start of the next symbol will be padded to 16 byte alignment from the end of this one.
381
+ * This should be fine, since the point of getting the size is just to minimize scan area for tramp insertions.
382
+ */
289
383
  *size = (next_entry->n_value - addr);
290
384
  }
291
385
  break;
@@ -297,9 +391,26 @@ bin_find_symbol(char *symbol, size_t *size) {
297
391
  return ptr;
298
392
  }
299
393
 
300
- void
301
- bin_update_image(int entry, char *trampee, struct tramp_st2_entry *tramp)
394
+ /*
395
+ * I will explain bin_update_image with imaginary Ruby code:
396
+ *
397
+ * Process.mach_images.each do |image|
398
+ * image.segments.each do |segment|
399
+ * segment.sections.each do |section|
400
+ * if section.name == "__text"
401
+ * tramp_normal_callsites(section)
402
+ * elsif section.name =~ /__symbol_stub/ && image.filename =~ /libruby\.dylib|bundle/
403
+ * tramp_dyld_stubs(section)
404
+ * end
405
+ * end
406
+ * end
407
+ * end
408
+ */
409
+
410
+ int
411
+ bin_update_image(const char *trampee, struct tramp_st2_entry *tramp)
302
412
  {
413
+ int ret = -1;
303
414
  int i;
304
415
  int header_count = _dyld_image_count();
305
416
  void *trampee_addr = bin_find_symbol(trampee, NULL);
@@ -310,8 +421,10 @@ bin_update_image(int entry, char *trampee, struct tramp_st2_entry *tramp)
310
421
  if ((void*)current_hdr == &_mh_bundle_header)
311
422
  continue;
312
423
 
313
- update_bin_for_mach_header(current_hdr, _dyld_get_image_vmaddr_slide(i), trampee_addr, tramp);
424
+ if (update_bin_for_mach_header(current_hdr, _dyld_get_image_vmaddr_slide(i), trampee_addr, tramp) == 0)
425
+ ret = 0;
314
426
  }
427
+ return ret;
315
428
  }
316
429
 
317
430
  void *
@@ -332,14 +445,14 @@ bin_allocate_page()
332
445
  return NULL;
333
446
  }
334
447
 
335
- int
336
- bin_type_size(char *type)
448
+ size_t
449
+ bin_type_size(const char *type)
337
450
  {
338
- return -1;
451
+ return 0;
339
452
  }
340
453
 
341
454
  int
342
- bin_type_member_offset(char *type, char *member)
455
+ bin_type_member_offset(const char *type, const char *member)
343
456
  {
344
457
  return -1;
345
458
  }
@@ -51,6 +51,51 @@ struct obj_track {
51
51
  int line;
52
52
  };
53
53
 
54
+ static VALUE gc_hook;
55
+ static void **ptr_to_rb_mark_table_add_filename = NULL;
56
+ static void (*rb_mark_table_add_filename)(char*);
57
+
58
+ static int
59
+ ree_sourcefile_mark_each(st_data_t key, st_data_t val, st_data_t arg)
60
+ {
61
+ struct obj_track *tracker = (struct obj_track *)val;
62
+ assert(tracker != NULL);
63
+
64
+ if (tracker->source)
65
+ rb_mark_table_add_filename(tracker->source);
66
+ return ST_CONTINUE;
67
+ }
68
+
69
+ static int
70
+ mri_sourcefile_mark_each(st_data_t key, st_data_t val, st_data_t arg)
71
+ {
72
+ struct obj_track *tracker = (struct obj_track *)val;
73
+ assert(tracker != NULL);
74
+
75
+ if (tracker->source)
76
+ (tracker->source)[-1] = 1;
77
+ return ST_CONTINUE;
78
+ }
79
+
80
+ /* Accomodate the different source file marking techniques of MRI and REE.
81
+ *
82
+ * The function pointer for REE changes depending on whether COW is enabled,
83
+ * which can be toggled at runtime. We need to deference it to get the
84
+ * real function every time we come here, as it may have changed.
85
+ */
86
+
87
+ static void
88
+ sourcefile_marker()
89
+ {
90
+ if (ptr_to_rb_mark_table_add_filename) {
91
+ rb_mark_table_add_filename = *ptr_to_rb_mark_table_add_filename;
92
+ assert(rb_mark_table_add_filename != NULL);
93
+ st_foreach(objs, ree_sourcefile_mark_each, (st_data_t)NULL);
94
+ } else {
95
+ st_foreach(objs, mri_sourcefile_mark_each, (st_data_t)NULL);
96
+ }
97
+ }
98
+
54
99
  static VALUE
55
100
  newobj_tramp()
56
101
  {
@@ -62,13 +107,13 @@ newobj_tramp()
62
107
 
63
108
  if (tracker) {
64
109
  if (ruby_current_node && ruby_current_node->nd_file && *ruby_current_node->nd_file) {
65
- tracker->source = strdup(ruby_current_node->nd_file);
110
+ tracker->source = ruby_current_node->nd_file;
66
111
  tracker->line = nd_line(ruby_current_node);
67
112
  } else if (ruby_sourcefile) {
68
- tracker->source = strdup(ruby_sourcefile);
113
+ tracker->source = ruby_sourcefile;
69
114
  tracker->line = ruby_sourceline;
70
115
  } else {
71
- tracker->source = strdup("__null__");
116
+ tracker->source = NULL;
72
117
  tracker->line = 0;
73
118
  }
74
119
 
@@ -98,7 +143,6 @@ freelist_tramp(unsigned long rval)
98
143
  if (track_objs && objs) {
99
144
  st_delete(objs, (st_data_t *) &rval, (st_data_t *) &tracker);
100
145
  if (tracker) {
101
- free(tracker->source);
102
146
  free(tracker);
103
147
  }
104
148
  }
@@ -108,7 +152,6 @@ static int
108
152
  objs_free(st_data_t key, st_data_t record, st_data_t arg)
109
153
  {
110
154
  struct obj_track *tracker = (struct obj_track *)record;
111
- free(tracker->source);
112
155
  free(tracker);
113
156
  return ST_DELETE;
114
157
  }
@@ -121,6 +164,7 @@ objs_tabulate(st_data_t key, st_data_t record, st_data_t arg)
121
164
  char *source_key = NULL;
122
165
  unsigned long count = 0;
123
166
  char *type = NULL;
167
+ int bytes_printed = 0;
124
168
 
125
169
  switch (TYPE(tracker->obj)) {
126
170
  case T_NONE:
@@ -143,7 +187,8 @@ objs_tabulate(st_data_t key, st_data_t record, st_data_t arg)
143
187
  }
144
188
  }
145
189
 
146
- asprintf(&source_key, "%s:%d:%s", tracker->source, tracker->line, type);
190
+ bytes_printed = asprintf(&source_key, "%s:%d:%s", tracker->source ? tracker->source : "__null__", tracker->line, type);
191
+ assert(bytes_printed != -1);
147
192
  st_lookup(table, (st_data_t)source_key, (st_data_t *)&count);
148
193
  if (st_insert(table, (st_data_t)source_key, ++count)) {
149
194
  free(source_key);
@@ -163,8 +208,10 @@ objs_to_array(st_data_t key, st_data_t record, st_data_t arg)
163
208
  struct results *res = (struct results *)arg;
164
209
  unsigned long count = (unsigned long)record;
165
210
  char *source = (char *)key;
166
-
167
- asprintf(&(res->entries[res->num_entries++]), "%7li %s", count, source);
211
+ int bytes_printed = 0;
212
+
213
+ bytes_printed = asprintf(&(res->entries[res->num_entries++]), "%7li %s", count, source);
214
+ assert(bytes_printed != -1);
168
215
 
169
216
  free(source);
170
217
  return ST_DELETE;
@@ -285,10 +332,13 @@ yajl_gen_format(yajl_gen gen, char *format, ...)
285
332
  {
286
333
  va_list args;
287
334
  char *str;
335
+ int bytes_printed = 0;
336
+
288
337
  yajl_gen_status ret;
289
338
 
290
339
  va_start(args, format);
291
- vasprintf(&str, format, args);
340
+ bytes_printed = vasprintf(&str, format, args);
341
+ assert(bytes_printed != -1);
292
342
  va_end(args);
293
343
 
294
344
  ret = yajl_gen_string(gen, (unsigned char *)str, strlen(str));
@@ -423,6 +473,16 @@ obj_dump(VALUE obj, yajl_gen gen)
423
473
  }
424
474
  break;
425
475
 
476
+ case T_STRUCT:
477
+ yajl_gen_cstr(gen, "struct");
478
+
479
+ yajl_gen_cstr(gen, "class");
480
+ yajl_gen_value(gen, RBASIC(obj)->klass);
481
+
482
+ yajl_gen_cstr(gen, "class_name");
483
+ yajl_gen_cstr(gen, rb_obj_classname(obj));
484
+ break;
485
+
426
486
  case T_FILE:
427
487
  yajl_gen_cstr(gen, "file");
428
488
  break;
@@ -730,10 +790,10 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
730
790
  int heaps_used = *(int*)bin_find_symbol("heaps_used",0);
731
791
 
732
792
  #ifndef sizeof__RVALUE
733
- int sizeof__RVALUE = bin_type_size("RVALUE");
793
+ size_t sizeof__RVALUE = bin_type_size("RVALUE");
734
794
  #endif
735
795
  #ifndef sizeof__heaps_slot
736
- int sizeof__heaps_slot = bin_type_size("heaps_slot");
796
+ size_t sizeof__heaps_slot = bin_type_size("heaps_slot");
737
797
  #endif
738
798
  #ifndef offset__heaps_slot__limit
739
799
  int offset__heaps_slot__limit = bin_type_member_offset("heaps_slot", "limit");
@@ -745,7 +805,7 @@ memprof_dump_all(int argc, VALUE *argv, VALUE self)
745
805
  char *p, *pend;
746
806
  int i, limit;
747
807
 
748
- if (sizeof__RVALUE < 0 || sizeof__heaps_slot < 0)
808
+ if (sizeof__RVALUE == 0 || sizeof__heaps_slot == 0)
749
809
  rb_raise(eUnsupported, "could not find internal heap");
750
810
 
751
811
  VALUE str;
@@ -807,10 +867,8 @@ create_tramp_table()
807
867
  void *ent = arch_get_st2_tramp(&tramp_sz);
808
868
  void *inline_ent = arch_get_inline_st2_tramp(&inline_tramp_sz);
809
869
 
810
- if ((region = bin_allocate_page()) == MAP_FAILED) {
811
- fprintf(stderr, "Failed to allocate memory for stage 1 trampolines.\n");
812
- return;
813
- }
870
+ if ((region = bin_allocate_page()) == MAP_FAILED)
871
+ errx(EX_SOFTWARE, "Failed to allocate memory for stage 1 trampolines.");
814
872
 
815
873
  tramp_table = region;
816
874
  inline_tramp_table = region + pagesize/2;
@@ -844,27 +902,20 @@ hook_freelist(int entry)
844
902
  freelist_inliners[0] = bin_find_symbol("garbage_collect", &sizes[0]);
845
903
  if (!freelist_inliners[0]) {
846
904
  /* couldn't find anything containing gc_sweep. */
847
- fprintf(stderr, "Couldn't find gc_sweep or garbage_collect!\n");
848
- return;
905
+ errx(EX_SOFTWARE, "Couldn't find gc_sweep or garbage_collect!");
849
906
  }
850
907
 
851
908
  freelist_inliners[1] = bin_find_symbol("finalize_list", &sizes[1]);
852
- if (!freelist_inliners[1]) {
853
- fprintf(stderr, "Couldn't find finalize_list!\n");
854
- /* XXX continue or exit? */
855
- }
909
+ if (!freelist_inliners[1])
910
+ errx(EX_SOFTWARE, "Couldn't find finalize_list!");
856
911
 
857
912
  freelist_inliners[2] = bin_find_symbol("rb_gc_force_recycle", &sizes[2]);
858
- if (!freelist_inliners[2]) {
859
- fprintf(stderr, "Couldn't find rb_gc_force_recycle!\n");
860
- /* XXX continue or exit? */
861
- }
913
+ if (!freelist_inliners[2])
914
+ errx(EX_SOFTWARE, "Couldn't find rb_gc_force_recycle!");
862
915
 
863
916
  freelist = bin_find_symbol("freelist", NULL);
864
- if (!freelist) {
865
- fprintf(stderr, "Couldn't find freelist!\n");
866
- return;
867
- }
917
+ if (!freelist)
918
+ errx(EX_SOFTWARE, "Couldn't find freelist!");
868
919
 
869
920
  /* start the search for places to insert the inline tramp */
870
921
 
@@ -896,14 +947,14 @@ hook_freelist(int entry)
896
947
  byte++;
897
948
  }
898
949
 
899
- assert(tramps_completed == 3);
950
+ if (tramps_completed != 3)
951
+ errx(EX_SOFTWARE, "Inline add_freelist tramp insertion failed! Only inserted %d tramps.", tramps_completed);
900
952
  }
901
953
 
902
954
  static void
903
- insert_tramp(char *trampee, void *tramp)
955
+ insert_tramp(const char *trampee, void *tramp)
904
956
  {
905
957
  void *trampee_addr = bin_find_symbol(trampee, NULL);
906
- int entry = tramp_size;
907
958
  int inline_ent = inline_tramp_size;
908
959
 
909
960
  if (trampee_addr == NULL) {
@@ -912,7 +963,7 @@ insert_tramp(char *trampee, void *tramp)
912
963
  inline_tramp_size++;
913
964
  hook_freelist(inline_ent);
914
965
  } else {
915
- return;
966
+ errx(EX_SOFTWARE, "Failed to locate required symbol %s", trampee);
916
967
  }
917
968
  } else {
918
969
  if (strcmp("add_freelist", trampee) == 0) {
@@ -920,7 +971,8 @@ insert_tramp(char *trampee, void *tramp)
920
971
  }
921
972
 
922
973
  tramp_table[tramp_size].addr = tramp;
923
- bin_update_image(entry, trampee, &tramp_table[tramp_size]);
974
+ if (bin_update_image(trampee, &tramp_table[tramp_size]) != 0)
975
+ errx(EX_SOFTWARE, "Failed to insert tramp for %s", trampee);
924
976
  tramp_size++;
925
977
  }
926
978
  }
@@ -944,6 +996,10 @@ Init_memprof()
944
996
  create_tramp_table();
945
997
  rb_add_freelist = NULL;
946
998
 
999
+ gc_hook = Data_Wrap_Struct(rb_cObject, sourcefile_marker, NULL, NULL);
1000
+ rb_global_variable(&gc_hook);
1001
+ ptr_to_rb_mark_table_add_filename = bin_find_symbol("rb_mark_table_add_filename", NULL);
1002
+
947
1003
  insert_tramp("rb_newobj", newobj_tramp);
948
1004
  insert_tramp("add_freelist", freelist_tramp);
949
1005