tmm1-perftools.rb 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,151 @@
1
+ google-perftools for ruby code
2
+ (c) 2009 Aman Gupta (tmm1)
3
+
4
+ === Usage
5
+
6
+ Run the profiler with a block:
7
+
8
+ require 'perftools'
9
+ PerfTools::CpuProfiler.start("/tmp/add_numbers_profile") do
10
+ 5_000_000.times{ 1+2+3+4+5 }
11
+ end
12
+
13
+ Start and stop the profiler manually:
14
+
15
+ require 'perftools'
16
+ PerfTools::CpuProfiler.start("/tmp/add_numbers_profile")
17
+ 5_000_000.times{ 1+2+3+4+5 }
18
+ PerfTools::CpuProfiler.stop
19
+
20
+ Profile an existing ruby application without modifying it:
21
+
22
+ $ CPUPROFILE=/tmp/my_app_profile RUBYOPT="-r`gem which perftools | tail -1`" ruby my_app.rb
23
+
24
+
25
+ === Reporting
26
+
27
+ pprof.rb --text /tmp/add_numbers_profile
28
+
29
+ pprof.rb --pdf /tmp/add_numbers_profile > /tmp/add_numbers_profile.pdf
30
+
31
+ pprof.rb --gif /tmp/add_numbers_profile > /tmp/add_numbers_profile.gif
32
+
33
+ pprof.rb --callgrind /tmp/add_numbers_profile > /tmp/add_numbers_profile.grind
34
+ kcachegrind /tmp/add_numbers_profile.grind
35
+
36
+ pprof.rb --gif --focus=Integer /tmp/add_numbers_profile > /tmp/add_numbers_custom.gif
37
+
38
+ pprof.rb --text --ignore=Gem /tmp/my_app_profile
39
+
40
+
41
+ For more options, see http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html#pprof
42
+
43
+
44
+ === Examples
45
+
46
+ pprof.rb --text
47
+
48
+ Total: 1735 samples
49
+ 1487 85.7% 85.7% 1735 100.0% Integer#times
50
+ 248 14.3% 100.0% 248 14.3% Fixnum#+
51
+
52
+ pprof.rb --gif
53
+
54
+ Simple require 'rubygems' profile
55
+
56
+ http://github.com/tmm1/perftools.rb/blob/master/examples/rubygems.gif
57
+
58
+ Comparing redis-rb with and without SystemTimer based socket timeouts
59
+
60
+ http://github.com/tmm1/perftools.rb/blob/master/examples/redis-rb.gif
61
+ http://github.com/tmm1/perftools.rb/blob/master/examples/redis-rb-notimeout.gif
62
+
63
+ Sinatra vs. Merb vs. Rails
64
+
65
+ http://github.com/tmm1/perftools.rb/blob/master/examples/sinatra.gif
66
+ http://github.com/tmm1/perftools.rb/blob/master/examples/merb.gif
67
+ http://github.com/tmm1/perftools.rb/blob/master/examples/rails.gif
68
+
69
+ C-level profile of EventMachine + epoll + Ruby threads
70
+ before http://timetobleed.com/6-line-eventmachine-bugfix-2x-faster-gc-1300-requestssec/
71
+
72
+ http://github.com/tmm1/perftools.rb/blob/master/examples/eventmachine-epoll+nothreads.gif
73
+ http://github.com/tmm1/perftools.rb/blob/master/examples/eventmachine-epoll+threads.gif
74
+
75
+ C-level profile of the ruby interpreter
76
+ 12% time spent in re_match_exec because of excessive calls to rb_str_sub_bang by Date.parse
77
+ easily fixed by using the ThirdBase gem
78
+
79
+ http://github.com/tmm1/perftools.rb/blob/master/examples/ruby_interpreter.gif
80
+
81
+
82
+ === Installation
83
+
84
+ You'll need a patched version of Ruby 1.8:
85
+
86
+ wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p368.tar.gz
87
+ tar zxvf ruby-1.8.6-p368.tar.gz
88
+ cd ruby-1.8.6-p368
89
+
90
+ wget http://github.com/tmm1/perftools.rb/raw/master/patches/ruby.patch
91
+ patch -p1 < ruby.patch
92
+
93
+ ./configure --prefix=/opt/ruby-profiling
94
+ make
95
+ sudo make install
96
+
97
+ Install rubygems:
98
+
99
+ wget http://rubyforge.org/frs/download.php/57643/rubygems-1.3.4.tgz
100
+ tar zxvf rubygems-1.3.4.tgz
101
+ cd rubygems-1.3.4
102
+ /opt/ruby-profiling/bin/ruby setup.rb
103
+
104
+ Install the perftools.rb gem, which will download and compile google-perftools for you:
105
+
106
+ git clone git://github.com/tmm1/perftools.rb
107
+ cd perftools.rb
108
+ gem build perftools.rb.gemspec
109
+ /opt/ruby-profiling/bin/gem install perftools.rb
110
+
111
+
112
+ === Advantages over ruby-prof
113
+
114
+ Sampling profiler
115
+
116
+ perftools samples your process using setitimer() so it can be used in production with minimal overhead.
117
+
118
+
119
+ === Profiling the Ruby VM and C extensions
120
+
121
+ To profile C code, simply download and build perftools (libunwind or ./configure --enable-frame-pointers required on x86_64):
122
+
123
+ wget http://google-perftools.googlecode.com/files/google-perftools-1.2.tar.gz
124
+ tar zxvf google-perftools-1.2.tar.gz
125
+ cd google-perftools-1.2
126
+
127
+ ./configure --prefix=/opt --disable-shared
128
+ make
129
+ sudo make install
130
+
131
+ export LD_PRELOAD=/opt/lib/libprofiler.so # for linux
132
+ export DYLD_INSERT_LIBRARIES=/opt/lib/libprofiler.dylib # for osx
133
+ CPUPROFILE=/tmp/ruby_interpreter.profile ruby -e' 5_000_000.times{ "hello world" } '
134
+
135
+ pprof `which ruby` --text /tmp/ruby_interpreter.profile
136
+
137
+
138
+ === TODO
139
+
140
+ * Add support for heap profiling to find memory leaks (PerfTools::HeapProfiler)
141
+ * Allow both C and Ruby profiling
142
+ * Add setter for the sampling interval
143
+
144
+
145
+ === Resources
146
+
147
+ Google Perftools
148
+ http://code.google.com/p/google-perftools/
149
+
150
+ Analyzing profiles and interpreting different output formats
151
+ http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html#pprof
data/bin/pprof.rb ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+ # XXX assuming that we're going to be run from inside the gem installation
3
+ `dirname $0`/pprof `which ruby` $*
data/ext/extconf.rb ADDED
@@ -0,0 +1,46 @@
1
+ require 'mkmf'
2
+ require 'fileutils'
3
+ require 'net/http'
4
+
5
+ # http://google-perftools.googlecode.com/files/google-perftools-1.2.tar.gz
6
+ perftools = "google-perftools-1.2.tar.gz"
7
+ dir = File.basename(perftools, '.tar.gz')
8
+
9
+ Logging.message "(I'm about to download and compile google-perftools.. this will definitely take a while)"
10
+
11
+ FileUtils.mkdir("src") unless File.exists?("src")
12
+ Dir.chdir("src") do
13
+ Net::HTTP.start("google-perftools.googlecode.com") { |http|
14
+ resp = http.get("/files/#{perftools}")
15
+ open(perftools, "wb") { |file|
16
+ file.write(resp.body)
17
+ }
18
+ } unless File.exists?(perftools)
19
+
20
+ unless File.exists?(dir)
21
+ xsystem("tar zxvf #{perftools}")
22
+ Dir.chdir(dir) do
23
+ xsystem("patch -p1 < ../../../patches/perftools.patch")
24
+ xsystem("patch -p1 < ../../../patches/perftools-static.patch")
25
+ xsystem("patch -p1 < ../../../patches/perftools-osx.patch") if RUBY_PLATFORM =~ /darwin/
26
+ xsystem("patch -p1 < ../../../patches/perftools-debug.patch")# if ENV['DEBUG']
27
+ end
28
+ end
29
+
30
+ unless File.exists?('../bin/pprof')
31
+ Dir.chdir(dir) do
32
+ FileUtils.cp 'src/pprof', '../../../bin/'
33
+ end
34
+ end
35
+
36
+ unless File.exists?('../libprofiler.a')
37
+ Dir.chdir(dir) do
38
+ xsystem("./configure --disable-heap-profiler --disable-heap-checker --disable-minimal --disable-shared")
39
+ xsystem("make")
40
+ FileUtils.cp '.libs/libprofiler.a', '../../'
41
+ end
42
+ end
43
+ end
44
+
45
+ $libs = append_library($libs, 'profiler')
46
+ create_makefile 'perftools'
data/ext/perftools.c ADDED
@@ -0,0 +1,54 @@
1
+ #include <ruby.h>
2
+
3
+ static VALUE cPerfTools;
4
+ static VALUE cCpuProfiler;
5
+ static VALUE bProfilerRunning;
6
+
7
+ VALUE
8
+ cpuprofiler_running_p(VALUE self)
9
+ {
10
+ return bProfilerRunning;
11
+ }
12
+
13
+ VALUE
14
+ cpuprofiler_stop(VALUE self)
15
+ {
16
+ if (!bProfilerRunning)
17
+ return Qfalse;
18
+
19
+ bProfilerRunning = Qfalse;
20
+ ProfilerStop();
21
+ ProfilerFlush();
22
+ return Qtrue;
23
+ }
24
+
25
+ VALUE
26
+ cpuprofiler_start(VALUE self, VALUE filename)
27
+ {
28
+ StringValue(filename);
29
+
30
+ if (bProfilerRunning)
31
+ return Qfalse;
32
+
33
+ ProfilerStart(RSTRING_PTR(filename));
34
+ bProfilerRunning = Qtrue;
35
+
36
+ if (rb_block_given_p()) {
37
+ rb_yield(Qnil);
38
+ cpuprofiler_stop(self);
39
+ }
40
+
41
+ return Qtrue;
42
+ }
43
+
44
+ void
45
+ Init_perftools()
46
+ {
47
+ cPerfTools = rb_define_class("PerfTools", rb_cObject);
48
+ cCpuProfiler = rb_define_class_under(cPerfTools, "CpuProfiler", rb_cObject);
49
+ bProfilerRunning = Qfalse;
50
+
51
+ rb_define_singleton_method(cCpuProfiler, "running?", cpuprofiler_running_p, 0);
52
+ rb_define_singleton_method(cCpuProfiler, "start", cpuprofiler_start, 1);
53
+ rb_define_singleton_method(cCpuProfiler, "stop", cpuprofiler_stop, 0);
54
+ }
@@ -0,0 +1,20 @@
1
+ diff --git a/Makefile.in b/Makefile.in
2
+ index 1247b91..45179f3 100644
3
+ --- a/Makefile.in
4
+ +++ b/Makefile.in
5
+ @@ -989,13 +989,13 @@ AUTOMAKE = @AUTOMAKE@
6
+ AWK = @AWK@
7
+ CC = @CC@
8
+ CCDEPMODE = @CCDEPMODE@
9
+ -CFLAGS = @CFLAGS@ -DBUILD_FOR_RUBY
10
+ +CFLAGS = @CFLAGS@ -DBUILD_FOR_RUBY -O0 -ggdb
11
+ CPP = @CPP@
12
+ CPPFLAGS = @CPPFLAGS@
13
+ CXX = @CXX@
14
+ CXXCPP = @CXXCPP@
15
+ CXXDEPMODE = @CXXDEPMODE@
16
+ -CXXFLAGS = @CXXFLAGS@ -DBUILD_FOR_RUBY
17
+ +CXXFLAGS = @CXXFLAGS@ -DBUILD_FOR_RUBY -O0 -ggdb
18
+ CYGPATH_W = @CYGPATH_W@
19
+ DEFS = @DEFS@
20
+ DEPDIR = @DEPDIR@
@@ -0,0 +1,13 @@
1
+ diff --git a/Makefile.in b/Makefile.in
2
+ index 1247b91..3f6e133 100644
3
+ --- a/Makefile.in
4
+ +++ b/Makefile.in
5
+ @@ -1017,7 +1017,7 @@ INSTALL_DATA = @INSTALL_DATA@
6
+ INSTALL_PROGRAM = @INSTALL_PROGRAM@
7
+ INSTALL_SCRIPT = @INSTALL_SCRIPT@
8
+ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
9
+ -LDFLAGS = @LDFLAGS@
10
+ +LDFLAGS = @LDFLAGS@ -Wl,-flat_namespace,-undefined,dynamic_lookup
11
+ LIBOBJS = @LIBOBJS@
12
+ LIBS = @LIBS@
13
+ LIBSTDCXX_LA_LINKER_FLAG = @LIBSTDCXX_LA_LINKER_FLAG@
@@ -0,0 +1,27 @@
1
+ diff --git a/Makefile.in b/Makefile.in
2
+ index 1247b91..e7c5bca 100644
3
+ --- a/Makefile.in
4
+ +++ b/Makefile.in
5
+ @@ -3736,14 +3736,14 @@ uninstall-man: uninstall-man1
6
+ # (since it does work only in global constructors and destructors).
7
+ # We just replace the .a with an 'ld-r-ized' version under libtool's
8
+ # nose.
9
+ -@WITH_CPU_PROFILER_TRUE@libprofiler.o: libprofiler.la $(libprofiler_la_OBJECTS) $(libprofiler_la_DEPENDENCIES) $(libprofiler_la_LIBADD)
10
+ -@WITH_CPU_PROFILER_TRUE@ $(CXXLINK) $(libprofiler_la_OBJECTS) $(libprofiler_la_LIBADD)
11
+ -@WITH_CPU_PROFILER_TRUE@ cp -f "$@" .libs/libprofiler.a
12
+ -@WITH_CPU_PROFILER_TRUE@profiler_unittest.sh$(EXEEXT): $(top_srcdir)/$(profiler_unittest_sh_SOURCES) \
13
+ -@WITH_CPU_PROFILER_TRUE@ profiler1_unittest profiler2_unittest \
14
+ -@WITH_CPU_PROFILER_TRUE@ profiler3_unittest profiler4_unittest
15
+ -@WITH_CPU_PROFILER_TRUE@ rm -f $@
16
+ -@WITH_CPU_PROFILER_TRUE@ cp -p $(top_srcdir)/$(profiler_unittest_sh_SOURCES) $@
17
+ +# @WITH_CPU_PROFILER_TRUE@libprofiler.o: libprofiler.la $(libprofiler_la_OBJECTS) $(libprofiler_la_DEPENDENCIES) $(libprofiler_la_LIBADD)
18
+ +# @WITH_CPU_PROFILER_TRUE@ $(CXXLINK) $(libprofiler_la_OBJECTS) $(libprofiler_la_LIBADD)
19
+ +# @WITH_CPU_PROFILER_TRUE@ cp -f "$@" .libs/libprofiler.a
20
+ +# @WITH_CPU_PROFILER_TRUE@profiler_unittest.sh$(EXEEXT): $(top_srcdir)/$(profiler_unittest_sh_SOURCES) \
21
+ +# @WITH_CPU_PROFILER_TRUE@ profiler1_unittest profiler2_unittest \
22
+ +# @WITH_CPU_PROFILER_TRUE@ profiler3_unittest profiler4_unittest
23
+ +# @WITH_CPU_PROFILER_TRUE@ rm -f $@
24
+ +# @WITH_CPU_PROFILER_TRUE@ cp -p $(top_srcdir)/$(profiler_unittest_sh_SOURCES) $@
25
+
26
+ rpm: dist-gzip packages/rpm.sh packages/rpm/rpm.spec
27
+ @cd packages && ./rpm.sh ${PACKAGE} ${VERSION}
@@ -0,0 +1,302 @@
1
+ diff --git a/src/pprof b/src/pprof
2
+ index f23786d..21ccd12 100755
3
+ --- a/src/pprof
4
+ +++ b/src/pprof
5
+ @@ -504,7 +504,8 @@ sub Main() {
6
+ FetchDynamicProfiles();
7
+
8
+ # Read one profile, pick the last item on the list
9
+ - my $data = ReadProfile($main::prog, pop(@main::profile_files));
10
+ + my $fname = pop(@main::profile_files);
11
+ + my $data = ReadProfile($main::prog, $fname);
12
+ my $profile = $data->{profile};
13
+ my $pcs = $data->{pcs};
14
+ my $libs = $data->{libs}; # Info about main program and shared libraries
15
+ @@ -536,6 +537,17 @@ sub Main() {
16
+ $symbols = ExtractSymbols($libs, $pcs);
17
+ }
18
+
19
+ + if (-e "$fname.symbols") {
20
+ + open(SYMBOLS, "<$fname.symbols");
21
+ + while(<SYMBOLS>){
22
+ + chop;
23
+ + if (m/(.+?)\s*:\s*(.*)/){
24
+ + $symbols->{$1}[0] = $2;
25
+ + }
26
+ + }
27
+ + close(SYMBOLS);
28
+ + }
29
+ +
30
+ my $calls = ExtractCalls($symbols, $profile);
31
+
32
+ # Remove uniniteresting stack items
33
+ diff --git a/src/profile-handler.cc b/src/profile-handler.cc
34
+ index 0a9f54c..5b99a81 100644
35
+ --- a/src/profile-handler.cc
36
+ +++ b/src/profile-handler.cc
37
+ @@ -263,6 +263,11 @@ ProfileHandler::~ProfileHandler() {
38
+ void ProfileHandler::RegisterThread() {
39
+ SpinLockHolder cl(&control_lock_);
40
+
41
+ +#ifdef BUILD_FOR_RUBY
42
+ + timer_sharing_ = TIMERS_SHARED;
43
+ + if (callback_count_ > 0 && !IsTimerRunning())
44
+ + StartTimer();
45
+ +#else
46
+ // We try to detect whether timers are being shared by setting a
47
+ // timer in the first call to this function, then checking whether
48
+ // it's set in the second call.
49
+ @@ -304,6 +309,7 @@ void ProfileHandler::RegisterThread() {
50
+ StartTimer();
51
+ break;
52
+ }
53
+ +#endif
54
+ }
55
+
56
+ ProfileHandlerToken* ProfileHandler::RegisterCallback(
57
+ diff --git a/src/profiledata.cc b/src/profiledata.cc
58
+ index 5f2531b..e6240d9 100644
59
+ --- a/src/profiledata.cc
60
+ +++ b/src/profiledata.cc
61
+ @@ -56,6 +56,19 @@ const int ProfileData::kAssociativity;
62
+ const int ProfileData::kBuckets;
63
+ const int ProfileData::kBufferLength;
64
+
65
+ +#ifdef BUILD_FOR_RUBY
66
+ +extern "C" {
67
+ + typedef unsigned long ID;
68
+ + typedef unsigned long VALUE;
69
+ +
70
+ + void rb_gc();
71
+ + const char *rb_id2name(ID);
72
+ + const char *rb_class2name(VALUE);
73
+ +}
74
+ +
75
+ +#include <set>
76
+ +#endif
77
+ +
78
+ ProfileData::Options::Options()
79
+ : frequency_(1) {
80
+ }
81
+ @@ -63,17 +76,33 @@ ProfileData::Options::Options()
82
+ // This function is safe to call from asynchronous signals (but is not
83
+ // re-entrant). However, that's not part of its public interface.
84
+ void ProfileData::Evict(const Entry& entry) {
85
+ +#ifdef BUILD_FOR_RUBY
86
+ + const int d = entry.depth == 1 ? 1 : entry.depth/3;
87
+ +#else
88
+ const int d = entry.depth;
89
+ +#endif
90
+ const int nslots = d + 2; // Number of slots needed in eviction buffer
91
+ +
92
+ if (num_evicted_ + nslots > kBufferLength) {
93
+ FlushEvicted();
94
+ assert(num_evicted_ == 0);
95
+ assert(nslots <= kBufferLength);
96
+ }
97
+ +
98
+ evict_[num_evicted_++] = entry.count;
99
+ evict_[num_evicted_++] = d;
100
+ +
101
+ +#ifdef BUILD_FOR_RUBY
102
+ + if (entry.depth > 1) {
103
+ + for (int n=0; n<entry.depth; n+=3)
104
+ + evict_[num_evicted_++] = entry.stack[n] + entry.stack[n+1] + entry.stack[n+2];
105
+ + } else if (entry.depth == 1) {
106
+ + evict_[num_evicted_++] = entry.stack[0];
107
+ + }
108
+ +#else
109
+ memcpy(&evict_[num_evicted_], entry.stack, d * sizeof(Slot));
110
+ num_evicted_ += d;
111
+ +#endif
112
+ }
113
+
114
+ ProfileData::ProfileData()
115
+ @@ -85,6 +114,7 @@ ProfileData::ProfileData()
116
+ evictions_(0),
117
+ total_bytes_(0),
118
+ fname_(0),
119
+ + sym_fname_(0),
120
+ start_time_(0) {
121
+ }
122
+
123
+ @@ -101,6 +131,13 @@ bool ProfileData::Start(const char* fname,
124
+ return false;
125
+ }
126
+
127
+ +#ifdef BUILD_FOR_RUBY
128
+ + int len = strlen(fname);
129
+ + sym_fname_ = (char*)malloc((len+9) * sizeof(char));
130
+ + strncpy(sym_fname_, fname, len);
131
+ + strcpy(sym_fname_+len, ".symbols");
132
+ +#endif
133
+ +
134
+ start_time_ = time(NULL);
135
+ fname_ = strdup(fname);
136
+
137
+ @@ -166,16 +203,47 @@ void ProfileData::Stop() {
138
+ return;
139
+ }
140
+
141
+ +#ifdef BUILD_FOR_RUBY
142
+ + FILE *symbols;
143
+ + symbols = fopen(sym_fname_, "w");
144
+ + fprintf(symbols, "%0*lx: garbage_collector\n", sizeof(unsigned long)*2, (ID)rb_gc);
145
+ +
146
+ + std::set<ID> known_symbols;
147
+ +#endif
148
+ +
149
+ // Move data from hash table to eviction buffer
150
+ for (int b = 0; b < kBuckets; b++) {
151
+ Bucket* bucket = &hash_[b];
152
+ for (int a = 0; a < kAssociativity; a++) {
153
+ if (bucket->entry[a].count > 0) {
154
+ - Evict(bucket->entry[a]);
155
+ + Entry e = bucket->entry[a];
156
+ + Evict(e);
157
+ +#ifdef BUILD_FOR_RUBY
158
+ + if (e.depth > 1)
159
+ + for (int n=0; n<e.depth; n+=3) {
160
+ + ID sym = e.stack[n] + e.stack[n+1] + e.stack[n+2];
161
+ +
162
+ + if (known_symbols.find(sym) == known_symbols.end()) {
163
+ + fprintf(symbols, "%0*lx: ", sizeof(unsigned long)*2, sym);
164
+ +
165
+ + if (e.stack[n])
166
+ + fprintf(symbols, "%s.", rb_class2name(e.stack[n]));
167
+ + else
168
+ + fprintf(symbols, "%s#", rb_class2name(e.stack[n+1]));
169
+ +
170
+ + fprintf(symbols, "%s\n", rb_id2name(e.stack[n+2]));
171
+ + known_symbols.insert(sym);
172
+ + }
173
+ + }
174
+ +#endif
175
+ }
176
+ }
177
+ }
178
+
179
+ +#ifdef BUILD_FOR_RUBY
180
+ + fclose(symbols);
181
+ +#endif
182
+ +
183
+ if (num_evicted_ + 3 > kBufferLength) {
184
+ // Ensure there is enough room for end of data marker
185
+ FlushEvicted();
186
+ @@ -211,6 +279,10 @@ void ProfileData::Reset() {
187
+ num_evicted_ = 0;
188
+ free(fname_);
189
+ fname_ = 0;
190
+ +#ifdef BUILD_FOR_RUBY
191
+ + free(sym_fname_);
192
+ + sym_fname_ = 0;
193
+ +#endif
194
+ start_time_ = 0;
195
+
196
+ out_ = -1;
197
+ diff --git a/src/profiledata.h b/src/profiledata.h
198
+ index da7ea9e..67c463d 100644
199
+ --- a/src/profiledata.h
200
+ +++ b/src/profiledata.h
201
+ @@ -169,6 +169,7 @@ class ProfileData {
202
+ int evictions_; // How many evictions
203
+ size_t total_bytes_; // How much output
204
+ char* fname_; // Profile file name
205
+ + char* sym_fname_; // Symbol file name
206
+ time_t start_time_; // Start time, or 0
207
+
208
+ // Move 'entry' to the eviction buffer.
209
+ diff --git a/src/profiler.cc b/src/profiler.cc
210
+ index c51c7b2..21c7669 100644
211
+ --- a/src/profiler.cc
212
+ +++ b/src/profiler.cc
213
+ @@ -63,6 +63,12 @@ typedef int ucontext_t; // just to quiet the compiler, mostly
214
+ #include "conflict-signal.h" /* used on msvc machines */
215
+ #endif
216
+
217
+ +#ifdef BUILD_FOR_RUBY
218
+ +extern "C" {
219
+ + int rb_stack_trace(void**,int);
220
+ +}
221
+ +#endif
222
+ +
223
+ using std::string;
224
+
225
+ // Collects up all profile data. This is a singleton, which is
226
+ @@ -261,6 +267,9 @@ void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,
227
+ (*instance->filter_)(instance->filter_arg_)) {
228
+ void* stack[ProfileData::kMaxStackDepth];
229
+
230
+ +#ifdef BUILD_FOR_RUBY
231
+ + int depth = rb_stack_trace(stack, arraysize(stack));
232
+ +#else
233
+ // The top-most active routine doesn't show up as a normal
234
+ // frame, but as the "pc" value in the signal handler context.
235
+ stack[0] = GetPC(*reinterpret_cast<ucontext_t*>(signal_ucontext));
236
+ @@ -274,8 +283,10 @@ void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,
237
+ int depth = GetStackTraceWithContext(stack + 1, arraysize(stack) - 1,
238
+ 2, signal_ucontext);
239
+ depth++; // To account for pc value in stack[0];
240
+ +#endif
241
+
242
+ - instance->collector_.Add(depth, stack);
243
+ + if (depth > 0)
244
+ + instance->collector_.Add(depth, stack);
245
+ }
246
+ }
247
+
248
+ diff --git a/src/stacktrace.cc b/src/stacktrace.cc
249
+ index d158eea..e4132fe 100644
250
+ --- a/src/stacktrace.cc
251
+ +++ b/src/stacktrace.cc
252
+ @@ -52,6 +52,7 @@
253
+ // correctly when GetStackTrace() is called with max_depth == 0.
254
+ // Some code may do that.
255
+
256
+ +#ifndef BUILD_FOR_RUBY
257
+ #include "config.h"
258
+ #include <google/stacktrace.h>
259
+ #include "stacktrace_config.h"
260
+ @@ -69,3 +70,4 @@
261
+ #else
262
+ # error Cannot calculate stack trace: will need to write for your environment
263
+ #endif
264
+ +#endif
265
+
266
+ diff --git a/Makefile.in b/Makefile.in
267
+ index 1247b91..45179f3 100644
268
+ --- a/Makefile.in
269
+ +++ b/Makefile.in
270
+ @@ -989,13 +989,13 @@ AUTOMAKE = @AUTOMAKE@
271
+ AWK = @AWK@
272
+ CC = @CC@
273
+ CCDEPMODE = @CCDEPMODE@
274
+ -CFLAGS = @CFLAGS@
275
+ +CFLAGS = @CFLAGS@ -DBUILD_FOR_RUBY
276
+ CPP = @CPP@
277
+ CPPFLAGS = @CPPFLAGS@
278
+ CXX = @CXX@
279
+ CXXCPP = @CXXCPP@
280
+ CXXDEPMODE = @CXXDEPMODE@
281
+ -CXXFLAGS = @CXXFLAGS@
282
+ +CXXFLAGS = @CXXFLAGS@ -DBUILD_FOR_RUBY
283
+ CYGPATH_W = @CYGPATH_W@
284
+ DEFS = @DEFS@
285
+ DEPDIR = @DEPDIR@
286
+ diff --git a/src/stacktrace_with_context.cc b/src/stacktrace_with_context.cc
287
+ index f9fc28f..beb7562 100644
288
+ --- a/src/stacktrace_with_context.cc
289
+ +++ b/src/stacktrace_with_context.cc
290
+ @@ -41,6 +41,7 @@
291
+ // may inline this code anyway. Let's hope they respect
292
+ // ATTRIBUTE_NOINLINE.
293
+
294
+ +#ifndef BUILD_FOR_RUBY
295
+ #include "config.h"
296
+ #include <google/stacktrace.h>
297
+ #include "stacktrace_config.h"
298
+ @@ -59,3 +60,4 @@ int GetStackTraceWithContext(void** result, int max_depth,
299
+ return GetStackTrace(result, max_depth, skip_count + 1);
300
+ }
301
+ #endif
302
+ +#endif
@@ -0,0 +1,72 @@
1
+ diff --git a/eval.c b/eval.c
2
+ index 4989aac..51fb0e5 100644
3
+ --- a/eval.c
4
+ +++ b/eval.c
5
+ @@ -6385,6 +6385,67 @@ backtrace(lev)
6
+ return ary;
7
+ }
8
+
9
+ +int
10
+ +rb_stack_trace(result, max_depth)
11
+ + void** result;
12
+ + int max_depth;
13
+ +{
14
+ + int depth = 0;
15
+ + struct FRAME *frame = ruby_frame;
16
+ + NODE *n;
17
+ + st_data_t key;
18
+ +
19
+ + if (max_depth == 0) return 0;
20
+ + // if (rb_prohibit_interrupt || !rb_trap_immediate) return 0;
21
+ +
22
+ + if (rb_during_gc()) {
23
+ + result[0] = rb_gc;
24
+ + return 1;
25
+ + }
26
+ +
27
+ + if (frame->last_func == ID_ALLOCATOR) {
28
+ + frame = frame->prev;
29
+ + }
30
+ +
31
+ + if (frame->last_func) {
32
+ + VALUE klass = frame->last_class;
33
+ + // if (BUILTIN_TYPE(klass) == T_ICLASS)
34
+ + // klass = RBASIC(klass)->klass;
35
+ +
36
+ + if (FL_TEST(klass, FL_SINGLETON) && (BUILTIN_TYPE(frame->self) == T_CLASS || BUILTIN_TYPE(frame->self) == T_MODULE))
37
+ + result[depth++] = frame->self;
38
+ + else
39
+ + result[depth++] = 0;
40
+ +
41
+ + result[depth++] = klass;
42
+ + result[depth++] = frame->last_func;
43
+ + }
44
+ +
45
+ + for (; frame && (n = frame->node); frame = frame->prev) {
46
+ + if (frame->prev && frame->prev->last_func) {
47
+ + if (frame->prev->node == n) {
48
+ + if (frame->prev->last_func == frame->last_func) continue;
49
+ + }
50
+ +
51
+ + if (depth+3 > max_depth) break;
52
+ +
53
+ + VALUE klass = frame->prev->last_class;
54
+ + // if (BUILTIN_TYPE(klass) == T_ICLASS)
55
+ + // klass = RBASIC(klass)->klass;
56
+ +
57
+ + if (FL_TEST(klass, FL_SINGLETON) && (BUILTIN_TYPE(frame->prev->self) == T_CLASS || BUILTIN_TYPE(frame->prev->self) == T_MODULE))
58
+ + result[depth++] = frame->prev->self;
59
+ + else
60
+ + result[depth++] = 0;
61
+ +
62
+ + result[depth++] = klass;
63
+ + result[depth++] = frame->prev->last_func;
64
+ + }
65
+ + }
66
+ +
67
+ + return depth;
68
+ +}
69
+ +
70
+ /*
71
+ * call-seq:
72
+ * caller(start=1) => array
@@ -0,0 +1,31 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'perftools.rb'
3
+ s.version = '0.1.1'
4
+ s.date = '2009-06-01'
5
+ s.summary = 'google-perftools for ruby code'
6
+ s.description = 'A sampling profiler for ruby code based on patches to google-perftools'
7
+
8
+ s.homepage = "http://github.com/tmm1/perftools.rb"
9
+
10
+ s.authors = ["Aman Gupta"]
11
+ s.email = "aman@tmm1.net"
12
+
13
+ s.has_rdoc = false
14
+ s.extensions = 'ext/extconf.rb'
15
+ s.bindir = 'bin'
16
+ s.executables << 'pprof.rb'
17
+
18
+ # ruby -rpp -e' pp `git ls-files | grep -v examples`.split("\n") '
19
+ s.files = [
20
+ "README",
21
+ "bin/pprof.rb",
22
+ "ext/extconf.rb",
23
+ "ext/perftools.c",
24
+ "patches/perftools-debug.patch",
25
+ "patches/perftools-osx.patch",
26
+ "patches/perftools-static.patch",
27
+ "patches/perftools.patch",
28
+ "patches/ruby.patch",
29
+ "perftools.rb.gemspec"
30
+ ]
31
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tmm1-perftools.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Aman Gupta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A sampling profiler for ruby code based on patches to google-perftools
17
+ email: aman@tmm1.net
18
+ executables:
19
+ - pprof.rb
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README
26
+ - bin/pprof.rb
27
+ - ext/extconf.rb
28
+ - ext/perftools.c
29
+ - patches/perftools-debug.patch
30
+ - patches/perftools-osx.patch
31
+ - patches/perftools-static.patch
32
+ - patches/perftools.patch
33
+ - patches/ruby.patch
34
+ - perftools.rb.gemspec
35
+ has_rdoc: false
36
+ homepage: http://github.com/tmm1/perftools.rb
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: google-perftools for ruby code
61
+ test_files: []
62
+