memprof 0.3.6 → 0.3.7

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