pprof: fix /pprof/contention to work with the go version of pprof

The old perl version of pprof that ships with gperftools is no longer
maintained. It's been replaced by github.com/google/pprof which is
written in go.

The go version is slightly pickier about the output than the old perl
version. This patch updates a few things so that the new version works
with the /pprof/contention profile:

- the expected first line format is slightly different
- hex addresses must be prefixed with '0x', no tab characters expected
- memory maps needs a section header ahead of it
- cycles/second needs to be formatted as an integer, not float

I verified that with this patch I was able to run the following:

  pprof -seconds=1  http://localhost:8050/pprof/contention

... and got a reasonable looking profile when running under load.

It also appears that the old perl pprof is still forgiving enough to
handle this new format.

Change-Id: Ie25b2332688575c57be10b437566adb3f0c156e3
Reviewed-on: http://gerrit.cloudera.org:8080/9741
Tested-by: Kudu Jenkins
Reviewed-by: Alexey Serbin <aser...@cloudera.com>


Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/600723e0
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/600723e0
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/600723e0

Branch: refs/heads/master
Commit: 600723e0e954a1b199e6a0bf1dfd63fe7289a6a8
Parents: ffcadd5
Author: Todd Lipcon <t...@apache.org>
Authored: Wed Mar 21 12:01:36 2018 -0700
Committer: Todd Lipcon <t...@apache.org>
Committed: Thu Mar 22 18:51:47 2018 +0000

----------------------------------------------------------------------
 src/kudu/server/pprof_path_handlers.cc   | 12 +++++++-----
 src/kudu/util/debug-util.cc              | 14 ++++++++++++--
 src/kudu/util/debug-util.h               |  6 +++++-
 src/kudu/util/spinlock_profiling-test.cc |  2 +-
 src/kudu/util/spinlock_profiling.cc      |  5 +++--
 5 files changed, 28 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/600723e0/src/kudu/server/pprof_path_handlers.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/pprof_path_handlers.cc 
