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.
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
+