feat add log for cpu profiler
Some checks failed
linux-arm-gcc / linux-gcc-armhf (push) Successful in 1m48s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 2m26s
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 2m35s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Successful in 3m20s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Successful in 3m42s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Has been cancelled
Some checks failed
linux-arm-gcc / linux-gcc-armhf (push) Successful in 1m48s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 2m26s
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 2m35s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Successful in 3m20s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Successful in 3m42s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Has been cancelled
This commit is contained in:
parent
cd7612700d
commit
07b72a53ec
@ -35,14 +35,14 @@
|
|||||||
// Profile current program by sampling stack-trace every so often
|
// Profile current program by sampling stack-trace every so often
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "getpc.h" // should be first to get the _GNU_SOURCE dfn
|
#include "getpc.h"// should be first to get the _GNU_SOURCE dfn
|
||||||
#include <signal.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h> // for getpid()
|
#include <unistd.h>// for getpid()
|
||||||
#endif
|
#endif
|
||||||
#if HAVE_SYS_UCONTEXT_H
|
#if HAVE_SYS_UCONTEXT_H
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
@ -52,19 +52,19 @@
|
|||||||
#include <cygwin/signal.h>
|
#include <cygwin/signal.h>
|
||||||
typedef ucontext ucontext_t;
|
typedef ucontext ucontext_t;
|
||||||
#else
|
#else
|
||||||
typedef int ucontext_t; // just to quiet the compiler, mostly
|
typedef int ucontext_t;// just to quiet the compiler, mostly
|
||||||
#endif
|
#endif
|
||||||
#include <sys/time.h>
|
#include "base/commandlineflags.h"
|
||||||
#include <string>
|
#include "base/googleinit.h"
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/spinlock.h"
|
||||||
|
#include "base/sysinfo.h" /* for GetUniquePathFromEnv, etc */
|
||||||
|
#include "profile-handler.h"
|
||||||
|
#include "profiledata.h"
|
||||||
#include <gperftools/profiler.h>
|
#include <gperftools/profiler.h>
|
||||||
#include <gperftools/stacktrace.h>
|
#include <gperftools/stacktrace.h>
|
||||||
#include "base/commandlineflags.h"
|
#include <string>
|
||||||
#include "base/logging.h"
|
#include <sys/time.h>
|
||||||
#include "base/googleinit.h"
|
|
||||||
#include "base/spinlock.h"
|
|
||||||
#include "base/sysinfo.h" /* for GetUniquePathFromEnv, etc */
|
|
||||||
#include "profiledata.h"
|
|
||||||
#include "profile-handler.h"
|
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
@ -84,88 +84,87 @@ DEFINE_bool(cpu_profiler_unittest,
|
|||||||
// having to start and stop the daemon or having to modify the
|
// having to start and stop the daemon or having to modify the
|
||||||
// source code to use the cpu profiler API.
|
// source code to use the cpu profiler API.
|
||||||
class CpuProfiler {
|
class CpuProfiler {
|
||||||
public:
|
public:
|
||||||
CpuProfiler();
|
CpuProfiler();
|
||||||
~CpuProfiler();
|
~CpuProfiler();
|
||||||
|
|
||||||
// Start profiler to write profile info into fname
|
// Start profiler to write profile info into fname
|
||||||
bool Start(const char* fname, const ProfilerOptions* options);
|
bool Start(const char *fname, const ProfilerOptions *options);
|
||||||
|
|
||||||
// Stop profiling and write the data to disk.
|
// Stop profiling and write the data to disk.
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
// Write the data to disk (and continue profiling).
|
// Write the data to disk (and continue profiling).
|
||||||
void FlushTable();
|
void FlushTable();
|
||||||
|
|
||||||
bool Enabled();
|
bool Enabled();
|
||||||
|
|
||||||
void GetCurrentState(ProfilerState* state);
|
void GetCurrentState(ProfilerState *state);
|
||||||
|
|
||||||
static CpuProfiler instance_;
|
static CpuProfiler instance_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This lock implements the locking requirements described in the ProfileData
|
// This lock implements the locking requirements described in the ProfileData
|
||||||
// documentation, specifically:
|
// documentation, specifically:
|
||||||
//
|
//
|
||||||
// lock_ is held all over all collector_ method calls except for the 'Add'
|
// 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
|
// call made from the signal handler, to protect against concurrent use of
|
||||||
// collector_'s control routines. Code other than signal handler must
|
// collector_'s control routines. Code other than signal handler must
|
||||||
// unregister the signal handler before calling any collector_ method.
|
// unregister the signal handler before calling any collector_ method.
|
||||||
// 'Add' method in the collector is protected by a guarantee from
|
// 'Add' method in the collector is protected by a guarantee from
|
||||||
// ProfileHandle that only one instance of prof_handler can run at a time.
|
// ProfileHandle that only one instance of prof_handler can run at a time.
|
||||||
SpinLock lock_;
|
SpinLock lock_;
|
||||||
ProfileData collector_;
|
ProfileData collector_;
|
||||||
|
|
||||||
// Filter function and its argument, if any. (NULL means include all
|
// Filter function and its argument, if any. (NULL means include all
|
||||||
// samples). Set at start, read-only while running. Written while holding
|
// samples). Set at start, read-only while running. Written while holding
|
||||||
// lock_, read and executed in the context of SIGPROF interrupt.
|
// lock_, read and executed in the context of SIGPROF interrupt.
|
||||||
int (*filter_)(void*);
|
int (*filter_)(void *);
|
||||||
void* filter_arg_;
|
void *filter_arg_;
|
||||||
|
|
||||||
// Opaque token returned by the profile handler. To be used when calling
|
// Opaque token returned by the profile handler. To be used when calling
|
||||||
// ProfileHandlerUnregisterCallback.
|
// ProfileHandlerUnregisterCallback.
|
||||||
ProfileHandlerToken* prof_handler_token_;
|
ProfileHandlerToken *prof_handler_token_;
|
||||||
|
|
||||||
// Sets up a callback to receive SIGPROF interrupt.
|
// Sets up a callback to receive SIGPROF interrupt.
|
||||||
void EnableHandler();
|
void EnableHandler();
|
||||||
|
|
||||||
// Disables receiving SIGPROF interrupt.
|
// Disables receiving SIGPROF interrupt.
|
||||||
void DisableHandler();
|
void DisableHandler();
|
||||||
|
|
||||||
// Signal handler that records the interrupted pc in the profile data.
|
// Signal handler that records the interrupted pc in the profile data.
|
||||||
static void prof_handler(int sig, siginfo_t*, void* signal_ucontext,
|
static void prof_handler(int sig, siginfo_t *, void *signal_ucontext, void *cpu_profiler);
|
||||||
void* cpu_profiler);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Signal handler that is registered when a user selectable signal
|
// Signal handler that is registered when a user selectable signal
|
||||||
// number is defined in the environment variable CPUPROFILESIGNAL.
|
// number is defined in the environment variable CPUPROFILESIGNAL.
|
||||||
static void CpuProfilerSwitch(int signal_number)
|
static void
|
||||||
|
CpuProfilerSwitch(int signal_number)
|
||||||
{
|
{
|
||||||
static unsigned profile_count;
|
static unsigned profile_count;
|
||||||
static char base_profile_name[PATH_MAX];
|
static char base_profile_name[PATH_MAX];
|
||||||
static bool started = false;
|
static bool started = false;
|
||||||
|
|
||||||
if (base_profile_name[0] == '\0') {
|
if (base_profile_name[0] == '\0') {
|
||||||
if (!GetUniquePathFromEnv("CPUPROFILE", base_profile_name)) {
|
if (!GetUniquePathFromEnv("CPUPROFILE", base_profile_name)) {
|
||||||
RAW_LOG(FATAL,"Cpu profiler switch is registered but no CPUPROFILE is defined");
|
RAW_LOG(FATAL, "Cpu profiler switch is registered but no CPUPROFILE is defined");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!started) {
|
|
||||||
char full_profile_name[PATH_MAX + 16];
|
char full_profile_name[PATH_MAX + 16];
|
||||||
|
snprintf(full_profile_name, sizeof(full_profile_name), "%s.%u", base_profile_name, profile_count);
|
||||||
snprintf(full_profile_name, sizeof(full_profile_name), "%s.%u",
|
if (!started) {
|
||||||
base_profile_name, profile_count++);
|
++profile_count;
|
||||||
|
if (!ProfilerStart(full_profile_name)) {
|
||||||
if(!ProfilerStart(full_profile_name)) {
|
RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n", full_profile_name, strerror(errno));
|
||||||
RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
|
}
|
||||||
full_profile_name, strerror(errno));
|
RAW_LOG(INFO, "Start New cpu Profiling for '%s'", full_profile_name);
|
||||||
|
} else {
|
||||||
|
ProfilerStop();
|
||||||
|
RAW_LOG(INFO, "Stop cpu Profiling for '%s'", full_profile_name);
|
||||||
}
|
}
|
||||||
} else {
|
started = !started;
|
||||||
ProfilerStop();
|
|
||||||
}
|
|
||||||
started = !started;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Profile data structure singleton: Constructor will check to see if
|
// Profile data structure singleton: Constructor will check to see if
|
||||||
@ -174,154 +173,157 @@ static void CpuProfilerSwitch(int signal_number)
|
|||||||
CpuProfiler CpuProfiler::instance_;
|
CpuProfiler CpuProfiler::instance_;
|
||||||
|
|
||||||
// Initialize profiling: activated if getenv("CPUPROFILE") exists.
|
// Initialize profiling: activated if getenv("CPUPROFILE") exists.
|
||||||
CpuProfiler::CpuProfiler()
|
CpuProfiler::CpuProfiler() : prof_handler_token_(NULL)
|
||||||
: prof_handler_token_(NULL) {
|
{
|
||||||
// TODO(cgd) Move this code *out* of the CpuProfile constructor into a
|
// TODO(cgd) Move this code *out* of the CpuProfile constructor into a
|
||||||
// separate object responsible for initialization. With ProfileHandler there
|
// separate object responsible for initialization. With ProfileHandler there
|
||||||
// is no need to limit the number of profilers.
|
// is no need to limit the number of profilers.
|
||||||
if (getenv("CPUPROFILE") == NULL) {
|
if (getenv("CPUPROFILE") == NULL) {
|
||||||
if (!FLAGS_cpu_profiler_unittest) {
|
if (!FLAGS_cpu_profiler_unittest) {
|
||||||
RAW_LOG(WARNING, "CPU profiler linked but no valid CPUPROFILE environment variable found\n");
|
RAW_LOG(WARNING, "CPU profiler linked but no valid CPUPROFILE environment variable found\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't enable profiling if setuid -- it's a security risk
|
// We don't enable profiling if setuid -- it's a security risk
|
||||||
#ifdef HAVE_GETEUID
|
#ifdef HAVE_GETEUID
|
||||||
if (getuid() != geteuid()) {
|
if (getuid() != geteuid()) {
|
||||||
if (!FLAGS_cpu_profiler_unittest) {
|
if (!FLAGS_cpu_profiler_unittest) {
|
||||||
RAW_LOG(WARNING, "Cannot perform CPU profiling when running with setuid\n");
|
RAW_LOG(WARNING, "Cannot perform CPU profiling when running with setuid\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char *signal_number_str = getenv("CPUPROFILESIGNAL");
|
char *signal_number_str = getenv("CPUPROFILESIGNAL");
|
||||||
if (signal_number_str != NULL) {
|
if (signal_number_str != NULL) {
|
||||||
long int signal_number = strtol(signal_number_str, NULL, 10);
|
long int signal_number = strtol(signal_number_str, NULL, 10);
|
||||||
if (signal_number >= 1 && signal_number <= 64) {
|
if (signal_number >= 1 && signal_number <= 64) {
|
||||||
intptr_t old_signal_handler = reinterpret_cast<intptr_t>(signal(signal_number, CpuProfilerSwitch));
|
intptr_t old_signal_handler = reinterpret_cast<intptr_t>(signal(signal_number, CpuProfilerSwitch));
|
||||||
if (old_signal_handler == 0) {
|
if (old_signal_handler == 0) {
|
||||||
RAW_LOG(INFO,"Using signal %d as cpu profiling switch", signal_number);
|
RAW_LOG(INFO, "Using signal %d as cpu profiling switch", signal_number);
|
||||||
} else {
|
} else {
|
||||||
RAW_LOG(FATAL, "Signal %d already in use\n", signal_number);
|
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 {
|
} else {
|
||||||
RAW_LOG(FATAL, "Signal number %s is invalid\n", signal_number_str);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} 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)) {
|
bool
|
||||||
RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
|
CpuProfiler::Start(const char *fname, const ProfilerOptions *options)
|
||||||
fname, strerror(errno));
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CpuProfiler::Start(const char* fname, const ProfilerOptions* options) {
|
CpuProfiler::~CpuProfiler() { Stop(); }
|
||||||
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
|
// Stop profiling and write out any collected profile data
|
||||||
void CpuProfiler::Stop() {
|
void
|
||||||
SpinLockHolder cl(&lock_);
|
CpuProfiler::Stop()
|
||||||
|
{
|
||||||
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_);
|
SpinLockHolder cl(&lock_);
|
||||||
collector_.GetCurrentState(&collector_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state->enabled = collector_state.enabled;
|
if (!collector_.enabled()) { return; }
|
||||||
state->start_time = static_cast<time_t>(collector_state.start_time);
|
|
||||||
state->samples_gathered = collector_state.samples_gathered;
|
// Unregister prof_handler to stop receiving SIGPROF interrupts before
|
||||||
int buf_size = sizeof(state->profile_name);
|
// stopping the collector.
|
||||||
strncpy(state->profile_name, collector_state.profile_name, buf_size);
|
DisableHandler();
|
||||||
state->profile_name[buf_size-1] = '\0';
|
|
||||||
|
// 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::EnableHandler() {
|
void
|
||||||
RAW_CHECK(prof_handler_token_ == NULL, "SIGPROF handler already registered");
|
CpuProfiler::FlushTable()
|
||||||
prof_handler_token_ = ProfileHandlerRegisterCallback(prof_handler, this);
|
{
|
||||||
RAW_CHECK(prof_handler_token_ != NULL, "Failed to set up SIGPROF handler");
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuProfiler::DisableHandler() {
|
bool
|
||||||
RAW_CHECK(prof_handler_token_ != NULL, "SIGPROF handler is not registered");
|
CpuProfiler::Enabled()
|
||||||
ProfileHandlerUnregisterCallback(prof_handler_token_);
|
{
|
||||||
prof_handler_token_ = NULL;
|
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
|
// Signal handler that records the pc in the profile-data structure. We do no
|
||||||
@ -330,105 +332,147 @@ void CpuProfiler::DisableHandler() {
|
|||||||
// access the data touched by prof_handler() disable this signal handler before
|
// access the data touched by prof_handler() disable this signal handler before
|
||||||
// accessing the data and therefore cannot execute concurrently with
|
// accessing the data and therefore cannot execute concurrently with
|
||||||
// prof_handler().
|
// prof_handler().
|
||||||
void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,
|
void
|
||||||
void* cpu_profiler) {
|
CpuProfiler::prof_handler(int sig, siginfo_t *, void *signal_ucontext, void *cpu_profiler)
|
||||||
CpuProfiler* instance = static_cast<CpuProfiler*>(cpu_profiler);
|
{
|
||||||
|
CpuProfiler *instance = static_cast<CpuProfiler *>(cpu_profiler);
|
||||||
|
|
||||||
if (instance->filter_ == NULL ||
|
if (instance->filter_ == NULL || (*instance->filter_)(instance->filter_arg_)) {
|
||||||
(*instance->filter_)(instance->filter_arg_)) {
|
void *stack[ProfileData::kMaxStackDepth];
|
||||||
void* stack[ProfileData::kMaxStackDepth];
|
|
||||||
|
|
||||||
// Under frame-pointer-based unwinding at least on x86, the
|
// Under frame-pointer-based unwinding at least on x86, the
|
||||||
// top-most active routine doesn't show up as a normal frame, but
|
// top-most active routine doesn't show up as a normal frame, but
|
||||||
// as the "pc" value in the signal handler context.
|
// as the "pc" value in the signal handler context.
|
||||||
stack[0] = GetPC(*reinterpret_cast<ucontext_t*>(signal_ucontext));
|
stack[0] = GetPC(*reinterpret_cast<ucontext_t *>(signal_ucontext));
|
||||||
|
|
||||||
// We skip the top three stack trace entries (this function,
|
// We skip the top three stack trace entries (this function,
|
||||||
// SignalHandler::SignalHandler and one signal handler frame)
|
// SignalHandler::SignalHandler and one signal handler frame)
|
||||||
// since they are artifacts of profiling and should not be
|
// since they are artifacts of profiling and should not be
|
||||||
// measured. Other profiling related frames may be removed by
|
// measured. Other profiling related frames may be removed by
|
||||||
// "pprof" at analysis time. Instead of skipping the top frames,
|
// "pprof" at analysis time. Instead of skipping the top frames,
|
||||||
// we could skip nothing, but that would increase the profile size
|
// we could skip nothing, but that would increase the profile size
|
||||||
// unnecessarily.
|
// unnecessarily.
|
||||||
int depth = GetStackTraceWithContext(stack + 1, arraysize(stack) - 1,
|
int depth = GetStackTraceWithContext(stack + 1, arraysize(stack) - 1, 3, signal_ucontext);
|
||||||
3, signal_ucontext);
|
|
||||||
|
|
||||||
void **used_stack;
|
void **used_stack;
|
||||||
if (depth > 0 && stack[1] == stack[0]) {
|
if (depth > 0 && stack[1] == stack[0]) {
|
||||||
// in case of non-frame-pointer-based unwinding we will get
|
// in case of non-frame-pointer-based unwinding we will get
|
||||||
// duplicate of PC in stack[1], which we don't want
|
// duplicate of PC in stack[1], which we don't want
|
||||||
used_stack = stack + 1;
|
used_stack = stack + 1;
|
||||||
} else {
|
} else {
|
||||||
used_stack = stack;
|
used_stack = stack;
|
||||||
depth++; // To account for pc value in stack[0];
|
depth++;// To account for pc value in stack[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->collector_.Add(depth, used_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
instance->collector_.Add(depth, used_stack);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !(defined(__CYGWIN__) || defined(__CYGWIN32__))
|
#if !(defined(__CYGWIN__) || defined(__CYGWIN32__))
|
||||||
|
|
||||||
extern "C" PERFTOOLS_DLL_DECL void ProfilerRegisterThread() {
|
extern "C" PERFTOOLS_DLL_DECL void
|
||||||
ProfileHandlerRegisterThread();
|
ProfilerRegisterThread()
|
||||||
|
{
|
||||||
|
ProfileHandlerRegisterThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() {
|
extern "C" PERFTOOLS_DLL_DECL void
|
||||||
CpuProfiler::instance_.FlushTable();
|
ProfilerFlush()
|
||||||
|
{
|
||||||
|
CpuProfiler::instance_.FlushTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads() {
|
extern "C" PERFTOOLS_DLL_DECL int
|
||||||
return CpuProfiler::instance_.Enabled();
|
ProfilingIsEnabledForAllThreads()
|
||||||
|
{
|
||||||
|
return CpuProfiler::instance_.Enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" PERFTOOLS_DLL_DECL int ProfilerStart(const char* fname) {
|
extern "C" PERFTOOLS_DLL_DECL int
|
||||||
return CpuProfiler::instance_.Start(fname, NULL);
|
ProfilerStart(const char *fname)
|
||||||
|
{
|
||||||
|
return CpuProfiler::instance_.Start(fname, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" PERFTOOLS_DLL_DECL int ProfilerStartWithOptions(
|
extern "C" PERFTOOLS_DLL_DECL int
|
||||||
const char *fname, const ProfilerOptions *options) {
|
ProfilerStartWithOptions(const char *fname, const ProfilerOptions *options)
|
||||||
return CpuProfiler::instance_.Start(fname, options);
|
{
|
||||||
|
return CpuProfiler::instance_.Start(fname, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" PERFTOOLS_DLL_DECL void ProfilerStop() {
|
extern "C" PERFTOOLS_DLL_DECL void
|
||||||
CpuProfiler::instance_.Stop();
|
ProfilerStop()
|
||||||
|
{
|
||||||
|
CpuProfiler::instance_.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" PERFTOOLS_DLL_DECL void ProfilerGetCurrentState(
|
extern "C" PERFTOOLS_DLL_DECL void
|
||||||
ProfilerState* state) {
|
ProfilerGetCurrentState(ProfilerState *state)
|
||||||
CpuProfiler::instance_.GetCurrentState(state);
|
{
|
||||||
|
CpuProfiler::instance_.GetCurrentState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" PERFTOOLS_DLL_DECL int ProfilerGetStackTrace(
|
extern "C" PERFTOOLS_DLL_DECL int
|
||||||
void** result, int max_depth, int skip_count, const void *uc) {
|
ProfilerGetStackTrace(void **result, int max_depth, int skip_count, const void *uc)
|
||||||
return GetStackTraceWithContext(result, max_depth, skip_count, uc);
|
{
|
||||||
|
return GetStackTraceWithContext(result, max_depth, skip_count, uc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // OS_CYGWIN
|
#else// OS_CYGWIN
|
||||||
|
|
||||||
// ITIMER_PROF doesn't work under cygwin. ITIMER_REAL is available, but doesn't
|
// 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
|
// work as well for profiling, and also interferes with alarm(). Because of
|
||||||
// these issues, unless a specific need is identified, profiler support is
|
// these issues, unless a specific need is identified, profiler support is
|
||||||
// disabled under Cygwin.
|
// disabled under Cygwin.
|
||||||
extern "C" void ProfilerRegisterThread() { }
|
extern "C" void
|
||||||
extern "C" void ProfilerFlush() { }
|
ProfilerRegisterThread()
|
||||||
extern "C" int ProfilingIsEnabledForAllThreads() { return 0; }
|
{}
|
||||||
extern "C" int ProfilerStart(const char* fname) { return 0; }
|
|
||||||
extern "C" int ProfilerStartWithOptions(const char *fname,
|
extern "C" void
|
||||||
const ProfilerOptions *options) {
|
ProfilerFlush()
|
||||||
return 0;
|
{}
|
||||||
}
|
|
||||||
extern "C" void ProfilerStop() { }
|
extern "C" int
|
||||||
extern "C" void ProfilerGetCurrentState(ProfilerState* state) {
|
ProfilingIsEnabledForAllThreads()
|
||||||
memset(state, 0, sizeof(*state));
|
{
|
||||||
}
|
return 0;
|
||||||
extern "C" int ProfilerGetStackTrace(
|
|
||||||
void** result, int max_depth, int skip_count, const void *uc) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // OS_CYGWIN
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
ProfilerGetStackTrace(void **result, int max_depth, int skip_count, const void *uc)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif// OS_CYGWIN
|
||||||
|
|
||||||
// DEPRECATED routines
|
// DEPRECATED routines
|
||||||
extern "C" PERFTOOLS_DLL_DECL void ProfilerEnable() { }
|
extern "C" PERFTOOLS_DLL_DECL void
|
||||||
extern "C" PERFTOOLS_DLL_DECL void ProfilerDisable() { }
|
ProfilerEnable()
|
||||||
|
{}
|
||||||
|
|
||||||
|
extern "C" PERFTOOLS_DLL_DECL void
|
||||||
|
ProfilerDisable()
|
||||||
|
{}
|
||||||
|
32
src/sled/profiling/internal/remote_gperf_impl.cc
Normal file
32
src/sled/profiling/internal/remote_gperf_impl.cc
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "remote_gperf_impl.h"
|
||||||
|
#include <gperftools/heap-profiler.h>
|
||||||
|
#include <gperftools/malloc_extension.h>
|
||||||
|
#include <gperftools/profiler.h>
|
||||||
|
|
||||||
|
namespace sled {
|
||||||
|
RemoteGPerfImpl::RemoteGPerfImpl(int port) {}
|
||||||
|
|
||||||
|
RemoteGPerfImpl::~RemoteGPerfImpl() {}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
RemoteGPerfImpl::HeapGET()
|
||||||
|
{
|
||||||
|
MallocExtensionWriter writer;
|
||||||
|
MallocExtension::instance()->GetHeapSample(&writer);
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
RemoteGPerfImpl::GrowthGET()
|
||||||
|
{
|
||||||
|
MallocExtensionWriter writer;
|
||||||
|
MallocExtension::instance()->GetHeapGrowthStacks(&writer);
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
RemoteGPerfImpl::CmdLineGET()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}// namespace sled
|
28
src/sled/profiling/internal/remote_gperf_impl.h
Normal file
28
src/sled/profiling/internal/remote_gperf_impl.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef SLED_PROFILING_INTERNAL_REMOTE_GPERF_IMPL_H
|
||||||
|
#define SLED_PROFILING_INTERNAL_REMOTE_GPERF_IMPL_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "sled/buffer.h"
|
||||||
|
|
||||||
|
namespace sled {
|
||||||
|
class RemoteGPerfImpl {
|
||||||
|
public:
|
||||||
|
RemoteGPerfImpl(int port);
|
||||||
|
~RemoteGPerfImpl();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string HeapGET();
|
||||||
|
std::string GrowthGET();
|
||||||
|
|
||||||
|
// 1. read data from /proc/selfcmdline
|
||||||
|
// 2. replace all NULL(\0) by new Lines
|
||||||
|
std::string CmdLineGET();
|
||||||
|
|
||||||
|
std::string SymbolGET();
|
||||||
|
// request: 0x0824d061+0x0824d1cf
|
||||||
|
// response: <hex address><tab><function name>
|
||||||
|
std::string SymbolPOST(const std::string &address);
|
||||||
|
};
|
||||||
|
}// namespace sled
|
||||||
|
|
||||||
|
#endif// SLED_PROFILING_INTERNAL_REMOTE_GPERF_IMPL_H
|
28
src/sled/profiling/remote_gperf.h
Normal file
28
src/sled/profiling/remote_gperf.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef SLED_PROFILING_REMOTE_GPERF_H
|
||||||
|
#define SLED_PROFILING_REMOTE_GPERF_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace sled {
|
||||||
|
// DOCS: https://gperftools.github.io/gperftools/pprof_remote_servers.html
|
||||||
|
|
||||||
|
// interface
|
||||||
|
// 1. http://host:80/pprof/heap
|
||||||
|
// 2. http://host:80/pprof/profile
|
||||||
|
// http://host:80/
|
||||||
|
// http://host:80
|
||||||
|
// 3. http://host:80/pprof/growth
|
||||||
|
// 4. http://host:80/myservice/pprof/heap
|
||||||
|
// 5. http://host:80/profile/
|
||||||
|
|
||||||
|
class RemoteGPerf {
|
||||||
|
public:
|
||||||
|
RemoteGPerf(int port);
|
||||||
|
~RemoteGPerf();
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace sled
|
||||||
|
|
||||||
|
#endif// SLED_PROFILING_REMOTE_GPERF_H
|
@ -1,5 +1,6 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <sled/synchronization/event.h>
|
#include <sled/synchronization/event.h>
|
||||||
|
#include <sled/system/fiber/wait_group.h>
|
||||||
#include <sled/system/thread_pool.h>
|
#include <sled/system/thread_pool.h>
|
||||||
|
|
||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
@ -81,4 +82,15 @@ TEST_SUITE("ThreadPool")
|
|||||||
CHECK(waiter.Wait(sled::TimeDelta::Millis(150)));
|
CHECK(waiter.Wait(sled::TimeDelta::Millis(150)));
|
||||||
delete tp;
|
delete tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("10^6 task test")
|
||||||
|
{
|
||||||
|
sled::ThreadPool *tp = new sled::ThreadPool();
|
||||||
|
const int task_num = 1E6;
|
||||||
|
sled::WaitGroup wg(task_num);
|
||||||
|
for (int i = 0; i < task_num; i++) {
|
||||||
|
tp->PostTask([wg] { wg.Done(); });
|
||||||
|
}
|
||||||
|
wg.Wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user