honkster-perftools.rb 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +177 -0
- data/bin/pprof.rb +4 -0
- data/ext/extconf.rb +116 -0
- data/ext/perftools.c +493 -0
- data/ext/src/google-perftools-1.6.tar.gz +0 -0
- data/patches/perftools-debug.patch +20 -0
- data/patches/perftools-frames.patch +13 -0
- data/patches/perftools-gc.patch +107 -0
- data/patches/perftools-notests.patch +15 -0
- data/patches/perftools-objects.patch +85 -0
- data/patches/perftools-osx.patch +13 -0
- data/patches/perftools-pprof.patch +76 -0
- data/patches/perftools-stddef.patch +34 -0
- data/patches/perftools.patch +266 -0
- data/perftools.rb.gemspec +21 -0
- metadata +70 -0
Binary file
|
@@ -0,0 +1,20 @@
|
|
1
|
+
diff --git a/Makefile.in b/Makefile.in
|
2
|
+
index c3bf409..c613939 100644
|
3
|
+
--- a/Makefile.in
|
4
|
+
+++ b/Makefile.in
|
5
|
+
@@ -1367,13 +1367,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/src/profiledata.h b/src/profiledata.h
|
2
|
+
index ccdf96f..0517614 100644
|
3
|
+
--- a/src/profiledata.h
|
4
|
+
+++ b/src/profiledata.h
|
5
|
+
@@ -106,7 +106,7 @@ class ProfileData {
|
6
|
+
int frequency_; // Sample frequency.
|
7
|
+
};
|
8
|
+
|
9
|
+
- static const int kMaxStackDepth = 64; // Max stack depth stored in profile
|
10
|
+
+ static const int kMaxStackDepth = 300; // Max stack depth stored in profile
|
11
|
+
|
12
|
+
ProfileData();
|
13
|
+
~ProfileData();
|
@@ -0,0 +1,107 @@
|
|
1
|
+
diff --git a/src/profiledata.cc b/src/profiledata.cc
|
2
|
+
index e6240d9..b901ee8 100644
|
3
|
+
--- a/src/profiledata.cc
|
4
|
+
+++ b/src/profiledata.cc
|
5
|
+
@@ -198,6 +198,29 @@ static void DumpProcSelfMaps(int fd) {
|
6
|
+
}
|
7
|
+
}
|
8
|
+
|
9
|
+
+#ifdef BUILD_FOR_RUBY
|
10
|
+
+void ProfileData::GcMark(void (*mark)(VALUE)) {
|
11
|
+
+ if (!enabled()) {
|
12
|
+
+ return;
|
13
|
+
+ }
|
14
|
+
+
|
15
|
+
+ for (int b = 0; b < kBuckets; b++) {
|
16
|
+
+ Bucket* bucket = &hash_[b];
|
17
|
+
+ for (int a = 0; a < kAssociativity; a++) {
|
18
|
+
+ if (bucket->entry[a].count > 0) {
|
19
|
+
+ Entry e = bucket->entry[a];
|
20
|
+
+ if (e.depth > 1)
|
21
|
+
+ for (int n=0; n<e.depth; n+=3) {
|
22
|
+
+ if (e.stack[n])
|
23
|
+
+ mark(e.stack[n]);
|
24
|
+
+ mark(e.stack[n+1]);
|
25
|
+
+ }
|
26
|
+
+ }
|
27
|
+
+ }
|
28
|
+
+ }
|
29
|
+
+}
|
30
|
+
+#endif
|
31
|
+
+
|
32
|
+
void ProfileData::Stop() {
|
33
|
+
if (!enabled()) {
|
34
|
+
return;
|
35
|
+
diff --git a/src/profiledata.h b/src/profiledata.h
|
36
|
+
index 67c463d..a68e12f 100644
|
37
|
+
--- a/src/profiledata.h
|
38
|
+
+++ b/src/profiledata.h
|
39
|
+
@@ -40,6 +40,12 @@
|
40
|
+
#ifndef BASE_PROFILEDATA_H_
|
41
|
+
#define BASE_PROFILEDATA_H_
|
42
|
+
|
43
|
+
+#ifdef BUILD_FOR_RUBY
|
44
|
+
+extern "C" {
|
45
|
+
+ typedef unsigned long VALUE;
|
46
|
+
+}
|
47
|
+
+#endif
|
48
|
+
+
|
49
|
+
#include <config.h>
|
50
|
+
#include <time.h> // for time_t
|
51
|
+
#include <stdint.h>
|
52
|
+
@@ -141,6 +147,10 @@ class ProfileData {
|
53
|
+
// Get the current state of the data collector.
|
54
|
+
void GetCurrentState(State* state) const;
|
55
|
+
|
56
|
+
+#ifdef BUILD_FOR_RUBY
|
57
|
+
+ void GcMark(void (*cb)(VALUE));
|
58
|
+
+#endif
|
59
|
+
+
|
60
|
+
private:
|
61
|
+
static const int kAssociativity = 4; // For hashtable
|
62
|
+
static const int kBuckets = 1 << 10; // For hashtable
|
63
|
+
diff --git a/src/profiler.cc b/src/profiler.cc
|
64
|
+
index d89a53a..37234b2 100644
|
65
|
+
--- a/src/profiler.cc
|
66
|
+
+++ b/src/profiler.cc
|
67
|
+
@@ -87,6 +87,10 @@ class CpuProfiler {
|
68
|
+
// Write the data to disk (and continue profiling).
|
69
|
+
void FlushTable();
|
70
|
+
|
71
|
+
+#ifdef BUILD_FOR_RUBY
|
72
|
+
+ void GcMark(void (*cb)(VALUE));
|
73
|
+
+#endif
|
74
|
+
+
|
75
|
+
bool Enabled();
|
76
|
+
|
77
|
+
void GetCurrentState(ProfilerState* state);
|
78
|
+
@@ -221,6 +225,16 @@ void CpuProfiler::FlushTable() {
|
79
|
+
EnableHandler();
|
80
|
+
}
|
81
|
+
|
82
|
+
+#ifdef BUILD_FOR_RUBY
|
83
|
+
+void CpuProfiler::GcMark(void (*cb)(VALUE)) {
|
84
|
+
+ if (!collector_.enabled()) {
|
85
|
+
+ return;
|
86
|
+
+ }
|
87
|
+
+
|
88
|
+
+ collector_.GcMark(cb);
|
89
|
+
+}
|
90
|
+
+#endif
|
91
|
+
+
|
92
|
+
bool CpuProfiler::Enabled() {
|
93
|
+
SpinLockHolder cl(&lock_);
|
94
|
+
return collector_.enabled();
|
95
|
+
@@ -300,6 +314,12 @@ extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() {
|
96
|
+
CpuProfiler::instance_.FlushTable();
|
97
|
+
}
|
98
|
+
|
99
|
+
+#ifdef BUILD_FOR_RUBY
|
100
|
+
+extern "C" PERFTOOLS_DLL_DECL void ProfilerGcMark(void (*cb)(VALUE)) {
|
101
|
+
+ CpuProfiler::instance_.GcMark(cb);
|
102
|
+
+}
|
103
|
+
+#endif
|
104
|
+
+
|
105
|
+
extern "C" PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads() {
|
106
|
+
return CpuProfiler::instance_.Enabled();
|
107
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
diff --git a/Makefile.in b/Makefile.in
|
2
|
+
index b301f4d..969db5e 100644
|
3
|
+
--- a/Makefile.in
|
4
|
+
+++ b/Makefile.in
|
5
|
+
@@ -65,9 +65,7 @@ host_triplet = @host@
|
6
|
+
@ENABLE_FRAME_POINTERS_TRUE@@X86_64_AND_NO_FP_BY_DEFAULT_TRUE@am__append_3 = -fno-omit-frame-pointer
|
7
|
+
@ENABLE_FRAME_POINTERS_FALSE@@X86_64_AND_NO_FP_BY_DEFAULT_TRUE@am__append_4 = -DNO_FRAME_POINTER
|
8
|
+
@MINGW_TRUE@am__append_5 = -Wl,-u__tcmalloc
|
9
|
+
-noinst_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
|
10
|
+
- $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \
|
11
|
+
- $(am__EXEEXT_7) $(am__EXEEXT_8) $(am__EXEEXT_23)
|
12
|
+
+noinst_PROGRAMS =
|
13
|
+
bin_PROGRAMS =
|
14
|
+
@MINGW_TRUE@am__append_6 = libwindows.la libspinlock.la
|
15
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
diff --git a/src/profile-handler.cc b/src/profile-handler.cc
|
2
|
+
index 5df5054..2335617 100644
|
3
|
+
--- a/src/profile-handler.cc
|
4
|
+
+++ b/src/profile-handler.cc
|
5
|
+
@@ -395,6 +395,8 @@ void ProfileHandler::GetState(ProfileHandlerState* state) {
|
6
|
+
}
|
7
|
+
|
8
|
+
void ProfileHandler::StartTimer() {
|
9
|
+
+ if (getenv("CPUPROFILE_OBJECTS") || getenv("CPUPROFILE_METHODS")) return;
|
10
|
+
+
|
11
|
+
struct itimerval timer;
|
12
|
+
timer.it_interval.tv_sec = 0;
|
13
|
+
timer.it_interval.tv_usec = 1000000 / frequency_;
|
14
|
+
@@ -403,12 +405,16 @@ void ProfileHandler::StartTimer() {
|
15
|
+
}
|
16
|
+
|
17
|
+
void ProfileHandler::StopTimer() {
|
18
|
+
+ if (getenv("CPUPROFILE_OBJECTS") || getenv("CPUPROFILE_METHODS")) return;
|
19
|
+
+
|
20
|
+
struct itimerval timer;
|
21
|
+
memset(&timer, 0, sizeof timer);
|
22
|
+
setitimer(timer_type_, &timer, 0);
|
23
|
+
}
|
24
|
+
|
25
|
+
bool ProfileHandler::IsTimerRunning() {
|
26
|
+
+ if (getenv("CPUPROFILE_OBJECTS") || getenv("CPUPROFILE_METHODS")) return false;
|
27
|
+
+
|
28
|
+
struct itimerval current_timer;
|
29
|
+
RAW_CHECK(0 == getitimer(timer_type_, ¤t_timer), "getitimer");
|
30
|
+
return (current_timer.it_value.tv_sec != 0 ||
|
31
|
+
@@ -416,6 +422,8 @@ bool ProfileHandler::IsTimerRunning() {
|
32
|
+
}
|
33
|
+
|
34
|
+
void ProfileHandler::EnableHandler() {
|
35
|
+
+ if (getenv("CPUPROFILE_OBJECTS") || getenv("CPUPROFILE_METHODS")) return;
|
36
|
+
+
|
37
|
+
struct sigaction sa;
|
38
|
+
sa.sa_sigaction = SignalHandler;
|
39
|
+
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
40
|
+
@@ -425,6 +433,8 @@ void ProfileHandler::EnableHandler() {
|
41
|
+
}
|
42
|
+
|
43
|
+
void ProfileHandler::DisableHandler() {
|
44
|
+
+ if (getenv("CPUPROFILE_OBJECTS") || getenv("CPUPROFILE_METHODS")) return;
|
45
|
+
+
|
46
|
+
struct sigaction sa;
|
47
|
+
sa.sa_handler = SIG_IGN;
|
48
|
+
sa.sa_flags = SA_RESTART;
|
49
|
+
diff --git a/src/profiler.cc b/src/profiler.cc
|
50
|
+
index f408cf8..7645c45 100644
|
51
|
+
--- a/src/profiler.cc
|
52
|
+
+++ b/src/profiler.cc
|
53
|
+
@@ -102,6 +102,10 @@ class CpuProfiler {
|
54
|
+
|
55
|
+
static CpuProfiler instance_;
|
56
|
+
|
57
|
+
+ // Signal handler that records the interrupted pc in the profile data.
|
58
|
+
+ static void prof_handler(int sig, siginfo_t*, void* signal_ucontext,
|
59
|
+
+ void* cpu_profiler);
|
60
|
+
+
|
61
|
+
private:
|
62
|
+
// This lock implements the locking requirements described in the ProfileData
|
63
|
+
// documentation, specifically:
|
64
|
+
@@ -130,10 +134,6 @@ class CpuProfiler {
|
65
|
+
|
66
|
+
// Disables receiving SIGPROF interrupt.
|
67
|
+
void DisableHandler();
|
68
|
+
-
|
69
|
+
- // Signal handler that records the interrupted pc in the profile data.
|
70
|
+
- static void prof_handler(int sig, siginfo_t*, void* signal_ucontext,
|
71
|
+
- void* cpu_profiler);
|
72
|
+
};
|
73
|
+
|
74
|
+
// Profile data structure singleton: Constructor will check to see if
|
75
|
+
@@ -323,6 +323,10 @@ extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() {
|
76
|
+
extern "C" PERFTOOLS_DLL_DECL void ProfilerGcMark(void (*cb)(VALUE)) {
|
77
|
+
CpuProfiler::instance_.GcMark(cb);
|
78
|
+
}
|
79
|
+
+
|
80
|
+
+extern "C" PERFTOOLS_DLL_DECL void ProfilerRecord(int sig, siginfo_t* info, void* signal_ucontext) {
|
81
|
+
+ CpuProfiler::prof_handler(sig, info, signal_ucontext, (void*)&CpuProfiler::instance_);
|
82
|
+
+}
|
83
|
+
#endif
|
84
|
+
|
85
|
+
extern "C" PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads() {
|
@@ -0,0 +1,13 @@
|
|
1
|
+
diff --git a/Makefile.in b/Makefile.in
|
2
|
+
index 566d77c..c3bf409 100644
|
3
|
+
--- a/Makefile.in
|
4
|
+
+++ b/Makefile.in
|
5
|
+
@@ -1397,7 +1397,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,76 @@
|
|
1
|
+
commit 1828f461a3ea523b7018d03ab83850621cf66ba9
|
2
|
+
Author: Aman Gupta <aman@tmm1.net>
|
3
|
+
Date: Fri Nov 12 16:33:15 2010 -0600
|
4
|
+
|
5
|
+
perftools-pprof
|
6
|
+
|
7
|
+
diff --git a/src/pprof b/src/pprof
|
8
|
+
index e67e42e..b04d988 100755
|
9
|
+
--- a/src/pprof
|
10
|
+
+++ b/src/pprof
|
11
|
+
@@ -560,7 +560,8 @@ sub Main() {
|
12
|
+
my $symbol_map = {};
|
13
|
+
|
14
|
+
# Read one profile, pick the last item on the list
|
15
|
+
- my $data = ReadProfile($main::prog, pop(@main::profile_files));
|
16
|
+
+ my $fname = pop(@main::profile_files);
|
17
|
+
+ my $data = ReadProfile($main::prog, $fname);
|
18
|
+
my $profile = $data->{profile};
|
19
|
+
my $pcs = $data->{pcs};
|
20
|
+
my $libs = $data->{libs}; # Info about main program and shared libraries
|
21
|
+
@@ -589,7 +590,18 @@ sub Main() {
|
22
|
+
|
23
|
+
# Collect symbols
|
24
|
+
my $symbols;
|
25
|
+
- if ($main::use_symbolized_profile) {
|
26
|
+
+ if (-e "$fname.symbols") {
|
27
|
+
+ open(SYMBOLS, "<$fname.symbols");
|
28
|
+
+ while(<SYMBOLS>){
|
29
|
+
+ chop;
|
30
|
+
+ if (m/(.+?)\s*:\s*(.*)/){
|
31
|
+
+ $symbols->{$1}[0] = $2;
|
32
|
+
+ $symbols->{$1}[1] = "?";
|
33
|
+
+ $symbols->{$1}[2] = $2;
|
34
|
+
+ }
|
35
|
+
+ }
|
36
|
+
+ close(SYMBOLS);
|
37
|
+
+ } elsif ($main::use_symbolized_profile) {
|
38
|
+
$symbols = FetchSymbols($pcs, $symbol_map);
|
39
|
+
} elsif ($main::use_symbol_page) {
|
40
|
+
$symbols = FetchSymbols($pcs);
|
41
|
+
@@ -2425,6 +2437,10 @@ sub RemoveUninterestingFrames {
|
42
|
+
foreach my $name ('ProfileData::Add', # historical
|
43
|
+
'ProfileData::prof_handler', # historical
|
44
|
+
'CpuProfiler::prof_handler',
|
45
|
+
+ 'PerfTools::CpuProfiler.start',
|
46
|
+
+ 'Array#each',
|
47
|
+
+ 'Proc#call',
|
48
|
+
+ 'Class#new',
|
49
|
+
'__FRAME_END__',
|
50
|
+
'__pthread_sighandler',
|
51
|
+
'__restore') {
|
52
|
+
@@ -2434,6 +2450,7 @@ sub RemoveUninterestingFrames {
|
53
|
+
# Nothing skipped for unknown types
|
54
|
+
}
|
55
|
+
|
56
|
+
+=pod
|
57
|
+
if ($main::profile_type eq 'cpu') {
|
58
|
+
# If all the second-youngest program counters are the same,
|
59
|
+
# this STRONGLY suggests that it is an artifact of measurement,
|
60
|
+
@@ -2458,6 +2475,7 @@ sub RemoveUninterestingFrames {
|
61
|
+
$profile = $result;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
+=cut
|
65
|
+
|
66
|
+
my $result = {};
|
67
|
+
foreach my $k (keys(%{$profile})) {
|
68
|
+
@@ -3360,7 +3378,7 @@ sub ReadCPUProfile {
|
69
|
+
# file, in which case the subtract-one was done when the file
|
70
|
+
# was written.
|
71
|
+
if ($j > 0 && !$main::use_symbolized_profile) {
|
72
|
+
- $pc--;
|
73
|
+
+ # $pc--;
|
74
|
+
}
|
75
|
+
$pc = sprintf("%0*x", $address_length, $pc);
|
76
|
+
$pcs->{$pc} = 1;
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Only in ./: perftools-stddef.patch
|
2
|
+
diff -ru .//src/base/vdso_support.h ../google-perftools-1.6-patched/src/base/vdso_support.h
|
3
|
+
--- .//src/base/vdso_support.h 2010-08-03 03:32:20.000000000 +0800
|
4
|
+
+++ ../google-perftools-1.6-patched/src/base/vdso_support.h 2011-06-16 14:33:10.000000000 +0800
|
5
|
+
@@ -38,6 +38,7 @@
|
6
|
+
#define HAVE_VDSO_SUPPORT 1
|
7
|
+
|
8
|
+
#include <stdlib.h> // for NULL
|
9
|
+
+#include <stddef.h>
|
10
|
+
#include <link.h> // for ElfW
|
11
|
+
#include "base/basictypes.h"
|
12
|
+
|
13
|
+
diff -ru .//src/symbolize.h ../google-perftools-1.6-patched/src/symbolize.h
|
14
|
+
--- .//src/symbolize.h 2009-11-11 03:59:46.000000000 +0800
|
15
|
+
+++ ../google-perftools-1.6-patched/src/symbolize.h 2011-06-16 14:32:55.000000000 +0800
|
16
|
+
@@ -38,6 +38,7 @@
|
17
|
+
#include <stdint.h> // for uintptr_t
|
18
|
+
#endif
|
19
|
+
#include <map>
|
20
|
+
+#include <stddef.h>
|
21
|
+
|
22
|
+
using std::map;
|
23
|
+
|
24
|
+
diff -ru .//src/system-alloc.h ../google-perftools-1.6-patched/src/system-alloc.h
|
25
|
+
--- .//src/system-alloc.h 2010-04-01 07:50:18.000000000 +0800
|
26
|
+
+++ ../google-perftools-1.6-patched/src/system-alloc.h 2011-06-16 14:32:38.000000000 +0800
|
27
|
+
@@ -38,6 +38,7 @@
|
28
|
+
|
29
|
+
#include <config.h>
|
30
|
+
#include "internal_logging.h"
|
31
|
+
+#include <stddef.h>
|
32
|
+
|
33
|
+
// REQUIRES: "alignment" is a power of two or "0" to indicate default alignment
|
34
|
+
//
|
@@ -0,0 +1,266 @@
|
|
1
|
+
diff --git a/Makefile.in b/Makefile.in
|
2
|
+
index 232d2ec..566d77c 100644
|
3
|
+
--- a/Makefile.in
|
4
|
+
+++ b/Makefile.in
|
5
|
+
@@ -1367,13 +1367,13 @@ AUTOMAKE = @AUTOMAKE@
|
6
|
+
AWK = @AWK@
|
7
|
+
CC = @CC@
|
8
|
+
CCDEPMODE = @CCDEPMODE@
|
9
|
+
-CFLAGS = @CFLAGS@
|
10
|
+
+CFLAGS = @CFLAGS@ -DBUILD_FOR_RUBY -fPIC
|
11
|
+
CPP = @CPP@
|
12
|
+
CPPFLAGS = @CPPFLAGS@
|
13
|
+
CXX = @CXX@
|
14
|
+
CXXCPP = @CXXCPP@
|
15
|
+
CXXDEPMODE = @CXXDEPMODE@
|
16
|
+
-CXXFLAGS = @CXXFLAGS@
|
17
|
+
+CXXFLAGS = @CXXFLAGS@ -DBUILD_FOR_RUBY -fPIC
|
18
|
+
CYGPATH_W = @CYGPATH_W@
|
19
|
+
DEFS = @DEFS@
|
20
|
+
DEPDIR = @DEPDIR@
|
21
|
+
diff --git a/src/profile-handler.cc b/src/profile-handler.cc
|
22
|
+
index e658d30..370d012 100644
|
23
|
+
--- a/src/profile-handler.cc
|
24
|
+
+++ b/src/profile-handler.cc
|
25
|
+
@@ -264,6 +264,11 @@ ProfileHandler::~ProfileHandler() {
|
26
|
+
void ProfileHandler::RegisterThread() {
|
27
|
+
SpinLockHolder cl(&control_lock_);
|
28
|
+
|
29
|
+
+#ifdef BUILD_FOR_RUBY
|
30
|
+
+ timer_sharing_ = TIMERS_SHARED;
|
31
|
+
+ if (callback_count_ > 0 && !IsTimerRunning())
|
32
|
+
+ StartTimer();
|
33
|
+
+#else
|
34
|
+
// We try to detect whether timers are being shared by setting a
|
35
|
+
// timer in the first call to this function, then checking whether
|
36
|
+
// it's set in the second call.
|
37
|
+
@@ -305,6 +310,7 @@ void ProfileHandler::RegisterThread() {
|
38
|
+
StartTimer();
|
39
|
+
break;
|
40
|
+
}
|
41
|
+
+#endif
|
42
|
+
}
|
43
|
+
|
44
|
+
ProfileHandlerToken* ProfileHandler::RegisterCallback(
|
45
|
+
diff --git a/src/profiledata.cc b/src/profiledata.cc
|
46
|
+
index 5f2531b..ea95493 100644
|
47
|
+
--- a/src/profiledata.cc
|
48
|
+
+++ b/src/profiledata.cc
|
49
|
+
@@ -56,6 +56,19 @@ const int ProfileData::kAssociativity;
|
50
|
+
const int ProfileData::kBuckets;
|
51
|
+
const int ProfileData::kBufferLength;
|
52
|
+
|
53
|
+
+#ifdef BUILD_FOR_RUBY
|
54
|
+
+extern "C" {
|
55
|
+
+ typedef unsigned long ID;
|
56
|
+
+ typedef unsigned long VALUE;
|
57
|
+
+
|
58
|
+
+ void rb_gc();
|
59
|
+
+ const char *rb_id2name(ID);
|
60
|
+
+ const char *rb_class2name(VALUE);
|
61
|
+
+}
|
62
|
+
+
|
63
|
+
+#include <set>
|
64
|
+
+#endif
|
65
|
+
+
|
66
|
+
ProfileData::Options::Options()
|
67
|
+
: frequency_(1) {
|
68
|
+
}
|
69
|
+
@@ -63,17 +76,33 @@ ProfileData::Options::Options()
|
70
|
+
// This function is safe to call from asynchronous signals (but is not
|
71
|
+
// re-entrant). However, that's not part of its public interface.
|
72
|
+
void ProfileData::Evict(const Entry& entry) {
|
73
|
+
+#ifdef BUILD_FOR_RUBY
|
74
|
+
+ const int d = entry.depth == 1 ? 1 : entry.depth/3;
|
75
|
+
+#else
|
76
|
+
const int d = entry.depth;
|
77
|
+
+#endif
|
78
|
+
const int nslots = d + 2; // Number of slots needed in eviction buffer
|
79
|
+
+
|
80
|
+
if (num_evicted_ + nslots > kBufferLength) {
|
81
|
+
FlushEvicted();
|
82
|
+
assert(num_evicted_ == 0);
|
83
|
+
assert(nslots <= kBufferLength);
|
84
|
+
}
|
85
|
+
+
|
86
|
+
evict_[num_evicted_++] = entry.count;
|
87
|
+
evict_[num_evicted_++] = d;
|
88
|
+
+
|
89
|
+
+#ifdef BUILD_FOR_RUBY
|
90
|
+
+ if (entry.depth > 1) {
|
91
|
+
+ for (int n=0; n<entry.depth; n+=3)
|
92
|
+
+ evict_[num_evicted_++] = entry.stack[n] + entry.stack[n+1] + entry.stack[n+2];
|
93
|
+
+ } else if (entry.depth == 1) {
|
94
|
+
+ evict_[num_evicted_++] = entry.stack[0];
|
95
|
+
+ }
|
96
|
+
+#else
|
97
|
+
memcpy(&evict_[num_evicted_], entry.stack, d * sizeof(Slot));
|
98
|
+
num_evicted_ += d;
|
99
|
+
+#endif
|
100
|
+
}
|
101
|
+
|
102
|
+
ProfileData::ProfileData()
|
103
|
+
@@ -85,6 +114,7 @@ ProfileData::ProfileData()
|
104
|
+
evictions_(0),
|
105
|
+
total_bytes_(0),
|
106
|
+
fname_(0),
|
107
|
+
+ sym_fname_(0),
|
108
|
+
start_time_(0) {
|
109
|
+
}
|
110
|
+
|
111
|
+
@@ -101,6 +131,13 @@ bool ProfileData::Start(const char* fname,
|
112
|
+
return false;
|
113
|
+
}
|
114
|
+
|
115
|
+
+#ifdef BUILD_FOR_RUBY
|
116
|
+
+ int len = strlen(fname);
|
117
|
+
+ sym_fname_ = (char*)malloc((len+9) * sizeof(char));
|
118
|
+
+ strncpy(sym_fname_, fname, len);
|
119
|
+
+ strcpy(sym_fname_+len, ".symbols");
|
120
|
+
+#endif
|
121
|
+
+
|
122
|
+
start_time_ = time(NULL);
|
123
|
+
fname_ = strdup(fname);
|
124
|
+
|
125
|
+
@@ -166,16 +203,61 @@ void ProfileData::Stop() {
|
126
|
+
return;
|
127
|
+
}
|
128
|
+
|
129
|
+
+#ifdef BUILD_FOR_RUBY
|
130
|
+
+ FILE *symbols;
|
131
|
+
+ int precision;
|
132
|
+
+
|
133
|
+
+ symbols = fopen(sym_fname_, "w");
|
134
|
+
+ if (!symbols) {
|
135
|
+
+ fprintf(stderr, "PROFILER ERROR: Unable to open %s (%s)\n", sym_fname_, strerror(errno));
|
136
|
+
+ Reset();
|
137
|
+
+ return;
|
138
|
+
+ }
|
139
|
+
+
|
140
|
+
+ precision = sizeof(unsigned long)*2;
|
141
|
+
+ fprintf(symbols, "%0*lx: garbage_collector\n", precision, (ID)rb_gc);
|
142
|
+
+
|
143
|
+
+ std::set<ID> known_symbols;
|
144
|
+
+#endif
|
145
|
+
+
|
146
|
+
// Move data from hash table to eviction buffer
|
147
|
+
for (int b = 0; b < kBuckets; b++) {
|
148
|
+
Bucket* bucket = &hash_[b];
|
149
|
+
for (int a = 0; a < kAssociativity; a++) {
|
150
|
+
if (bucket->entry[a].count > 0) {
|
151
|
+
- Evict(bucket->entry[a]);
|
152
|
+
+ Entry e = bucket->entry[a];
|
153
|
+
+ Evict(e);
|
154
|
+
+#ifdef BUILD_FOR_RUBY
|
155
|
+
+ if (e.depth > 1) {
|
156
|
+
+ for (int n=0; n<e.depth; n+=3) {
|
157
|
+
+ VALUE self = e.stack[n];
|
158
|
+
+ VALUE klass = e.stack[n+1];
|
159
|
+
+ ID method = e.stack[n+2];
|
160
|
+
+
|
161
|
+
+ ID sym = self + klass + method; // unique identifer
|
162
|
+
+
|
163
|
+
+ if (known_symbols.find(sym) == known_symbols.end()) {
|
164
|
+
+ fprintf(symbols, "%0*lx: ", precision, sym);
|
165
|
+
+
|
166
|
+
+ if (self) { // class method
|
167
|
+
+ fprintf(symbols, "%s.%s\n", rb_class2name(self), rb_id2name(method));
|
168
|
+
+ } else { // instance method
|
169
|
+
+ fprintf(symbols, "%s#%s\n", rb_class2name(klass), rb_id2name(method));
|
170
|
+
+ }
|
171
|
+
+
|
172
|
+
+ known_symbols.insert(sym);
|
173
|
+
+ }
|
174
|
+
+ }
|
175
|
+
+ }
|
176
|
+
+#endif
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
+#ifdef BUILD_FOR_RUBY
|
182
|
+
+ fclose(symbols);
|
183
|
+
+#endif
|
184
|
+
+
|
185
|
+
if (num_evicted_ + 3 > kBufferLength) {
|
186
|
+
// Ensure there is enough room for end of data marker
|
187
|
+
FlushEvicted();
|
188
|
+
@@ -211,6 +293,10 @@ void ProfileData::Reset() {
|
189
|
+
num_evicted_ = 0;
|
190
|
+
free(fname_);
|
191
|
+
fname_ = 0;
|
192
|
+
+#ifdef BUILD_FOR_RUBY
|
193
|
+
+ free(sym_fname_);
|
194
|
+
+ sym_fname_ = 0;
|
195
|
+
+#endif
|
196
|
+
start_time_ = 0;
|
197
|
+
|
198
|
+
out_ = -1;
|
199
|
+
diff --git a/src/profiledata.h b/src/profiledata.h
|
200
|
+
index da7ea9e..67c463d 100644
|
201
|
+
--- a/src/profiledata.h
|
202
|
+
+++ b/src/profiledata.h
|
203
|
+
@@ -169,6 +169,7 @@ class ProfileData {
|
204
|
+
int evictions_; // How many evictions
|
205
|
+
size_t total_bytes_; // How much output
|
206
|
+
char* fname_; // Profile file name
|
207
|
+
+ char* sym_fname_; // Symbol file name
|
208
|
+
time_t start_time_; // Start time, or 0
|
209
|
+
|
210
|
+
// Move 'entry' to the eviction buffer.
|
211
|
+
diff --git a/src/profiler.cc b/src/profiler.cc
|
212
|
+
index 183a7c7..d89a53a 100644
|
213
|
+
--- a/src/profiler.cc
|
214
|
+
+++ b/src/profiler.cc
|
215
|
+
@@ -63,6 +63,12 @@ typedef int ucontext_t; // just to quiet the compiler, mostly
|
216
|
+
#include "conflict-signal.h" /* used on msvc machines */
|
217
|
+
#endif
|
218
|
+
|
219
|
+
+#ifdef BUILD_FOR_RUBY
|
220
|
+
+extern "C" {
|
221
|
+
+ int rb_stack_trace(void**,int);
|
222
|
+
+}
|
223
|
+
+#endif
|
224
|
+
+
|
225
|
+
using std::string;
|
226
|
+
|
227
|
+
// Collects up all profile data. This is a singleton, which is
|
228
|
+
@@ -261,6 +267,9 @@ void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,
|
229
|
+
(*instance->filter_)(instance->filter_arg_)) {
|
230
|
+
void* stack[ProfileData::kMaxStackDepth];
|
231
|
+
|
232
|
+
+#ifdef BUILD_FOR_RUBY
|
233
|
+
+ int depth = rb_stack_trace(stack, arraysize(stack));
|
234
|
+
+#else
|
235
|
+
// The top-most active routine doesn't show up as a normal
|
236
|
+
// frame, but as the "pc" value in the signal handler context.
|
237
|
+
stack[0] = GetPC(*reinterpret_cast<ucontext_t*>(signal_ucontext));
|
238
|
+
@@ -274,8 +283,10 @@ void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,
|
239
|
+
int depth = GetStackTraceWithContext(stack + 1, arraysize(stack) - 1,
|
240
|
+
2, signal_ucontext);
|
241
|
+
depth++; // To account for pc value in stack[0];
|
242
|
+
+#endif
|
243
|
+
|
244
|
+
- instance->collector_.Add(depth, stack);
|
245
|
+
+ if (depth > 0)
|
246
|
+
+ instance->collector_.Add(depth, stack);
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
diff --git a/src/stacktrace.cc b/src/stacktrace.cc
|
251
|
+
index d158eea..e8509fe 100644
|
252
|
+
--- a/src/stacktrace.cc
|
253
|
+
+++ b/src/stacktrace.cc
|
254
|
+
@@ -52,6 +52,7 @@
|
255
|
+
// correctly when GetStackTrace() is called with max_depth == 0.
|
256
|
+
// Some code may do that.
|
257
|
+
|
258
|
+
+#ifndef BUILD_FOR_RUBY
|
259
|
+
#include <config.h>
|
260
|
+
#include <google/stacktrace.h>
|
261
|
+
#include "stacktrace_config.h"
|
262
|
+
@@ -69,3 +70,4 @@
|
263
|
+
#else
|
264
|
+
# error Cannot calculate stack trace: will need to write for your environment
|
265
|
+
#endif
|
266
|
+
+#endif
|