From 0a89c2e4ca3714d01c3c3a514e0820b5ac7623a4 Mon Sep 17 00:00:00 2001 From: Gregory LEOCADIE Date: Wed, 12 Jun 2024 16:49:47 +0200 Subject: [PATCH] WIP --- .../TimerCreateCpuProfiler.cpp | 154 +++++++----------- .../TimerCreateCpuProfiler.h | 14 +- .../Datadog.Profiler.Native/ServiceBase.cpp | 7 +- .../Datadog.Profiler.Native/ServiceBase.h | 5 +- 4 files changed, 80 insertions(+), 100 deletions(-) diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/TimerCreateCpuProfiler.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/TimerCreateCpuProfiler.cpp index 8a05d3437ac4..485aa1c7977b 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/TimerCreateCpuProfiler.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/TimerCreateCpuProfiler.cpp @@ -32,8 +32,6 @@ TimerCreateCpuProfiler::TimerCreateCpuProfiler( _samplingInterval{pConfiguration->GetCpuProfilingInterval()} { Log::Info("Cpu profiling interval: ", _samplingInterval.count(), "ms"); - - Instance = this; } TimerCreateCpuProfiler::~TimerCreateCpuProfiler() @@ -43,64 +41,27 @@ TimerCreateCpuProfiler::~TimerCreateCpuProfiler() void TimerCreateCpuProfiler::RegisterThread(std::shared_ptr threadInfo) { - if (_serviceState != ServiceState::Started) - { - return; - } - - auto tid = threadInfo->GetOsThreadId(); - Log::Debug("Creating timer for thread ", tid); - - struct sigevent sev; - sev.sigev_value.sival_ptr = nullptr; - sev.sigev_signo = _pSignalManager->GetSignal(); - sev.sigev_notify = SIGEV_THREAD_ID; - ((int*)&sev.sigev_notify)[1] = tid; - - // Use raw syscalls, since libc wrapper allows only predefined clocks - clockid_t clock = ((~tid) << 3) | 6; // CPUCLOCK_SCHED | CPUCLOCK_PERTHREAD_MASK thread_cpu_clock(tid); - int timerId; - if (syscall(__NR_timer_create, clock, &sev, &timerId) < 0) - { - Log::Error("Call to timer_create failed for thread ", tid); - return; - } - - auto oldTimerId = threadInfo->SetTimerId(timerId); + std::shared_lock(_registerLock); - // In case of SSI: - // There is a race when Start() and RegisterThread : - // The thread can be added while lookping over the in the managed threads list - // and a call being made to RegisterThread - // In that case, just keep the first timer id and delete the other - if (oldTimerId != -1) + if (GetState() != SeriveBase::State::Started) { - // delete the newly created timer - syscall(__NR_timer_delete, timerId); - threadInfo->SetTimerId(oldTimerId); return; } - std::int32_t _interval = std::chrono::duration_cast(_samplingInterval).count(); - struct itimerspec ts; - ts.it_interval.tv_sec = (time_t)(_interval / 1000000000); - ts.it_interval.tv_nsec = _interval % 1000000000; - ts.it_value = ts.it_interval; - syscall(__NR_timer_settime, timerId, 0, &ts, nullptr); + RegisterThreadImpl(threadInfo.get()); } void TimerCreateCpuProfiler::UnregisterThread(std::shared_ptr threadInfo) { - if (_serviceState != ServiceState::Started) - { - Log::Debug("timer_create-based cpu profiler is not started. Cannot unregister thread."); - return; - } + std::shared_lock lock(_registerLock); auto timerId = threadInfo->SetTimerId(-1); - Log::Debug("Unregister timer for thread ", threadInfo->GetOsThreadId()); - syscall(__NR_timer_delete, timerId); + if (timerId != -1) + { + Log::Debug("Unregister timer for thread ", threadInfo->GetOsThreadId()); + syscall(__NR_timer_delete, timerId); + } } const char* TimerCreateCpuProfiler::GetName() @@ -108,73 +69,44 @@ const char* TimerCreateCpuProfiler::GetName() return "timer_create-based Cpu Profiler"; } -bool TimerCreateCpuProfiler::Start() +bool TimerCreateCpuProfiler::StartImpl() { - if (_serviceState.exchange(ServiceState::Started) == ServiceState::Started) - { - Log::Debug("timer_create-based Cpu profiler has already been started."); - return true; - } - // If the signal is higjacked, what to do? auto registered = _pSignalManager->RegisterHandler(TimerCreateCpuProfiler::CollectStackSampleSignalHandler); - auto it = _pManagedThreadsList->CreateIterator(); - - ManagedThreadInfo* first = nullptr; - - auto current = _pManagedThreadsList->LoopNext(it); - while (current != nullptr && current.get() != first) + if (registered) { - if (first == nullptr) - { - first = current.get(); - } - - if (current->GetTimerId() != -1) - { - continue; - } - - RegisterThread(current); - current = _pManagedThreadsList->LoopNext(it); + std::unique_lock lock(_registerLock); + Instance = this; + + // Create and start timer for all threads + _pManagedThreadsList->ForEach([this](ManagedThreadList* thread) { RegisterImpl(thread); }); } return registered; } -bool TimerCreateCpuProfiler::Stop() +bool TimerCreateCpuProfiler::StopImpl() { - auto old = _serviceState.exchange(ServiceState::Stopped); - if (old == ServiceState::Initialized) - { - Log::Debug("Cannot call Stop on the timer_create-based Cpu profiler since it was not started yet."); - return false; - } - - if (old == ServiceState::Stopped) - { - Log::Debug("timer_create-based Cpu profiler was already stopped."); - return true; - } - _pSignalManager->UnRegisterHandler(); + Instance = nullptr; return true; } bool TimerCreateCpuProfiler::CollectStackSampleSignalHandler(int sig, siginfo_t* info, void* ucontext) { - return Instance->Collect(ucontext); + auto instance = Instance; + if (instance == nullptr) + { + return; + } + + return instance->Collect(ucontext); } bool TimerCreateCpuProfiler::Collect(void* ctx) { - if (_serviceState == ServiceState::Stopped) - { - return false; - } - auto threadInfo = ManagedThreadInfo::CurrentThreadInfo; if (threadInfo == nullptr) { @@ -214,3 +146,39 @@ bool TimerCreateCpuProfiler::Collect(void* ctx) return true; } + +void TimerCreateCpuProfiler::RegisterThreadImpl(ManagedThreadInfo* threadInfo) +{ + auto timerId = threadInfo->GetTimerId(); + auto tid = threadInfo->GetOsThreadId(); + + if (timerId != -1) + { + // already register (lost the race) + Log::Debug("Timer was already created for thread ", tid); + return; + } + + Log::Debug("Creating timer for thread ", tid); + + struct sigevent sev; + sev.sigev_value.sival_ptr = nullptr; + sev.sigev_signo = _pSignalManager->GetSignal(); + sev.sigev_notify = SIGEV_THREAD_ID; + ((int*)&sev.sigev_notify)[1] = tid; + + // Use raw syscalls, since libc wrapper allows only predefined clocks + clockid_t clock = ((~tid) << 3) | 6; // CPUCLOCK_SCHED | CPUCLOCK_PERTHREAD_MASK thread_cpu_clock(tid); + if (syscall(__NR_timer_create, clock, &sev, &timerId) < 0) + { + Log::Error("Call to timer_create failed for thread ", tid); + return; + } + + std::int32_t _interval = std::chrono::duration_cast(_samplingInterval).count(); + struct itimerspec ts; + ts.it_interval.tv_sec = (time_t)(_interval / 1000000000); + ts.it_interval.tv_nsec = _interval % 1000000000; + ts.it_value = ts.it_interval; + syscall(__NR_timer_settime, timerId, 0, &ts, nullptr); +} diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/TimerCreateCpuProfiler.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/TimerCreateCpuProfiler.h index 442848bc87c6..0096662293a8 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/TimerCreateCpuProfiler.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Linux/TimerCreateCpuProfiler.h @@ -3,15 +3,16 @@ #pragma once -#include "IService.h" -#include "ManagedThreadInfo.h" #include "CallstackProvider.h" +#include "ManagedThreadInfo.h" +#include "ServiceBase.h" #include #include #include #include +#include #include class IConfiguration; @@ -21,7 +22,7 @@ class ProfilerSignalManager; class CpuTimeProvider; class CallstackProvider; -class TimerCreateCpuProfiler : public IService +class TimerCreateCpuProfiler : public ServiceBase { public: TimerCreateCpuProfiler( @@ -37,14 +38,16 @@ class TimerCreateCpuProfiler : public IService void UnregisterThread(std::shared_ptr threadInfo); const char* GetName() override; - bool Start() override; - bool Stop() override; private: static bool CollectStackSampleSignalHandler(int sig, siginfo_t* info, void* ucontext); static TimerCreateCpuProfiler* Instance; bool Collect(void* ucontext); + void RegisterThreadImpl(ManagedThreadInfo* thread); + + bool StartImpl() override; + bool StopImp() override; enum class ServiceState { @@ -59,4 +62,5 @@ class TimerCreateCpuProfiler : public IService CallstackProvider _callstackProvider; std::atomic _serviceState; std::chrono::milliseconds _samplingInterval; + std::shared_mutex _registerLock; }; \ No newline at end of file diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ServiceBase.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ServiceBase.cpp index e7e1f094341f..c92753c7fb71 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ServiceBase.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ServiceBase.cpp @@ -77,4 +77,9 @@ bool ServiceBase::Stop() } return result; -} \ No newline at end of file +} + +ServiceBase::State ServiceBase::GetState() const +{ + return _currentState; +} diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ServiceBase.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ServiceBase.h index f7d4c152a6ae..f64382486849 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ServiceBase.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ServiceBase.h @@ -19,7 +19,6 @@ class ServiceBase : public IService virtual bool StartImpl() = 0; virtual bool StopImpl() = 0; -private: enum class State { Init, @@ -29,6 +28,10 @@ class ServiceBase : public IService Stopped }; + State GetState() const; + +private: + friend std::string to_string(ServiceBase::State); std::atomic _currentState; };