summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchappedm@gmail.com <chappedm@gmail.com@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2012-11-05 04:45:01 +0000
committerchappedm@gmail.com <chappedm@gmail.com@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2012-11-05 04:45:01 +0000
commit8de78fd85b69bc569ac8fc9e75144e02f5cae851 (patch)
tree86b70143bc8a648a42cc93aa9d0c7683db822c8f
parent46f06ca0db41d3f598750caefdaf3c6063969e64 (diff)
downloadgperftools-8de78fd85b69bc569ac8fc9e75144e02f5cae851.tar.gz
issue-452: Adds a control mechanism to the cpu profiler to be switched on and off using a user defined signal.
CPUPROFILESIGNAL - Takes a signal number between the value of 1 and 64 inclusive which represents a signal number as defined by signal.h. The signal must not be in use by the program. Sending this signal to the program turns profiling on and off like a switch. By default the switch is off when the program starts. Successive profiles are suffixed with a monotonically increasing number. git-svn-id: http://gperftools.googlecode.com/svn/trunk@182 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
-rw-r--r--doc/cpuprofile.html17
-rw-r--r--src/profiler.cc95
2 files changed, 101 insertions, 11 deletions
diff --git a/doc/cpuprofile.html b/doc/cpuprofile.html
index 769ec68..c81feb6 100644
--- a/doc/cpuprofile.html
+++ b/doc/cpuprofile.html
@@ -54,12 +54,27 @@ for a given run of an executable:</p>
<code>/bin/ls</code> that had been linked against libprofiler,
you could run:</p>
<pre>% env CPUPROFILE=ls.prof /bin/ls</pre>
-
+ </li>
+ <li> <p>In addition to defining the environment variable CPUPROFILE
+ you can also define CPUPROFILESIGNAL. This allows profiling to be
+ controlled via the signal number that you specify. The signal number
+ must be unused by the program under normal operation. Internally it
+ acts as a switch, triggered by the signal, which is off by default.
+ For instance, if you had a copy of <code>/bin/chrome</code> that had been
+ been linked against libprofiler, you could run:</p>
+ <pre>% env CPUPROFILE=chrome.prof CPUPROFILESIGNAL=12 /bin/chrome &</pre>
+ <p>You can then trigger profiling to start:</p>
+ <pre>% killall -12 chrome</pre>
+ <p>Then after a period of time you can tell it to stop which will
+ generate the profile:</p>
+ <pre>% killall -12 chrome</pre>
+ </li>
<li> <p>In your code, bracket the code you want profiled in calls to
<code>ProfilerStart()</code> and <code>ProfilerStop()</code>.
(These functions are declared in <code>&lt;gperftools/profiler.h&gt;</code>.)
<code>ProfilerStart()</code> will take
the profile-filename as an argument.</p>
+ </li>
</ol>
<p>In Linux 2.6 and above, profiling works correctly with threads,
diff --git a/src/profiler.cc b/src/profiler.cc
index 3272fa4..a850bb7 100644
--- a/src/profiler.cc
+++ b/src/profiler.cc
@@ -70,14 +70,21 @@ typedef int ucontext_t; // just to quiet the compiler, mostly
using std::string;
-DEFINE_bool(cpu_profile_unittest,
+DEFINE_bool(cpu_profiler_unittest,
EnvToBool("PERFTOOLS_UNITTEST", true),
"Determines whether or not we are running under the \
control of a unit test. This allows us to include or \
exclude certain behaviours.");
-// Collects up all profile data. This is a singleton, which is
-// initialized by a constructor at startup.
+// Collects up all profile data. This is a singleton, which is
+// initialized by a constructor at startup. If no cpu profiler
+// signal is specified then the profiler lifecycle is either
+// manaully controlled via the API or attached to the scope of
+// the singleton (program scope). Otherwise the cpu toggle is
+// used to allow for user selectable control via signal generation.
+// This is very useful for profiling a daemon process without
+// having to start and stop the daemon or having to modify the
+// source code to use the cpu profiler API.
class CpuProfiler {
public:
CpuProfiler();
@@ -132,6 +139,40 @@ class CpuProfiler {
void* cpu_profiler);
};
+// Signal handler that is registered when a user selectable signal
+// number is defined in the environment variable CPUPROFILESIGNAL.
+static void CpuProfilerSwitch(int signal_number)
+{
+ bool static started = false;
+ static unsigned profile_count = 0;
+ static char base_profile_name[1024] = "\0";
+
+ if (base_profile_name[0] == '\0') {
+ if (!GetUniquePathFromEnv("CPUPROFILE", base_profile_name)) {
+ RAW_LOG(FATAL,"Cpu profiler switch is registered but no CPUPROFILE is defined");
+ return;
+ }
+ }
+ if (!started)
+ {
+ char full_profile_name[1024];
+
+ snprintf(full_profile_name, sizeof(full_profile_name), "%s.%u",
+ base_profile_name, profile_count++);
+
+ if(!ProfilerStart(full_profile_name))
+ {
+ RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
+ full_profile_name, strerror(errno));
+ }
+ }
+ else
+ {
+ ProfilerStop();
+ }
+ started = !started;
+}
+
// Profile data structure singleton: Constructor will check to see if
// profiling should be enabled. Destructor will write profile data
// out to disk.
@@ -143,26 +184,60 @@ CpuProfiler::CpuProfiler()
// TODO(cgd) Move this code *out* of the CpuProfile constructor into a
// separate object responsible for initialization. With ProfileHandler there
// is no need to limit the number of profilers.
- char fname[PATH_MAX];
- if (!GetUniquePathFromEnv("CPUPROFILE", fname)) {
- if (!FLAGS_cpu_profile_unittest) {
+ if (getenv("CPUPROFILE") == NULL) {
+ if (!FLAGS_cpu_profiler_unittest) {
RAW_LOG(WARNING, "CPU profiler linked but no valid CPUPROFILE environment variable found\n");
}
return;
}
+
// We don't enable profiling if setuid -- it's a security risk
#ifdef HAVE_GETEUID
if (getuid() != geteuid()) {
- if (!FLAGS_cpu_profile_unittest) {
+ if (!FLAGS_cpu_profiler_unittest) {
RAW_LOG(WARNING, "Cannot perform CPU profiling when running with setuid\n");
}
return;
}
#endif
- if (!Start(fname, NULL)) {
- RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
- fname, strerror(errno));
+ char *signal_number_str = getenv("CPUPROFILESIGNAL");
+ if (signal_number_str != NULL)
+ {
+ long int signal_number = strtol(signal_number_str, NULL, 10);
+ printf("<debug> signal_number=%d\n", signal_number);
+ if (signal_number >=1 && signal_number <=64)
+ {
+ sighandler_t old_signal_handler = signal(signal_number, CpuProfilerSwitch);
+ if (old_signal_handler == NULL)
+ {
+ RAW_LOG(INFO,"Using signal %d as cpu profiling switch", signal_number);
+
+ }
+ else
+ {
+ RAW_LOG(FATAL, "Signal %d already in use\n", signal_number);
+ }
+ }
+ else
+ {
+ RAW_LOG(FATAL, "Signal number %s is invalid\n", signal_number_str);
+ }
+ }
+ else
+ {
+ char fname[PATH_MAX];
+ if (!GetUniquePathFromEnv("CPUPROFILE", fname)) {
+ if (!FLAGS_cpu_profiler_unittest) {
+ RAW_LOG(WARNING, "CPU profiler linked but no valid CPUPROFILE environment variable found\n");
+ }
+ return;
+ }
+
+ if (!Start(fname, NULL)) {
+ RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
+ fname, strerror(errno));
+ }
}
}