From 840df2a8f1b69dcdbfe2cfae692a7ecb5a4692db Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Tue, 1 Nov 2022 23:42:37 +0800 Subject: [PATCH] refactor: consolidate dump actions (#210) --- src/commands/dump.cc | 208 +++++++++++++++++++------------------ src/commands/dump.h | 16 +-- src/environment_data-inl.h | 33 ++++++ src/environment_data.cc | 23 ++-- src/environment_data.h | 36 +++++-- 5 files changed, 179 insertions(+), 137 deletions(-) create mode 100644 src/environment_data-inl.h diff --git a/src/commands/dump.cc b/src/commands/dump.cc index f4fa2af..60e90b3 100644 --- a/src/commands/dump.cc +++ b/src/commands/dump.cc @@ -1,5 +1,7 @@ #include "dump.h" +#include + #include "configure-inl.h" #include "coredumper/coredumper.h" #include "cpuprofiler/cpu_profiler.h" @@ -130,27 +132,20 @@ void AfterDumpFile(Isolate* isolate, string& filepath, string notify_type, filepath = ""; } -#define CLEAR_DATA \ - DebugT(module_type, env_data->thread_id(), "<%s> %s dump_data cleared.", \ - notify_type.c_str(), unique_key.c_str()); \ - delete dump_data; - #define CHECK_ERR(func) \ if (need_check) { \ func; \ if (err.Fail()) { \ DebugT(module_type, env_data->thread_id(), "<%s> %s error: %s", \ notify_type.c_str(), unique_key.c_str(), err.GetErrMessage()); \ - CLEAR_DATA; \ return; \ } \ } -void HandleAction(v8::Isolate* isolate, void* data, string notify_type, - bool need_check = true) { - BaseDumpData* dump_data = static_cast(data); - string traceid = dump_data->traceid; - DumpAction action = dump_data->action; +void HandleAction(v8::Isolate* isolate, std::unique_ptr data, + const string& notify_type, bool need_check = true) { + const string& traceid = data->traceid; + DumpAction action = data->action; EnvironmentData* env_data = EnvironmentData::GetCurrent(isolate); if (env_data == nullptr) { @@ -174,16 +169,11 @@ void HandleAction(v8::Isolate* isolate, void* data, string notify_type, // start run action switch (action) { case START_CPU_PROFILING: { - CpuProfilerDumpData* tmp = GetProfilingData( - isolate, data, notify_type, unique_key); - CpuProfiler::StartProfiling(isolate, tmp->title); - tmp->action = STOP_CPU_PROFILING; + CpuProfiler::StartProfiling(isolate, "xprofiler"); break; } case STOP_CPU_PROFILING: { - dump_data->run_once = true; - CpuProfilerDumpData* tmp = static_cast(data); - CpuProfiler::StopProfiling(isolate, tmp->title, + CpuProfiler::StopProfiling(isolate, "xprofiler", env_data->cpuprofile_filepath); AfterDumpFile(isolate, env_data->cpuprofile_filepath, notify_type, unique_key); @@ -199,15 +189,10 @@ void HandleAction(v8::Isolate* isolate, void* data, string notify_type, break; } case START_SAMPLING_HEAP_PROFILING: { - SamplingHeapProfilerDumpData* tmp = - GetProfilingData( - isolate, data, notify_type, unique_key); SamplingHeapProfiler::StartSamplingHeapProfiling(isolate); - tmp->action = STOP_SAMPLING_HEAP_PROFILING; break; } case STOP_SAMPLING_HEAP_PROFILING: { - dump_data->run_once = true; SamplingHeapProfiler::StopSamplingHeapProfiling( isolate, env_data->sampling_heapprofile_filepath); AfterDumpFile(isolate, env_data->sampling_heapprofile_filepath, @@ -217,14 +202,10 @@ void HandleAction(v8::Isolate* isolate, void* data, string notify_type, break; } case START_GC_PROFILING: { - GcProfilerDumpData* tmp = GetProfilingData( - isolate, data, notify_type, unique_key); GcProfiler::StartGCProfiling(isolate, env_data->gcprofile_filepath); - tmp->action = STOP_GC_PROFILING; break; } case STOP_GC_PROFILING: { - dump_data->run_once = true; GcProfiler::StopGCProfiling(isolate); AfterDumpFile(isolate, env_data->gcprofile_filepath, notify_type, unique_key); @@ -251,19 +232,13 @@ void HandleAction(v8::Isolate* isolate, void* data, string notify_type, action); break; } - - // clear dump_data - if (dump_data->run_once) { - CLEAR_DATA; - } } #undef CHECK_ERR -#undef CLEAR_DATA -template -T* CreateFinishDumpData(EnvironmentData* env_data) { - T* data = new T; +template +std::unique_ptr CreateFinishDumpData(EnvironmentData* env_data) { + std::unique_ptr data = std::make_unique(); data->traceid = "finish"; data->thread_id = env_data->thread_id(); data->action = action; @@ -279,24 +254,18 @@ void FinishSampling(Isolate* isolate, const char* reason) { ActionMap current; current.swap(*env_data->action_map()); - void* data = nullptr; + std::unique_ptr data; for (auto itor : current) { switch (itor.first) { case START_CPU_PROFILING: - data = static_cast( - CreateFinishDumpData( - env_data)); + data = CreateFinishDumpData(env_data); break; case START_SAMPLING_HEAP_PROFILING: - data = static_cast( - CreateFinishDumpData(env_data)); + data = CreateFinishDumpData(env_data); break; case START_GC_PROFILING: - data = static_cast( - CreateFinishDumpData( - env_data)); + data = CreateFinishDumpData(env_data); break; default: break; @@ -306,7 +275,7 @@ void FinishSampling(Isolate* isolate, const char* reason) { continue; } - HandleAction(isolate, data, reason, false); + HandleAction(isolate, std::move(data), reason, false); } } @@ -318,32 +287,63 @@ static void WaitForProfile(uint64_t profiling_time) { } } -static void NotifyJsThread(EnvironmentData* env_data, void* data) { +constexpr const char* GetNotifyType(InterruptKind kind) { + return kind == InterruptKind::kBusy ? "v8_request_interrupt" + : "uv_async_send"; +} + +static void NotifyJsThread(EnvironmentData* env_data, + std::unique_ptr data) { env_data->RequestInterrupt( - [data](EnvironmentData* env_data, InterruptKind kind) { - HandleAction(env_data->isolate(), data, - kind == InterruptKind::kBusy ? "v8_request_interrupt" - : "uv_async_send"); + [data = std::move(data)](EnvironmentData* env_data, + InterruptKind kind) mutable { + HandleAction(env_data->isolate(), std::move(data), GetNotifyType(kind)); }); } -static void ProfilingWatchDog(void* data) { - BaseDumpData* dump_data = static_cast(data); +class ProfilingWatchdog { + public: + static void Run(std::unique_ptr data) { + // self-destructive. + new ProfilingWatchdog(std::move(data)); + } - // sleep for profiling time - WaitForProfile(dump_data->profiling_time); + private: + static void ThreadMain(void* data) { + ProfilingWatchdog* watchdog = static_cast(data); + watchdog->ThreadEntry(); + } - // get environment - ThreadId thread_id = dump_data->thread_id; - EnvironmentRegistry* registry = ProcessData::Get()->environment_registry(); - EnvironmentRegistry::NoExitScope scope(registry); - EnvironmentData* env_data = registry->Get(thread_id); - if (env_data == nullptr) { - return; + ProfilingWatchdog(std::unique_ptr data) + : data_(std::move(data)), thread_(ThreadMain, this) { + thread_.detach(); } - NotifyJsThread(env_data, dump_data); -} + void ThreadEntry() { + // sleep for profiling time + WaitForProfile(data_->profiling_time); + + // get environment + ThreadId thread_id = data_->thread_id; + EnvironmentRegistry* registry = ProcessData::Get()->environment_registry(); + EnvironmentRegistry::NoExitScope scope(registry); + EnvironmentData* env_data = registry->Get(thread_id); + if (env_data == nullptr) { + delete this; + return; + } + + env_data->RequestInterrupt([this](EnvironmentData* env_data, + InterruptKind kind) mutable { + HandleAction(env_data->isolate(), std::move(data_), GetNotifyType(kind)); + + delete this; + }); + } + + std::unique_ptr data_; + std::thread thread_; +}; static string CreateFilepath(string prefix, string ext) { return GetLogDir() + GetSep() + "x-" + prefix + "-" + to_string(GetPid()) + @@ -355,8 +355,24 @@ static string CreateFilepath(string prefix, string ext) { func; \ if (err.Fail()) return result; -template -static json DoDumpAction(json command, string prefix, string ext, T* data, +constexpr DumpAction GetStopAction(DumpAction action) { + switch (action) { + case START_CPU_PROFILING: { + return STOP_CPU_PROFILING; + } + case START_SAMPLING_HEAP_PROFILING: { + return STOP_SAMPLING_HEAP_PROFILING; + } + case START_GC_PROFILING: { + return STOP_GC_PROFILING; + } + default: + abort(); + } +} + +template +static json DoDumpAction(json command, string prefix, string ext, XpfError& err) { json result; @@ -433,12 +449,13 @@ static json DoDumpAction(json command, string prefix, string ext, T* data, if (err.Fail()) return result; // set action callback data + std::unique_ptr data = std::make_unique(); data->traceid = traceid; data->thread_id = thread_id; data->action = action; - // send data - NotifyJsThread(env_data, data); + // send (copied) data + NotifyJsThread(env_data, std::make_unique(*data)); if (!profiling) return result; @@ -446,10 +463,9 @@ static json DoDumpAction(json command, string prefix, string ext, T* data, json options = command["options"]; int profiling_time = GetJsonValue(options, "profiling_time", err); if (err.Success()) { - data->run_once = false; + data->action = GetStopAction(action); data->profiling_time = profiling_time; - uv_thread_create(env_data->uv_profiling_callback_thread(), - ProfilingWatchDog, (void*)data); + ProfilingWatchdog::Run(std::move(data)); } else { err = XpfError::Succeed(); } @@ -457,46 +473,40 @@ static json DoDumpAction(json command, string prefix, string ext, T* data, return result; } -#define V(func, data_type, action, profiling, prefix, ext) \ - COMMAND_CALLBACK(func) { \ - data_type* data = new data_type; \ - XpfError err; \ - json result = DoDumpAction(command, #prefix, \ - #ext, data, err); \ - if (err.Fail()) { \ - error(format("%s", err.GetErrMessage())); \ - delete data; \ - return; \ - } \ - success(result); \ +#define V(func, action, profiling, prefix, ext) \ + COMMAND_CALLBACK(func) { \ + XpfError err; \ + json result = \ + DoDumpAction(command, #prefix, #ext, err); \ + if (err.Fail()) { \ + error(format("%s", err.GetErrMessage())); \ + return; \ + } \ + success(result); \ } // cpu profiling -V(StartCpuProfiling, CpuProfilerDumpData, START_CPU_PROFILING, true, cpuprofile, - cpuprofile) -V(StopCpuProfiling, CpuProfilerDumpData, STOP_CPU_PROFILING, false, cpuprofile, - cpuprofile) +V(StartCpuProfiling, START_CPU_PROFILING, true, cpuprofile, cpuprofile) +V(StopCpuProfiling, STOP_CPU_PROFILING, false, cpuprofile, cpuprofile) // sampling heap profiling -V(StartSamplingHeapProfiling, SamplingHeapProfilerDumpData, - START_SAMPLING_HEAP_PROFILING, true, heapprofile, heapprofile) -V(StopSamplingHeapProfiling, SamplingHeapProfilerDumpData, - STOP_SAMPLING_HEAP_PROFILING, false, heapprofile, heapprofile) +V(StartSamplingHeapProfiling, START_SAMPLING_HEAP_PROFILING, true, heapprofile, + heapprofile) +V(StopSamplingHeapProfiling, STOP_SAMPLING_HEAP_PROFILING, false, heapprofile, + heapprofile) // gc profiling -V(StartGcProfiling, GcProfilerDumpData, START_GC_PROFILING, true, gcprofile, - gcprofile) -V(StopGcProfiling, GcProfilerDumpData, STOP_GC_PROFILING, false, gcprofile, - gcprofile) +V(StartGcProfiling, START_GC_PROFILING, true, gcprofile, gcprofile) +V(StopGcProfiling, STOP_GC_PROFILING, false, gcprofile, gcprofile) // heapdump -V(Heapdump, HeapdumpDumpData, HEAPDUMP, false, heapdump, heapsnapshot) +V(Heapdump, HEAPDUMP, false, heapdump, heapsnapshot) // dynamic report -V(GetNodeReport, NodeReportDumpData, NODE_REPORT, false, diagreport, diag) +V(GetNodeReport, NODE_REPORT, false, diagreport, diag) // generate coredump -V(GenerateCoredump, CoreDumpData, COREDUMP, false, coredump, core) +V(GenerateCoredump, COREDUMP, false, coredump, core) #undef V diff --git a/src/commands/dump.h b/src/commands/dump.h index e51af5b..f7e69af 100644 --- a/src/commands/dump.h +++ b/src/commands/dump.h @@ -25,7 +25,7 @@ using ActionMap = std::unordered_map; using ConflictMap = std::unordered_map>; using DependentMap = std::unordered_map; -struct BaseDumpData { +struct DumpData { std::string traceid; DumpAction action; ThreadId thread_id; @@ -33,20 +33,6 @@ struct BaseDumpData { bool run_once = true; }; -struct CpuProfilerDumpData : BaseDumpData { - std::string title = "xprofiler"; -}; - -struct HeapdumpDumpData : BaseDumpData {}; - -struct SamplingHeapProfilerDumpData : BaseDumpData {}; - -struct GcProfilerDumpData : BaseDumpData {}; - -struct NodeReportDumpData : BaseDumpData {}; - -struct CoreDumpData : BaseDumpData {}; - void FinishSampling(v8::Isolate* isolate, const char* reason); COMMAND_CALLBACK(StartCpuProfiling); diff --git a/src/environment_data-inl.h b/src/environment_data-inl.h new file mode 100644 index 0000000..95fb8aa --- /dev/null +++ b/src/environment_data-inl.h @@ -0,0 +1,33 @@ +#ifndef SRC_ENVIRONMENT_DATA_INL_H_ +#define SRC_ENVIRONMENT_DATA_INL_H_ + +#include "environment_data.h" + +namespace xprofiler { + +template +void EnvironmentData::RequestInterrupt(Fn&& callback) { + { + Mutex::ScopedLock lock(interrupt_mutex_); + std::unique_ptr interrupt_callback = + std::make_unique>(std::move(callback)); + interrupt_callback->next_.swap(interrupt_requests_); + interrupt_requests_.swap(interrupt_callback); + } + isolate_->RequestInterrupt(InterruptBusyCallback, this); + uv_async_send(&interrupt_async_); +} + +template +EnvironmentData::InterruptCallbackImpl::InterruptCallbackImpl(Fn&& callback) + : callback_(std::move(callback)) {} + +template +void EnvironmentData::InterruptCallbackImpl::Call(EnvironmentData* env_data, + InterruptKind kind) { + callback_(env_data, kind); +} + +} // namespace xprofiler + +#endif // SRC_ENVIRONMENT_DATA_INL_H_ diff --git a/src/environment_data.cc b/src/environment_data.cc index d6d7c02..9019196 100644 --- a/src/environment_data.cc +++ b/src/environment_data.cc @@ -101,15 +101,6 @@ void EnvironmentData::SendCollectStatistics() { uv_async_send(&statistics_async_); } -void EnvironmentData::RequestInterrupt(InterruptCallback interrupt) { - { - Mutex::ScopedLock lock(interrupt_mutex_); - interrupt_requests_.push_back(interrupt); - } - isolate_->RequestInterrupt(InterruptBusyCallback, this); - uv_async_send(&interrupt_async_); -} - void EnvironmentData::AddGCEpilogueCallback(Nan::GCEpilogueCallback callback, v8::GCType gc_type_filter) { gc_epilogue_callbacks_.push_back(callback); @@ -146,14 +137,15 @@ uint64_t EnvironmentData::GetUptime() const { // static void EnvironmentData::InterruptBusyCallback(v8::Isolate* isolate, void* data) { EnvironmentData* env_data = static_cast(data); - std::list requests; + std::unique_ptr requests; { Mutex::ScopedLock lock(env_data->interrupt_mutex_); requests.swap(env_data->interrupt_requests_); } - for (auto it : requests) { - it(env_data, InterruptKind::kBusy); + while (requests != nullptr) { + requests->Call(env_data, InterruptKind::kBusy); + requests = std::move(requests->next_); } } @@ -161,14 +153,15 @@ void EnvironmentData::InterruptBusyCallback(v8::Isolate* isolate, void* data) { void EnvironmentData::InterruptIdleCallback(uv_async_t* handle) { EnvironmentData* env_data = ContainerOf(&EnvironmentData::interrupt_async_, handle); - std::list requests; + std::unique_ptr requests; { Mutex::ScopedLock lock(env_data->interrupt_mutex_); requests.swap(env_data->interrupt_requests_); } - for (auto it : requests) { - it(env_data, InterruptKind::kIdle); + while (requests != nullptr) { + requests->Call(env_data, InterruptKind::kBusy); + requests = std::move(requests->next_); } } diff --git a/src/environment_data.h b/src/environment_data.h index 0f6dba3..1a95a4b 100644 --- a/src/environment_data.h +++ b/src/environment_data.h @@ -22,8 +22,6 @@ enum class InterruptKind { kIdle, }; -using InterruptCallback = std::function; - class EnvironmentData { public: static EnvironmentData* GetCurrent(v8::Isolate* isolate); @@ -40,7 +38,8 @@ class EnvironmentData { void SendCollectStatistics(); - void RequestInterrupt(InterruptCallback interrupt); + template + void RequestInterrupt(Fn&& callback); void AddGCEpilogueCallback(Nan::GCEpilogueCallback callback, v8::GCType gc_type_filter = v8::kGCTypeAll); void RemoveGCEpilogueCallback(Nan::GCEpilogueCallback callback); @@ -68,9 +67,6 @@ class EnvironmentData { std::unique_ptr cpu_profiler; // dump action - inline uv_thread_t* uv_profiling_callback_thread() { - return &uv_profiling_callback_thread_; - }; inline ActionMap* action_map() { return &action_map_; } std::string cpuprofile_filepath = ""; std::string sampling_heapprofile_filepath = ""; @@ -80,6 +76,29 @@ class EnvironmentData { std::string coredump_filepath = ""; private: + class InterruptCallback { + public: + inline InterruptCallback() {} + + virtual ~InterruptCallback() = default; + virtual void Call(EnvironmentData* env_data, InterruptKind kind) = 0; + + private: + std::unique_ptr next_; + friend class EnvironmentData; + }; + + template + class InterruptCallbackImpl final : public InterruptCallback { + public: + InterruptCallbackImpl(Fn&& callback); + void Call(EnvironmentData* env_data, InterruptKind kind) override; + + private: + Fn callback_; + friend class EnvironmentData; + }; + static void AtExit(void* arg); template static void CloseCallback(uv_handle_t* handle); @@ -94,7 +113,6 @@ class EnvironmentData { v8::Isolate* isolate_; uv_loop_t* loop_; uv_async_t statistics_async_; - uv_thread_t uv_profiling_callback_thread_; bool is_main_thread_ = false; /* We don't have a native method to get the uint64_t thread id. @@ -104,7 +122,7 @@ class EnvironmentData { std::string node_version_ = ""; Mutex interrupt_mutex_; - std::list interrupt_requests_; + std::unique_ptr interrupt_requests_; uv_async_t interrupt_async_; std::list gc_epilogue_callbacks_; std::list gc_prologue_callbacks_; @@ -122,4 +140,6 @@ class EnvironmentData { } // namespace xprofiler +#include "environment_data-inl.h" + #endif /* XPROFILER_SRC_ENVIRONMENT_DATA_H */