memprof 0.1.3 → 0.2.0
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 +5 -5
- data/ext/arch.h +30 -0
- data/ext/bin_api.h +13 -52
- data/ext/elf.c +479 -45
- data/ext/extconf.rb +96 -8
- data/ext/i386.c +106 -0
- data/ext/i386.h +75 -0
- data/ext/mach.c +12 -0
- data/ext/memprof.c +575 -186
- data/ext/src/libdwarf-20091118.tar.gz +0 -0
- data/ext/src/yajl-1.0.8.tar.gz +0 -0
- data/ext/x86_64.c +124 -0
- data/ext/x86_64.h +78 -0
- data/ext/x86_gen.h +100 -0
- data/memprof.gemspec +10 -1
- data/spec/memprof_spec.rb +85 -0
- metadata +10 -1
data/ext/extconf.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
if RUBY_PLATFORM =~ /darwin/
|
2
|
+
STDERR.puts "\n\n"
|
3
|
+
STDERR.puts "***************************************************************************************"
|
4
|
+
STDERR.puts "**************************** osx is not supported (yet) =( ****************************"
|
5
|
+
STDERR.puts "***************************************************************************************"
|
6
|
+
exit(1)
|
7
|
+
end
|
8
|
+
|
1
9
|
if RUBY_VERSION >= "1.9"
|
2
10
|
STDERR.puts "\n\n"
|
3
11
|
STDERR.puts "***************************************************************************************"
|
@@ -14,23 +22,65 @@ CWD = File.expand_path(File.dirname(__FILE__))
|
|
14
22
|
def sys(cmd)
|
15
23
|
puts " -- #{cmd}"
|
16
24
|
unless ret = xsystem(cmd)
|
17
|
-
raise "#{cmd} failed, please report to
|
25
|
+
raise "#{cmd} failed, please report to memprof@tmm1.net with pastie.org link to #{CWD}/mkmf.log"
|
18
26
|
end
|
19
27
|
ret
|
20
28
|
end
|
21
29
|
|
30
|
+
###
|
31
|
+
# yajl
|
32
|
+
|
33
|
+
yajl = File.basename('yajl-1.0.8.tar.gz')
|
34
|
+
dir = File.basename(yajl, '.tar.gz')
|
35
|
+
|
36
|
+
unless File.exists?("#{CWD}/dst/lib/libyajl_ext.a")
|
37
|
+
puts "(I'm about to compile yajl.. this will definitely take a while)"
|
38
|
+
|
39
|
+
Dir.chdir('src') do
|
40
|
+
FileUtils.rm_rf(dir) if File.exists?(dir)
|
41
|
+
|
42
|
+
sys("tar zxvf #{yajl}")
|
43
|
+
Dir.chdir("#{dir}/src") do
|
44
|
+
FileUtils.mkdir_p "api/yajl"
|
45
|
+
%w[ common parse gen ].each do |f|
|
46
|
+
FileUtils.cp "api/yajl_#{f}.h", 'api/yajl/'
|
47
|
+
end
|
48
|
+
|
49
|
+
File.open("extconf.rb",'w') do |f|
|
50
|
+
f.puts "require 'mkmf'; $INCFLAGS[0,0] = '-I./api/ '; create_makefile 'libyajl'"
|
51
|
+
end
|
52
|
+
sys("#{Config::CONFIG['bindir']}/#{Config::CONFIG['ruby_install_name']} extconf.rb")
|
53
|
+
|
54
|
+
sys("make")
|
55
|
+
sys("ar rv libyajl_ext.a #{Dir['*.o'].join(' ')}")
|
56
|
+
|
57
|
+
FileUtils.mkdir_p "#{CWD}/dst/lib"
|
58
|
+
FileUtils.cp 'libyajl_ext.a', "#{CWD}/dst/lib"
|
59
|
+
FileUtils.mkdir_p "#{CWD}/dst/include"
|
60
|
+
FileUtils.cp_r 'api/yajl', "#{CWD}/dst/include/"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
$LIBPATH.unshift "#{CWD}/dst/lib"
|
66
|
+
$INCFLAGS[0,0] = "-I#{CWD}/dst/include "
|
67
|
+
|
68
|
+
unless have_library('yajl_ext') and have_header('yajl/yajl_gen.h')
|
69
|
+
raise 'Yajl build failed'
|
70
|
+
end
|
71
|
+
|
22
72
|
def add_define(name)
|
23
73
|
$defs.push("-D#{name}")
|
24
74
|
end
|
25
75
|
|
26
|
-
###
|
27
|
-
# libelf
|
28
|
-
|
29
76
|
if RUBY_PLATFORM =~ /linux/
|
77
|
+
###
|
78
|
+
# libelf
|
79
|
+
|
30
80
|
libelf = File.basename('libelf-0.8.13.tar.gz')
|
31
81
|
dir = File.basename(libelf, '.tar.gz')
|
32
82
|
|
33
|
-
unless File.exists?("#{CWD}/dst/lib/libelf_ext.
|
83
|
+
unless File.exists?("#{CWD}/dst/lib/libelf_ext.a")
|
34
84
|
puts "(I'm about to compile libelf.. this will definitely take a while)"
|
35
85
|
|
36
86
|
Dir.chdir('src') do
|
@@ -38,26 +88,58 @@ if RUBY_PLATFORM =~ /linux/
|
|
38
88
|
|
39
89
|
sys("tar zxvf #{libelf}")
|
40
90
|
Dir.chdir(dir) do
|
41
|
-
|
91
|
+
ENV['CFLAGS'] = '-fPIC'
|
92
|
+
sys("./configure --prefix=#{CWD}/dst --disable-nls --disable-shared")
|
42
93
|
sys("make")
|
43
94
|
sys("make install")
|
44
95
|
end
|
45
96
|
end
|
46
97
|
|
47
98
|
Dir.chdir('dst/lib') do
|
48
|
-
FileUtils.ln_s 'libelf.
|
99
|
+
FileUtils.ln_s 'libelf.a', 'libelf_ext.a'
|
49
100
|
end
|
50
101
|
end
|
51
102
|
|
52
103
|
$LIBPATH.unshift "#{CWD}/dst/lib"
|
53
104
|
$INCFLAGS[0,0] = "-I#{CWD}/dst/include "
|
54
105
|
|
55
|
-
unless have_library('
|
106
|
+
unless have_library('elf_ext', 'gelf_getshdr')
|
56
107
|
raise 'libelf build failed'
|
57
108
|
end
|
58
109
|
|
110
|
+
###
|
111
|
+
# libdwarf
|
112
|
+
|
113
|
+
libdwarf = File.basename('libdwarf-20091118.tar.gz')
|
114
|
+
dir = File.basename(libdwarf, '.tar.gz').sub('lib','')
|
115
|
+
|
116
|
+
unless File.exists?("#{CWD}/dst/lib/libdwarf_ext.a")
|
117
|
+
puts "(I'm about to compile libdwarf.. this will definitely take a while)"
|
118
|
+
|
119
|
+
Dir.chdir('src') do
|
120
|
+
FileUtils.rm_rf(dir) if File.exists?(dir)
|
121
|
+
|
122
|
+
sys("tar zxvf #{libdwarf}")
|
123
|
+
Dir.chdir("#{dir}/libdwarf") do
|
124
|
+
ENV['CFLAGS'] = "-fPIC -I#{CWD}/dst/include"
|
125
|
+
ENV['LDFLAGS'] = "-L#{CWD}/dst/lib"
|
126
|
+
sys("./configure")
|
127
|
+
sys("make")
|
128
|
+
|
129
|
+
FileUtils.cp 'libdwarf.a', "#{CWD}/dst/lib/libdwarf_ext.a"
|
130
|
+
FileUtils.cp 'dwarf.h', "#{CWD}/dst/include/"
|
131
|
+
FileUtils.cp 'libdwarf.h', "#{CWD}/dst/include/"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
unless have_library('dwarf_ext')
|
137
|
+
raise 'libdwarf build failed'
|
138
|
+
end
|
139
|
+
|
59
140
|
is_elf = true
|
60
141
|
add_define 'HAVE_ELF'
|
142
|
+
add_define 'HAVE_DWARF'
|
61
143
|
end
|
62
144
|
|
63
145
|
if have_header('mach-o/dyld')
|
@@ -65,6 +147,12 @@ if have_header('mach-o/dyld')
|
|
65
147
|
add_define 'HAVE_MACH'
|
66
148
|
end
|
67
149
|
|
150
|
+
arch = RUBY_PLATFORM[/(.*)-linux/,1]
|
151
|
+
if arch == 'universal'
|
152
|
+
arch = 'x86_64'
|
153
|
+
end
|
154
|
+
add_define "_ARCH_#{arch}_"
|
155
|
+
|
68
156
|
if is_elf or is_macho
|
69
157
|
create_makefile('memprof')
|
70
158
|
else
|
data/ext/i386.c
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#if defined (_ARCH_i386_) || defined(_ARCH_i686_)
|
2
|
+
|
3
|
+
#include <stdint.h>
|
4
|
+
#include <string.h>
|
5
|
+
|
6
|
+
#include <sys/mman.h>
|
7
|
+
|
8
|
+
#include "arch.h"
|
9
|
+
#include "x86_gen.h"
|
10
|
+
|
11
|
+
/* This is the stage 1 inline trampoline for hooking the inlined add_freelist
|
12
|
+
* function .
|
13
|
+
*
|
14
|
+
* NOTE: The original instruction mov %reg, freelist is 7 bytes wide,
|
15
|
+
* whereas jmpq $displacement is only 5 bytes wide. We *must* pad out
|
16
|
+
* the next two bytes. This will be important to remember below.
|
17
|
+
*/
|
18
|
+
struct inline_st1_tramp {
|
19
|
+
unsigned char jmp;
|
20
|
+
uint32_t displacement;
|
21
|
+
unsigned char pad;
|
22
|
+
} __attribute__((__packed__)) inline_st1_tramp = {
|
23
|
+
.jmp = '\xe9',
|
24
|
+
.displacement = 0,
|
25
|
+
.pad = '\x90',
|
26
|
+
};
|
27
|
+
|
28
|
+
struct inline_st1_base_short {
|
29
|
+
unsigned char mov;
|
30
|
+
void *addr;
|
31
|
+
} __attribute__((__packed__)) inline_st1_short = {
|
32
|
+
.mov = '\xa3',
|
33
|
+
.addr = 0,
|
34
|
+
};
|
35
|
+
|
36
|
+
struct inline_st1_base_long {
|
37
|
+
unsigned char mov;
|
38
|
+
unsigned char src_reg;
|
39
|
+
void *addr;
|
40
|
+
} __attribute__((__packed__)) inline_st1_long = {
|
41
|
+
.mov = '\x89',
|
42
|
+
.src_reg = 0,
|
43
|
+
.addr = 0
|
44
|
+
};
|
45
|
+
|
46
|
+
static int
|
47
|
+
arch_check_ins(unsigned char *base)
|
48
|
+
{
|
49
|
+
|
50
|
+
/* if the byte is 0xa3 then we're moving from %eax, so
|
51
|
+
* the length is only 5, so we don't need the pad.
|
52
|
+
*
|
53
|
+
* otherwise, we're moving from something else, so the
|
54
|
+
* length is going to be 6 and we need a NOP.
|
55
|
+
*/
|
56
|
+
|
57
|
+
/* is it a mov instruction? */
|
58
|
+
if (*base == 0xa3)
|
59
|
+
return 0;
|
60
|
+
else if (*base == 0x89)
|
61
|
+
return 1;
|
62
|
+
|
63
|
+
return -1;
|
64
|
+
}
|
65
|
+
|
66
|
+
int
|
67
|
+
arch_insert_inline_st2_tramp(void *addr, void *marker, void *trampoline, void *table_entry)
|
68
|
+
{
|
69
|
+
struct inline_st1_base_long *long_base = addr;
|
70
|
+
struct inline_st1_base_short *short_base = addr;
|
71
|
+
struct inline_st1_tramp *st1_tramp = addr;
|
72
|
+
void *mov_target = NULL;
|
73
|
+
size_t pad_length = 0;
|
74
|
+
|
75
|
+
if ((pad_length = arch_check_ins(addr)) == -1)
|
76
|
+
return 1;
|
77
|
+
|
78
|
+
if (pad_length == 0) {
|
79
|
+
mov_target = short_base->addr;
|
80
|
+
default_inline_st2_tramp.mov = 0x90;
|
81
|
+
default_inline_st2_tramp.src_reg = 0xa3;
|
82
|
+
inline_st1_tramp.displacement = table_entry - (void *)(short_base + 1);
|
83
|
+
default_inline_st2_tramp.jmp_displacement = (void *)(short_base + 1) - (table_entry + sizeof(default_inline_st2_tramp));
|
84
|
+
} else {
|
85
|
+
mov_target = long_base->addr;
|
86
|
+
default_inline_st2_tramp.mov = long_base->mov;
|
87
|
+
default_inline_st2_tramp.src_reg = long_base->src_reg;
|
88
|
+
inline_st1_tramp.displacement = table_entry - (void *)(long_base + 1) + 1;
|
89
|
+
default_inline_st2_tramp.jmp_displacement = (void *)(long_base + 1) - (table_entry + sizeof(default_inline_st2_tramp));
|
90
|
+
}
|
91
|
+
|
92
|
+
if (marker == mov_target) {
|
93
|
+
default_inline_st2_tramp.mov_addr= default_inline_st2_tramp.frame.freelist = marker;
|
94
|
+
default_inline_st2_tramp.frame.fn_addr = trampoline;
|
95
|
+
if (pad_length) {
|
96
|
+
copy_instructions(addr, &inline_st1_tramp, sizeof(inline_st1_tramp));
|
97
|
+
} else {
|
98
|
+
copy_instructions(addr, &inline_st1_tramp, sizeof(inline_st1_tramp) - 1);
|
99
|
+
}
|
100
|
+
memcpy(table_entry, &default_inline_st2_tramp, sizeof(default_inline_st2_tramp));
|
101
|
+
return 0;
|
102
|
+
}
|
103
|
+
|
104
|
+
return 1;
|
105
|
+
}
|
106
|
+
#endif
|
data/ext/i386.h
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#if !defined(_i386_h_)
|
2
|
+
#define _i386_h_
|
3
|
+
|
4
|
+
#include <stdint.h>
|
5
|
+
#include "arch.h"
|
6
|
+
|
7
|
+
/*
|
8
|
+
* This is the "normal" stage 2 trampoline with a default entry pre-filled
|
9
|
+
*/
|
10
|
+
static struct tramp_st2_entry {
|
11
|
+
unsigned char ebx_save;
|
12
|
+
unsigned char mov;
|
13
|
+
void *addr;
|
14
|
+
unsigned char call[2];
|
15
|
+
unsigned char ebx_restore;
|
16
|
+
unsigned char ret;
|
17
|
+
} __attribute__((__packed__)) default_st2_tramp = {
|
18
|
+
.ebx_save = 0x53, /* push ebx */
|
19
|
+
.mov = 0xbb, /* mov addr into ebx */
|
20
|
+
.addr = 0, /* this is filled in later */
|
21
|
+
.call = {0xff, 0xd3}, /* calll *ebx */
|
22
|
+
.ebx_restore = 0x5b, /* pop ebx */
|
23
|
+
.ret = 0xc3, /* ret */
|
24
|
+
};
|
25
|
+
|
26
|
+
|
27
|
+
/*
|
28
|
+
* This is the inline stage 2 trampoline with a default entry pre-filled
|
29
|
+
*/
|
30
|
+
static struct inline_tramp_st2_entry {
|
31
|
+
|
32
|
+
/* this block will be filled in at runtime to replicate the overwritten
|
33
|
+
* instruction.
|
34
|
+
*/
|
35
|
+
unsigned char mov;
|
36
|
+
unsigned char src_reg;
|
37
|
+
void *mov_addr;
|
38
|
+
|
39
|
+
/* this frame will arrange freelist to be passed as an argument to
|
40
|
+
* the third and final trampoline (C level).
|
41
|
+
*/
|
42
|
+
struct {
|
43
|
+
unsigned char push_ebx;
|
44
|
+
unsigned char pushl[2];
|
45
|
+
void * freelist;
|
46
|
+
unsigned char mov_ebx;
|
47
|
+
void * fn_addr;
|
48
|
+
unsigned char call[2];
|
49
|
+
unsigned char pop_ebx;
|
50
|
+
unsigned char restore_ebx;
|
51
|
+
} __attribute__((__packed__)) frame;
|
52
|
+
|
53
|
+
/* this block jumps back to resume execution */
|
54
|
+
unsigned char jmp;
|
55
|
+
uint32_t jmp_displacement;
|
56
|
+
} __attribute__((__packed__)) default_inline_st2_tramp = {
|
57
|
+
.mov = 0x89,
|
58
|
+
.src_reg = 0,
|
59
|
+
.mov_addr = 0,
|
60
|
+
|
61
|
+
.frame = {
|
62
|
+
.push_ebx = 0x53,
|
63
|
+
.pushl = {0xff, 0x35},
|
64
|
+
.freelist = 0,
|
65
|
+
.mov_ebx = 0xbb,
|
66
|
+
.fn_addr = 0,
|
67
|
+
.call = {0xff, 0xd3},
|
68
|
+
.pop_ebx = 0x5b,
|
69
|
+
.restore_ebx = 0x5b,
|
70
|
+
},
|
71
|
+
|
72
|
+
.jmp = 0xe9,
|
73
|
+
.jmp_displacement = 0,
|
74
|
+
};
|
75
|
+
#endif
|
data/ext/mach.c
CHANGED
@@ -104,6 +104,18 @@ bin_find_symbol(char *sym, size_t *size) {
|
|
104
104
|
return ptr;
|
105
105
|
}
|
106
106
|
|
107
|
+
int
|
108
|
+
bin_type_size(char *type)
|
109
|
+
{
|
110
|
+
return -1;
|
111
|
+
}
|
112
|
+
|
113
|
+
int
|
114
|
+
bin_type_member_offset(char *type, char *member)
|
115
|
+
{
|
116
|
+
return -1;
|
117
|
+
}
|
118
|
+
|
107
119
|
void
|
108
120
|
bin_init()
|
109
121
|
{
|
data/ext/memprof.c
CHANGED
@@ -15,27 +15,27 @@
|
|
15
15
|
#include <intern.h>
|
16
16
|
#include <node.h>
|
17
17
|
|
18
|
+
#include "arch.h"
|
18
19
|
#include "bin_api.h"
|
19
20
|
|
20
21
|
size_t pagesize;
|
21
|
-
void *text_segment = NULL;
|
22
|
-
unsigned long text_segment_len = 0;
|
23
22
|
|
24
23
|
/*
|
25
24
|
trampoline specific stuff
|
26
25
|
*/
|
27
|
-
struct
|
26
|
+
struct tramp_st2_entry *tramp_table = NULL;
|
28
27
|
size_t tramp_size = 0;
|
29
28
|
|
30
29
|
/*
|
31
30
|
inline trampoline specific stuff
|
32
31
|
*/
|
33
32
|
size_t inline_tramp_size = 0;
|
34
|
-
struct
|
33
|
+
struct inline_tramp_st2_entry *inline_tramp_table = NULL;
|
35
34
|
|
36
35
|
/*
|
37
36
|
* bleak_house stuff
|
38
37
|
*/
|
38
|
+
static VALUE eUnsupported;
|
39
39
|
static int track_objs = 0;
|
40
40
|
static st_table *objs = NULL;
|
41
41
|
|
@@ -45,13 +45,6 @@ struct obj_track {
|
|
45
45
|
int line;
|
46
46
|
};
|
47
47
|
|
48
|
-
static void
|
49
|
-
error_tramp()
|
50
|
-
{
|
51
|
-
printf("WARNING: NO TRAMPOLINE SET.\n");
|
52
|
-
return;
|
53
|
-
}
|
54
|
-
|
55
48
|
static VALUE
|
56
49
|
newobj_tramp()
|
57
50
|
{
|
@@ -87,7 +80,6 @@ static void
|
|
87
80
|
freelist_tramp(unsigned long rval)
|
88
81
|
{
|
89
82
|
struct obj_track *tracker = NULL;
|
90
|
-
|
91
83
|
if (track_objs) {
|
92
84
|
st_delete(objs, (st_data_t *) &rval, (st_data_t *) &tracker);
|
93
85
|
if (tracker) {
|
@@ -157,7 +149,7 @@ objs_to_array(st_data_t key, st_data_t record, st_data_t arg)
|
|
157
149
|
unsigned long count = (unsigned long)record;
|
158
150
|
char *source = (char *)key;
|
159
151
|
|
160
|
-
asprintf(&(res->entries[res->num_entries++]), "%
|
152
|
+
asprintf(&(res->entries[res->num_entries++]), "%7li %s", count, source);
|
161
153
|
|
162
154
|
free(source);
|
163
155
|
return ST_DELETE;
|
@@ -231,6 +223,9 @@ memprof_stats(int argc, VALUE *argv, VALUE self)
|
|
231
223
|
}
|
232
224
|
free(res.entries);
|
233
225
|
|
226
|
+
if (out)
|
227
|
+
fclose(out);
|
228
|
+
|
234
229
|
track_objs = 1;
|
235
230
|
return Qnil;
|
236
231
|
}
|
@@ -256,46 +251,518 @@ memprof_track(int argc, VALUE *argv, VALUE self)
|
|
256
251
|
return Qnil;
|
257
252
|
}
|
258
253
|
|
254
|
+
#include <yajl/yajl_gen.h>
|
255
|
+
#include <stdarg.h>
|
256
|
+
#include "env.h"
|
257
|
+
#include "re.h"
|
258
|
+
|
259
|
+
yajl_gen_status
|
260
|
+
yajl_gen_cstr(yajl_gen gen, const char * str)
|
261
|
+
{
|
262
|
+
if (!str || str[0] == 0)
|
263
|
+
return yajl_gen_null(gen);
|
264
|
+
else
|
265
|
+
return yajl_gen_string(gen, (unsigned char *)str, strlen(str));
|
266
|
+
}
|
267
|
+
|
268
|
+
yajl_gen_status
|
269
|
+
yajl_gen_format(yajl_gen gen, char *format, ...)
|
270
|
+
{
|
271
|
+
va_list args;
|
272
|
+
char *str;
|
273
|
+
yajl_gen_status ret;
|
274
|
+
|
275
|
+
va_start(args, format);
|
276
|
+
vasprintf(&str, format, args);
|
277
|
+
va_end(args);
|
278
|
+
|
279
|
+
ret = yajl_gen_string(gen, (unsigned char *)str, strlen(str));
|
280
|
+
free(str);
|
281
|
+
return ret;
|
282
|
+
}
|
283
|
+
|
284
|
+
yajl_gen_status
|
285
|
+
yajl_gen_value(yajl_gen gen, VALUE obj)
|
286
|
+
{
|
287
|
+
if (FIXNUM_P(obj))
|
288
|
+
return yajl_gen_integer(gen, FIX2INT(obj));
|
289
|
+
else if (NIL_P(obj) || obj == Qundef)
|
290
|
+
return yajl_gen_null(gen);
|
291
|
+
else if (obj == Qtrue)
|
292
|
+
return yajl_gen_bool(gen, 1);
|
293
|
+
else if (obj == Qfalse)
|
294
|
+
return yajl_gen_bool(gen, 0);
|
295
|
+
else if (SYMBOL_P(obj))
|
296
|
+
return yajl_gen_format(gen, ":%s", rb_id2name(SYM2ID(obj)));
|
297
|
+
else
|
298
|
+
return yajl_gen_format(gen, "0x%x", obj);
|
299
|
+
}
|
300
|
+
|
301
|
+
static int
|
302
|
+
each_hash_entry(st_data_t key, st_data_t record, st_data_t arg)
|
303
|
+
{
|
304
|
+
yajl_gen gen = (yajl_gen)arg;
|
305
|
+
VALUE k = (VALUE)key;
|
306
|
+
VALUE v = (VALUE)record;
|
307
|
+
|
308
|
+
yajl_gen_value(gen, k);
|
309
|
+
yajl_gen_value(gen, v);
|
310
|
+
|
311
|
+
return ST_CONTINUE;
|
312
|
+
}
|
313
|
+
|
314
|
+
static int
|
315
|
+
each_ivar(st_data_t key, st_data_t record, st_data_t arg)
|
316
|
+
{
|
317
|
+
yajl_gen gen = (yajl_gen)arg;
|
318
|
+
ID id = (ID)key;
|
319
|
+
VALUE val = (VALUE)record;
|
320
|
+
|
321
|
+
yajl_gen_cstr(gen, rb_id2name(id));
|
322
|
+
yajl_gen_value(gen, val);
|
323
|
+
|
324
|
+
return ST_CONTINUE;
|
325
|
+
}
|
326
|
+
|
327
|
+
char *
|
328
|
+
nd_type_str(VALUE obj)
|
329
|
+
{
|
330
|
+
switch(nd_type(obj)) {
|
331
|
+
#define ND(type) case NODE_##type: return #type;
|
332
|
+
ND(METHOD); ND(FBODY); ND(CFUNC); ND(SCOPE);
|
333
|
+
ND(BLOCK); ND(IF); ND(CASE); ND(WHEN);
|
334
|
+
ND(OPT_N); ND(WHILE); ND(UNTIL); ND(ITER);
|
335
|
+
ND(FOR); ND(BREAK); ND(NEXT); ND(REDO);
|
336
|
+
ND(RETRY); ND(BEGIN); ND(RESCUE); ND(RESBODY);
|
337
|
+
ND(ENSURE); ND(AND); ND(OR); ND(NOT);
|
338
|
+
ND(MASGN); ND(LASGN); ND(DASGN); ND(DASGN_CURR);
|
339
|
+
ND(GASGN); ND(IASGN); ND(CDECL); ND(CVASGN);
|
340
|
+
ND(CVDECL); ND(OP_ASGN1); ND(OP_ASGN2); ND(OP_ASGN_AND);
|
341
|
+
ND(OP_ASGN_OR); ND(CALL); ND(FCALL); ND(VCALL);
|
342
|
+
ND(SUPER); ND(ZSUPER); ND(ARRAY); ND(ZARRAY);
|
343
|
+
ND(HASH); ND(RETURN); ND(YIELD); ND(LVAR);
|
344
|
+
ND(DVAR); ND(GVAR); ND(IVAR); ND(CONST);
|
345
|
+
ND(CVAR); ND(NTH_REF); ND(BACK_REF); ND(MATCH);
|
346
|
+
ND(MATCH2); ND(MATCH3); ND(LIT); ND(STR);
|
347
|
+
ND(DSTR); ND(XSTR); ND(DXSTR); ND(EVSTR);
|
348
|
+
ND(DREGX); ND(DREGX_ONCE); ND(ARGS); ND(ARGSCAT);
|
349
|
+
ND(ARGSPUSH); ND(SPLAT); ND(TO_ARY); ND(SVALUE);
|
350
|
+
ND(BLOCK_ARG); ND(BLOCK_PASS); ND(DEFN); ND(DEFS);
|
351
|
+
ND(ALIAS); ND(VALIAS); ND(UNDEF); ND(CLASS);
|
352
|
+
ND(MODULE); ND(SCLASS); ND(COLON2); ND(COLON3)
|
353
|
+
ND(CREF); ND(DOT2); ND(DOT3); ND(FLIP2);
|
354
|
+
ND(FLIP3); ND(ATTRSET); ND(SELF); ND(NIL);
|
355
|
+
ND(TRUE); ND(FALSE); ND(DEFINED); ND(NEWLINE);
|
356
|
+
ND(POSTEXE); ND(ALLOCA); ND(DMETHOD); ND(BMETHOD);
|
357
|
+
ND(MEMO); ND(IFUNC); ND(DSYM); ND(ATTRASGN);
|
358
|
+
ND(LAST);
|
359
|
+
default:
|
360
|
+
return "unknown";
|
361
|
+
}
|
362
|
+
}
|
363
|
+
|
364
|
+
static VALUE (*rb_classname)(VALUE);
|
365
|
+
|
366
|
+
/* TODO
|
367
|
+
* look for FL_EXIVAR flag and print ivars
|
368
|
+
* print more detail about Proc/struct BLOCK in T_DATA if freefunc == blk_free
|
369
|
+
* add Memprof.dump_all for full heap dump
|
370
|
+
* print details on different types of nodes (nd_next, nd_lit, nd_nth, etc)
|
371
|
+
*/
|
372
|
+
|
373
|
+
void
|
374
|
+
obj_dump(VALUE obj, yajl_gen gen)
|
375
|
+
{
|
376
|
+
int type;
|
377
|
+
yajl_gen_map_open(gen);
|
378
|
+
|
379
|
+
yajl_gen_cstr(gen, "address");
|
380
|
+
yajl_gen_value(gen, obj);
|
381
|
+
|
382
|
+
struct obj_track *tracker = NULL;
|
383
|
+
if (st_lookup(objs, (st_data_t)obj, (st_data_t *)&tracker)) {
|
384
|
+
yajl_gen_cstr(gen, "source");
|
385
|
+
yajl_gen_format(gen, "%s:%d", tracker->source, tracker->line);
|
386
|
+
}
|
387
|
+
|
388
|
+
yajl_gen_cstr(gen, "type");
|
389
|
+
switch (type=BUILTIN_TYPE(obj)) {
|
390
|
+
case T_DATA:
|
391
|
+
yajl_gen_cstr(gen, "data");
|
392
|
+
|
393
|
+
if (RBASIC(obj)->klass) {
|
394
|
+
yajl_gen_cstr(gen, "class");
|
395
|
+
yajl_gen_value(gen, RBASIC(obj)->klass);
|
396
|
+
|
397
|
+
yajl_gen_cstr(gen, "class_name");
|
398
|
+
VALUE name = rb_classname(RBASIC(obj)->klass);
|
399
|
+
if (RTEST(name))
|
400
|
+
yajl_gen_cstr(gen, RSTRING(name)->ptr);
|
401
|
+
else
|
402
|
+
yajl_gen_cstr(gen, 0);
|
403
|
+
}
|
404
|
+
break;
|
405
|
+
|
406
|
+
case T_FILE:
|
407
|
+
yajl_gen_cstr(gen, "file");
|
408
|
+
break;
|
409
|
+
|
410
|
+
case T_FLOAT:
|
411
|
+
yajl_gen_cstr(gen, "float");
|
412
|
+
|
413
|
+
yajl_gen_cstr(gen, "data");
|
414
|
+
yajl_gen_double(gen, RFLOAT(obj)->value);
|
415
|
+
break;
|
416
|
+
|
417
|
+
case T_BIGNUM:
|
418
|
+
yajl_gen_cstr(gen, "bignum");
|
419
|
+
|
420
|
+
yajl_gen_cstr(gen, "negative");
|
421
|
+
yajl_gen_bool(gen, RBIGNUM(obj)->sign == 0);
|
422
|
+
|
423
|
+
yajl_gen_cstr(gen, "length");
|
424
|
+
yajl_gen_integer(gen, RBIGNUM(obj)->len);
|
425
|
+
|
426
|
+
yajl_gen_cstr(gen, "data");
|
427
|
+
yajl_gen_string(gen, RBIGNUM(obj)->digits, RBIGNUM(obj)->len);
|
428
|
+
break;
|
429
|
+
|
430
|
+
case T_MATCH:
|
431
|
+
yajl_gen_cstr(gen, "match");
|
432
|
+
|
433
|
+
yajl_gen_cstr(gen, "data");
|
434
|
+
yajl_gen_value(gen, RMATCH(obj)->str);
|
435
|
+
break;
|
436
|
+
|
437
|
+
case T_REGEXP:
|
438
|
+
yajl_gen_cstr(gen, "regexp");
|
439
|
+
|
440
|
+
yajl_gen_cstr(gen, "length");
|
441
|
+
yajl_gen_integer(gen, RREGEXP(obj)->len);
|
442
|
+
|
443
|
+
yajl_gen_cstr(gen, "data");
|
444
|
+
yajl_gen_cstr(gen, RREGEXP(obj)->str);
|
445
|
+
break;
|
446
|
+
|
447
|
+
case T_SCOPE:
|
448
|
+
yajl_gen_cstr(gen, "scope");
|
449
|
+
|
450
|
+
struct SCOPE *scope = (struct SCOPE *)obj;
|
451
|
+
if (scope->local_tbl) {
|
452
|
+
int i = 1;
|
453
|
+
int n = scope->local_tbl[0];
|
454
|
+
VALUE *list = &scope->local_vars[-1];
|
455
|
+
VALUE cur = *list++;
|
456
|
+
|
457
|
+
yajl_gen_cstr(gen, "node");
|
458
|
+
yajl_gen_value(gen, cur);
|
459
|
+
|
460
|
+
if (n) {
|
461
|
+
yajl_gen_cstr(gen, "variables");
|
462
|
+
yajl_gen_map_open(gen);
|
463
|
+
while (n--) {
|
464
|
+
cur = *list++;
|
465
|
+
yajl_gen_cstr(gen, scope->local_tbl[i] == 95 ? "_" : rb_id2name(scope->local_tbl[i]));
|
466
|
+
yajl_gen_value(gen, cur);
|
467
|
+
i++;
|
468
|
+
}
|
469
|
+
yajl_gen_map_close(gen);
|
470
|
+
}
|
471
|
+
}
|
472
|
+
break;
|
473
|
+
|
474
|
+
case T_NODE:
|
475
|
+
yajl_gen_cstr(gen, "node");
|
476
|
+
|
477
|
+
yajl_gen_cstr(gen, "node_type");
|
478
|
+
yajl_gen_cstr(gen, nd_type_str(obj));
|
479
|
+
|
480
|
+
yajl_gen_cstr(gen, "file");
|
481
|
+
yajl_gen_cstr(gen, RNODE(obj)->nd_file);
|
482
|
+
|
483
|
+
yajl_gen_cstr(gen, "line");
|
484
|
+
yajl_gen_integer(gen, nd_line(obj));
|
485
|
+
|
486
|
+
yajl_gen_cstr(gen, "node_code");
|
487
|
+
yajl_gen_integer(gen, nd_type(obj));
|
488
|
+
|
489
|
+
switch (nd_type(obj)) {
|
490
|
+
case NODE_SCOPE:
|
491
|
+
break;
|
492
|
+
}
|
493
|
+
break;
|
494
|
+
|
495
|
+
case T_STRING:
|
496
|
+
yajl_gen_cstr(gen, "string");
|
497
|
+
|
498
|
+
yajl_gen_cstr(gen, "length");
|
499
|
+
yajl_gen_integer(gen, RSTRING(obj)->len);
|
500
|
+
|
501
|
+
if (FL_TEST(obj, ELTS_SHARED|FL_USER3)) {
|
502
|
+
yajl_gen_cstr(gen, "shared");
|
503
|
+
yajl_gen_value(gen, RSTRING(obj)->aux.shared);
|
504
|
+
|
505
|
+
yajl_gen_cstr(gen, "flags");
|
506
|
+
yajl_gen_array_open(gen);
|
507
|
+
if (FL_TEST(obj, ELTS_SHARED))
|
508
|
+
yajl_gen_cstr(gen, "elts_shared");
|
509
|
+
if (FL_TEST(obj, FL_USER3))
|
510
|
+
yajl_gen_cstr(gen, "str_assoc");
|
511
|
+
yajl_gen_array_close(gen);
|
512
|
+
} else {
|
513
|
+
yajl_gen_cstr(gen, "data");
|
514
|
+
yajl_gen_string(gen, (unsigned char *)RSTRING(obj)->ptr, RSTRING(obj)->len);
|
515
|
+
}
|
516
|
+
break;
|
517
|
+
|
518
|
+
case T_VARMAP:
|
519
|
+
yajl_gen_cstr(gen, "varmap");
|
520
|
+
|
521
|
+
struct RVarmap *vars = (struct RVarmap *)obj;
|
522
|
+
|
523
|
+
if (vars->next) {
|
524
|
+
yajl_gen_cstr(gen, "next");
|
525
|
+
yajl_gen_value(gen, (VALUE)vars->next);
|
526
|
+
}
|
527
|
+
|
528
|
+
if (vars->id) {
|
529
|
+
yajl_gen_cstr(gen, "data");
|
530
|
+
yajl_gen_map_open(gen);
|
531
|
+
yajl_gen_cstr(gen, rb_id2name(vars->id));
|
532
|
+
yajl_gen_value(gen, vars->val);
|
533
|
+
yajl_gen_map_close(gen);
|
534
|
+
}
|
535
|
+
break;
|
536
|
+
|
537
|
+
case T_CLASS:
|
538
|
+
case T_MODULE:
|
539
|
+
case T_ICLASS:
|
540
|
+
yajl_gen_cstr(gen, type==T_CLASS ? "class" : type==T_MODULE ? "module" : "iclass");
|
541
|
+
|
542
|
+
yajl_gen_cstr(gen, "name");
|
543
|
+
VALUE name = rb_classname(obj);
|
544
|
+
if (RTEST(name))
|
545
|
+
yajl_gen_cstr(gen, RSTRING(name)->ptr);
|
546
|
+
else
|
547
|
+
yajl_gen_cstr(gen, 0);
|
548
|
+
|
549
|
+
yajl_gen_cstr(gen, "super");
|
550
|
+
yajl_gen_value(gen, RCLASS(obj)->super);
|
551
|
+
|
552
|
+
yajl_gen_cstr(gen, "super_name");
|
553
|
+
VALUE super_name = rb_classname(RCLASS(obj)->super);
|
554
|
+
if (RTEST(super_name))
|
555
|
+
yajl_gen_cstr(gen, RSTRING(super_name)->ptr);
|
556
|
+
else
|
557
|
+
yajl_gen_cstr(gen, 0);
|
558
|
+
|
559
|
+
if (FL_TEST(obj, FL_SINGLETON)) {
|
560
|
+
yajl_gen_cstr(gen, "singleton");
|
561
|
+
yajl_gen_bool(gen, 1);
|
562
|
+
}
|
563
|
+
|
564
|
+
if (RCLASS(obj)->iv_tbl && RCLASS(obj)->iv_tbl->num_entries) {
|
565
|
+
yajl_gen_cstr(gen, "ivars");
|
566
|
+
yajl_gen_map_open(gen);
|
567
|
+
st_foreach(RCLASS(obj)->iv_tbl, each_ivar, (st_data_t)gen);
|
568
|
+
yajl_gen_map_close(gen);
|
569
|
+
}
|
570
|
+
|
571
|
+
if (type != T_ICLASS && RCLASS(obj)->m_tbl && RCLASS(obj)->m_tbl->num_entries) {
|
572
|
+
yajl_gen_cstr(gen, "methods");
|
573
|
+
yajl_gen_map_open(gen);
|
574
|
+
st_foreach(RCLASS(obj)->m_tbl, each_ivar, (st_data_t)gen);
|
575
|
+
yajl_gen_map_close(gen);
|
576
|
+
}
|
577
|
+
break;
|
578
|
+
|
579
|
+
case T_OBJECT:
|
580
|
+
yajl_gen_cstr(gen, "object");
|
581
|
+
|
582
|
+
yajl_gen_cstr(gen, "class");
|
583
|
+
yajl_gen_value(gen, RBASIC(obj)->klass);
|
584
|
+
|
585
|
+
yajl_gen_cstr(gen, "class_name");
|
586
|
+
yajl_gen_cstr(gen, rb_obj_classname(obj));
|
587
|
+
|
588
|
+
struct RClass *klass = RCLASS(obj);
|
589
|
+
|
590
|
+
if (klass->iv_tbl && klass->iv_tbl->num_entries) {
|
591
|
+
yajl_gen_cstr(gen, "ivars");
|
592
|
+
yajl_gen_map_open(gen);
|
593
|
+
st_foreach(klass->iv_tbl, each_ivar, (st_data_t)gen);
|
594
|
+
yajl_gen_map_close(gen);
|
595
|
+
}
|
596
|
+
break;
|
597
|
+
|
598
|
+
case T_ARRAY:
|
599
|
+
yajl_gen_cstr(gen, "array");
|
600
|
+
|
601
|
+
struct RArray *ary = RARRAY(obj);
|
602
|
+
|
603
|
+
yajl_gen_cstr(gen, "length");
|
604
|
+
yajl_gen_integer(gen, ary->len);
|
605
|
+
|
606
|
+
if (FL_TEST(obj, ELTS_SHARED)) {
|
607
|
+
yajl_gen_cstr(gen, "shared");
|
608
|
+
yajl_gen_value(gen, ary->aux.shared);
|
609
|
+
} else if (ary->len) {
|
610
|
+
yajl_gen_cstr(gen, "data");
|
611
|
+
yajl_gen_array_open(gen);
|
612
|
+
int i;
|
613
|
+
for(i=0; i < ary->len; i++)
|
614
|
+
yajl_gen_value(gen, ary->ptr[i]);
|
615
|
+
yajl_gen_array_close(gen);
|
616
|
+
}
|
617
|
+
break;
|
618
|
+
|
619
|
+
case T_HASH:
|
620
|
+
yajl_gen_cstr(gen, "hash");
|
621
|
+
|
622
|
+
struct RHash *hash = RHASH(obj);
|
623
|
+
|
624
|
+
yajl_gen_cstr(gen, "length");
|
625
|
+
if (hash->tbl)
|
626
|
+
yajl_gen_integer(gen, hash->tbl->num_entries);
|
627
|
+
else
|
628
|
+
yajl_gen_integer(gen, 0);
|
629
|
+
|
630
|
+
yajl_gen_cstr(gen, "default");
|
631
|
+
yajl_gen_value(gen, hash->ifnone);
|
632
|
+
|
633
|
+
if (hash->tbl && hash->tbl->num_entries) {
|
634
|
+
yajl_gen_cstr(gen, "data");
|
635
|
+
yajl_gen_map_open(gen);
|
636
|
+
st_foreach(hash->tbl, each_hash_entry, (st_data_t)gen);
|
637
|
+
yajl_gen_map_close(gen);
|
638
|
+
}
|
639
|
+
break;
|
640
|
+
|
641
|
+
default:
|
642
|
+
yajl_gen_cstr(gen, "unknown");
|
643
|
+
}
|
644
|
+
|
645
|
+
yajl_gen_cstr(gen, "code");
|
646
|
+
yajl_gen_integer(gen, BUILTIN_TYPE(obj));
|
647
|
+
|
648
|
+
yajl_gen_map_close(gen);
|
649
|
+
}
|
650
|
+
|
651
|
+
static int
|
652
|
+
objs_each_dump(st_data_t key, st_data_t record, st_data_t arg)
|
653
|
+
{
|
654
|
+
obj_dump((VALUE)key, (yajl_gen)arg);
|
655
|
+
return ST_CONTINUE;
|
656
|
+
}
|
657
|
+
|
658
|
+
void
|
659
|
+
json_print(void *ctx, const char * str, unsigned int len)
|
660
|
+
{
|
661
|
+
FILE *out = (FILE *)ctx;
|
662
|
+
fwrite(str, sizeof(char), len, out ? out : stdout);
|
663
|
+
}
|
664
|
+
|
665
|
+
static VALUE
|
666
|
+
memprof_dump(int argc, VALUE *argv, VALUE self)
|
667
|
+
{
|
668
|
+
VALUE str;
|
669
|
+
FILE *out = NULL;
|
670
|
+
|
671
|
+
if (!track_objs)
|
672
|
+
rb_raise(rb_eRuntimeError, "object tracking disabled, call Memprof.start first");
|
673
|
+
|
674
|
+
rb_scan_args(argc, argv, "01", &str);
|
675
|
+
|
676
|
+
if (RTEST(str)) {
|
677
|
+
out = fopen(StringValueCStr(str), "w");
|
678
|
+
if (!out)
|
679
|
+
rb_raise(rb_eArgError, "unable to open output file");
|
680
|
+
}
|
681
|
+
|
682
|
+
yajl_gen_config conf = { .beautify = 1, .indentString = " " };
|
683
|
+
yajl_gen gen = yajl_gen_alloc2((yajl_print_t)&json_print, &conf, NULL, (void*)out);
|
684
|
+
|
685
|
+
track_objs = 0;
|
686
|
+
|
687
|
+
yajl_gen_array_open(gen);
|
688
|
+
st_foreach(objs, objs_each_dump, (st_data_t)gen);
|
689
|
+
yajl_gen_array_close(gen);
|
690
|
+
yajl_gen_free(gen);
|
691
|
+
|
692
|
+
if (out)
|
693
|
+
fclose(out);
|
694
|
+
|
695
|
+
track_objs = 1;
|
696
|
+
|
697
|
+
return Qnil;
|
698
|
+
}
|
699
|
+
|
700
|
+
static VALUE
|
701
|
+
memprof_dump_all(int argc, VALUE *argv, VALUE self)
|
702
|
+
{
|
703
|
+
int sizeof_RVALUE = bin_type_size("RVALUE");
|
704
|
+
char *heaps = *(char**)bin_find_symbol("heaps",0);
|
705
|
+
int heaps_used = *(int*)bin_find_symbol("heaps_used",0);
|
706
|
+
int sizeof_heaps_slot = bin_type_size("heaps_slot");
|
707
|
+
int offset_limit = bin_type_member_offset("heaps_slot", "limit");
|
708
|
+
int offset_slot = bin_type_member_offset("heaps_slot", "slot");
|
709
|
+
char *p, *pend;
|
710
|
+
int i, limit;
|
711
|
+
|
712
|
+
if (sizeof_RVALUE < 0 || sizeof_heaps_slot < 0)
|
713
|
+
rb_raise(eUnsupported, "could not find internal heap");
|
714
|
+
|
715
|
+
VALUE str;
|
716
|
+
FILE *out = NULL;
|
717
|
+
|
718
|
+
rb_scan_args(argc, argv, "01", &str);
|
719
|
+
|
720
|
+
if (RTEST(str)) {
|
721
|
+
out = fopen(StringValueCStr(str), "w");
|
722
|
+
if (!out)
|
723
|
+
rb_raise(rb_eArgError, "unable to open output file");
|
724
|
+
}
|
725
|
+
|
726
|
+
yajl_gen_config conf = { .beautify = 1, .indentString = " " };
|
727
|
+
yajl_gen gen = yajl_gen_alloc2((yajl_print_t)&json_print, &conf, NULL, (void*)out);
|
728
|
+
|
729
|
+
track_objs = 0;
|
730
|
+
|
731
|
+
yajl_gen_array_open(gen);
|
732
|
+
|
733
|
+
for (i=0; i < heaps_used; i++) {
|
734
|
+
p = *(char**)(heaps + (i * sizeof_heaps_slot) + offset_slot);
|
735
|
+
limit = *(int*)(heaps + (i * sizeof_heaps_slot) + offset_limit);
|
736
|
+
pend = p + (sizeof_RVALUE * limit);
|
737
|
+
|
738
|
+
while (p < pend) {
|
739
|
+
if (RBASIC(p)->flags)
|
740
|
+
obj_dump((VALUE)p, gen);
|
741
|
+
|
742
|
+
p += sizeof_RVALUE;
|
743
|
+
}
|
744
|
+
}
|
745
|
+
|
746
|
+
yajl_gen_array_close(gen);
|
747
|
+
yajl_gen_free(gen);
|
748
|
+
|
749
|
+
if (out)
|
750
|
+
fclose(out);
|
751
|
+
|
752
|
+
track_objs = 1;
|
753
|
+
|
754
|
+
return Qnil;
|
755
|
+
}
|
756
|
+
|
259
757
|
static void
|
260
758
|
create_tramp_table()
|
261
759
|
{
|
262
760
|
int i = 0;
|
263
761
|
void *region = NULL;
|
762
|
+
size_t tramp_sz = 0, inline_tramp_sz = 0;
|
264
763
|
|
265
|
-
|
266
|
-
|
267
|
-
.mov = {'\x48', '\xbb'}, // mov addr into rbx
|
268
|
-
.addr = error_tramp, // ^^^
|
269
|
-
.callq = {'\xff', '\xd3'}, // callq rbx
|
270
|
-
.rbx_restore = {'\x5b'}, // pop rbx
|
271
|
-
.ret = {'\xc3'}, // ret
|
272
|
-
};
|
273
|
-
|
274
|
-
struct inline_tramp_tbl_entry inline_ent = {
|
275
|
-
.rex = {'\x48'},
|
276
|
-
.mov = {'\x89'},
|
277
|
-
.src_reg = {'\x05'},
|
278
|
-
.mov_displacement = 0,
|
279
|
-
|
280
|
-
.frame = {
|
281
|
-
.push_rdi = {'\x57'},
|
282
|
-
.mov_rdi = {'\x48', '\x8b', '\x3d'},
|
283
|
-
.rdi_source_displacement = 0,
|
284
|
-
.push_rbx = {'\x53'},
|
285
|
-
.push_rbp = {'\x55'},
|
286
|
-
.save_rsp = {'\x48', '\x89', '\xe5'},
|
287
|
-
.align_rsp = {'\x48', '\x83', '\xe4', '\xf0'},
|
288
|
-
.mov = {'\x48', '\xbb'},
|
289
|
-
.addr = error_tramp,
|
290
|
-
.callq = {'\xff', '\xd3'},
|
291
|
-
.leave = {'\xc9'},
|
292
|
-
.rbx_restore = {'\x5b'},
|
293
|
-
.rdi_restore = {'\x5f'},
|
294
|
-
},
|
295
|
-
|
296
|
-
.jmp = {'\xe9'},
|
297
|
-
.jmp_displacement = 0,
|
298
|
-
};
|
764
|
+
void *ent = arch_get_st2_tramp(&tramp_sz);
|
765
|
+
void *inline_ent = arch_get_inline_st2_tramp(&inline_tramp_sz);
|
299
766
|
|
300
767
|
if ((region = bin_allocate_page()) == MAP_FAILED) {
|
301
768
|
fprintf(stderr, "Failed to allocate memory for stage 1 trampolines.\n");
|
@@ -305,159 +772,72 @@ create_tramp_table()
|
|
305
772
|
tramp_table = region;
|
306
773
|
inline_tramp_table = region + pagesize/2;
|
307
774
|
|
308
|
-
for (i = 0; i < (pagesize/2)/
|
309
|
-
memcpy(tramp_table + i,
|
775
|
+
for (i = 0; i < (pagesize/2)/tramp_sz; i++) {
|
776
|
+
memcpy(tramp_table + i, ent, tramp_sz);
|
310
777
|
}
|
311
778
|
|
312
|
-
for (i = 0; i < (pagesize/2)/
|
313
|
-
memcpy(inline_tramp_table + i,
|
779
|
+
for (i = 0; i < (pagesize/2)/inline_tramp_sz; i++) {
|
780
|
+
memcpy(inline_tramp_table + i, inline_ent, inline_tramp_sz);
|
314
781
|
}
|
315
782
|
}
|
316
783
|
|
317
|
-
|
318
|
-
|
784
|
+
#define FREELIST_INLINES (3)
|
785
|
+
|
786
|
+
static void
|
787
|
+
hook_freelist(int entry)
|
319
788
|
{
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
}
|
789
|
+
size_t sizes[FREELIST_INLINES], i = 0;
|
790
|
+
void *freelist_inliners[FREELIST_INLINES];
|
791
|
+
void *freelist = NULL;
|
792
|
+
unsigned char *byte = NULL;
|
793
|
+
|
794
|
+
freelist_inliners[0] = bin_find_symbol("gc_sweep", &sizes[0]);
|
795
|
+
/* sometimes gc_sweep gets inlined in garbage_collect */
|
796
|
+
if (!freelist_inliners[0]) {
|
797
|
+
freelist_inliners[0] = bin_find_symbol("garbage_collect", &sizes[0]);
|
798
|
+
if (!freelist_inliners[0]) {
|
799
|
+
/* couldn't find garbage_collect either. */
|
800
|
+
fprintf(stderr, "Couldn't find gc_sweep or garbage_collect!\n");
|
801
|
+
return;
|
334
802
|
}
|
335
|
-
byte++;
|
336
803
|
}
|
337
|
-
}
|
338
804
|
|
805
|
+
freelist_inliners[1] = bin_find_symbol("finalize_list", &sizes[1]);
|
806
|
+
if (!freelist_inliners[1]) {
|
807
|
+
fprintf(stderr, "Couldn't find finalize_list!\n");
|
808
|
+
/* XXX continue or exit? */
|
809
|
+
}
|
339
810
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
811
|
+
freelist_inliners[2] = bin_find_symbol("rb_gc_force_recycle", &sizes[2]);
|
812
|
+
if (!freelist_inliners[2]) {
|
813
|
+
fprintf(stderr, "Couldn't find rb_gc_force_recycle!\n");
|
814
|
+
/* XXX continue or exit? */
|
815
|
+
}
|
345
816
|
|
346
|
-
|
347
|
-
|
348
|
-
|
817
|
+
freelist = bin_find_symbol("freelist", NULL);
|
818
|
+
if (!freelist) {
|
819
|
+
fprintf(stderr, "Couldn't find freelist!\n");
|
820
|
+
return;
|
349
821
|
}
|
350
822
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
/* This is the stage 1 trampoline for hooking the inlined add_freelist
|
363
|
-
* function .
|
364
|
-
*
|
365
|
-
* NOTE: The original instruction mov %reg, freelist is 7 bytes wide,
|
366
|
-
* whereas jmpq $displacement is only 5 bytes wide. We *must* pad out
|
367
|
-
* the next two bytes. This will be important to remember below.
|
368
|
-
*/
|
369
|
-
struct tramp_inline tramp = {
|
370
|
-
.jmp = {'\xe9'},
|
371
|
-
.displacement = 0,
|
372
|
-
.pad = {'\x90', '\x90'},
|
373
|
-
};
|
374
|
-
|
375
|
-
struct inline_tramp_tbl_entry *inl_tramp_st2 = NULL;
|
376
|
-
|
377
|
-
for (;i < max;) {
|
378
|
-
/* make sure it is a mov instruction */
|
379
|
-
if (byte[1] == '\x89') {
|
380
|
-
|
381
|
-
/* Read the REX byte to make sure it is a mov that we care about */
|
382
|
-
if ((byte[0] == '\x48') ||
|
383
|
-
(byte[0] == '\x4c')) {
|
384
|
-
|
385
|
-
/* Grab the target of the mov. REMEMBER: in this case the target is
|
386
|
-
* a 32bit displacment that gets added to RIP (where RIP is the adress of
|
387
|
-
* the next instruction).
|
388
|
-
*/
|
389
|
-
mov_target = *(uint32_t *)(byte + 3);
|
390
|
-
|
391
|
-
/* Sanity check. Ensure that the displacement from freelist to the next
|
392
|
-
* instruction matches the mov_target. If so, we know this mov is
|
393
|
-
* updating freelist.
|
394
|
-
*/
|
395
|
-
if ((freelist - (void *)(byte+7)) == mov_target) {
|
396
|
-
/* Before the stage 1 trampoline gets written, we need to generate
|
397
|
-
* the code for the stage 2 trampoline. Let's copy over the REX byte
|
398
|
-
* and the byte which mentions the source register into the stage 2
|
399
|
-
* trampoline.
|
400
|
-
*/
|
401
|
-
inl_tramp_st2 = inline_tramp_table + entry;
|
402
|
-
inl_tramp_st2->rex[0] = byte[0];
|
403
|
-
inl_tramp_st2->src_reg[0] = byte[2];
|
404
|
-
|
405
|
-
/* Setup the stage 1 trampoline. Calculate the displacement to
|
406
|
-
* the stage 2 trampoline from the next instruction.
|
407
|
-
*
|
408
|
-
* REMEMBER!!!! The next instruction will be NOP after our stage 1
|
409
|
-
* trampoline is written. This is 5 bytes into the structure, even
|
410
|
-
* though the original instruction we overwrote was 7 bytes.
|
411
|
-
*/
|
412
|
-
tramp.displacement = (uint32_t)((void *)(inl_tramp_st2) - (void *)(byte+5));
|
413
|
-
|
414
|
-
/* Figure out what page the stage 1 tramp is gonna be written to, mark
|
415
|
-
* it WRITE, write the trampoline in, and then remove WRITE permission.
|
416
|
-
*/
|
417
|
-
aligned_addr = (void*)(((long)byte)&~(0xffff));
|
418
|
-
mprotect(aligned_addr, (((void *)byte) - aligned_addr) + 10, PROT_READ|PROT_WRITE|PROT_EXEC);
|
419
|
-
memcpy(byte, &tramp, sizeof(struct tramp_inline));
|
420
|
-
mprotect(aligned_addr, (((void *)byte) - aligned_addr) + 10, PROT_READ|PROT_EXEC);
|
421
|
-
|
422
|
-
/* Finish setting up the stage 2 trampoline. */
|
423
|
-
|
424
|
-
/* calculate the displacement to freelist from the next instruction.
|
425
|
-
*
|
426
|
-
* This is used to replicate the original instruction we overwrote.
|
427
|
-
*/
|
428
|
-
inl_tramp_st2->mov_displacement = freelist - (void *)&(inl_tramp_st2->frame);
|
429
|
-
|
430
|
-
/* fill in the displacement to freelist from the next instruction.
|
431
|
-
*
|
432
|
-
* This is to arrange for the new value in freelist to be in %rdi, and as such
|
433
|
-
* be the first argument to the C handler. As per the amd64 ABI.
|
434
|
-
*/
|
435
|
-
inl_tramp_st2->frame.rdi_source_displacement = freelist - (void *)&(inl_tramp_st2->frame.push_rbx);
|
436
|
-
|
437
|
-
/* jmp back to the instruction after stage 1 trampoline was inserted
|
438
|
-
*
|
439
|
-
* This can be 5 or 7, it doesn't matter. If its 5, we'll hit our 2
|
440
|
-
* NOPS. If its 7, we'll land directly on the next instruction.
|
441
|
-
*/
|
442
|
-
inl_tramp_st2->jmp_displacement = (uint32_t)((void *)(byte + 7) -
|
443
|
-
(void *)(inline_tramp_table + entry + 1));
|
444
|
-
|
445
|
-
/* write the address of our C level trampoline in to the structure */
|
446
|
-
inl_tramp_st2->frame.addr = freelist_tramp;
|
447
|
-
|
448
|
-
/* track the new entry and new trampoline size */
|
449
|
-
entry++;
|
450
|
-
inline_tramp_size++;
|
451
|
-
}
|
452
|
-
}
|
823
|
+
/* start the search for places to insert the inline tramp */
|
824
|
+
|
825
|
+
byte = freelist_inliners[i];
|
826
|
+
|
827
|
+
while (i < FREELIST_INLINES) {
|
828
|
+
if (arch_insert_inline_st2_tramp(byte, freelist, freelist_tramp,
|
829
|
+
&inline_tramp_table[entry]) == 0) {
|
830
|
+
/* insert occurred, so increment internal counters for the tramp table */
|
831
|
+
entry++;
|
832
|
+
inline_tramp_size++;
|
453
833
|
}
|
454
834
|
|
455
|
-
if
|
456
|
-
|
457
|
-
|
458
|
-
|
835
|
+
/* if we've looked at all the bytes in this function... */
|
836
|
+
if (((void *)byte - freelist_inliners[i]) >= sizes[i]) {
|
837
|
+
/* move on to the next function */
|
838
|
+
i++;
|
839
|
+
byte = freelist_inliners[i];
|
459
840
|
}
|
460
|
-
count++;
|
461
841
|
byte++;
|
462
842
|
}
|
463
843
|
}
|
@@ -466,13 +846,14 @@ static void
|
|
466
846
|
insert_tramp(char *trampee, void *tramp)
|
467
847
|
{
|
468
848
|
void *trampee_addr = bin_find_symbol(trampee, NULL);
|
849
|
+
void *plt_addr = NULL;
|
469
850
|
int entry = tramp_size;
|
470
851
|
int inline_ent = inline_tramp_size;
|
852
|
+
void *info;
|
471
853
|
|
472
854
|
if (trampee_addr == NULL) {
|
473
855
|
if (strcmp("add_freelist", trampee) == 0) {
|
474
856
|
/* XXX super hack */
|
475
|
-
inline_tramp_table[inline_tramp_size].frame.addr = tramp;
|
476
857
|
inline_tramp_size++;
|
477
858
|
hook_freelist(inline_ent);
|
478
859
|
} else {
|
@@ -480,8 +861,8 @@ insert_tramp(char *trampee, void *tramp)
|
|
480
861
|
}
|
481
862
|
} else {
|
482
863
|
tramp_table[tramp_size].addr = tramp;
|
864
|
+
bin_update_image(entry, trampee, &tramp_table[tramp_size]);
|
483
865
|
tramp_size++;
|
484
|
-
bin_update_image(entry, trampee_addr);
|
485
866
|
}
|
486
867
|
}
|
487
868
|
|
@@ -489,11 +870,14 @@ void
|
|
489
870
|
Init_memprof()
|
490
871
|
{
|
491
872
|
VALUE memprof = rb_define_module("Memprof");
|
873
|
+
eUnsupported = rb_define_class_under(memprof, "Unsupported", rb_eStandardError);
|
492
874
|
rb_define_singleton_method(memprof, "start", memprof_start, 0);
|
493
875
|
rb_define_singleton_method(memprof, "stop", memprof_stop, 0);
|
494
876
|
rb_define_singleton_method(memprof, "stats", memprof_stats, -1);
|
495
877
|
rb_define_singleton_method(memprof, "stats!", memprof_stats_bang, -1);
|
496
878
|
rb_define_singleton_method(memprof, "track", memprof_track, -1);
|
879
|
+
rb_define_singleton_method(memprof, "dump", memprof_dump, -1);
|
880
|
+
rb_define_singleton_method(memprof, "dump_all", memprof_dump_all, -1);
|
497
881
|
|
498
882
|
pagesize = getpagesize();
|
499
883
|
objs = st_init_numtable();
|
@@ -507,5 +891,10 @@ Init_memprof()
|
|
507
891
|
insert_tramp("add_freelist", freelist_tramp);
|
508
892
|
#endif
|
509
893
|
|
894
|
+
rb_classname = bin_find_symbol("classname", 0);
|
895
|
+
|
896
|
+
if (getenv("MEMPROF"))
|
897
|
+
track_objs = 1;
|
898
|
+
|
510
899
|
return;
|
511
900
|
}
|