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/README +12 -16
- data/ext/arch.h +49 -6
- data/ext/bin_api.h +67 -19
- data/ext/elf.c +276 -56
- data/ext/mach.c +171 -58
- data/ext/memprof.c +91 -35
- data/ext/x86_64.c +77 -10
- data/ext/x86_64.h +86 -17
- data/ext/x86_gen.h +78 -38
- data/memprof.gemspec +2 -2
- metadata +3 -3
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
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
|
-
|
66
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
301
|
-
|
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
|
-
|
336
|
-
bin_type_size(char *type)
|
448
|
+
size_t
|
449
|
+
bin_type_size(const char *type)
|
337
450
|
{
|
338
|
-
return
|
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
|
}
|
data/ext/memprof.c
CHANGED
@@ -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 =
|
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 =
|
113
|
+
tracker->source = ruby_sourcefile;
|
69
114
|
tracker->line = ruby_sourceline;
|
70
115
|
} else {
|
71
|
-
tracker->source =
|
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
|
-
|
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
|
-
|
793
|
+
size_t sizeof__RVALUE = bin_type_size("RVALUE");
|
734
794
|
#endif
|
735
795
|
#ifndef sizeof__heaps_slot
|
736
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
|