Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9fea260
feat(taskblock): capture stacks synchronously at block exit
kaahos Jul 15, 2026
3740c0e
fix: restrict block suppression to all-thread scope
kaahos Jul 16, 2026
b169551
fix: preserve JVMTI frames in overlapping buffers
kaahos Jul 16, 2026
c1a9bb5
fix: address sphinx review
kaahos Jul 19, 2026
58e4182
fix(taskblock): preserve unowned wall fallback outside owned blocks
kaahos Jul 24, 2026
5ef75d9
fix: fix emission problems
kaahos Jul 27, 2026
f2a85e1
test(taskblock): migrate core context setup
kaahos Aug 3, 2026
8f01ff0
fix(taskblock): report park entry ownership
kaahos Aug 3, 2026
6a9b591
fix(taskblock): migrate TaskBlock tests to the jafar-backed JFR API
kaahos Aug 16, 2026
ea7f6db
fix: address review comments
kaahos Aug 18, 2026
1910a35
feat(taskblock): capture stacks synchronously at block exit
kaahos Jul 15, 2026
f88d1d5
fix: fix emission problems
kaahos Jul 27, 2026
c81bb71
feat(taskblock): add JVM blocking producers
kaahos Jul 15, 2026
76c5e1e
test: verify JVM producers in all-thread scope
kaahos Jul 16, 2026
40b3567
fix(taskblock): harden JVM producer initialization
kaahos Jul 24, 2026
3cadf05
fix: address review suggestions
kaahos Jul 28, 2026
8d064cb
refactor(taskblock): clarify monitor-wait delegation naming
kaahos Jul 30, 2026
f38ccb5
fix(taskblock): preserve requested monitor-wait ownership
kaahos Jul 30, 2026
905416d
fix(taskblock): make monitor callback activation transactional
kaahos Jul 30, 2026
1f22bec
test: migrate task block context setup
kaahos Aug 3, 2026
607b400
fix(taskblock): avoid reinitializing active profiler bridge
kaahos Aug 3, 2026
434b89b
fix(taskblock): restore parkEnter ownership return value
kaahos Aug 16, 2026
b27fda8
feat: instrument blocking native I/O for TaskBlock events
kaahos Jul 15, 2026
232a6de
test: run native I/O producers in all-thread scope
kaahos Jul 16, 2026
f1b778c
fix: skip VDSO parsing under sanitizers
kaahos Jul 17, 2026
cb73be0
fix: build native fixtures for GTest binaries
kaahos Jul 17, 2026
5e7c8f8
fix: compute ELF load bias in integer space
kaahos Jul 17, 2026
6a93e5b
fix: apply review
kaahos Jul 23, 2026
67817d7
fix(nativesocket): mark interposer stack boundaries
kaahos Jul 30, 2026
4b66726
fix(wall): scope precheck suppression by engine
kaahos Jul 30, 2026
5cd2f5e
test(taskblock): enable supported native I/O integration coverage
kaahos Jul 30, 2026
3d08ead
fix(taskblock): use supported all-thread wall configuration
kaahos Jul 31, 2026
009a07d
fix(taskblock): suppress native I/O before wall sampling
kaahos Jul 31, 2026
a1a68f4
test(taskblock): cover OpenJ9 native I/O paths
kaahos Jul 31, 2026
e850758
test(taskblock): align native I/O test scope
kaahos Jul 31, 2026
cec3dc1
fix: update tests
kaahos Aug 3, 2026
fa3e0a3
fix(profiler): preserve task blocks across rotations
kaahos Aug 4, 2026
abd0959
fix(taskblock): discard in-flight runs on profiler stop
kaahos Aug 4, 2026
23f7712
test: migrate remaining native socket TaskBlock tests to jafar
kaahos Aug 16, 2026
54c2392
feat(natsock): patch Netty's native epoll transport library by symbol
kaahos Aug 9, 2026
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
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.datadoghq.native.config

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright 2026, Datadog, Inc.
// SPDX-License-Identifier: Apache-2.0

package com.datadoghq.native.gtest

