From c18b1f6d80e98570ca0d1f10dab3087e3157b28a Mon Sep 17 00:00:00 2001 From: sanchda <838104+sanchda@users.noreply.github.com> Date: Thu, 30 Jan 2025 16:44:03 +0000 Subject: [PATCH] Update --- .../profiling/stack_v2/include/sampler.hpp | 6 +- .../profiling/stack_v2/src/sampler.cpp | 68 +++++++++++-------- .../profiling/stack_v2/src/stack_v2.cpp | 6 +- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/ddtrace/internal/datadog/profiling/stack_v2/include/sampler.hpp b/ddtrace/internal/datadog/profiling/stack_v2/include/sampler.hpp index 7050b6fcaa4..2493b35ca25 100644 --- a/ddtrace/internal/datadog/profiling/stack_v2/include/sampler.hpp +++ b/ddtrace/internal/datadog/profiling/stack_v2/include/sampler.hpp @@ -25,9 +25,6 @@ class Sampler // Parameters uint64_t echion_frame_cache_size = g_default_echion_frame_cache_size; - // Helper function; implementation of the echion sampling thread - void sampling_thread(const uint64_t seq_num); - // This is a singleton, so no public constructor Sampler(); @@ -37,7 +34,7 @@ class Sampler public: // Singleton instance static Sampler& get(); - void start(); + bool start(); void stop(); void register_thread(uint64_t id, uint64_t native_id, const char* name); void unregister_thread(uint64_t id); @@ -46,6 +43,7 @@ class Sampler PyObject* _asyncio_scheduled_tasks, PyObject* _asyncio_eager_tasks); void link_tasks(PyObject* parent, PyObject* child); + void sampling_thread(const uint64_t seq_num); // The Python side dynamically adjusts the sampling rate based on overhead, so we need to be able to update our // own intervals accordingly. Rather than a preemptive measure, we assume the rate is ~fairly stable and just diff --git a/ddtrace/internal/datadog/profiling/stack_v2/src/sampler.cpp b/ddtrace/internal/datadog/profiling/stack_v2/src/sampler.cpp index c365c36e9dc..53a4492c3b2 100644 --- a/ddtrace/internal/datadog/profiling/stack_v2/src/sampler.cpp +++ b/ddtrace/internal/datadog/profiling/stack_v2/src/sampler.cpp @@ -10,44 +10,47 @@ using namespace Datadog; - // Helper class for spawning a std::thread with control over its default stack size #ifdef __linux__ -#include #include -template -pthread_t create_thread_with_stack(size_t stack_size, Function&& f, Args&&... args) { +#include + +struct ThreadArgs +{ + Sampler* sampler; + uint64_t seq_num; +}; + +void* +call_sampling_thread(void* args) +{ + ThreadArgs* thread_args = static_cast(args); + Sampler* sampler = thread_args->sampler; + sampler->sampling_thread(thread_args->seq_num); + return nullptr; +} + +pthread_t +create_thread_with_stack(size_t stack_size, Sampler* sampler, uint64_t seq_num) +{ pthread_attr_t attr; - pthread_attr_init(&attr); + if (pthread_attr_init(&attr) != 0) { + return 0; + } if (stack_size > 0) { pthread_attr_setstacksize(&attr, stack_size); } - // DAS: I think std::bind here is necessary? - pthread_t thread; - auto* thread_func = new auto( - std::bind(std::forward(f), std::forward(args)...) - ); - - int ret = pthread_create( - &thread, - &attr, - [](void* arg) -> void* { - auto* func = static_cast(arg); - (*func)(); - delete func; - return nullptr; - }, - thread_func - ); + pthread_t thread_id; + ThreadArgs thread_args = { sampler, seq_num }; + int ret = pthread_create(&thread_id, &attr, call_sampling_thread, &thread_args); pthread_attr_destroy(&attr); if (ret != 0) { - delete thread_func; - throw std::runtime_error("Failed to create thread"); + return 0; } - return thread; + return thread_id; } #endif @@ -175,7 +178,7 @@ Sampler::unregister_thread(uint64_t id) thread_info_map.erase(id); } -void +bool Sampler::start() { static std::once_flag once; @@ -188,11 +191,18 @@ Sampler::start() // We might as well get the default stack size and use that rlimit stack_sz = {}; getrlimit(RLIMIT_STACK, &stack_sz); - create_thread_with_stack(stack_sz.rlim_cur, &Sampler::sampling_thread, this, ++thread_seq_num); // leak the thread + if (create_thread_with_stack(stack_sz.rlim_cur, this, ++thread_seq_num) == 0) { + return false; + } #else - std::thread t(&Sampler::sampling_thread, this, ++thread_seq_num); - t.detach(); + try { + std::thread t(&Sampler::sampling_thread, this, ++thread_seq_num); + t.detach(); + } catch (const std::exception& e) { + return false; + } #endif + return true; } void diff --git a/ddtrace/internal/datadog/profiling/stack_v2/src/stack_v2.cpp b/ddtrace/internal/datadog/profiling/stack_v2/src/stack_v2.cpp index c56b5524bcd..2e562943333 100644 --- a/ddtrace/internal/datadog/profiling/stack_v2/src/stack_v2.cpp +++ b/ddtrace/internal/datadog/profiling/stack_v2/src/stack_v2.cpp @@ -21,8 +21,10 @@ _stack_v2_start(PyObject* self, PyObject* args, PyObject* kwargs) } Sampler::get().set_interval(min_interval_s); - Sampler::get().start(); - Py_RETURN_NONE; + if (Sampler::get().start()) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; } // Bypasses the old-style cast warning with an unchecked helper function