Commit 07b72a53 authored by tqcq's avatar tqcq
Browse files

feat add log for cpu profiler

parent cd761270
Loading
Loading
Loading
Loading
+298 −254
Original line number Diff line number Diff line
@@ -36,10 +36,10 @@

#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 <signal.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>// for getpid()
@@ -54,17 +54,17 @@ 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/logging.h"
#include "base/spinlock.h"
#include "base/sysinfo.h" /* for GetUniquePathFromEnv, etc */
#include "profiledata.h"
#include "profile-handler.h"
#include "profiledata.h"
#include <gperftools/profiler.h>
#include <gperftools/stacktrace.h>
#include <string>
#include <sys/time.h>

using std::string;

@@ -133,13 +133,13 @@ class CpuProfiler {
    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);
    static void prof_handler(int sig, siginfo_t *, void *signal_ucontext, void *cpu_profiler);
};

// Signal handler that is registered when a user selectable signal
// 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 char base_profile_name[PATH_MAX];
@@ -152,18 +152,17 @@ static void CpuProfilerSwitch(int signal_number)
        }
    }

  if (!started) {
    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", base_profile_name, profile_count);
    if (!started) {
        ++profile_count;
        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);
    }
    started = !started;
}
@@ -174,8 +173,8 @@ static void CpuProfilerSwitch(int signal_number)
CpuProfiler CpuProfiler::instance_;

