perftools.rb 0.4.0 → 0.4.2

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 CHANGED
@@ -29,6 +29,10 @@ google-perftools for ruby code
29
29
 
30
30
  $ CPUPROFILE_REALTIME=1 ruby my_app.rb
31
31
 
32
+ Profile object allocations instead of cpu/wall time:
33
+
34
+ $ CPUPROFILE_OBJECTS=1 ruby my_app.rb
35
+
32
36
 
33
37
  === Reporting
34
38
 
data/ext/extconf.rb CHANGED
@@ -46,7 +46,8 @@ Dir.chdir('src') do
46
46
  ['perftools-osx', RUBY_PLATFORM =~ /darwin/],
47
47
  ['perftools-osx-106', RUBY_PLATFORM =~ /darwin10/],
48
48
  ['perftools-debug', true],
49
- ['perftools-realtime', true]
49
+ ['perftools-realtime', true],
50
+ ['perftools-objects', true]
50
51
  ].each do |patch, apply|
51
52
  if apply
52
53
  sys("patch -p1 < ../../../patches/#{patch}.patch")
@@ -80,7 +81,7 @@ def add_define(name)
80
81
  end
81
82
 
82
83
  case RUBY_PLATFORM
83
- when /darwin/, /linux/
84
+ when /darwin/, /linux/, /freebsd/
84
85
  CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
85
86
  end
86
87
 
@@ -88,6 +89,7 @@ if RUBY_VERSION >= "1.9"
88
89
  add_define 'RUBY19'
89
90
 
90
91
  hdrs = proc {
92
+ have_header("method.h") # exists on 1.9.2
91
93
  have_header("vm_core.h") and
92
94
  have_header("iseq.h") and
93
95
  have_header("insns.inc") and
data/ext/perftools.c CHANGED
@@ -5,6 +5,7 @@ void ProfilerGcMark(void (*cb)(VALUE));
5
5
  int ProfilerStart(const char*);
6
6
  void ProfilerStop();
7
7
  void ProfilerFlush();
8
+ void ProfilerRecord(int, void*, void*);
8
9
 
9
10
  static VALUE Iallocate;
10
11
  static VALUE I__send__;
@@ -147,8 +148,13 @@ static VALUE Isend;
147
148
  case VM_FRAME_MAGIC_METHOD:
148
149
  case VM_FRAME_MAGIC_CFUNC:
149
150
  self = cfp->self;
151
+ #ifdef HAVE_METHOD_H
152
+ klass = cfp->me->klass;
153
+ method = cfp->me->called_id;
154
+ #else
150
155
  klass = cfp->method_class;
151
156
  method = cfp->method_id;
157
+ #endif
152
158
  SAVE_FRAME();
153
159
  break;
154
160
  }
@@ -193,18 +199,23 @@ static VALUE Isend;
193
199
  #endif
194
200
  #endif
195
201
 
202
+ static VALUE objprofiler_setup();
203
+ static VALUE objprofiler_teardown();
204
+
205
+ /* CpuProfiler */
206
+
196
207
  static VALUE cPerfTools;
197
208
  static VALUE cCpuProfiler;
198
209
  static VALUE bProfilerRunning;
199
210
  static VALUE gc_hook;
200
211
 
201
- VALUE
212
+ static VALUE
202
213
  cpuprofiler_running_p(VALUE self)
203
214
  {
204
215
  return bProfilerRunning;
205
216
  }
206
217
 
207
- VALUE
218
+ static VALUE
208
219
  cpuprofiler_stop(VALUE self)
209
220
  {
210
221
  if (!bProfilerRunning)
@@ -213,10 +224,14 @@ cpuprofiler_stop(VALUE self)
213
224
  bProfilerRunning = Qfalse;
214
225
  ProfilerStop();
215
226
  ProfilerFlush();
227
+
228
+ if (getenv("CPUPROFILE_OBJECTS"))
229
+ objprofiler_teardown();
230
+
216
231
  return Qtrue;
217
232
  }
218
233
 
219
- VALUE
234
+ static VALUE
220
235
  cpuprofiler_start(VALUE self, VALUE filename)
221
236
  {
222
237
  StringValue(filename);
@@ -224,6 +239,9 @@ cpuprofiler_start(VALUE self, VALUE filename)
224
239
  if (bProfilerRunning)
225
240
  return Qfalse;
226
241
 
242
+ if (getenv("CPUPROFILE_OBJECTS"))
243
+ objprofiler_setup();
244
+
227
245
  ProfilerStart(RSTRING_PTR(filename));
228
246
  bProfilerRunning = Qtrue;
229
247
 
@@ -241,20 +259,153 @@ cpuprofiler_gc_mark()
241
259
  ProfilerGcMark(rb_gc_mark);
242
260
  }
