| // 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() { } |