Skip to content

Commit 3954635

Browse files
committed
Otel allocation profiling POC
1 parent 8852113 commit 3954635

File tree

9 files changed

+623
-34
lines changed

9 files changed

+623
-34
lines changed

CMakeLists.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,8 @@ target_link_libraries(
280280
PRIVATE
281281
"$<$<AND:$<C_COMPILER_ID:GNU>,$<CONFIG:SanitizedDebug>>:-Wl,-Bstatic;-lubsan;-lasan;-lstdc++;-Wl,-Bdynamic>"
282282
)
283-
target_link_libraries(dd_profiling-embedded PUBLIC dl pthread rt absl::base absl::str_format)
283+
target_link_libraries(dd_profiling-embedded PUBLIC dl pthread rt absl::base absl::str_format
284+
systemtap)
284285

285286
set(LIBDD_PROFILING_EMBEDDED_OBJECT "${CMAKE_BINARY_DIR}/libdd_profiling-embedded.o")
286287
set(LIBDD_PROFILING_EMBEDDED_HASH_HEADER "${CMAKE_BINARY_DIR}/libdd_profiling-embedded_hash.h")
@@ -365,7 +366,8 @@ target_include_directories(dd_profiling-static PUBLIC ${CMAKE_SOURCE_DIR}/includ
365366
set_target_properties(dd_profiling-static
366367
PROPERTIES PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/include/lib/dd_profiling.h")
367368
target_link_libraries(dd_profiling-static PRIVATE ddprof_exe_object)
368-
target_link_libraries(dd_profiling-static PUBLIC dl pthread rt absl::base absl::str_format)
369+
target_link_libraries(dd_profiling-static PUBLIC dl pthread rt absl::base absl::str_format
370+
systemtap)
369371

370372
if(USE_LOADER)
371373
# When using a loader, libdd_profiling.so is a loader that embeds both libdd_profiling-embedded.so
@@ -398,7 +400,8 @@ else()
398400
PRIVATE
399401
"$<$<AND:$<C_COMPILER_ID:GNU>,$<CONFIG:SanitizedDebug>>:-Wl,-Bstatic;-lubsan;-lasan;-lstdc++;-Wl,-Bdynamic>"
400402
)
401-
target_link_libraries(dd_profiling-shared PUBLIC dl pthread rt absl::base absl::str_format)
403+
target_link_libraries(dd_profiling-shared PUBLIC dl pthread rt absl::base absl::str_format
404+
systemtap)
402405
endif()
403406

404407
target_static_libcxx(dd_profiling-shared)

include/lib/allocation_tracker.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class AllocationTracker {
3535

3636
enum AllocationTrackingFlags {
3737
kTrackDeallocations = 0x1,
38-
kDeterministicSampling = 0x2
38+
kDeterministicSampling = 0x2,
39+
kOtelProfilerMode = 0x4,
3940
};
4041

4142
struct IntervalTimerCheck {
@@ -101,9 +102,8 @@ class AllocationTracker {
101102

102103
uint64_t next_sample_interval(std::minstd_rand &gen) const;
103104

104-
DDRes init(uint64_t mem_profile_interval, bool deterministic_sampling,
105-
bool track_deallocations, uint32_t stack_sample_size,
106-
const RingBufferInfo &ring_buffer,
105+
DDRes init(uint64_t mem_profile_interval, uint32_t flags,
106+
uint32_t stack_sample_size, const RingBufferInfo &ring_buffer,
107107
const IntervalTimerCheck &timer_check);
108108
void free();
109109

@@ -141,6 +141,7 @@ class AllocationTracker {
141141
uint32_t _stack_sample_size;
142142
PEvent _pevent;
143143
bool _deterministic_sampling;
144+
bool _otel_profiler_mode;
144145

145146
AddressBitset _allocated_address_set;
146147
IntervalTimerCheck _interval_timer_check;

src/lib/allocation_tracker.cc

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <atomic>
2222
#include <cassert>
2323
#include <cstdlib>
24+
#include <sys/sdt.h>
2425
#include <unistd.h>
2526

2627
namespace ddprof {
@@ -100,45 +101,49 @@ DDRes AllocationTracker::allocation_tracking_init(
100101
DDRES_RETURN_ERROR_LOG(DD_WHAT_UKNW, "Allocation profiler already started");
101102
}
102103

103-
DDRES_CHECK_FWD(instance->init(allocation_profiling_rate,
104-
flags & kDeterministicSampling,
105-
flags & kTrackDeallocations, stack_sample_size,
106-
ring_buffer, timer_check));
104+
DDRES_CHECK_FWD(instance->init(allocation_profiling_rate, flags,
105+
stack_sample_size, ring_buffer, timer_check));
107106
_instance = instance;
108107

109108
state.init(true, flags & kTrackDeallocations);
110109

111110
return {};
112111
}
113112

114-
DDRes AllocationTracker::init(uint64_t mem_profile_interval,
115-
bool deterministic_sampling,
116-
bool track_deallocations,
113+
DDRes AllocationTracker::init(uint64_t mem_profile_interval, uint32_t flags,
117114
uint32_t stack_sample_size,
118115
const RingBufferInfo &ring_buffer,
119116
const IntervalTimerCheck &timer_check) {
120117
_sampling_interval = mem_profile_interval;
121-
_deterministic_sampling = deterministic_sampling;
118+
_deterministic_sampling = flags & kDeterministicSampling;
122119
_stack_sample_size = stack_sample_size;
123-
if (ring_buffer.ring_buffer_type !=
124-
static_cast<int>(RingBufferType::kMPSCRingBuffer)) {
125-
return ddres_error(DD_WHAT_PERFRB);
120+
_otel_profiler_mode = flags & kOtelProfilerMode;
121+
122+
if (!_otel_profiler_mode) {
123+
if (ring_buffer.ring_buffer_type !=
124+
static_cast<int>(RingBufferType::kMPSCRingBuffer)) {
125+
return ddres_error(DD_WHAT_PERFRB);
126+
}
127+
128+
DDRES_CHECK_FWD(ddprof::ring_buffer_attach(ring_buffer, &_pevent));
129+
130+
const auto &rb = _pevent.rb;
131+
if (rb.tsc_available) {
132+
TscClock::init(TscClock::CalibrationParams{
133+
.offset = TscClock::time_point{TscClock::duration{rb.time_zero}},
134+
.mult = rb.time_mult,
135+
.shift = rb.time_shift});
136+
}
137+
PerfClock::init(static_cast<PerfClockSource>(rb.perf_clock_source));
138+
} else {
139+
PerfClock::init(PerfClockSource::kClockMonotonic);
126140
}
127-
if (track_deallocations) {
141+
142+
if (flags & kTrackDeallocations) {
128143
// 16 times as we want to probability of collision to be low enough
129144
_allocated_address_set = AddressBitset(liveallocation::kMaxTracked *
130145
k_ratio_max_elt_to_bitset_size);
131146
}
132-
DDRES_CHECK_FWD(ddprof::ring_buffer_attach(ring_buffer, &_pevent));
133-
134-
const auto &rb = _pevent.rb;
135-
if (rb.tsc_available) {
136-
TscClock::init(TscClock::CalibrationParams{
137-
.offset = TscClock::time_point{TscClock::duration{rb.time_zero}},
138-
.mult = rb.time_mult,
139-
.shift = rb.time_shift});
140-
}
141-
PerfClock::init(static_cast<PerfClockSource>(rb.perf_clock_source));
142147

143148
_interval_timer_check = timer_check;
144149
if (_interval_timer_check.is_set()) {
@@ -282,6 +287,7 @@ void AllocationTracker::track_deallocation(uintptr_t addr,
282287
DDRes AllocationTracker::push_lost_sample(MPSCRingBufferWriter &writer,
283288
TrackerThreadLocalState &tl_state,
284289
bool &notify_needed) {
290+
DDPROF_DCHECK_FATAL(!_otel_profiler_mode);
285291
auto lost_count = _state.lost_count.exchange(0, std::memory_order_acq_rel);
286292
if (lost_count == 0) {
287293
return {};
@@ -322,6 +328,12 @@ DDRes AllocationTracker::push_lost_sample(MPSCRingBufferWriter &writer,
322328
// Return true if consumer should be notified
323329
DDRes AllocationTracker::push_clear_live_allocation(
324330
TrackerThreadLocalState &tl_state) {
331+
if (_otel_profiler_mode) {
332+
// NOLINTNEXTLINE
333+
DTRACE_PROBE(ddprof, clear_live_allocations);
334+
check_timer(PerfClock::now(), tl_state);
335+
return {};
336+
}
325337
MPSCRingBufferWriter writer{&_pevent.rb};
326338
bool timeout = false;
327339

@@ -361,6 +373,13 @@ DDRes AllocationTracker::push_clear_live_allocation(
361373

362374
DDRes AllocationTracker::push_dealloc_sample(
363375
uintptr_t addr, TrackerThreadLocalState &tl_state) {
376+
if (_otel_profiler_mode) {
377+
// NOLINTNEXTLINE
378+
DTRACE_PROBE1(ddprof, deallocation, addr);
379+
check_timer(PerfClock::now(), tl_state);
380+
return {};
381+
}
382+
364383
MPSCRingBufferWriter writer{&_pevent.rb};
365384
bool notify_consumer{false};
366385

@@ -414,6 +433,13 @@ DDRes AllocationTracker::push_dealloc_sample(
414433
DDRes AllocationTracker::push_alloc_sample(uintptr_t addr,
415434
uint64_t allocated_size,
416435
TrackerThreadLocalState &tl_state) {
436+
if (_otel_profiler_mode) {
437+
// NOLINTNEXTLINE
438+
DTRACE_PROBE2(ddprof, allocation, addr, allocated_size);
439+
check_timer(PerfClock::now(), tl_state);
440+
return {};
441+
}
442+
417443
MPSCRingBufferWriter writer{&_pevent.rb};
418444
bool notify_consumer{false};
419445

@@ -427,12 +453,12 @@ DDRes AllocationTracker::push_alloc_sample(uintptr_t addr,
427453
const auto *stack_base_ptr = reinterpret_cast<const std::byte *>(&p);
428454
auto stack_size = to_address(tl_state.stack_bounds.end()) - stack_base_ptr;
429455

430-
// stack will be saved in save_context, add some margin to account for call
456+
// stack will be saved in save_context, add some ` to account for call
431457
// frames
432458
#ifdef NDEBUG
433459
constexpr int64_t kStackMargin = 192;
434460
#else
435-
constexpr int64_t kStackMargin = 720;
461+
constexpr int64_t kStackMargin = 4000;
436462
#endif
437463
uint32_t const sample_stack_size =
438464
align_up(std::min(std::max(stack_size + kStackMargin, 0L),

src/lib/dd_profiling.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "symbol_overrides.hpp"
1919
#include "syscalls.hpp"
2020

21+
#include <absl/strings/numbers.h>
2122
#include <cassert>
2223
#include <cerrno>
2324
#include <charconv>
@@ -46,6 +47,7 @@ struct ProfilerState {
4647
bool allocation_profiling_started = false;
4748
bool follow_execs = true;
4849
pid_t profiler_pid = 0;
50+
bool otel_profiler_mode = false;
4951

5052
decltype(&::getenv) getenv = &::getenv;
5153
decltype(&::putenv) putenv = &::putenv;
@@ -151,6 +153,8 @@ void init_state() {
151153

152154
auto *follow_execs_env = g_state.getenv(k_allocation_profiling_follow_execs);
153155
g_state.follow_execs = !(follow_execs_env && arg_no(follow_execs_env));
156+
g_state.otel_profiler_mode =
157+
g_state.getenv("DD_PROFILING_NATIVE_OTEL_MODE") != nullptr;
154158

155159
init_logger();
156160
g_state.initialized = true;
@@ -323,6 +327,38 @@ int ddprof_start_profiling_internal() {
323327
return -1;
324328
}
325329

330+
if (g_state.otel_profiler_mode) {
331+
auto *allocation_profiling_rate_str =
332+
g_state.getenv("DD_PROFILING_NATIVE_ALLOCATION_PROFILING_RATE");
333+
int64_t allocation_profiling_rate = 524288;
334+
if (allocation_profiling_rate_str) {
335+
(void)absl::SimpleAtoi(allocation_profiling_rate_str,
336+
&allocation_profiling_rate);
337+
}
338+
uint32_t flags = AllocationTracker::kOtelProfilerMode;
339+
if (allocation_profiling_rate < 0) {
340+
flags |= AllocationTracker::kDeterministicSampling;
341+
allocation_profiling_rate = -allocation_profiling_rate;
342+
}
343+
constexpr std::chrono::milliseconds initial_loaded_libs_check_delay{5000};
344+
constexpr std::chrono::milliseconds loaded_libs_check_interval{59000};
345+
346+
try {
347+
if (IsDDResOK(AllocationTracker::allocation_tracking_init(
348+
allocation_profiling_rate, flags, 0, RingBufferInfo{},
349+
{update_overrides, initial_loaded_libs_check_delay,
350+
loaded_libs_check_interval}))) {
351+
setup_overrides();
352+
g_state.allocation_profiling_started = true;
353+
} else {
354+
LG_ERR("Failed to start allocation profiling\n");
355+
return -1;
356+
}
357+
} catch (const DDException &e) { return -1; }
358+
359+
return 0;
360+
}
361+
326362
auto socket_path = get_ddprof_socket_path();
327363
pid_t const target_pid = getpid();
328364

@@ -398,6 +434,7 @@ int ddprof_start_profiling_internal() {
398434
g_state.allocation_profiling_started = true;
399435
} else {
400436
LG_ERR("Failed to start allocation profiling\n");
437+
return -1;
401438
}
402439
}
403440
} catch (const DDException &e) { return -1; }

test/CMakeLists.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,13 +314,19 @@ set(ALLOCATION_TRACKER_UT_SRCS
314314

315315
add_unit_test(
316316
allocation_tracker-ut ${ALLOCATION_TRACKER_UT_SRCS}
317-
LIBRARIES ${ELFUTILS_LIBRARIES} llvm-demangle dl rt Datadog::Profiling
317+
LIBRARIES ${ELFUTILS_LIBRARIES} llvm-demangle dl rt Datadog::Profiling systemtap
318318
DEFINITIONS ${DDPROF_DEFINITION_LIST} KMAX_TRACKED_ALLOCATIONS=16384)
319319

320320
if(TARGET jemalloc::jemalloc_shared)
321321
add_unit_test(
322322
allocation_tracker_jemalloc-ut ${ALLOCATION_TRACKER_UT_SRCS}
323-
LIBRARIES ${ELFUTILS_LIBRARIES} llvm-demangle dl rt jemalloc::jemalloc_shared Datadog::Profiling
323+
LIBRARIES ${ELFUTILS_LIBRARIES}
324+
llvm-demangle
325+
dl
326+
rt
327+
jemalloc::jemalloc_shared
328+
Datadog::Profiling
329+
systemtap
324330
DEFINITIONS ${DDPROF_DEFINITION_LIST} KMAX_TRACKED_ALLOCATIONS=16384 USE_JEMALLOC=1)
325331
endif()
326332

@@ -431,7 +437,7 @@ add_benchmark(
431437
../src/tsc_clock.cc
432438
../src/user_override.cc
433439
../src/sys_utils.cc
434-
LIBRARIES ${ELFUTILS_LIBRARIES} llvm-demangle
440+
LIBRARIES ${ELFUTILS_LIBRARIES} llvm-demangle systemtap
435441
DEFINITIONS ${DDPROF_DEFINITION_LIST} KMAX_TRACKED_ALLOCATIONS=16384)
436442

437443
if(NOT CMAKE_BUILD_TYPE STREQUAL "SanitizedDebug")

third_party/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
add_subdirectory(CLI)
22
add_subdirectory(llvm)
3+
add_subdirectory(systemtap)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
add_library(systemtap INTERFACE)
2+
target_include_directories(systemtap INTERFACE include/)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/* @configure_input@
2+
3+
This file just defines _SDT_ASM_SECTION_AUTOGROUP_SUPPORT to 0 or 1 to
4+
indicate whether the assembler supports "?" in .pushsection directives. */
5+
6+
#define _SDT_ASM_SECTION_AUTOGROUP_SUPPORT 1

0 commit comments

Comments
 (0)