| // Copyright (c) 2005, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| // --- |
| // Author: Sanjay Ghemawat |
| // Chris Demetriou (refactoring) |
| // |
| // Profile current program by sampling stack-trace every so often |
| |
| #include "config.h" |
| #include "getpc.h" // should be first to get the _GNU_SOURCE dfn |
| #include <signal.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <string.h> |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> // for getpid() |
| #endif |
| #if defined(HAVE_SYS_UCONTEXT_H) |
| #include <sys/ucontext.h> |
| #elif defined(HAVE_UCONTEXT_H) |
| #include <ucontext.h> |
| #elif defined(HAVE_CYGWIN_SIGNAL_H) |
| #include <cygwin/signal.h> |
| typedef ucontext ucontext_t; |
| #elif defined(__ANDROID__) |
| // Do not define ucontext_t here. |
| #else |
| typedef int ucontext_t; // just to quiet the compiler, mostly |
| #endif |
| #include <sys/time.h> |
| #include <string> |
| #include <gperftools/profiler.h> |
| #include <gperftools/stacktrace.h> |
| #include "base/commandlineflags.h" |
| #include "base/logging.h" |
| #include "base/googleinit.h" |
| #include "base/spinlock.h" |
| #include "base/sysinfo.h" /* for GetUniquePathFromEnv, etc */ |
| #include "profiledata.h" |
| #include "profile-handler.h" |
| #ifdef HAVE_CONFLICT_SIGNAL_H |
| #include "conflict-signal.h" /* used on msvc machines */ |
| #endif |
| |
| using std::string; |
| |
| // Collects up all profile data. This is a singleton, which is |
| // initialized by a constructor at startup. |
| class CpuProfiler { |
| public: |
| CpuProfiler(); |
| ~CpuProfiler(); |
| |
| // Start profiler to write profile info into fname |
| bool Start(const char* fname, const ProfilerOptions* options); |
| |
| // Stop profiling and write the data to disk. |
| void Stop(); |
| |
| // Write the data to disk (and continue profiling). |
| void FlushTable(); |
| |
| bool Enabled(); |
| |
| void GetCurrentState(ProfilerState* state); |
| |
| static CpuProfiler instance_; |
| |
| private: |
| // This lock implements the locking requirements described in the ProfileData |
| // documentation, specifically: |
| // |
| // lock_ is held all over all collector_ method calls except for the 'Add' |
| // call made from the signal handler, to protect against concurrent use of |
| // collector_'s control routines. Code other than signal handler must |
| // unregister the signal handler before calling any collector_ method. |
| // 'Add' method in the collector is protected by a guarantee from |
| // ProfileHandle that only one instance of prof_handler can run at a time. |
| SpinLock lock_; |
| ProfileData collector_; |
| |
| // Filter function and its argument, if any. (NULL means include all |
| // samples). Set at start, read-only while running. Written while holding |
| // lock_, read and executed in the context of SIGPROF interrupt. |
| int (*filter_)(void*); |
| void* filter_arg_; |
| |
| // Opaque token returned by the profile handler. To be used when calling |
| // ProfileHandlerUnregisterCallback. |
| ProfileHandlerToken* prof_handler_token_; |
| |
| // Sets up a callback to receive SIGPROF interrupt. |
| void EnableHandler(); |
| |
| // Disables receiving SIGPROF interrupt. |
| void DisableHandler(); |
| |
| // Signal handler that records the interrupted pc in the profile data. |
| static void prof_handler(int sig, siginfo_t*, void* signal_ucontext, |
| void* cpu_profiler); |
| }; |
| |
| // Profile data structure singleton: Constructor will check to see if |
| // profiling should be enabled. Destructor will write profile data |
| // out to disk. |
| CpuProfiler CpuProfiler::instance_; |
| |
| // Initialize profiling: activated if getenv("CPUPROFILE") exists. |
| CpuProfiler::CpuProfiler() |
| : prof_handler_token_(NULL) { |
| // 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)) { |
| return; |
| } |
| // We don't enable profiling if setuid -- it's a security risk |
| #ifdef HAVE_GETEUID |
| if (getuid() != geteuid()) |
| return; |
| #endif |
| |
| if (!Start(fname, NULL)) { |
| RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n", |
| fname, strerror(errno)); |
| } |
| } |
| |
| bool CpuProfiler::Start(const char* fname, const ProfilerOptions* options) { |
| SpinLockHolder cl(&lock_); |
| |
| if (collector_.enabled()) { |
| return false; |
| } |
| |
| ProfileHandlerState prof_handler_state; |
| ProfileHandlerGetState(&prof_handler_state); |
| |
| ProfileData::Options collector_options; |
| collector_options.set_frequency(prof_handler_state.frequency); |
| if (!collector_.Start(fname, collector_options)) { |
| return false; |
| } |
| |
| filter_ = NULL; |
| if (options != NULL && options->filter_in_thread != NULL) { |
| filter_ = options->filter_in_thread; |
| filter_arg_ = options->filter_in_thread_arg; |
| } |
| |
| // Setup handler for SIGPROF interrupts |
| EnableHandler(); |
| |
| return true; |
| } |
| |
| CpuProfiler::~CpuProfiler() { |
| Stop(); |
| } |
| |
| // Stop profiling and write out any collected profile data |
| void CpuProfiler::Stop() { |
| SpinLockHolder cl(&lock_); |
| |
| if (!collector_.enabled()) { |
| return; |
| } |
| |
| // Unregister prof_handler to stop receiving SIGPROF interrupts before |
| // stopping the collector. |
| DisableHandler(); |
| |
| // DisableHandler waits for the currently running callback to complete and |
| // guarantees no future invocations. It is safe to stop the collector. |
| collector_.Stop(); |
| } |
| |
| void CpuProfiler::FlushTable() { |
| SpinLockHolder cl(&lock_); |
| |
| if (!collector_.enabled()) { |
| return; |
| } |
| |
| // Unregister prof_handler to stop receiving SIGPROF interrupts before |
| // flushing the profile data. |
| DisableHandler(); |
| |
| // DisableHandler waits for the currently running callback to complete and |
| // guarantees no future invocations. It is safe to flush the profile data. |
| collector_.FlushTable(); |
| |
| EnableHandler(); |
| } |
| |
| bool CpuProfiler::Enabled() { |
| SpinLockHolder cl(&lock_); |
| return collector_.enabled(); |
| } |
| |
| void CpuProfiler::GetCurrentState(ProfilerState* state) { |
| ProfileData::State collector_state; |
| { |
| SpinLockHolder cl(&lock_); |
| collector_.GetCurrentState(&collector_state); |
| } |
| |
| state->enabled = collector_state.enabled; |
| state->start_time = static_cast<time_t>(collector_state.start_time); |
| state->samples_gathered = collector_state.samples_gathered; |
| int buf_size = sizeof(state->profile_name); |
| strncpy(state->profile_name, collector_state.profile_name, buf_size); |
| state->profile_name[buf_size-1] = '\0'; |
| } |
| |
| void CpuProfiler::EnableHandler() { |
| RAW_CHECK(prof_handler_token_ == NULL, "SIGPROF handler already registered"); |
| prof_handler_token_ = ProfileHandlerRegisterCallback(prof_handler, this); |
| RAW_CHECK(prof_handler_token_ != NULL, "Failed to set up SIGPROF handler"); |
| } |
| |
| void CpuProfiler::DisableHandler() { |
| RAW_CHECK(prof_handler_token_ != NULL, "SIGPROF handler is not registered"); |
| ProfileHandlerUnregisterCallback(prof_handler_token_); |
| prof_handler_token_ = NULL; |
| } |
| |
| // Signal handler that records the pc in the profile-data structure. We do no |
| // synchronization here. profile-handler.cc guarantees that at most one |
| // instance of prof_handler() will run at a time. All other routines that |
| // access the data touched by prof_handler() disable this signal handler before |
| // accessing the data and therefore cannot execute concurrently with |
| // prof_handler(). |
| void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext, |
| void* cpu_profiler) { |
| CpuProfiler* instance = static_cast<CpuProfiler*>(cpu_profiler); |
| |
| if (instance->filter_ == NULL || |
| (*instance->filter_)(instance->filter_arg_)) { |
| void* stack[ProfileData::kMaxStackDepth]; |
| |
| // The top-most active routine doesn't show up as a normal |
| // frame, but as the "pc" value in the signal handler context. |
| stack[0] = GetPC(*reinterpret_cast<ucontext_t*>(signal_ucontext)); |
| |
| // We skip the top two stack trace entries (this function and one |
| // signal handler frame) since they are artifacts of profiling and |
| // should not be measured. Other profiling related frames may be |
| // removed by "pprof" at analysis time. Instead of skipping the top |
| // frames, we could skip nothing, but that would increase the |
| // profile size unnecessarily. |
| int depth = GetStackTraceWithContext(stack + 1, arraysize(stack) - 1, |
| 2, signal_ucontext); |
| depth++; // To account for pc value in stack[0]; |
| |
| instance->collector_.Add(depth, stack); |
| } |
| } |
| |
| #if !(defined(__CYGWIN__) || defined(__CYGWIN32__)) |
| |
| extern "C" PERFTOOLS_DLL_DECL void ProfilerRegisterThread() { |
| ProfileHandlerRegisterThread(); |
| } |
| |
| extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() { |
| CpuProfiler::instance_.FlushTable(); |
| } |
| |
| extern "C" PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads() { |
| return CpuProfiler::instance_.Enabled(); |
| } |
| |
| extern "C" PERFTOOLS_DLL_DECL int ProfilerStart(const char* fname) { |
| return CpuProfiler::instance_.Start(fname, NULL); |
| } |
| |
| extern "C" PERFTOOLS_DLL_DECL int ProfilerStartWithOptions( |
| const char *fname, const ProfilerOptions *options) { |
| return CpuProfiler::instance_.Start(fname, options); |
| } |
| |
| extern "C" PERFTOOLS_DLL_DECL void ProfilerStop() { |
| CpuProfiler::instance_.Stop(); |
| } |
| |
| extern "C" PERFTOOLS_DLL_DECL void ProfilerGetCurrentState( |
| ProfilerState* state) { |
| CpuProfiler::instance_.GetCurrentState(state); |
| } |
| |
| #else // OS_CYGWIN |
| |
| // ITIMER_PROF doesn't work under cygwin. ITIMER_REAL is available, but doesn't |
| // work as well for profiling, and also interferes with alarm(). Because of |
| // these issues, unless a specific need is identified, profiler support is |
| // disabled under Cygwin. |
| extern "C" void ProfilerRegisterThread() { } |
| extern "C" void ProfilerFlush() { } |
| extern "C" int ProfilingIsEnabledForAllThreads() { return 0; } |
| extern "C" int ProfilerStart(const char* fname) { return 0; } |
| extern "C" int ProfilerStartWithOptions(const char *fname, |
| const ProfilerOptions *options) { |
| return 0; |
| } |
| extern "C" void ProfilerStop() { } |
| extern "C" void ProfilerGetCurrentState(ProfilerState* state) { |
| memset(state, 0, sizeof(*state)); |
| } |
| |
| #endif // OS_CYGWIN |
| |
| // DEPRECATED routines |
| extern "C" PERFTOOLS_DLL_DECL void ProfilerEnable() { } |
| extern "C" PERFTOOLS_DLL_DECL void ProfilerDisable() { } |