honkster-perftools.rb 0.5.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.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
|