Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion ddprof-lib/src/main/cpp/arguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,21 @@ Error Arguments::parse(const char *args) {
}
}

CASE("nativemem")
_nativemem = value == NULL ? 0 : parseUnits(value, BYTES);
if (_nativemem < 0) {
msg = "nativemem must be >= 0";
}

CASE("natsock")
_nativesocket = true;
if (value != NULL) {
_nativesocket_interval = parseUnits(value, NANOS);
if (_nativesocket_interval < 0) {
msg = "natsock interval must be >= 0";
}
}

DEFAULT()
if (_unknown_arg == NULL)
_unknown_arg = arg;
Expand All @@ -385,7 +400,7 @@ Error Arguments::parse(const char *args) {
return Error(msg);
}

if (_event == NULL && _cpu < 0 && _wall < 0 && _memory < 0) {
if (_event == NULL && _cpu < 0 && _wall < 0 && _memory < 0 && _nativemem < 0) {
_event = EVENT_CPU;
}

Expand Down
14 changes: 11 additions & 3 deletions ddprof-lib/src/main/cpp/arguments.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright 2017 Andrei Pangin
* Copyright 2026, Datadog, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -99,9 +100,10 @@ enum EventMask {
EM_LOCK = 4,
EM_WALL = 8,
EM_NATIVEMEM = 16,
EM_METHOD_TRACE = 32
EM_METHOD_TRACE = 32,
EM_NATIVESOCKET = 64
};
constexpr int EVENT_MASK_SIZE = 6;
constexpr int EVENT_MASK_SIZE = 7;

struct StackWalkFeatures {
// Deprecated stack recovery techniques used to workaround AsyncGetCallTrace flaws
Expand Down Expand Up @@ -174,6 +176,7 @@ class Arguments {
double _live_samples_ratio;
bool _record_heap_usage;
bool _gc_generations;
long _nativemem;
int _jstackdepth;
int _safe_mode;
StackWalkFeatures _features;
Expand All @@ -189,6 +192,8 @@ class Arguments {
bool _lightweight;
bool _enable_method_cleanup;
bool _remote_symbolication; // Enable remote symbolication for native frames
bool _nativesocket;
long _nativesocket_interval; // initial sampling period in nanoseconds; 0 = engine default

Arguments(bool persistent = false)
: _buf(NULL),
Expand All @@ -209,6 +214,7 @@ class Arguments {
_live_samples_ratio(0.1), // default to liveness-tracking 10% of the allocation samples
_record_heap_usage(false),
_gc_generations(false),
_nativemem(-1),
_jstackdepth(DEFAULT_JSTACKDEPTH),
_safe_mode(0),
_features{1, 1, 1, 1, 1, 1},
Expand All @@ -223,7 +229,9 @@ class Arguments {
_context_attributes({}),
_lightweight(false),
_enable_method_cleanup(true),
_remote_symbolication(false) {}
_remote_symbolication(false),
_nativesocket(false),
_nativesocket_interval(0) {}

~Arguments();

Expand Down
21 changes: 20 additions & 1 deletion ddprof-lib/src/main/cpp/codeCache.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright The async-profiler authors
* Copyright 2026, Datadog, Inc.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -308,6 +309,11 @@ void CodeCache::saveImport(ImportId id, void** entry) {

void CodeCache::addImport(void **entry, const char *name) {
switch (name[0]) {
case 'a':
if (strcmp(name, "aligned_alloc") == 0) {
saveImport(im_aligned_alloc, entry);
}
break;
case 'c':
if (strcmp(name, "calloc") == 0) {
saveImport(im_calloc, entry);
Expand Down Expand Up @@ -337,18 +343,31 @@ void CodeCache::addImport(void **entry, const char *name) {
saveImport(im_pthread_setspecific, entry);
} else if (strcmp(name, "poll") == 0) {
saveImport(im_poll, entry);
} else if (strcmp(name, "posix_memalign") == 0) {
saveImport(im_posix_memalign, entry);
}
break;
case 'r':
if (strcmp(name, "realloc") == 0) {
saveImport(im_realloc, entry);
} else if (strcmp(name, "recv") == 0) {
saveImport(im_recv, entry);
} else if (strcmp(name, "read") == 0) {
saveImport(im_read, entry);
}
break;
case 's':
if (strcmp(name, "sigaction") == 0) {
if (strcmp(name, "send") == 0) {
saveImport(im_send, entry);
} else if (strcmp(name, "sigaction") == 0) {
saveImport(im_sigaction, entry);
}
break;
case 'w':
if (strcmp(name, "write") == 0) {
saveImport(im_write, entry);
}
break;
}
}

Expand Down
7 changes: 7 additions & 0 deletions ddprof-lib/src/main/cpp/codeCache.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright The async-profiler authors
* Copyright 2026, Datadog, Inc.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -34,7 +35,13 @@ enum ImportId {
im_calloc,
im_realloc,
im_free,
im_posix_memalign,
im_aligned_alloc,
im_sigaction,
im_send,
im_recv,
im_write,
im_read,
NUM_IMPORTS
};

Expand Down
24 changes: 24 additions & 0 deletions ddprof-lib/src/main/cpp/event.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright 2020 Andrei Pangin
* Copyright 2026, Datadog, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -88,6 +89,29 @@ class ObjectLivenessEvent : public Event {
Context _ctx;
};

class MallocEvent : public Event {
public:
u64 _start_time;
uintptr_t _address;
u64 _size; // 0 for free events
float _weight;

MallocEvent() : Event(), _start_time(0), _address(0), _size(0), _weight(1.0f) {}
};

class NativeSocketEvent : public Event {
public:
u64 _start_time; // TSC ticks at call entry
u64 _end_time; // TSC ticks at call return
u8 _operation; // 0 = SEND, 1 = RECV
char _remote_addr[64]; // "ip:port" null-terminated string
u64 _bytes; // bytes transferred (return value of send/recv/write/read)
float _weight; // inverse-transform sample weight

NativeSocketEvent() : Event(), _start_time(0), _end_time(0), _operation(0),
_bytes(0), _weight(1.0f) { _remote_addr[0] = '\0'; }
};

class WallClockEpochEvent {
public:
bool _dirty;
Expand Down
42 changes: 42 additions & 0 deletions ddprof-lib/src/main/cpp/flightRecorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,10 @@ void Recording::writeSettings(Buffer *buf, Arguments &args) {

writeBoolSetting(buf, T_ALLOC, "enabled", args._record_allocations);
writeBoolSetting(buf, T_HEAP_LIVE_OBJECT, "enabled", args._record_liveness);
writeBoolSetting(buf, T_MALLOC, "enabled", args._nativemem >= 0);
if (args._nativemem >= 0) {
writeIntSetting(buf, T_MALLOC, "nativemem", args._nativemem);
}

writeBoolSetting(buf, T_ACTIVE_RECORDING, "debugSymbols",
VMStructs::libjvm()->hasDebugSymbols());
Expand Down Expand Up @@ -1575,6 +1579,38 @@ void Recording::recordAllocation(RecordingBuffer *buf, int tid,
flushIfNeeded(buf);
}

void Recording::recordMallocSample(Buffer *buf, int tid, u64 call_trace_id,
MallocEvent *event) {
int start = buf->skip(1);
buf->putVar64(T_MALLOC);
buf->putVar64(event->_start_time);
buf->putVar32(tid);
buf->putVar64(call_trace_id);
buf->putVar64(event->_address);
buf->putVar64(event->_size);
buf->putFloat(event->_weight);
writeCurrentContext(buf);
writeEventSizePrefix(buf, start);
flushIfNeeded(buf);
}

void Recording::recordNativeSocketSample(Buffer *buf, int tid, u64 call_trace_id,
NativeSocketEvent *event) {
int start = buf->skip(1);
buf->putVar64(T_NATIVE_SOCKET);
buf->putVar64(event->_start_time);
buf->putVar32(tid);
buf->putVar64(call_trace_id);
buf->putVar64(event->_end_time - event->_start_time);
buf->putUtf8(event->_operation == 0 ? "SEND" : "RECV");
buf->putUtf8(event->_remote_addr);
buf->putVar64(event->_bytes);
buf->putFloat(event->_weight);
writeCurrentContext(buf);
writeEventSizePrefix(buf, start);
flushIfNeeded(buf);
}

void Recording::recordHeapLiveObject(Buffer *buf, int tid, u64 call_trace_id,
ObjectLivenessEvent *event) {
int start = buf->skip(1);
Expand Down Expand Up @@ -1817,6 +1853,12 @@ void FlightRecorder::recordEvent(int lock_index, int tid, u64 call_trace_id,
case BCI_PARK:
rec->recordThreadPark(buf, tid, call_trace_id, (LockEvent *)event);
break;
case BCI_NATIVE_MALLOC:
rec->recordMallocSample(buf, tid, call_trace_id, (MallocEvent *)event);
break;
case BCI_NATIVE_SOCKET:
rec->recordNativeSocketSample(buf, tid, call_trace_id, (NativeSocketEvent *)event);
break;
}
rec->flushIfNeeded(buf);
rec->addThread(lock_index, tid);
Expand Down
4 changes: 4 additions & 0 deletions ddprof-lib/src/main/cpp/flightRecorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ class Recording {
void recordQueueTime(Buffer *buf, int tid, QueueTimeEvent *event);
void recordAllocation(RecordingBuffer *buf, int tid, u64 call_trace_id,
AllocEvent *event);
void recordMallocSample(Buffer *buf, int tid, u64 call_trace_id,
MallocEvent *event);
void recordNativeSocketSample(Buffer *buf, int tid, u64 call_trace_id,
NativeSocketEvent *event);
void recordHeapLiveObject(Buffer *buf, int tid, u64 call_trace_id,
ObjectLivenessEvent *event);
void recordMonitorBlocked(Buffer *buf, int tid, u64 call_trace_id,
Expand Down
6 changes: 6 additions & 0 deletions ddprof-lib/src/main/cpp/hotspot/hotspotSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ inline EventType eventTypeFromBCI(jint bci_type) {
return LOCK_SAMPLE;
case BCI_PARK:
return PARK_SAMPLE;
case BCI_NATIVE_MALLOC:
return MALLOC_SAMPLE;
default:
// For unknown or invalid BCI types, default to EXECUTION_SAMPLE
// This maintains backward compatibility and prevents undefined behavior
Expand Down Expand Up @@ -955,6 +957,10 @@ int HotspotSupport::walkJavaStack(StackWalkRequest& request) {
int java_frames = 0;
if (features.mixed) {
java_frames = walkVM(ucontext, frames, max_depth, features, eventTypeFromBCI(request.event_type), lock_index, truncated);
} else if (request.event_type == BCI_NATIVE_MALLOC || request.event_type == BCI_NATIVE_SOCKET) {
if (cstack >= CSTACK_VM) {
java_frames = walkVM(ucontext, frames, max_depth, features, eventTypeFromBCI(request.event_type), lock_index, truncated);
}
} else if (request.event_type == BCI_CPU || request.event_type == BCI_WALL) {
if (cstack >= CSTACK_VM) {
java_frames = walkVM(ucontext, frames, max_depth, features, eventTypeFromBCI(request.event_type), lock_index, truncated);
Expand Down
27 changes: 27 additions & 0 deletions ddprof-lib/src/main/cpp/jfrMetadata.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright 2020 Andrei Pangin
* Copyright 2026, Datadog, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -298,6 +299,32 @@ void JfrMetadata::initialize(
<< field("name", T_STRING, "Name")
<< field("count", T_LONG, "Count"))

<< (type("profiler.Malloc", T_MALLOC, "malloc")
<< category("Java Virtual Machine", "Native Memory")
<< field("startTime", T_LONG, "Start Time", F_TIME_TICKS)
<< field("eventThread", T_THREAD, "Event Thread", F_CPOOL)
<< field("stackTrace", T_STACK_TRACE, "Stack Trace", F_CPOOL)
<< field("address", T_LONG, "Address", F_ADDRESS)
<< field("size", T_LONG, "Size", F_BYTES)
<< field("weight", T_FLOAT, "Sample weight")
<< field("spanId", T_LONG, "Span ID")
<< field("localRootSpanId", T_LONG, "Local Root Span ID") ||
contextAttributes)

<< (type("datadog.NativeSocketEvent", T_NATIVE_SOCKET, "Native Socket I/O")
<< category("Datadog", "Profiling")
<< field("startTime", T_LONG, "Start Time", F_TIME_TICKS)
<< field("eventThread", T_THREAD, "Event Thread", F_CPOOL)
<< field("stackTrace", T_STACK_TRACE, "Stack Trace", F_CPOOL)
<< field("duration", T_LONG, "Duration", F_DURATION_TICKS)
<< field("operation", T_STRING, "Operation")
<< field("remoteAddress", T_STRING, "Remote Address")
<< field("bytesTransferred", T_LONG, "Bytes Transferred", F_BYTES)
<< field("weight", T_FLOAT, "Sample weight")
<< field("spanId", T_LONG, "Span ID")
<< field("localRootSpanId", T_LONG, "Local Root Span ID") ||
contextAttributes)

<< (type("jdk.OSInformation", T_OS_INFORMATION, "OS Information")
<< category("Operating System")
<< field("startTime", T_LONG, "Start Time", F_TIME_TICKS)
Expand Down
3 changes: 3 additions & 0 deletions ddprof-lib/src/main/cpp/jfrMetadata.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright 2020 Andrei Pangin
* Copyright 2026, Datadog, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -78,6 +79,8 @@ enum JfrType {
T_DATADOG_CLASSREF_CACHE = 124,
T_DATADOG_COUNTER = 125,
T_UNWIND_FAILURE = 126,
T_MALLOC = 127,
T_NATIVE_SOCKET = 128,
T_ANNOTATION = 200,
T_LABEL = 201,
T_CATEGORY = 202,
Expand Down
5 changes: 4 additions & 1 deletion ddprof-lib/src/main/cpp/jvmSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ int JVMSupport::walkJavaStack(StackWalkRequest& request) {
if (VM::isHotspot()) {
return HotspotSupport::walkJavaStack(request);
} else if (VM::isOpenJ9() || VM::isZing()) {
assert(request.event_type == BCI_CPU || request.event_type == BCI_WALL);
assert(request.event_type == BCI_CPU ||
request.event_type == BCI_WALL ||
request.event_type == BCI_NATIVE_MALLOC ||
request.event_type == BCI_NATIVE_SOCKET);
return asyncGetCallTrace(request.frames, request.max_depth, request.ucontext);
}
assert(false && "Unsupported JVM");
Expand Down
Loading
Loading