243
261
 
262
+ /* ObjProfiler */
263
+
264
+ #ifndef _GNU_SOURCE
265
+ #define _GNU_SOURCE
266
+ #endif
267
+ #ifndef _XOPEN_SOURCE
268
+ #define _XOPEN_SOURCE 600
269
+ #endif
270
+
271
+ #include <assert.h>
272
+ #include <ucontext.h>
273
+ #include <unistd.h>
274
+ #include <signal.h>
275
+ #include <stdio.h>
276
+ #include <stdlib.h>
277
+ #include <string.h>
278
+ #include <sys/mman.h>
279
+
280
+ static VALUE bObjProfilerRunning;
281
+ #define NUM_ORIG_BYTES 2
282
+
283
+ struct {
284
+ void *location;
285
+ unsigned char value;
286
+ } orig_bytes[NUM_ORIG_BYTES];
287
+
288
+ static inline void *
289
+ page_align(void *addr) {
290
+ assert(addr != NULL);
291
+ return (void *)((size_t)addr & ~(0xFFFF));
292
+ }
293
+
294
+ static void
295
+ copy_instructions(void *dest, void *src, size_t count) {
296
+ assert(dest != NULL);
297
+ assert(src != NULL);
298
+
299
+ void *aligned_addr = page_align(dest);
300
+ if (mprotect(aligned_addr, (dest - aligned_addr) + count, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
301
+ perror("mprotect");
302
+ memcpy(dest, src, count);
303
+ }
304
+
305
+ static inline void**
306
+ uc_get_ip(ucontext_t *uc) {
307
+ #if defined(__FreeBSD__)
308
+ return (void**)&uc->uc_mcontext.mc_rip;
309
+ #elif defined(__dietlibc__)
310
+ return (void**)&uc->uc_mcontext.rip;
311
+ #elif defined(__APPLE__)
312
+ return (void**)&uc->uc_mcontext->__ss.__rip;
313
+ #else
314
+ return (void**)&uc->uc_mcontext.gregs[REG_RIP];
315
+ #endif
316
+ }
317
+
318
+ static void
319
+ trap_handler(int sig, siginfo_t *info, void *data) {
320
+ int i;
321
+ ucontext_t *uc = (ucontext_t *)data;
322
+ void **ip = uc_get_ip(uc);
323
+
324
+ // printf("signal: %d, addr: %p, ip: %p\n", signal, info->si_addr, *ip);
325
+
326
+ for (i=0; i<NUM_ORIG_BYTES; i++) {
327
+ if (orig_bytes[i].location == *ip-1) {
328
+ // restore original byte
329
+ copy_instructions(orig_bytes[i].location, &orig_bytes[i].value, 1);
330
+
331
+ // setup next breakpoint
332
+ copy_instructions(orig_bytes[(i+1)%NUM_ORIG_BYTES].location, "\xCC", 1);
333
+
334
+ // first breakpoint is the notification
335
+ if (i == 0)
336
+ ProfilerRecord(sig, info, data);
337
+
338
+ // reset instruction pointer
339
+ *ip -= 1;
340
+
341
+ break;
342
+ }
343
+ }
344
+ }
345
+
346
+ static VALUE
347
+ objprofiler_setup()
348
+ {
349
+ if (bObjProfilerRunning)
350
+ return Qtrue;
351
+
352
+ int i;
353
+ struct sigaction sig = { .sa_sigaction = trap_handler, .sa_flags = SA_SIGINFO };
354
+ sigemptyset(&sig.sa_mask);
355
+ sigaction(SIGTRAP, &sig, NULL);
356
+
357
+ for (i=0; i<NUM_ORIG_BYTES; i++) {
358
+ orig_bytes[i].location = rb_newobj + i;
359
+ orig_bytes[i].value = ((unsigned char*)rb_newobj)[i];
360
+ copy_instructions(rb_newobj + i, "\xCC", 1);
361
+ }
362
+
363
+ // setenv("CPUPROFILE_OBJECTS", "1", 1);
364
+ bObjProfilerRunning = Qtrue;
365
+ return Qtrue;
366
+ }
367
+
368
+ static VALUE
369
+ objprofiler_teardown()
370
+ {
371
+ if (!bObjProfilerRunning)
372
+ return Qfalse;
373
+
374
+ int i;
375
+ struct sigaction sig = { .sa_handler = SIG_IGN };
376
+ sigemptyset(&sig.sa_mask);
377
+ sigaction(SIGTRAP, &sig, NULL);
378
+
379
+ for (i=0; i<NUM_ORIG_BYTES; i++) {
380
+ copy_instructions(orig_bytes[i].location, &orig_bytes[i].value, 1);
381
+ }
382
+
383
+ // unsetenv("CPUPROFILE_OBJECTS");
384
+ bObjProfilerRunning = Qfalse;
385
+ return Qtrue;
386
+ }
387
+
388
+ /* Init */
389
+
244
390
  void
245
391
  Init_perftools()
246
392
  {
247
393
  cPerfTools = rb_define_class("PerfTools", rb_cObject);
248
394
  cCpuProfiler = rb_define_class_under(cPerfTools, "CpuProfiler", rb_cObject);
249
- bProfilerRunning = Qfalse;
395
+
250
396
  Iallocate = rb_intern("allocate");
251
397
  I__send__ = rb_intern("__send__");
252
398
  Isend = rb_intern("send");
253
399
 
400
+ bObjProfilerRunning = bProfilerRunning = Qfalse;
401
+
254
402
  rb_define_singleton_method(cCpuProfiler, "running?", cpuprofiler_running_p, 0);
255
403
  rb_define_singleton_method(cCpuProfiler, "start", cpuprofiler_start, 1);
256
404
  rb_define_singleton_method(cCpuProfiler, "stop", cpuprofiler_stop, 0);
257
405
 
258
406
  gc_hook = Data_Wrap_Struct(cCpuProfiler, cpuprofiler_gc_mark, NULL, NULL);
259
407
  rb_global_variable(&gc_hook);
408
+
409
+ if (getenv("CPUPROFILE") && getenv("CPUPROFILE_OBJECTS"))
410
+ objprofiler_setup();
260
411
  }
@@ -0,0 +1,7 @@
1
+ default: trap
2
+
3
+ trap: trap.c
4
+ gcc -o trap trap.c -ggdb -O0 -Wall
5
+
6
+ clean:
7
+ rm -rf *.o trap *.dSYM
@@ -0,0 +1,103 @@
1
+ #define _GNU_SOURCE
2
+ #define _XOPEN_SOURCE 600
3
+
4
+ #include <assert.h>
5
+ #include <ucontext.h>
6
+ #include <unistd.h>
7
+ #include <signal.h>
8
+ #include <stdio.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+ #include <sys/mman.h>
12
+
13
+
14
+ void
15
+ func() {
16
+ printf("hi\n");
17
+ }
18
+
19
+
20
+ static inline void *
21
+ page_align(void *addr) {
22
+ assert(addr != NULL);
23
+ return (void *)((size_t)addr & ~(0xFFFF));
24
+ }
25
+
26
+ static void
27
+ copy_instructions(void *dest, void *src, size_t count) {
28
+ assert(dest != NULL);
29
+ assert(src != NULL);
30
+
31
+ void *aligned_addr = page_align(dest);
32
+ if (mprotect(aligned_addr, (dest - aligned_addr) + count, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
33
+ perror("mprotect");
34
+ memcpy(dest, src, count);
35
+ }
36
+
37
+ #define NUM_ORIG_BYTES 2
38
+ struct {
39
+ void *location;
40
+ unsigned char value;
41
+ } orig_bytes[NUM_ORIG_BYTES];
42
+
43
+ static inline void**
44
+ uc_get_ip(ucontext_t *uc) {
45
+ #if defined(__FreeBSD__)
46
+ return (void**)&uc->uc_mcontext.mc_rip;
47
+ #elif defined(__dietlibc__)
48
+ return (void**)&uc->uc_mcontext.rip;
49
+ #elif defined(__APPLE__)
50
+ return (void**)&uc->uc_mcontext->__ss.__rip;
51
+ #else
52
+ return (void**)&uc->uc_mcontext.gregs[REG_RIP];
53
+ #endif
54
+ }
55
+
56
+ static void
57
+ trap_handler(int signal, siginfo_t *info, void *data) {
58
+ int i;
59
+ ucontext_t *uc = (ucontext_t *)data;
60
+ void **ip = uc_get_ip(uc);
61
+
62
+ // printf("signal: %d, addr: %p, ip: %p\n", signal, info->si_addr, *ip);
63
+
64
+ for (i=0; i<NUM_ORIG_BYTES; i++) {
65
+ if (orig_bytes[i].location == *ip-1) {
66
+ // restore original byte
67
+ copy_instructions(orig_bytes[i].location, &orig_bytes[i].value, 1);
68
+
69
+ // setup next breakpoint
70
+ copy_instructions(orig_bytes[(i+1)%NUM_ORIG_BYTES].location, "\xCC", 1);
71
+
72
+ // first breakpoint is the notification
73
+ if (i == 0)
74
+ printf(" ---> YOU'RE CALLING FUNC()\n");
75
+
76
+ // reset instruction pointer
77
+ *ip -= 1;
78
+
79
+ break;
80
+ }
81
+ }
82
+ }
83
+
84
+ int
85
+ main() {
86
+ int i;
87
+ struct sigaction sig = { .sa_sigaction = trap_handler, .sa_flags = SA_SIGINFO };
88
+ sigemptyset(&sig.sa_mask);
89
+ sigaction(SIGTRAP, &sig, NULL);
90
+
91
+ for (i=0; i<NUM_ORIG_BYTES; i++) {
92
+ orig_bytes[i].location = func + i;
93
+ orig_bytes[i].value = ((unsigned char*)func)[i];
94
+ copy_instructions(func + i, "\xCC", 1);
95
+ }
96
+
97
+ printf("func: %p\n", func);
98
+
99
+ for (i=0; i<10; i++)
100
+ func();
101
+
102
+ return 0;
103
+ }
@@ -0,0 +1,91 @@
1
+ commit 5ffd87871619a750ad19e247670a0887a6d38fbd
2
+ Author: Aman Gupta <aman@tmm1.net>
3
+ Date: Sun Aug 1 18:50:38 2010 -0700
4
+
5
+ perftools-objects
6
+
7
+ diff --git a/src/profile-handler.cc b/src/profile-handler.cc
8
+ index 619b980..2ecf2c0 100644
9
+ --- a/src/profile-handler.cc
10
+ +++ b/src/profile-handler.cc
11
+ @@ -402,6 +402,8 @@ void ProfileHandler::GetState(ProfileHandlerState* state) {
12
+ }
13
+
14
+ void ProfileHandler::StartTimer() {
15
+ + if (getenv("CPUPROFILE_OBJECTS")) return;
16
+ +
17
+ struct itimerval timer;
18
+ timer.it_interval.tv_sec = 0;
19
+ timer.it_interval.tv_usec = 1000000 / frequency_;
20
+ @@ -410,12 +412,16 @@ void ProfileHandler::StartTimer() {
21
+ }
22
+
23
+ void ProfileHandler::StopTimer() {
24
+ + if (getenv("CPUPROFILE_OBJECTS")) return;
25
+ +
26
+ struct itimerval timer;
27
+ memset(&timer, 0, sizeof timer);
28
+ setitimer(realtime_ ? ITIMER_REAL : ITIMER_PROF, &timer, 0);
29
+ }
30
+
31
+ bool ProfileHandler::IsTimerRunning() {
32
+ + if (getenv("CPUPROFILE_OBJECTS")) return false;
33
+ +
34
+ struct itimerval current_timer;
35
+ RAW_CHECK(0 == getitimer(realtime_ ? ITIMER_REAL : ITIMER_PROF, &current_timer), "getitimer");
36
+ return (current_timer.it_value.tv_sec != 0 ||
37
+ @@ -423,6 +429,8 @@ bool ProfileHandler::IsTimerRunning() {
38
+ }
39
+
40
+ void ProfileHandler::EnableHandler() {
41
+ + if (getenv("CPUPROFILE_OBJECTS")) return;
42
+ +
43
+ struct sigaction sa;
44
+ sa.sa_sigaction = SignalHandler;
45
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
46
+ @@ -431,6 +439,8 @@ void ProfileHandler::EnableHandler() {
47
+ }
48
+
49
+ void ProfileHandler::DisableHandler() {
50
+ + if (getenv("CPUPROFILE_OBJECTS")) return;
51
+ +
52
+ struct sigaction sa;
53
+ sa.sa_handler = SIG_IGN;
54
+ sa.sa_flags = SA_RESTART;
55
+ diff --git a/src/profiler.cc b/src/profiler.cc
56
+ index 37234b2..905288f 100644
57
+ --- a/src/profiler.cc
58
+ +++ b/src/profiler.cc
59
+ @@ -97,6 +97,10 @@ class CpuProfiler {
60
+
61
+ static CpuProfiler instance_;
62
+
63
+ + // Signal handler that records the interrupted pc in the profile data.
64
+ + static void prof_handler(int sig, siginfo_t*, void* signal_ucontext,
65
+ + void* cpu_profiler);
66
+ +
67
+ private:
68
+ // This lock implements the locking requirements described in the ProfileData
69
+ // documentation, specifically:
70
+ @@ -125,10 +129,6 @@ class CpuProfiler {
71
+
72
+ // Disables receiving SIGPROF interrupt.
73
+ void DisableHandler();
74
+ -
75
+ - // Signal handler that records the interrupted pc in the profile data.
76
+ - static void prof_handler(int sig, siginfo_t*, void* signal_ucontext,
77
+ - void* cpu_profiler);
78
+ };
79
+
80
+ // Profile data structure singleton: Constructor will check to see if
81
+ @@ -318,6 +318,10 @@ extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() {
82
+ extern "C" PERFTOOLS_DLL_DECL void ProfilerGcMark(void (*cb)(VALUE)) {
83
+ CpuProfiler::instance_.GcMark(cb);
84
+ }
85
+ +
86
+ +extern "C" PERFTOOLS_DLL_DECL void ProfilerRecord(int sig, siginfo_t* info, void* signal_ucontext) {
87
+ + CpuProfiler::prof_handler(sig, info, signal_ucontext, (void*)&CpuProfiler::instance_);
88
+ +}
89
+ #endif
90
+
91
+ extern "C" PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads() {
data/perftools.rb.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'perftools.rb'
3
- s.version = '0.4.0'
4
- s.date = '2009-11-16'
3
+ s.version = '0.4.2'
4
+ s.date = '2010-08-01'
5
5
  s.rubyforge_project = 'perftools-rb'
6
6
  s.summary = 'google-perftools for ruby code'
7
7
  s.description = 'A sampling profiler for ruby code based on patches to google-perftools'
@@ -17,20 +17,5 @@ spec = Gem::Specification.new do |s|
17
17
  s.executables << 'pprof.rb'
18
18
 
19
19
  # ruby -rpp -e' pp `git ls-files | grep -v examples`.split("\n").sort '
20
- s.files = [
21
- "README",
22
- "bin/pprof.rb",
23
- "ext/extconf.rb",
24
- "ext/perftools.c",
25
- "ext/src/google-perftools-1.4.tar.gz",
26
- "patches/perftools-debug.patch",
27
- "patches/perftools-gc.patch",
28
- "patches/perftools-osx-106.patch",
29
- "patches/perftools-osx.patch",
30
- "patches/perftools-pprof.patch",
31
- "patches/perftools-realtime.patch",
32
- "patches/perftools-notests.patch",
33
- "patches/perftools.patch",
34
- "perftools.rb.gemspec"
35
- ]
20
+ s.files = `git ls-files`.split("\n").reject{ |f| f =~ /^examples/ }
36
21
  end
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perftools.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ hash: 11
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 4
9
+ - 2
10
+ version: 0.4.2
5
11
  platform: ruby
6
12
  authors:
7
13
  - Aman Gupta
@@ -9,7 +15,7 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2009-11-16 00:00:00 -08:00
18
+ date: 2010-08-01 00:00:00 -07:00
13
19
  default_executable:
14
20
  dependencies: []
15
21
 
@@ -27,13 +33,16 @@ files:
27
33
  - ext/extconf.rb
28
34
  - ext/perftools.c
29
35
  - ext/src/google-perftools-1.4.tar.gz
36
+ - objalloc_tests/Makefile
37
+ - objalloc_tests/trap.c
30
38
  - patches/perftools-debug.patch
31
39
  - patches/perftools-gc.patch
40
+ - patches/perftools-notests.patch
41
+ - patches/perftools-objects.patch
32
42
  - patches/perftools-osx-106.patch
33
43
  - patches/perftools-osx.patch
34
44
  - patches/perftools-pprof.patch
35
45
  - patches/perftools-realtime.patch
36
- - patches/perftools-notests.patch
37
46
  - patches/perftools.patch
38
47
  - perftools.rb.gemspec
39
48
  has_rdoc: true
@@ -46,21 +55,27 @@ rdoc_options: []
46
55
  require_paths:
47
56
  - lib
48
57
  required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
49
59
  requirements:
50
60
  - - ">="
51
61
  - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
52
65
  version: "0"
53
- version:
54
66
  required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
55
68
  requirements:
56
69
  - - ">="
57
70
  - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
58
74
  version: "0"
59
- version:
60
75
  requirements: []
61
76
 
62
77
  rubyforge_project: perftools-rb
63
- rubygems_version: 1.3.5
78
+ rubygems_version: 1.3.7
64
79
  signing_key:
65
80
  specification_version: 3
66
81
  summary: google-perftools for ruby code