perftools.rb 0.4.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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