memprof 0.0.1
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 +18 -0
- data/ext/extconf.rb +5 -0
- data/ext/memprof.c +184 -0
- data/memprof.gemspec +17 -0
- metadata +59 -0
data/README
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
memprof (c) Joe Damato @joedamato http://timetobleed.com
|
2
|
+
|
3
|
+
What is memprof?
|
4
|
+
================
|
5
|
+
|
6
|
+
Memprof hopes to become a memory profiler for Ruby that will work without
|
7
|
+
modifying your Ruby binary. You just require the gem and off you go.
|
8
|
+
|
9
|
+
|
10
|
+
When will it be done?
|
11
|
+
=====================
|
12
|
+
|
13
|
+
No idea, but soon I hope.
|
14
|
+
|
15
|
+
Why release this then?
|
16
|
+
======================
|
17
|
+
|
18
|
+
This gem illustrates an ugly hack that I thought might interest other people.
|
data/ext/extconf.rb
ADDED
data/ext/memprof.c
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
#define _GNU_SOURCE
|
2
|
+
#include <err.h>
|
3
|
+
#include <fcntl.h>
|
4
|
+
#include <gelf.h>
|
5
|
+
#include <stdio.h>
|
6
|
+
#include <stdint.h>
|
7
|
+
#include <stdlib.h>
|
8
|
+
#include <unistd.h>
|
9
|
+
#include <link.h>
|
10
|
+
#include <sysexits.h>
|
11
|
+
#include <sys/mman.h>
|
12
|
+
|
13
|
+
#include <ruby.h>
|
14
|
+
#include <intern.h>
|
15
|
+
|
16
|
+
struct tramp_tbl_entry {
|
17
|
+
unsigned char mov[2];
|
18
|
+
long long addr;
|
19
|
+
unsigned char callq[2];
|
20
|
+
unsigned char ret;
|
21
|
+
unsigned char pad[3];
|
22
|
+
} __attribute__((__packed__));;
|
23
|
+
|
24
|
+
|
25
|
+
static void *text_segment = NULL;
|
26
|
+
static unsigned long text_segment_len = 0;
|
27
|
+
|
28
|
+
/*
|
29
|
+
trampoline specific stuff
|
30
|
+
*/
|
31
|
+
static struct tramp_tbl_entry *tramp_table = NULL;
|
32
|
+
static size_t tramp_size = 0;
|
33
|
+
|
34
|
+
/*
|
35
|
+
ELF specific stuff
|
36
|
+
*/
|
37
|
+
static ElfW(Shdr) symtab_shdr;
|
38
|
+
static Elf *elf = NULL;
|
39
|
+
static Elf_Data *symtab_data = NULL;
|
40
|
+
|
41
|
+
|
42
|
+
static void
|
43
|
+
error_tramp() {
|
44
|
+
printf("WARNING: NO TRAMPOLINE SET.\n");
|
45
|
+
return;
|
46
|
+
}
|
47
|
+
|
48
|
+
static VALUE
|
49
|
+
newobj_tramp() {
|
50
|
+
printf("source = %s, line = %d\n", ruby_sourcefile, ruby_sourceline);
|
51
|
+
return rb_newobj();
|
52
|
+
}
|
53
|
+
|
54
|
+
static void
|
55
|
+
create_tramp_table() {
|
56
|
+
int i = 0;
|
57
|
+
|
58
|
+
struct tramp_tbl_entry ent = {
|
59
|
+
.mov = {'\x48', '\xbb'},
|
60
|
+
.addr = (long long)&error_tramp,
|
61
|
+
.callq = { '\xff', '\xd3' },
|
62
|
+
.ret = '\xc3',
|
63
|
+
.pad = { '\x90', '\x90', '\x90'},
|
64
|
+
};
|
65
|
+
|
66
|
+
tramp_table = mmap(NULL, 4096, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_32BIT|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
67
|
+
if (tramp_table != MAP_FAILED) {
|
68
|
+
for (; i < 4096/sizeof(struct tramp_tbl_entry); i ++ ) {
|
69
|
+
memcpy(tramp_table + i, &ent, sizeof(struct tramp_tbl_entry));
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
static void
|
75
|
+
update_image(int entry, void *trampee_addr) {
|
76
|
+
char *byte = text_segment;
|
77
|
+
size_t count = 0;
|
78
|
+
int fn_addr = 0;
|
79
|
+
void *aligned_addr = NULL;
|
80
|
+
|
81
|
+
for(; count < text_segment_len; count++) {
|
82
|
+
if (*byte == '\xe8') {
|
83
|
+
fn_addr = *(int *)(byte+1);
|
84
|
+
if (((void *)trampee_addr - (void *)(byte+5)) == fn_addr) {
|
85
|
+
aligned_addr = (void*)(((long)byte+1)&~(0xffff));
|
86
|
+
mprotect(aligned_addr, (((void *)byte+1) - aligned_addr) + 10, PROT_READ|PROT_WRITE|PROT_EXEC);
|
87
|
+
*(int *)(byte+1) = (uint32_t)((void *)(tramp_table + entry) - (void *)(byte + 5));
|
88
|
+
mprotect(aligned_addr, (((void *)byte+1) - aligned_addr) + 10, PROT_READ|PROT_EXEC);
|
89
|
+
}
|
90
|
+
}
|
91
|
+
byte++;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
static void *
|
96
|
+
find_symbol(char *sym) {
|
97
|
+
char *name = NULL;
|
98
|
+
|
99
|
+
/*now print the symbols*/
|
100
|
+
ElfW(Sym) *esym = (ElfW(Sym)*) symtab_data->d_buf;
|
101
|
+
ElfW(Sym) *lastsym = (ElfW(Sym)*) ((char*) symtab_data->d_buf + symtab_data->d_size);
|
102
|
+
/* now loop through the symbol table and print it*/
|
103
|
+
for (; esym < lastsym; esym++){
|
104
|
+
if ((esym->st_value == 0) ||
|
105
|
+
(ELF32_ST_BIND(esym->st_info)== STB_WEAK) ||
|
106
|
+
(ELF32_ST_BIND(esym->st_info)== STB_NUM) ||
|
107
|
+
(ELF32_ST_TYPE(esym->st_info)!= STT_FUNC))
|
108
|
+
continue;
|
109
|
+
name = elf_strptr(elf, symtab_shdr.sh_link, (size_t)esym->st_name);
|
110
|
+
if (strcmp(name, sym) == 0) {
|
111
|
+
return (void *)esym->st_value;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
return NULL;
|
115
|
+
}
|
116
|
+
|
117
|
+
static void
|
118
|
+
insert_tramp(char *trampee, void *tramp) {
|
119
|
+
void *trampee_addr = find_symbol(trampee);
|
120
|
+
int entry = tramp_size;
|
121
|
+
tramp_table[tramp_size].addr = (long long)tramp;
|
122
|
+
tramp_size++;
|
123
|
+
update_image(entry, trampee_addr);
|
124
|
+
}
|
125
|
+
|
126
|
+
void Init_memprof()
|
127
|
+
{
|
128
|
+
int fd;
|
129
|
+
ElfW(Shdr) shdr;
|
130
|
+
size_t shstrndx;
|
131
|
+
char *filename;
|
132
|
+
Elf_Scn *scn;
|
133
|
+
|
134
|
+
if (elf_version(EV_CURRENT) == EV_NONE)
|
135
|
+
errx(EX_SOFTWARE, "ELF library initialization failed: %s",
|
136
|
+
elf_errmsg(-1));
|
137
|
+
|
138
|
+
asprintf(&filename, "/proc/%ld/exe", (long)getpid());
|
139
|
+
|
140
|
+
if ((fd = open(filename, O_RDONLY, 0)) < 0)
|
141
|
+
err(EX_NOINPUT, "open \%s\" failed", filename);
|
142
|
+
|
143
|
+
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
|
144
|
+
errx(EX_SOFTWARE, "elf_begin() failed: %s.",
|
145
|
+
elf_errmsg(-1));
|
146
|
+
|
147
|
+
if (elf_kind(elf) != ELF_K_ELF)
|
148
|
+
errx(EX_DATAERR, "%s is not an ELF object.", filename);
|
149
|
+
|
150
|
+
if (elf_getshstrndx(elf, &shstrndx) == 0)
|
151
|
+
errx(EX_SOFTWARE, "getshstrndx() failed: %s.",
|
152
|
+
elf_errmsg(-1));
|
153
|
+
|
154
|
+
scn = NULL;
|
155
|
+
|
156
|
+
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
157
|
+
if (gelf_getshdr(scn, &shdr) != &shdr)
|
158
|
+
errx(EX_SOFTWARE, "getshdr() failed: %s.",
|
159
|
+
elf_errmsg(-1));
|
160
|
+
|
161
|
+
if (shdr.sh_type == SHT_PROGBITS &&
|
162
|
+
(shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)) &&
|
163
|
+
strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".text") == 0) {
|
164
|
+
|
165
|
+
text_segment = (void *)shdr.sh_addr;
|
166
|
+
text_segment_len = shdr.sh_size;
|
167
|
+
} else if (shdr.sh_type == SHT_SYMTAB) {
|
168
|
+
symtab_shdr = shdr;
|
169
|
+
if ((symtab_data = elf_getdata(scn,symtab_data)) == 0 || symtab_data->d_size == 0) {
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
create_tramp_table();
|
177
|
+
|
178
|
+
insert_tramp("rb_newobj", newobj_tramp);
|
179
|
+
#if 0
|
180
|
+
(void) elf_end(e);
|
181
|
+
(void) close(fd);
|
182
|
+
#endif
|
183
|
+
return;
|
184
|
+
}
|
data/memprof.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
2
|
+
s.name = 'memprof'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.date = '2009-11-21'
|
5
|
+
s.summary = 'Ruby memory profiler gem'
|
6
|
+
s.email = "ice799@gmail.com"
|
7
|
+
s.homepage = "http://github.com/ice799/memprof"
|
8
|
+
s.description = "Ruby memory profiler gem"
|
9
|
+
s.has_rdoc = false
|
10
|
+
s.authors = ["Joe Damato"]
|
11
|
+
s.extensions = "ext/extconf.rb"
|
12
|
+
s.require_paths << "ext"
|
13
|
+
s.files = ["README",
|
14
|
+
"memprof.gemspec",
|
15
|
+
"ext/memprof.c",
|
16
|
+
"ext/extconf.rb"]
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: memprof
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joe Damato
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-21 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Ruby memory profiler gem
|
17
|
+
email: ice799@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions:
|
21
|
+
- ext/extconf.rb
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- memprof.gemspec
|
27
|
+
- ext/memprof.c
|
28
|
+
- ext/extconf.rb
|
29
|
+
has_rdoc: true
|
30
|
+
homepage: http://github.com/ice799/memprof
|
31
|
+
licenses: []
|
32
|
+
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
- ext
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
requirements: []
|
52
|
+
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 1.3.5
|
55
|
+
signing_key:
|
56
|
+
specification_version: 3
|
57
|
+
summary: Ruby memory profiler gem
|
58
|
+
test_files: []
|
59
|
+
|