memprof 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|