b/src/kudu/server/pprof_path_handlers.cc
index d371d52..634c196 100644
--- a/src/kudu/server/pprof_path_handlers.cc
+++ b/src/kudu/server/pprof_path_handlers.cc
@@ -171,10 +171,6 @@ static void PprofContentionHandler(const 
Webserver::WebRequest& req,
   int32_t seconds = ParseLeadingInt32Value(secs_str.c_str(), 
kPprofDefaultSampleSecs);
   int64_t discarded_samples = 0;
 
-  *output << "--- contention" << endl;
-  *output << "sampling period = 1" << endl;
-  *output << "cycles/second = " << base::CyclesPerSecond() << endl;
-
   MonoTime end = MonoTime::Now() + MonoDelta::FromSeconds(seconds);
   StartSynchronizationProfiling();
   while (MonoTime::Now() < end) {
@@ -182,16 +178,22 @@ static void PprofContentionHandler(const 
Webserver::WebRequest& req,
     FlushSynchronizationProfile(output, &discarded_samples);
   }
   StopSynchronizationProfiling();
-  FlushSynchronizationProfile(output, &discarded_samples);
+  ostringstream profile;
+  FlushSynchronizationProfile(&profile, &discarded_samples);
 
+  *output << "--- contention:" << endl;
+  *output << "sampling period = 1" << endl;
+  *output << "cycles/second = " << 
static_cast<int64_t>(base::CyclesPerSecond()) << endl;
   // pprof itself ignores this value, but we can at least look at it in the 
textual
   // output.
   *output << "discarded samples = " << discarded_samples << std::endl;
+  *output << profile.str();
 
 #if defined(__linux__)
   // procfs only exists on Linux.
   faststring maps;
   ReadFileToString(Env::Default(), "/proc/self/maps", &maps);
+  *output << "--- Memory map: ---" << endl;
   *output << maps.ToString();
 #endif // defined(__linux__)
 }

http://git-wip-us.apache.org/repos/asf/kudu/blob/600723e0/src/kudu/util/debug-util.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/debug-util.cc b/src/kudu/util/debug-util.cc
index c66b257..07703ea 100644
--- a/src/kudu/util/debug-util.cc
+++ b/src/kudu/util/debug-util.cc
@@ -632,6 +632,10 @@ void StackTrace::StringifyToHex(char* buf, size_t size, 
int flags) const {
     if (i != 0) {
       *dst++ = ' ';
     }
+    if (flags & HEX_0X_PREFIX) {
+      *dst++ = '0';
+      *dst++ = 'x';
+    }
     // See note in Symbolize() below about why we subtract 1 from each address 
here.
     uintptr_t addr = reinterpret_cast<uintptr_t>(frames_[i]);
     if (addr > 0 && !(flags & NO_FIX_CALLER_ADDRESSES)) {
@@ -646,8 +650,14 @@ void StackTrace::StringifyToHex(char* buf, size_t size, 
int flags) const {
 string StackTrace::ToHexString(int flags) const {
   // Each frame requires kHexEntryLength, plus a space
   // We also need one more byte at the end for '\0'
-  char buf[kMaxFrames * (kHexEntryLength + 1) + 1];
-  StringifyToHex(buf, arraysize(buf), flags);
+  int len_per_frame = kHexEntryLength;
+  len_per_frame++; // For the separating space.
+  if  (flags & HEX_0X_PREFIX) {
+    len_per_frame += 2;
+  }
+  int buf_len = kMaxFrames * len_per_frame + 1;
+  char buf[buf_len];
+  StringifyToHex(buf, buf_len, flags);
   return string(buf);
 }
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/600723e0/src/kudu/util/debug-util.h
----------------------------------------------------------------------
diff --git a/src/kudu/util/debug-util.h b/src/kudu/util/debug-util.h
index 193788e..44fccc3 100644
--- a/src/kudu/util/debug-util.h
+++ b/src/kudu/util/debug-util.h
@@ -159,7 +159,11 @@ class StackTrace {
     // Do not fix up the addresses on the stack to try to point to the 'call'
     // instructions instead of the return address. This is necessary when 
dumping
     // addresses to be interpreted by 'pprof', which does this fix-up itself.
-    NO_FIX_CALLER_ADDRESSES = 1
+    NO_FIX_CALLER_ADDRESSES = 1,
+
+    // Prefix each hex address with '0x'. This is required by the go version
+    // of pprof when parsing stack traces.
+    HEX_0X_PREFIX = 1 << 1,
   };
 
   // Stringify the trace into the given buffer.

http://git-wip-us.apache.org/repos/asf/kudu/blob/600723e0/src/kudu/util/spinlock_profiling-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/spinlock_profiling-test.cc 
b/src/kudu/util/spinlock_profiling-test.cc
index 28089df..d0ef2b4 100644
--- a/src/kudu/util/spinlock_profiling-test.cc
+++ b/src/kudu/util/spinlock_profiling-test.cc
@@ -74,7 +74,7 @@ TEST_F(SpinLockProfilingTest, TestStackCollection) {
   int64_t dropped = 0;
   FlushSynchronizationProfile(&str, &dropped);
   std::string s = str.str();
-  ASSERT_STR_CONTAINS(s, "12345\t1 @ ");
+  ASSERT_STR_CONTAINS(s, "12345 1 @ ");
   ASSERT_EQ(0, dropped);
 }
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/600723e0/src/kudu/util/spinlock_profiling.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/spinlock_profiling.cc 
b/src/kudu/util/spinlock_profiling.cc
index 5660760..e7f93b0 100644
--- a/src/kudu/util/spinlock_profiling.cc
+++ b/src/kudu/util/spinlock_profiling.cc
@@ -182,8 +182,9 @@ void ContentionStacks::Flush(std::ostringstream* out, 
int64_t* dropped) {
   int64_t cycles;
   int64_t count;
   while (g_contention_stacks->CollectSample(&iterator, &t, &count, &cycles)) {
-    *out << cycles << "\t" << count
-         << " @ " << t.ToHexString(StackTrace::NO_FIX_CALLER_ADDRESSES)
+    *out << cycles << " " << count
+         << " @ " << t.ToHexString(StackTrace::NO_FIX_CALLER_ADDRESSES |
+                                   StackTrace::HEX_0X_PREFIX)
          << std::endl;
   }
 

Reply via email to