// Initialize profiling: activated if getenv("CPUPROFILE") exists.
CpuProfiler::CpuProfiler()
    : prof_handler_token_(NULL) {
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.
@@ -219,27 +218,24 @@ CpuProfiler::CpuProfiler()
        }

        if (!Start(fname, NULL)) {
      RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
              fname, strerror(errno));
            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) {
bool
CpuProfiler::Start(const char *fname, const ProfilerOptions *options)
{
    SpinLockHolder cl(&lock_);

  if (collector_.enabled()) {
    return false;
  }
    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;
  }
    if (!collector_.Start(fname, collector_options)) { return false; }

    filter_ = NULL;
    if (options != NULL && options->filter_in_thread != NULL) {
@@ -253,17 +249,15 @@ bool CpuProfiler::Start(const char* fname, const ProfilerOptions* options) {
    return true;
}

CpuProfiler::~CpuProfiler() {
  Stop();
}
CpuProfiler::~CpuProfiler() { Stop(); }

// Stop profiling and write out any collected profile data
void CpuProfiler::Stop() {
void
CpuProfiler::Stop()
{
    SpinLockHolder cl(&lock_);

  if (!collector_.enabled()) {
    return;
  }
    if (!collector_.enabled()) { return; }

    // Unregister prof_handler to stop receiving SIGPROF interrupts before
    // stopping the collector.
@@ -274,12 +268,12 @@ void CpuProfiler::Stop() {
    collector_.Stop();
}

void CpuProfiler::FlushTable() {
void
CpuProfiler::FlushTable()
{
    SpinLockHolder cl(&lock_);

  if (!collector_.enabled()) {
    return;
  }
    if (!collector_.enabled()) { return; }

    // Unregister prof_handler to stop receiving SIGPROF interrupts before
    // flushing the profile data.
@@ -292,12 +286,16 @@ void CpuProfiler::FlushTable() {
    EnableHandler();
}

bool CpuProfiler::Enabled() {
bool
CpuProfiler::Enabled()
{
    SpinLockHolder cl(&lock_);
    return collector_.enabled();
}

void CpuProfiler::GetCurrentState(ProfilerState* state) {
void
CpuProfiler::GetCurrentState(ProfilerState *state)
{
    ProfileData::State collector_state;
    {
        SpinLockHolder cl(&lock_);
@@ -312,13 +310,17 @@ void CpuProfiler::GetCurrentState(ProfilerState* state) {
    state->profile_name[buf_size - 1] = '\0';
}

void CpuProfiler::EnableHandler() {
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() {
void
CpuProfiler::DisableHandler()
{
    RAW_CHECK(prof_handler_token_ != NULL, "SIGPROF handler is not registered");
    ProfileHandlerUnregisterCallback(prof_handler_token_);
    prof_handler_token_ = NULL;
@@ -330,12 +332,12 @@ void CpuProfiler::DisableHandler() {
// 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) {
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_)) {
    if (instance->filter_ == NULL || (*instance->filter_)(instance->filter_arg_)) {
        void *stack[ProfileData::kMaxStackDepth];

        // Under frame-pointer-based unwinding at least on x86, the
@@ -350,8 +352,7 @@ void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,
        // "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,
                                         3, signal_ucontext);
        int depth = GetStackTraceWithContext(stack + 1, arraysize(stack) - 1, 3, signal_ucontext);

        void **used_stack;
        if (depth > 0 && stack[1] == stack[0]) {
@@ -369,38 +370,51 @@ void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,

#if !(defined(__CYGWIN__) || defined(__CYGWIN32__))

extern "C" PERFTOOLS_DLL_DECL void ProfilerRegisterThread() {
extern "C" PERFTOOLS_DLL_DECL void
ProfilerRegisterThread()
{
    ProfileHandlerRegisterThread();
}

extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() {
extern "C" PERFTOOLS_DLL_DECL void
ProfilerFlush()
{
    CpuProfiler::instance_.FlushTable();
}

extern "C" PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads() {
extern "C" PERFTOOLS_DLL_DECL int
ProfilingIsEnabledForAllThreads()
{
    return CpuProfiler::instance_.Enabled();
}

extern "C" PERFTOOLS_DLL_DECL int ProfilerStart(const char* fname) {
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) {
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() {
extern "C" PERFTOOLS_DLL_DECL void
ProfilerStop()
{
    CpuProfiler::instance_.Stop();
}

extern "C" PERFTOOLS_DLL_DECL void ProfilerGetCurrentState(
    ProfilerState* state) {
extern "C" PERFTOOLS_DLL_DECL void
ProfilerGetCurrentState(ProfilerState *state)
{
    CpuProfiler::instance_.GetCurrentState(state);
}

extern "C" PERFTOOLS_DLL_DECL int ProfilerGetStackTrace(
    void** result, int max_depth, int skip_count, const void *uc) {
extern "C" PERFTOOLS_DLL_DECL int
ProfilerGetStackTrace(void **result, int max_depth, int skip_count, const void *uc)
{
    return GetStackTraceWithContext(result, max_depth, skip_count, uc);
}

@@ -410,25 +424,55 @@ extern "C" PERFTOOLS_DLL_DECL int ProfilerGetStackTrace(
// 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) {
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" void ProfilerStop() { }
extern "C" void ProfilerGetCurrentState(ProfilerState* state) {

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) {

extern "C" int
ProfilerGetStackTrace(void **result, int max_depth, int skip_count, const void *uc)
{
    return 0;
}

#endif// OS_CYGWIN

// DEPRECATED routines
extern "C" PERFTOOLS_DLL_DECL void ProfilerEnable() { }
extern "C" PERFTOOLS_DLL_DECL void ProfilerDisable() { }
extern "C" PERFTOOLS_DLL_DECL void
ProfilerEnable()
{}

extern "C" PERFTOOLS_DLL_DECL void
ProfilerDisable()
{}
+32 −0
Original line number Diff line number Diff line
#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 −0
Original line number Diff line number Diff line
#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 −0
Original line number Diff line number Diff line
#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
+12 −0
Original line number Diff line number Diff line
#include <random>
#include <sled/synchronization/event.h>
#include <sled/system/fiber/wait_group.h>
#include <sled/system/thread_pool.h>

std::random_device rd;
@@ -81,4 +82,15 @@ TEST_SUITE("ThreadPool")
        CHECK(waiter.Wait(sled::TimeDelta::Millis(150)));
        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();
    }
}