memprof 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +18 -0
  2. data/ext/extconf.rb +5 -0
  3. data/ext/memprof.c +184 -0
  4. data/memprof.gemspec +17 -0
  5. 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.
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ if (have_library('elf', 'gelf_getshdr'))
4
+ create_makefile ('memprof')
5
+ end
@@ -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
+ }
@@ -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
+