Change log:95336cb92b..191d55580eFull diff:95336cb92b..191d55580eRoll chromium third_party 4e16929f46..3a8f2a9e1e Change log:4e16929f46..3a8f2a9e1eChanged dependencies: * src/tools:c44a3f5eca..f524a53b81DEPS diff:95336cb92b..191d55580e/DEPS No update to Clang. TBR=titovartem@google.com, BUG=None CQ_INCLUDE_TRYBOTS=master.internal.tryserver.corp.webrtc:linux_internal Change-Id: Ic9c4a62b050383646e9fcf5cc07a5653c14ac06e Reviewed-on: https://webrtc-review.googlesource.com/76120 Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Artem Titov <titovartem@webrtc.org> Commit-Queue: Artem Titov <titovartem@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23205}
343 lines
11 KiB
C++
343 lines
11 KiB
C++
// 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;
|
|
#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() { }
|