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

This commit is contained in:
tqcq 2024-04-30 15:24:45 +08:00
parent cd7612700d
commit 07b72a53ec
5 changed files with 414 additions and 270 deletions

View File

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

View 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

View 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

View 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

View File

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