perftools.rb 0.1.6

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 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
+