memprof 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,284 @@
1
+ # memprof
2
+ (c) Joe Damato
3
+ @joedamato
4
+ http://timetobleed.com
5
+
6
+ Memprof is a Ruby level memory profiler that can help you find reference
7
+ leaks in your application.
8
+
9
+ Memprof can also do very lightweight function call tracing to figure out
10
+ which system and library calls are happening in your code.
11
+
12
+ # Installation
13
+
14
+ gem install memprof
15
+
16
+ # API
17
+
18
+ ## Memprof.stats
19
+
20
+ Memprof.start
21
+ 12.times{ "abc" }
22
+ Memprof.stats
23
+ Memprof.stop
24
+
25
+ Start tracking file/line information for objects created after calling
26
+ `Memprof.start`, and print out a summary of file:line/class pairs
27
+ created.
28
+
29
+ 12 file.rb:2:String
30
+
31
+ *Note*: Call `Memprof.stats` again after `GC.start` to see which objects
32
+ are cleaned up by the garbage collector:
33
+
34
+ Memprof.start
35
+ 10.times{ $last_str = "abc" }
36
+
37
+ puts '=== Before GC'
38
+ Memprof.stats
39
+
40
+ puts '=== After GC'
41
+ GC.start
42
+ Memprof.stats
43
+
44
+ Memprof.stop
45
+
46
+ After `GC.start`, only the very last instance of `"abc"` will still
47
+ exist:
48
+
49
+ === Before GC
50
+ 10 file.rb:2:String
51
+ === After GC
52
+ 1 file.rb:2:String
53
+
54
+ *Note*: Use `Memprof.stats("/path/to/file")` to write results to a file.
55
+
56
+ *Note*: Use `Memprof.stats!` to clear out tracking data after printing
57
+ out results.
58
+
59
+ ## Memprof.track
60
+
61
+ Simple wrapper for `Memprof.stats` that will start/stop memprof around a
62
+ given block of ruby code.
63
+
64
+ Memprof.track{
65
+ 100.times{ "abc" }
66
+ 100.times{ 1.23 + 1 }
67
+ 100.times{ Module.new }
68
+ }
69
+
70
+ For the block of ruby code, print out file:line/class pairs for
71
+ ruby objects created.
72
+
73
+ 100 file.rb:2:String
74
+ 100 file.rb:3:Float
75
+ 100 file.rb:4:Module
76
+
77
+ *Note*: You can call GC.start at the end of the block to print out only
78
+ objects that are 'leaking' (i.e. objects that still have inbound
79
+ references).
80
+
81
+ *Note*: Use `Memprof.track("/path/to/file")` to write the results to a
82
+ file instead of stdout.
83
+
84
+ ## Memprof.dump
85
+
86
+ Memprof.dump{
87
+ "hello" + "world"
88
+ }
89
+
90
+ Dump out all objects created in a given ruby block as detailed json
91
+ objects.
92
+
93
+ {
94
+ "_id": "0x15e5018",
95
+
96
+ "file": "file.rb",
97
+ "line": 2,
98
+
99
+ "type": "string",
100
+ "class_name": "String",
101
+
102
+ "length": 10,
103
+ "data": "helloworld"
104
+ }
105
+
106
+ *Note*: Use `Memprof.dump("/path/to/filename")` to write the json output
107
+ to a file, one per line.
108
+
109
+ ## Memprof.dump_all
110
+
111
+ Memprof.dump_all("myapp_heap.json")
112
+
113
+ Dump out all live objects inside the Ruby VM to `myapp_heap.json`, one
114
+ per line.
115
+
116
+ ### [memprof.com](http://memprof.com) heap visualizer
117
+
118
+ # load memprof before requiring rubygems, so objects created by
119
+ # rubygems itself are tracked by memprof too
120
+ require `gem which memprof/signal`.strip
121
+
122
+ require 'rubygems'
123
+ require 'myapp'
124
+
125
+ Installs a `URG` signal handler and starts tracking file/line
126
+ information for newly created ruby objects. When the process receives
127
+ `SIGURG`, it will fork and call `Memprof.dump_all` to write out the
128
+ entire heap to a json file.
129
+
130
+ Use the `memprof` command to send the signal and upload the heap to
131
+ [memprof.com](http://memprof.com):
132
+
133
+ memprof --pid <PID> --name my_leaky_app --key <API_KEY>
134
+
135
+ ## Memprof.trace
136
+
137
+ require 'open-uri'
138
+ require 'mysql'
139
+ require 'memcached'
140
+
141
+ Memprof.trace{
142
+ 10.times{ Module.new }
143
+ 10.times{ GC.start }
144
+ 10.times{ open('http://google.com/') }
145
+ 10.times{ Mysql.connect.query("select 1+2") }
146
+ 10.times{ Memcached.new.get('memprof') }
147
+ }
148
+
149
+ For a given block of ruby code, count:
150
+
151
+ - number of objects created per type
152
+ - number of calls to and time spent in GC
153
+ - number of calls to and time spent in connect/read/write/select
154
+ - number of calls to and time spent in mysql queries
155
+ - number of calls to and responses to memcached commands
156
+ - number of calls to and bytes through malloc/realloc/free
157
+
158
+ The resulting json report looks like:
159
+
160
+ {
161
+ "objects": {
162
+ "created": 10,
163
+ "types": {
164
+ "module": 10, # Module.new
165
+ }
166
+ },
167
+
168
+ "gc": {
169
+ "calls": 10, # GC.start
170
+ "time": 0.17198
171
+ },
172
+
173
+ "fd": {
174
+ "connect": {
175
+ "calls": 10, # open('http://google.com')
176
+ "time": 0.0110
177
+ }
178
+ },
179
+
180
+ "mysql": {
181
+ "queries": 10, # Mysql.connect.query("select 1+2")
182
+ "time": 0.0006
183
+ },
184
+
185
+ "memcache": {
186
+ "get": {
187
+ "calls": 10, # Memcached.new.get('memprof')
188
+ "responses": {
189
+ "notfound": 10
190
+ }
191
+ }
192
+ }
193
+ }
194
+
195
+ *Note*: To write json to a file instead, set `Memprof.trace_filename =
196
+ "/path/to/file.json"`
197
+
198
+ ## Memprof.trace_request
199
+
200
+ Memprof.trace_request(env){ @app.call(env) }
201
+
202
+ Like `Memprof.trace`, but assume an incoming Rack request and include
203
+ information about the request itself.
204
+
205
+ {
206
+ "start" : 1272424769750716,
207
+ "tracers" : {
208
+ /* ... */
209
+ },
210
+ "rails" : {
211
+ "controller" : "home",
212
+ "action" : "index"
213
+ },
214
+ "request" : {
215
+ "REQUEST_URI" : "/home",
216
+ "REQUEST_METHOD" : "GET",
217
+ "REMOTE_ADDR" : "127.0.0.1",
218
+ "QUERY_STRING" : null
219
+ },
220
+ "time" : 1.3442
221
+ }
222
+
223
+ # Middlewares
224
+
225
+ ## Memprof::Middleware
226
+
227
+ require 'memprof/middleware'
228
+ config.middlewares.use(Memprof::Middleware)
229
+
230
+ Wrap each request in a `Memprof.track` to print out all object
231
+ location/type pairs created during that request.
232
+
233
+ *Note*: It is preferable to run this in staging or production mode with
234
+ Rails applications, since development mode creates a lot of unnecessary
235
+ objects during each request.
236
+
237
+ *Note*: To force a GC run before printing out a report, pass in
238
+ `:force_gc => true` to the middleware.
239
+
240
+ ## Memprof::Tracer
241
+
242
+ require 'memprof/tracer'
243
+ config.middleware.insert(0, Memprof::Tracer)
244
+
245
+ Wrap each request in a `Memprof.trace_request` and write results to
246
+ `/tmp/memprof_tracer-PID.json`
247
+
248
+ ## Memprof::Filter
249
+
250
+ Similar to `Memprof::Tracer`, but for legacy Rails 2.2 applications.
251
+
252
+ class ApplicationController < ActionController::Base
253
+ require 'memprof/tracer'
254
+ around_filter(Memprof::Filter)
255
+ end
256
+
257
+ # Compatibility
258
+
259
+ Memprof supports all 1.8.x (MRI and REE) VMs, as long as they are 64-bit
260
+ and contain debugging symbols. For best results, use RVM to compile ruby
261
+ and make sure you are on a 64-bit machine.
262
+
263
+ The following ruby builds are not supported:
264
+
265
+ - Ruby on small/medium EC2 instances (32-bit machines)
266
+ - OSX's default system ruby (no debugging symbols, fat 32/64-bit
267
+ binary)
268
+
269
+ *Note*: Many linux distributions do not package debugging symbols by
270
+ default. You can usually install these separately, for example using
271
+ `apt-get install libruby1.8-dbg`
272
+
273
+ ## Coming soon
274
+
275
+ - support for Ruby 1.9
276
+ - support for i386/i686 ruby builds
277
+
278
+ # Credits
279
+
280
+ - Jake Douglas for the Mach-O Snow Leopard support
281
+ - Aman Gupta for various bug fixes and other cleanup
282
+ - Rob Benson for initial 1.9 support and cleanup
283
+ - Paul Barry for force_gc support in `Memprof::Middleware`
284
+
data/ext/elf.c CHANGED
@@ -51,6 +51,8 @@ struct elf_info {
51
51
  void *text_segment;
52
52
  size_t text_segment_len;
53
53
 
54
+ GElf_Addr got_addr;
55
+
54
56
  GElf_Addr relplt_addr;
55
57
  Elf_Data *relplt;
56
58
  size_t relplt_count;
@@ -129,12 +131,28 @@ struct plt_entry {
129
131
  * the entry uses.
130
132
  */
131
133
  static void *
132
- get_got_addr(struct plt_entry *plt)
134
+ get_got_addr(struct plt_entry *plt, struct elf_info *info)
133
135
  {
136
+ void *addr = NULL;
137
+
134
138
  assert(plt != NULL);
135
- /* the jump is relative to the start of the next instruction. */
136
- dbg_printf("PLT addr: %p, .got.plt slot: %p\n", plt, (void *)&(plt->pad) + plt->jmp_disp);
137
- return (void *)&(plt->pad) + plt->jmp_disp;
139
+ assert(plt->jmp[0] == 0xff);
140
+
141
+ if (plt->jmp[1] == 0x25) {
142
+ #if defined(_ARCH_x86_64_)
143
+ // jmpq *0x2ccf3a(%rip)
144
+ addr = (void *)&(plt->pad) + plt->jmp_disp;
145
+ #else
146
+ // jmp *0x81060f0
147
+ addr = (void *)(plt->jmp_disp);
148
+ #endif
149
+ } else if (plt->jmp[1] == 0xa3) {
150
+ // jmp *0x130(%ebx)
151
+ addr = (void *)(info->base_addr + info->got_addr + plt->jmp_disp);
152
+ }
153
+
154
+ dbg_printf("PLT addr: %p, .got.plt slot: %p\n", plt, addr);
155
+ return addr;
138
156
  }
139
157
 
140
158
  /*
@@ -144,15 +162,15 @@ get_got_addr(struct plt_entry *plt)
144
162
  * returns the original function address
145
163
  */
146
164
  static void *
147
- overwrite_got(void *plt, const void *tramp)
165
+ overwrite_got(void *plt, const void *tramp, struct elf_info *info)
148
166
  {
149
167
  assert(plt != NULL);
150
168
  assert(tramp != NULL);
151
169
  void *ret = NULL;
152
170
 
153
- memcpy(&ret, get_got_addr(plt), sizeof(void *));
154
- copy_instructions(get_got_addr(plt), &tramp, sizeof(void *));
155
- dbg_printf("GOT value overwritten to: %p\n", tramp);
171
+ memcpy(&ret, get_got_addr(plt, info), sizeof(void *));
172
+ copy_instructions(get_got_addr(plt, info), &tramp, sizeof(void *));
173
+ dbg_printf("GOT value overwritten to: %p, from: %p\n", tramp, ret);
156
174
  return ret;
157
175
  }
158
176
 
@@ -237,7 +255,7 @@ hook_required_objects(struct elf_info *info, void *data)
237
255
 
238
256
  if ((trampee_addr = find_plt_addr(hook_data->sym, info)) != NULL) {
239
257
  dbg_printf("found: %s @ %p\n", hook_data->sym, trampee_addr);
240
- overwrite_got(trampee_addr, hook_data->addr);
258
+ overwrite_got(trampee_addr, hook_data->addr, info);
241
259
  }
242
260
 
243
261
  return;
@@ -282,6 +300,9 @@ do_bin_allocate_page(struct elf_info *info)
282
300
  * grab a page in the lower 4gb of the address space.
283
301
  */
284
302
  assert((size_t)info->text_segment <= UINT_MAX);
303
+ #ifndef MAP_32BIT
304
+ #define MAP_32BIT 0 // no MAP_32BIT defined on certain 32bit systems
305
+ #endif
285
306
  return mmap(NULL, memprof_config.pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE|MAP_32BIT, -1, 0);
286
307
  }
287
308
 
@@ -344,29 +365,39 @@ find_plt_addr(const char *symname, struct elf_info *info)
344
365
 
345
366
  /* Search through each of the .rela.plt entries */
346
367
  for (i = 0; i < info->relplt_count; i++) {
368
+ GElf_Rel rel;
347
369
  GElf_Rela rela;
348
370
  GElf_Sym sym;
349
371
  GElf_Addr addr;
350
- void *ret;
372
+ void *ret = NULL;
351
373
  const char *name;
352
374
 
353
375
  if (info->relplt->d_type == ELF_T_RELA) {
354
376
  ret = gelf_getrela(info->relplt, i, &rela);
355
-
356
377
  if (ret == NULL
357
378
  || ELF64_R_SYM(rela.r_info) >= info->dynsym_count
358
379
  || gelf_getsym(info->dynsym, ELF64_R_SYM(rela.r_info), &sym) == NULL)
359
- return NULL;
380
+ continue;
360
381
 
361
- name = info->dynstr + sym.st_name;
382
+ } else if (info->relplt->d_type == ELF_T_REL) {
383
+ ret = gelf_getrel(info->relplt, i, &rel);
384
+ if (ret == NULL
385
+ || ELF64_R_SYM(rel.r_info) >= info->dynsym_count
386
+ || gelf_getsym(info->dynsym, ELF64_R_SYM(rel.r_info), &sym) == NULL)
387
+ continue;
388
+ } else {
389
+ dbg_printf("unknown relplt entry type: %d\n", info->relplt->d_type);
390
+ continue;
391
+ }
362
392
 
363
- /* The name matches the name of the symbol passed in, so get the PLT entry
364
- * address and return it.
365
- */
366
- if (strcmp(symname, name) == 0) {
367
- addr = get_plt_addr(info, i);
368
- return (void *)addr;
369
- }
393
+ name = info->dynstr + sym.st_name;
394
+
395
+ /* The name matches the name of the symbol passed in, so get the PLT entry
396
+ * address and return it.
397
+ */
398
+ if (strcmp(symname, name) == 0) {
399
+ addr = get_plt_addr(info, i);
400
+ return (void *)addr;
370
401
  }
371
402
  }
372
403
 
@@ -426,6 +457,7 @@ do_bin_find_symbol(const char *sym, size_t *size, struct elf_info *elf)
426
457
  for (; esym < lastsym; esym++){
427
458
  /* ignore numeric/empty symbols */
428
459
  if ((esym->st_value == 0) ||
460
+ (elf->dynstr == 0) ||
429
461
  (ELF32_ST_BIND(esym->st_info)== STB_NUM))
430
462
  continue;
431
463
 
@@ -563,7 +595,7 @@ bin_update_image(const char *trampee, struct tramp_st2_entry *tramp, void **orig
563
595
  if (trampee_addr) {
564
596
  void *ret = NULL;
565
597
  dbg_printf("Found %s in the PLT, inserting tramp...\n", trampee);
566
- ret = overwrite_got(trampee_addr, tramp->addr);
598
+ ret = overwrite_got(trampee_addr, tramp->addr, ruby_info);
567
599
 
568
600
  assert(ret != NULL);
569
601
 
@@ -1079,6 +1111,9 @@ dissect_elf(struct elf_info *info, int find_debug)
1079
1111
  if (dyn.d_tag == DT_JMPREL) {
1080
1112
  info->relplt_addr = dyn.d_un.d_ptr;
1081
1113
  }
1114
+ else if (dyn.d_tag == DT_PLTGOT) {
1115
+ info->got_addr = dyn.d_un.d_ptr;
1116
+ }
1082
1117
  else if (dyn.d_tag == DT_PLTRELSZ) {
1083
1118
  info->plt_size = dyn.d_un.d_val;
1084
1119
  }
@@ -1108,8 +1143,8 @@ dissect_elf(struct elf_info *info, int find_debug)
1108
1143
 
1109
1144
  data = elf_getdata(scn, NULL);
1110
1145
  if (data == NULL || elf_getdata(scn, data) != NULL
1111
- || shdr.sh_size != data->d_size || data->d_off) {
1112
- dbg_printf("Couldn't get .dynstr data");
1146
+ || shdr.sh_size != data->d_size) {// condition true on 32bit: || data->d_off) {
1147
+ dbg_printf("Couldn't get .dynstr data\n");
1113
1148
  ret = 1;
1114
1149
  goto out;
1115
1150
  }
@@ -1132,6 +1167,8 @@ dissect_elf(struct elf_info *info, int find_debug)
1132
1167
  else if (shdr.sh_type == SHT_PROGBITS) {
1133
1168
  if (strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".plt") == 0) {
1134
1169
  info->plt_addr = shdr.sh_addr;
1170
+ } else if (strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".got.plt") == 0) {
1171
+ info->got_addr = shdr.sh_addr;
1135
1172
  } else if (strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".gnu_debuglink") == 0) {
1136
1173
  dbg_printf("gnu_debuglink section found\n", shdr.sh_size);
1137
1174
  if ((info->debuglink_data = elf_getdata(scn, NULL)) == NULL ||
@@ -1214,9 +1251,6 @@ bin_init()
1214
1251
  {
1215
1252
  Dwarf_Error dwrf_err;
1216
1253
 
1217
- ASSERT_ON_COMPILE(sizeof(unsigned long) == sizeof(GElf_Addr));
1218
- ASSERT_ON_COMPILE(sizeof(unsigned long) == sizeof(Elf64_Addr));
1219
-
1220
1254
  ruby_info = calloc(1, sizeof(*ruby_info));
1221
1255
 
1222
1256
  if (!ruby_info) {
@@ -186,7 +186,7 @@ if have_header('mach-o/dyld.h')
186
186
  expressions << ["address__#{name}", "&#{name}"]
187
187
  end
188
188
 
189
- pid = fork{sleep}
189
+ pid = fork{sleep while true}
190
190
  output = IO.popen('gdb --interpreter=mi --quiet', 'w+') do |io|
191
191
  io.puts "attach #{pid}"
192
192
  expressions.each do |name, expr|
@@ -346,6 +346,35 @@ memprof_track(int argc, VALUE *argv, VALUE self)
346
346
  return Qnil;
347
347
  }
348
348
 
349
+ static json_gen_status
350
+ json_gen_id(json_gen gen, ID id)
351
+ {
352
+ if (id) {
353
+ if (id < 100)
354
+ return json_gen_format(gen, ":%c", id);
355
+ else
356
+ return json_gen_format(gen, ":%s", rb_id2name(id));
357
+ } else
358
+ return json_gen_null(gen);
359
+ }
360
+
361
+ static json_gen_status
362
+ json_gen_value(json_gen gen, VALUE obj)
363
+ {
364
+ if (FIXNUM_P(obj))
365
+ return json_gen_integer(gen, NUM2LONG(obj));
366
+ else if (NIL_P(obj) || obj == Qundef)
367
+ return json_gen_null(gen);
368
+ else if (obj == Qtrue)
369
+ return json_gen_bool(gen, 1);
370
+ else if (obj == Qfalse)
371
+ return json_gen_bool(gen, 0);
372
+ else if (SYMBOL_P(obj))
373
+ return json_gen_id(gen, SYM2ID(obj));
374
+ else
375
+ return json_gen_pointer(gen, (void*)obj);
376
+ }
377
+
349
378
  static json_gen_config fancy_conf = { .beautify = 1, .indentString = " " };
350
379
  static json_gen_config basic_conf = { .beautify = 0, .indentString = " " };
351
380
 
@@ -469,7 +498,7 @@ memprof_trace_request(VALUE self, VALUE env)
469
498
 
470
499
  json_gen_cstr(gen, "start");
471
500
  gettimeofday(&now, NULL);
472
- json_gen_integer(gen, (now.tv_sec * 1000000) + now.tv_usec);
501
+ json_gen_integer(gen, (now.tv_sec * 1000) + (now.tv_usec / 1000));
473
502
 
474
503
  json_gen_cstr(gen, "tracers");
475
504
  json_gen_map_open(gen);
@@ -533,6 +562,14 @@ memprof_trace_request(VALUE self, VALUE env)
533
562
  DUMP_HASH_ENTRY("QUERY_STRING");
534
563
 
535
564
  json_gen_map_close(gen);
565
+
566
+ if (RTEST(ret) && BUILTIN_TYPE(ret) == T_ARRAY) {
567
+ json_gen_cstr(gen, "response");
568
+ json_gen_map_open(gen);
569
+ json_gen_cstr(gen, "code");
570
+ json_gen_value(gen, RARRAY_PTR(ret)[0]);
571
+ json_gen_map_close(gen);
572
+ }
536
573
  }
537
574
 
538
575
  json_gen_cstr(gen, "time");
@@ -568,35 +605,6 @@ memprof_trace_request(VALUE self, VALUE env)
568
605
  #define RSTRING_LEN(str) RSTRING(str)->len
569
606
  #endif
570
607
 
571
- static json_gen_status
572
- json_gen_id(json_gen gen, ID id)
573
- {
574
- if (id) {
575
- if (id < 100)
576
- return json_gen_format(gen, ":%c", id);
577
- else
578
- return json_gen_format(gen, ":%s", rb_id2name(id));
579
- } else
580
- return json_gen_null(gen);
581
- }
582
-
583
- static json_gen_status
584
- json_gen_value(json_gen gen, VALUE obj)
585
- {
586
- if (FIXNUM_P(obj))
587
- return json_gen_integer(gen, NUM2LONG(obj));
588
- else if (NIL_P(obj) || obj == Qundef)
589
- return json_gen_null(gen);
590
- else if (obj == Qtrue)
591
- return json_gen_bool(gen, 1);
592
- else if (obj == Qfalse)
593
- return json_gen_bool(gen, 0);
594
- else if (SYMBOL_P(obj))
595
- return json_gen_id(gen, SYM2ID(obj));
596
- else
597
- return json_gen_pointer(gen, (void*)obj);
598
- }
599
-
600
608
  static int
601
609
  each_hash_entry(st_data_t key, st_data_t record, st_data_t arg)
602
610
  {
@@ -1942,7 +1950,9 @@ Init_memprof()
1942
1950
  install_objects_tracer();
1943
1951
  install_fd_tracer();
1944
1952
  install_mysql_tracer();
1953
+ install_postgres_tracer();
1945
1954
  install_memcache_tracer();
1955
+ install_resources_tracer();
1946
1956
 
1947
1957
  gc_hook = Data_Wrap_Struct(rb_cObject, sourcefile_marker, NULL, NULL);
1948
1958
  rb_global_variable(&gc_hook);
@@ -41,6 +41,8 @@ extern void install_malloc_tracer();
41
41
  extern void install_gc_tracer();
42
42
  extern void install_fd_tracer();
43
43
  extern void install_mysql_tracer();
44
+ extern void install_postgres_tracer();
44
45
  extern void install_objects_tracer();
45
46
  extern void install_memcache_tracer();
47
+ extern void install_resources_tracer();
46
48
  #endif
@@ -0,0 +1,73 @@
1
+ #include <assert.h>
2
+ #include <errno.h>
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include <sys/time.h>
7
+
8
+ #include "arch.h"
9
+ #include "bin_api.h"
10
+ #include "json.h"
11
+ #include "tracer.h"
12
+ #include "tramp.h"
13
+ #include "util.h"
14
+
15
+ struct memprof_postgres_stats {
16
+ size_t query_calls;
17
+ };
18
+
19
+ static struct tracer tracer;
20
+ static struct memprof_postgres_stats stats;
21
+ static void * (*orig_PQexec)(void *postgres, const char *stmt);
22
+
23
+ static void *
24
+ PQexec_tramp(void *postgres, const char *stmt) {
25
+ void *ret;
26
+
27
+ ret = orig_PQexec(postgres, stmt);
28
+ stats.query_calls++;
29
+
30
+ return ret;
31
+ }
32
+
33
+ static void
34
+ postgres_trace_start() {
35
+ static int inserted = 0;
36
+
37
+ if (!inserted)
38
+ inserted = 1;
39
+ else
40
+ return;
41
+
42
+ orig_PQexec = bin_find_symbol("PQexec", NULL, 1);
43
+ if (orig_PQexec)
44
+ insert_tramp("PQexec", PQexec_tramp);
45
+ }
46
+
47
+ static void
48
+ postgres_trace_stop() {
49
+ }
50
+
51
+ static void
52
+ postgres_trace_reset() {
53
+ memset(&stats, 0, sizeof(stats));
54
+ }
55
+
56
+ static void
57
+ postgres_trace_dump(json_gen gen) {
58
+ if (stats.query_calls > 0) {
59
+ json_gen_cstr(gen, "queries");
60
+ json_gen_integer(gen, stats.query_calls);
61
+ }
62
+ }
63
+
64
+ void install_postgres_tracer()
65
+ {
66
+ tracer.start = postgres_trace_start;
67
+ tracer.stop = postgres_trace_stop;
68
+ tracer.reset = postgres_trace_reset;
69
+ tracer.dump = postgres_trace_dump;
70
+ tracer.id = "postgres";
71
+
72
+ trace_insert(&tracer);
73
+ }
@@ -0,0 +1,89 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <sys/time.h>
5
+ #include <sys/resource.h>
6
+
7
+ #include "json.h"
8
+ #include "tracer.h"
9
+ #include "tramp.h"
10
+ #include "util.h"
11
+
12
+ struct memprof_resources_stats {
13
+ long nsignals;
14
+
15
+ long inblock;
16
+ long oublock;
17
+
18
+ double utime;
19
+ double stime;
20
+ };
21
+
22
+ static struct tracer tracer;
23
+ static struct memprof_resources_stats stats;
24
+
25
+ #define TVAL_TO_DBL(tv) ((double)tv.tv_sec + (double)tv.tv_usec * 1e-6)
26
+
27
+ static void
28
+ resources_trace_start() {
29
+ struct rusage usage;
30
+ getrusage(RUSAGE_SELF, &usage);
31
+
32
+ stats.nsignals = -usage.ru_nsignals;
33
+
34
+ stats.inblock = -usage.ru_inblock;
35
+ stats.oublock = -usage.ru_oublock;
36
+
37
+ stats.stime = -TVAL_TO_DBL(usage.ru_stime);
38
+ stats.utime = -TVAL_TO_DBL(usage.ru_utime);
39
+ }
40
+
41
+ static void
42
+ resources_trace_dump(json_gen gen) {
43
+ { // calculate diff before dump, since stop is called after dump
44
+ struct rusage usage;
45
+ getrusage(RUSAGE_SELF, &usage);
46
+
47
+ stats.nsignals += usage.ru_nsignals;
48
+
49
+ stats.inblock += usage.ru_inblock;
50
+ stats.oublock += usage.ru_oublock;
51
+
52
+ stats.stime += TVAL_TO_DBL(usage.ru_stime);
53
+ stats.utime += TVAL_TO_DBL(usage.ru_utime);
54
+ }
55
+
56
+ json_gen_cstr(gen, "signals");
57
+ json_gen_integer(gen, stats.nsignals);
58
+
59
+ json_gen_cstr(gen, "inputs");
60
+ json_gen_integer(gen, stats.inblock);
61
+
62
+ json_gen_cstr(gen, "outputs");
63
+ json_gen_integer(gen, stats.oublock);
64
+
65
+ json_gen_cstr(gen, "stime");
66
+ json_gen_double(gen, stats.stime);
67
+
68
+ json_gen_cstr(gen, "utime");
69
+ json_gen_double(gen, stats.utime);
70
+ }
71
+
72
+ static void
73
+ resources_trace_stop() {
74
+ }
75
+
76
+ static void
77
+ resources_trace_reset() {
78
+ }
79
+
80
+ void install_resources_tracer()
81
+ {
82
+ tracer.start = resources_trace_start;
83
+ tracer.stop = resources_trace_stop;
84
+ tracer.reset = resources_trace_reset;
85
+ tracer.dump = resources_trace_dump;
86
+ tracer.id = "resource";
87
+
88
+ trace_insert(&tracer);
89
+ }
@@ -1,7 +1,6 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'memprof'
3
- s.version = '0.3.6'
4
- s.date = '2010-04-13'
3
+ s.version = '0.3.7'
5
4
  s.summary = 'Ruby Memory Profiler'
6
5
  s.description = "Ruby memory profiler similar to bleak_house, but without patches to the Ruby VM"
7
6
  s.homepage = "http://github.com/ice799/memprof"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memprof
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
5
- prerelease: false
4
+ hash: 29
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 6
10
- version: 0.3.6
9
+ - 7
10
+ version: 0.3.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joe Damato
@@ -18,7 +18,7 @@ autorequire:
18
18
  bindir: bin
19
19
  cert_chain: []
20
20
 
21
- date: 2010-04-13 00:00:00 -07:00
21
+ date: 2011-01-28 00:00:00 -08:00
22
22
  default_executable:
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
@@ -64,7 +64,7 @@ extra_rdoc_files: []
64
64
 
65
65
  files:
66
66
  - .gitignore
67
- - README.rdoc
67
+ - README.md
68
68
  - Rakefile
69
69
  - bin/memprof
70
70
  - ext/arch.h
@@ -89,6 +89,8 @@ files:
89
89
  - ext/tracers/memory.c
90
90
  - ext/tracers/mysql.c
91
91
  - ext/tracers/objects.c
92
+ - ext/tracers/postgres.c
93
+ - ext/tracers/resources.c
92
94
  - ext/tramp.c
93
95
  - ext/tramp.h
94
96
  - ext/util.c
@@ -134,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
136
  requirements: []
135
137
 
136
138
  rubyforge_project:
137
- rubygems_version: 1.3.7
139
+ rubygems_version: 1.4.2
138
140
  signing_key:
139
141
  specification_version: 3
140
142
  summary: Ruby Memory Profiler
@@ -1,118 +0,0 @@
1
- = memprof (c) Joe Damato @joedamato http://timetobleed.com
2
-
3
- Memprof is a Ruby level memory profiler that can help you find reference leaks in your application.
4
- Memprof can also do very lightweight function call tracing to help you figure out which system calls, and library calls your code causes.
5
-
6
- == Installing
7
-
8
- gem install memprof
9
-
10
- == Usage
11
-
12
- == Memory Tracking
13
-
14
- == Blocks (memory tracking)
15
-
16
- Memprof.track {
17
- 100.times{ "abc" }
18
- 100.times{ 1.23 + 1 }
19
- 100.times{ Module.new }
20
- }
21
-
22
- Outputs:
23
-
24
- 100 file.rb:2:String
25
- 100 file.rb:3:Float
26
- 100 file.rb:4:Module
27
-
28
- == Rails requests (memory tracking)
29
-
30
- Use the Memprof::Middleware
31
-
32
- == Dump objects
33
-
34
- Memprof.dump {
35
- "hello" + "world"
36
- }
37
-
38
- Outputs:
39
-
40
- {
41
- "_id": "0x19c610",
42
- "file": "file.rb",
43
- "line": 2,
44
- "type": "string",
45
- "class": "0x1ba7f0",
46
- "class_name": "String",
47
- "length": 10,
48
- "data": "helloworld"
49
- }
50
-
51
- You can dump any Ruby object you want.
52
-
53
- == Dumping the entire heap
54
-
55
- Memprof.dump_all("file.json")
56
-
57
- This will dump out every single live object as json to /tmp/file.json
58
-
59
- == Less elegant tracking usage:
60
-
61
- require 'memprof'
62
- Memprof.start
63
-
64
- # ruby code
65
-
66
- Memprof.stats
67
-
68
- # more ruby code
69
-
70
- Memprof.stats
71
- Memprof.stop
72
-
73
- The above code will output 2 summaries, allowing you to compare which objects were
74
- destroyed and which are still around.
75
-
76
- Memprof.stats also takes an (optional) file name to write the output to a file.
77
-
78
- == Function call tracing
79
-
80
- This system is under development and the API may change without warning.
81
-
82
- You can use the middleware Memprof::Tracer to output function tracing and request information for
83
- each request that comes in to your app.
84
-
85
- == Compatibility
86
-
87
- You must have debug symbols installed or a an unstripped version of Ruby.
88
-
89
- To install debug symbols on Debian-like systems:
90
-
91
- apt-get install libruby1.8-dbg
92
-
93
- Not supporting:
94
- * OSX default Ruby
95
- * Stripped Ruby binaries without debug symbols
96
- * Any and all Windows Ruby builds
97
- * Ruby 1.9+ on all systems
98
- * 32bit systems
99
-
100
- Supporting:
101
- * Linux (enable-shared AND disable-shared):
102
- * x86_64 builds of Ruby Enterprise Edition 1.8.6/1.8.7
103
- * x86_64 builds of MRI Ruby
104
-
105
- * Snow Leopard (enable-shared AND disable-shared):
106
- * x86_64 builds of Ruby Enterprise Edition 1.8.6/1.8.7
107
- * x86_64 builds of MRI Ruby
108
-
109
- Coming soon:
110
-
111
- Official support for Ruby 1.9
112
- Official support for i386/i686
113
-
114
- == Special Thanks
115
- * Jake Douglas for the Mach O/snow leopard support.
116
- * Aman Gupta for various bug fixes and other cleanup.
117
- * Rob Benson for 1.9 support and cleanup.
118
- * Paul Barry for force_gc support in Memprof::Middleware