Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
gleocadie committed Jun 17, 2024
1 parent 37e2792 commit 0a89c2e
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ TimerCreateCpuProfiler::TimerCreateCpuProfiler(
_samplingInterval{pConfiguration->GetCpuProfilingInterval()}
{
Log::Info("Cpu profiling interval: ", _samplingInterval.count(), "ms");

Instance = this;
}

TimerCreateCpuProfiler::~TimerCreateCpuProfiler()
Expand All @@ -43,138 +41,72 @@ TimerCreateCpuProfiler::~TimerCreateCpuProfiler()

void TimerCreateCpuProfiler::RegisterThread(std::shared_ptr<ManagedThreadInfo> 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<std::chrono::nanoseconds>(_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<ManagedThreadInfo> 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()
{
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)
{
Expand Down Expand Up @@ -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<std::chrono::nanoseconds>(_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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@

#pragma once

#include "IService.h"
#include "ManagedThreadInfo.h"
#include "CallstackProvider.h"
#include "ManagedThreadInfo.h"
#include "ServiceBase.h"

#include <signal.h>

#include <atomic>
#include <chrono>
#include <memory>
#include <shared_mutex>
#include <unordered_set>

class IConfiguration;
Expand All @@ -21,7 +22,7 @@ class ProfilerSignalManager;
class CpuTimeProvider;
class CallstackProvider;

class TimerCreateCpuProfiler : public IService
class TimerCreateCpuProfiler : public ServiceBase
{
public:
TimerCreateCpuProfiler(
Expand All @@ -37,14 +38,16 @@ class TimerCreateCpuProfiler : public IService
void UnregisterThread(std::shared_ptr<ManagedThreadInfo> 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
{
Expand All @@ -59,4 +62,5 @@ class TimerCreateCpuProfiler : public IService
CallstackProvider _callstackProvider;
std::atomic<ServiceState> _serviceState;
std::chrono::milliseconds _samplingInterval;
std::shared_mutex _registerLock;
};
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,9 @@ bool ServiceBase::Stop()
}

return result;
}
}

ServiceBase::State ServiceBase::GetState() const
{
return _currentState;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class ServiceBase : public IService
virtual bool StartImpl() = 0;
virtual bool StopImpl() = 0;

private:
enum class State
{
Init,
Expand All @@ -29,6 +28,10 @@ class ServiceBase : public IService
Stopped
};

State GetState() const;

private:

friend std::string to_string(ServiceBase::State);
std::atomic<State> _currentState;
};

0 comments on commit 0a89c2e

Please sign in to comment.