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
This commit is contained in:
chappedm@gmail.com 2012-11-05 04:45:01 +00:00
parent 46f06ca0db
commit 8de78fd85b
2 changed files with 101 additions and 11 deletions

View File

@ -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,

View File

@ -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));
}
}
}