Expand Down Expand Up @@ -200,6 +202,11 @@ class GtestPlugin : Plugin<Project> {
val buildGtestConfigTask = project.tasks.register("buildGtest${config.capitalizedName()}") {
group = "build"
description = "Compile and link all Google Tests for the ${config.name} build (no run)"
if (extension.buildNativeLibs.get()) {
// CI executes these binaries directly, so the build-only task must also produce
// the native fixtures that the binaries load at runtime.
dependsOn("buildNativeLibs")
}
}

// Compile all library sources ONCE for this config. Each test
Expand Down
5 changes: 5 additions & 0 deletions ddprof-lib/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright 2026, Datadog, Inc.
// SPDX-License-Identifier: Apache-2.0

import com.datadoghq.native.model.Platform
import com.datadoghq.native.util.PlatformUtils
import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven
Expand Down Expand Up @@ -35,6 +38,8 @@ nativeBuild {
gtest {
testSourceDir.set(layout.projectDirectory.dir("src/test/cpp"))
mainSourceDir.set(layout.projectDirectory.dir("src/main/cpp"))
nativeLibsSourceDir.set(layout.projectDirectory.dir("src/test/resources/native-libs"))
nativeLibsOutputDir.set(rootProject.layout.buildDirectory.dir("test/resources/native-libs"))

// Include paths for compilation
val javaHome = PlatformUtils.javaHome()
Expand Down
163 changes: 126 additions & 37 deletions ddprof-lib/src/main/cpp/codeCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "safeAccess.h"

#include <cassert>
#include <algorithm>
#include <new>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -60,7 +62,9 @@ CodeCache::CodeCache(const char *name, short lib_index,
_build_id_len = 0;
_load_bias = 0;

memset(_imports, 0, sizeof(_imports));
memset(_import_offsets, 0, sizeof(_import_offsets));
_incomplete_imports = 0;
_imports_finalized = true;
_imports_patchable = imports_patchable;

_dwarf_table = NULL;
Expand Down Expand Up @@ -99,7 +103,10 @@ void CodeCache::copyFrom(const CodeCache& other) {
}
_load_bias = other._load_bias;

memset(_imports, 0, sizeof(_imports));
_imports.clear();
memset(_import_offsets, 0, sizeof(_import_offsets));
_incomplete_imports = 0;
_imports_finalized = true;
_imports_patchable = other._imports_patchable;

_dwarf_table_length = other._dwarf_table_length;
Expand Down Expand Up @@ -355,36 +362,58 @@ void CodeCache::findSymbolsByPrefix(std::vector<const char *> &prefixes,
}

void CodeCache::saveImport(ImportId id, void** entry) {
for (int ty = 0; ty < NUM_IMPORT_TYPES; ty++) {
if (_imports[id][ty] == nullptr) {
_imports[id][ty] = entry;
return;
}
if (entry == nullptr || id < 0 || id >= NUM_IMPORTS) {
return;
}
try {
_imports.push_back({id, entry});
_imports_finalized = false;
} catch (const std::bad_alloc&) {
_incomplete_imports |= 1ULL << id;
}
}

void CodeCache::addImport(void **entry, const char *name) {
switch (name[0]) {
case 'a':
if (strcmp(name, "aligned_alloc") == 0) {
if (strcmp(name, "accept") == 0) {
saveImport(im_accept, entry);
} else if (strcmp(name, "accept4") == 0) {
saveImport(im_accept4, entry);
} else if (strcmp(name, "aligned_alloc") == 0) {
saveImport(im_aligned_alloc, entry);
}
break;
case 'c':
if (strcmp(name, "calloc") == 0) {
saveImport(im_calloc, entry);
} else if (strcmp(name, "close") == 0) {
saveImport(im_close, entry);
} else if (strcmp(name, "connect") == 0) {
saveImport(im_connect, entry);
}
break;
case 'd':
if (strcmp(name, "dlopen") == 0) {
saveImport(im_dlopen, entry);
} else if (strcmp(name, "dup2") == 0) {
saveImport(im_dup2, entry);
} else if (strcmp(name, "dup3") == 0) {
saveImport(im_dup3, entry);
}
break;
case 'f':
if (strcmp(name, "free") == 0) {
saveImport(im_free, entry);
}
break;
case 'e':
if (strcmp(name, "epoll_wait") == 0) {
saveImport(im_epoll_wait, entry);
} else if (strcmp(name, "epoll_pwait") == 0) {
saveImport(im_epoll_pwait, entry);
}
break;
case 'm':
if (strcmp(name, "malloc") == 0) {
saveImport(im_malloc, entry);
Expand All @@ -399,6 +428,10 @@ 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, "ppoll") == 0) {
saveImport(im_ppoll, entry);
} else if (strcmp(name, "pselect") == 0) {
saveImport(im_pselect, entry);
} else if (strcmp(name, "posix_memalign") == 0) {
saveImport(im_posix_memalign, entry);
}
Expand All @@ -408,6 +441,10 @@ void CodeCache::addImport(void **entry, const char *name) {
saveImport(im_realloc, entry);
} else if (strcmp(name, "recv") == 0) {
saveImport(im_recv, entry);
} else if (strcmp(name, "recvfrom") == 0) {
saveImport(im_recvfrom, entry);
} else if (strcmp(name, "recvmsg") == 0) {
saveImport(im_recvmsg, entry);
} else if (strcmp(name, "read") == 0) {
saveImport(im_read, entry);
}
Expand All @@ -417,6 +454,8 @@ void CodeCache::addImport(void **entry, const char *name) {
saveImport(im_send, entry);
} else if (strcmp(name, "sigaction") == 0) {
saveImport(im_sigaction, entry);
} else if (strcmp(name, "select") == 0) {
saveImport(im_select, entry);
}
break;
case 'w':
Expand All @@ -427,46 +466,97 @@ void CodeCache::addImport(void **entry, const char *name) {
}
}

void **CodeCache::findImport(ImportId id) {
if (!_imports_patchable) {
makeImportsPatchable();
_imports_patchable = true;
void CodeCache::finalizeImports() {
if (_imports_finalized) {
return;
}
return _imports[id][PRIMARY];
}

void CodeCache::patchImport(ImportId id, void *hook_func) {
if (!_imports_patchable) {
makeImportsPatchable();
_imports_patchable = true;
std::sort(_imports.begin(), _imports.end(), [](const ImportLocation& a,
const ImportLocation& b) {
if (a._id != b._id) {
return a._id < b._id;
}
return reinterpret_cast<uintptr_t>(a._location) <
reinterpret_cast<uintptr_t>(b._location);
});
_imports.erase(std::unique(_imports.begin(), _imports.end(),
[](const ImportLocation& a, const ImportLocation& b) {
return a._id == b._id && a._location == b._location;
}), _imports.end());

memset(_import_offsets, 0, sizeof(_import_offsets));
for (const ImportLocation& entry : _imports) {
_import_offsets[entry._id + 1]++;
}
for (int id = 0; id < NUM_IMPORTS; id++) {
_import_offsets[id + 1] += _import_offsets[id];
}
_imports_finalized = true;
}

for (int ty = 0; ty < NUM_IMPORT_TYPES; ty++) {void **entry = _imports[id][ty];
if (entry != NULL) {
*entry = hook_func;
}}
size_t CodeCache::importCount(ImportId id) {
if (id < 0 || id >= NUM_IMPORTS) {
return 0;
}
finalizeImports();
return _import_offsets[id + 1] - _import_offsets[id];
}

void CodeCache::makeImportsPatchable() {
void **min_import = (void **)-1;
void **max_import = NULL;
for (int i = 0; i < NUM_IMPORTS; i++) {
for (int j = 0; j < NUM_IMPORT_TYPES; j++) {
void** entry = _imports[i][j];
if (entry == NULL) continue;
if (entry < min_import)
bool CodeCache::importsComplete(ImportId id) const {
return id >= 0 && id < NUM_IMPORTS &&
(_incomplete_imports & (1ULL << id)) == 0;
}

bool CodeCache::prepareImportsForPatch() {
if (_imports_patchable) {
return true;
}
return makeImportsPatchable();
}

void **CodeCache::findImport(ImportId id, size_t index) {
if (id < 0 || id >= NUM_IMPORTS || index >= importCount(id)) {
return nullptr;
}
if (!prepareImportsForPatch()) {
return nullptr;
}
return _imports[_import_offsets[id] + index]._location;
}

bool CodeCache::patchImport(ImportId id, void *hook_func) {
if (!prepareImportsForPatch()) {
return false;
}
size_t count = importCount(id);
for (size_t index = 0; index < count; index++) {
*_imports[_import_offsets[id] + index]._location = hook_func;
}
return true;
}

bool CodeCache::makeImportsPatchable() {
finalizeImports();
uintptr_t min_import = UINTPTR_MAX;
uintptr_t max_import = 0;
for (const ImportLocation& import : _imports) {
uintptr_t entry = reinterpret_cast<uintptr_t>(import._location);
if (entry < min_import)
min_import = entry;
if (entry > max_import)
max_import = entry;
}
}

if (max_import != NULL) {
uintptr_t patch_start = (uintptr_t)min_import & ~OS::page_mask;
uintptr_t patch_end = (uintptr_t)max_import & ~OS::page_mask;
mprotect((void *)patch_start, patch_end - patch_start + OS::page_size,
PROT_READ | PROT_WRITE);
if (max_import != 0) {
uintptr_t patch_start = min_import & ~OS::page_mask;
uintptr_t patch_end = max_import & ~OS::page_mask;
if (mprotect((void *)patch_start, patch_end - patch_start + OS::page_size,
PROT_READ | PROT_WRITE) != 0) {
return false;
}
}
_imports_patchable = true;
return true;
}

void CodeCache::setDwarfTable(FrameDesc *table, int length, const FrameDesc &default_frame) {
Expand Down Expand Up @@ -531,4 +621,3 @@ void CodeCache::setBuildId(const char* build_id, size_t build_id_len) {
}
}
}

Loading
Loading