perftools.rb 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,146 @@
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://perftools-rb.rubyforge.org/examples/rubygems.gif
57
+
58
+ Comparing redis-rb with and without SystemTimer based socket timeouts
59
+
60
+ http://perftools-rb.rubyforge.org/examples/redis-rb.gif
61
+ http://perftools-rb.rubyforge.org/examples/redis-rb-notimeout.gif
62
+
63
+ Sinatra vs. Merb vs. Rails
64
+
65
+ http://perftools-rb.rubyforge.org/examples/sinatra.gif
66
+ http://perftools-rb.rubyforge.org/examples/merb.gif
67
+ http://perftools-rb.rubyforge.org/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://perftools-rb.rubyforge.org/examples/eventmachine-epoll+nothreads.gif
73
+ http://perftools-rb.rubyforge.org/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://perftools-rb.rubyforge.org/examples/ruby_interpreter.gif
80
+
81
+
82
+ === Installation
83
+
84
+ Just install the gem, which will download, patch and compile google-perftools for you:
85
+
86
+ sudo gem install perftools.rb
87
+
88
+ Or use the dev gem from github:
89
+
90
+ gem install -s http://gems.github.com tmm1-perftools.rb
91
+
92
+ Or build your own gem:
93
+
94
+ git clone git://github.com/tmm1/perftools.rb
95
+ cd perftools.rb
96
+ gem build perftools.rb.gemspec
97
+ gem install perftools.rb
98
+
99
+
100
+ You'll also need graphviz to generate call graphs using dot:
101
+
102
+ sudo port install graphviz # osx
103
+ sudo apt-get install graphviz # debian/ubuntu
104
+
105
+
106
+ === Advantages over ruby-prof
107
+
108
+ Sampling profiler
109
+
110
+ perftools samples your process using setitimer() so it can be used in production with minimal overhead.
111
+
112
+
113
+ === Profiling the Ruby VM and C extensions
114
+
115
+ To profile C code, download and build an unpatched perftools (libunwind or ./configure --enable-frame-pointers required on x86_64):
116
+
117
+ wget http://google-perftools.googlecode.com/files/google-perftools-1.2.tar.gz
118
+ tar zxvf google-perftools-1.2.tar.gz
119
+ cd google-perftools-1.2
120
+
121
+ ./configure --prefix=/opt --disable-shared
122
+ make
123
+ sudo make install
124
+
125
+ export LD_PRELOAD=/opt/lib/libprofiler.so # for linux
126
+ export DYLD_INSERT_LIBRARIES=/opt/lib/libprofiler.dylib # for osx
127
+ CPUPROFILE=/tmp/ruby_interpreter.profile ruby -e' 5_000_000.times{ "hello world" } '
128
+
129
+ pprof `which ruby` --text /tmp/ruby_interpreter.profile
130
+
131
+
132
+ === TODO
133
+
134
+ * Add support for heap profiling to find memory leaks (PerfTools::HeapProfiler)
135
+ * Allow both C and Ruby profiling
136
+ * Add setter for the sampling interval
137
+ * Add support for ruby 1.9
138
+
139
+
140
+ === Resources
141
+
142
+ Google Perftools
143
+ http://code.google.com/p/google-perftools/
144
+
145
+ Analyzing profiles and interpreting different output formats
146
+ http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html#pprof
data/bin/pprof.rb ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ cmd = "#{File.dirname(__FILE__)}/pprof #{`which ruby`.strip} #{ARGV.join(" ")}"
3
+ exec(cmd)
data/ext/extconf.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'mkmf'
2
+ require 'fileutils'
3
+ require 'net/http'
4
+
5
+ url = 'http://google-perftools.googlecode.com/files/google-perftools-1.2.tar.gz'
6
+ perftools = File.basename(url)
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_p('src')
12
+
13
+ Dir.chdir('src') do
14
+ unless File.exists?(perftools)
15
+ Net::HTTP.get_response(URI(url)) do |res|
16
+ File.open(perftools, 'wb') do |out|
17
+ res.read_body do |chunk|
18
+ out.write(chunk)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ unless File.exists?(dir)
25
+ xsystem("tar zxvf #{perftools}")
26
+ Dir.chdir(dir) do
27
+ xsystem("patch -p1 < ../../../patches/perftools.patch")
28
+ xsystem("patch -p1 < ../../../patches/perftools-static.patch")
29
+ xsystem("patch -p1 < ../../../patches/perftools-osx.patch") if RUBY_PLATFORM =~ /darwin/
30
+ xsystem("patch -p1 < ../../../patches/perftools-debug.patch")# if ENV['DEBUG']
31
+ end
32
+ end
33
+
34
+ unless File.exists?('../bin/pprof')
35
+ Dir.chdir(dir) do
36
+ FileUtils.cp 'src/pprof', '../../../bin/'
37
+ end
38
+ end
39
+
40
+ unless File.exists?('../libprofiler.a')
41
+ Dir.chdir(dir) do
42
+ xsystem("./configure --disable-heap-profiler --disable-heap-checker --disable-shared")
43
+ xsystem("make")
44
+ FileUtils.cp '.libs/libprofiler.a', '../../'
45
+ end
46
+ end
47
+ end
48
+
49
+ case RUBY_PLATFORM
50
+ when /darwin/, /linux/
51
+ CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
52
+ end
53
+
54
+ $libs = append_library($libs, 'profiler')
55
+ have_func('rb_during_gc', 'ruby.h')
56
+ create_makefile 'perftools'
data/ext/perftools.c ADDED
@@ -0,0 +1,122 @@
1
+ #include <ruby.h>
2
+ #include <node.h>
3
+ #include <env.h>
4
+
5
+ static VALUE Iallocate;
6
+
7
+ static inline void
8
+ save_frame(struct FRAME *frame, void** result, int *depth)
9
+ {
10
+ VALUE klass = frame->last_class;
11
+ // XXX what is an ICLASS anyway?
12
+ // if (BUILTIN_TYPE(klass) == T_ICLASS)
13
+ // klass = RBASIC(klass)->klass;
14
+
15
+ if (FL_TEST(klass, FL_SINGLETON) &&
16
+ (BUILTIN_TYPE(frame->self) == T_CLASS || BUILTIN_TYPE(frame->self) == T_MODULE))
17
+ result[(*depth)++] = (void*) frame->self;
18
+ else
19
+ result[(*depth)++] = 0;
20
+
21
+ result[(*depth)++] = (void*) klass;
22
+ result[(*depth)++] = (void*) (frame->last_func == ID_ALLOCATOR ? Iallocate : frame->last_func);
23
+ }
24
+
25
+ int
26
+ rb_stack_trace(void** result, int max_depth)
27
+ {
28
+ int depth = 0;
29
+ struct FRAME *frame = ruby_frame;
30
+ NODE *n;
31
+
32
+ if (max_depth == 0)
33
+ return 0;
34
+
35
+ // XXX: figure out what these mean. is there a way to access them from an extension?
36
+ // if (rb_prohibit_interrupt || !rb_trap_immediate) return 0;
37
+
38
+ #ifdef HAVE_RB_DURING_GC
39
+ if (rb_during_gc()) {
40
+ result[0] = rb_gc;
41
+ return 1;
42
+ }
43
+ #endif
44
+
45
+ // XXX does it make sense to track allocations or not?
46
+ if (frame->last_func == ID_ALLOCATOR) {
47
+ frame = frame->prev;
48
+ }
49
+
50
+ if (frame->last_func) {
51
+ save_frame(frame, result, &depth);
52
+ }
53
+
54
+ for (; frame && (n = frame->node); frame = frame->prev) {
55
+ if (frame->prev && frame->prev->last_func) {
56
+ if (frame->prev->node == n) {
57
+ if (frame->prev->last_func == frame->last_func) continue;
58
+ }
59
+
60
+ if (depth+3 > max_depth)
61
+ break;
62
+
63
+ save_frame(frame->prev, result, &depth);
64
+ }
65
+ }
66
+
67
+ return depth;
68
+ }
69
+
70
+ static VALUE cPerfTools;
71
+ static VALUE cCpuProfiler;
72
+ static VALUE bProfilerRunning;
73
+
74
+ VALUE
75
+ cpuprofiler_running_p(VALUE self)
76
+ {
77
+ return bProfilerRunning;
78
+ }
79
+
80
+ VALUE
81
+ cpuprofiler_stop(VALUE self)
82
+ {
83
+ if (!bProfilerRunning)
84
+ return Qfalse;
85
+
86
+ bProfilerRunning = Qfalse;
87
+ ProfilerStop();
88
+ ProfilerFlush();
89
+ return Qtrue;
90
+ }
91
+
92
+ VALUE
93
+ cpuprofiler_start(VALUE self, VALUE filename)
94
+ {
95
+ StringValue(filename);
96
+
97
+ if (bProfilerRunning)
98
+ return Qfalse;
99
+
100
+ ProfilerStart(RSTRING_PTR(filename));
101
+ bProfilerRunning = Qtrue;
102
+
103
+ if (rb_block_given_p()) {
104
+ rb_yield(Qnil);
105
+ cpuprofiler_stop(self);
106
+ }
107
+
108
+ return Qtrue;
109
+ }
110
+
111
+ void
112
+ Init_perftools()
113
+ {
114
+ cPerfTools = rb_define_class("PerfTools", rb_cObject);
115
+ cCpuProfiler = rb_define_class_under(cPerfTools, "CpuProfiler", rb_cObject);
116
+ bProfilerRunning = Qfalse;
117
+ Iallocate = rb_intern("allocate");
118
+
119
+ rb_define_singleton_method(cCpuProfiler, "running?", cpuprofiler_running_p, 0);
120
+ rb_define_singleton_method(cCpuProfiler, "start", cpuprofiler_start, 1);
121
+ rb_define_singleton_method(cCpuProfiler, "stop", cpuprofiler_stop, 0);
122
+ }
@@ -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 -fPIC
10
+ +CFLAGS = @CFLAGS@ -DBUILD_FOR_RUBY -fPIC -O0 -ggdb
11
+ CPP = @CPP@
12
+ CPPFLAGS = @CPPFLAGS@
13
+ CXX = @CXX@
14
+ CXXCPP = @CXXCPP@
15
+ CXXDEPMODE = @CXXDEPMODE@
16
+ -CXXFLAGS = @CXXFLAGS@ -DBUILD_FOR_RUBY -fPIC
17
+ +CXXFLAGS = @CXXFLAGS@ -DBUILD_FOR_RUBY -fPIC -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 -fPIC
276
+ CPP = @CPP@
277
+ CPPFLAGS = @CPPFLAGS@
278
+ CXX = @CXX@
279
+ CXXCPP = @CXXCPP@
280
+ CXXDEPMODE = @CXXDEPMODE@
281
+ -CXXFLAGS = @CXXFLAGS@
282
+ +CXXFLAGS = @CXXFLAGS@ -DBUILD_FOR_RUBY -fPIC
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,31 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'perftools.rb'
3
+ s.version = '0.1.6'
4
+ s.date = '2009-06-02'
5
+ s.rubyforge_project = 'perftools-rb'
6
+ s.summary = 'google-perftools for ruby code'
7
+ s.description = 'A sampling profiler for ruby code based on patches to google-perftools'
8
+
9
+ s.homepage = "http://github.com/tmm1/perftools.rb"
10
+
11
+ s.authors = ["Aman Gupta"]
12
+ s.email = "aman@tmm1.net"
13
+
14
+ s.has_rdoc = false
15
+ s.extensions = 'ext/extconf.rb'
16
+ s.bindir = 'bin'
17
+ s.executables << 'pprof.rb'
18
+
19
+ # ruby -rpp -e' pp `git ls-files | grep -v examples`.split("\n") '
20
+ s.files = [
21
+ "README",
22
+ "bin/pprof.rb",
23
+ "ext/extconf.rb",
24
+ "ext/perftools.c",
25
+ "patches/perftools-debug.patch",
26
+ "patches/perftools-osx.patch",
27
+ "patches/perftools-static.patch",
28
+ "patches/perftools.patch",
29
+ "perftools.rb.gemspec"
30
+ ]
31
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: perftools.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ platform: ruby
6
+ authors:
7
+ - Aman Gupta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-02 00:00:00 -04: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
+ - perftools.rb.gemspec
34
+ has_rdoc: true
35
+ homepage: http://github.com/tmm1/perftools.rb
36
+ licenses: []
37
+
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project: perftools-rb
58
+ rubygems_version: 1.3.4
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: google-perftools for ruby code
62
+ test_files: []
63
+