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
40 changes: 35 additions & 5 deletions tests/benchmarks/resolution/tracer/native-tracer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@ trace_c_cpp() {
cp "$FIXTURE_DIR"/*.h "$TMP_DIR/" 2>/dev/null || true
cp "$FIXTURE_DIR"/*.hpp "$TMP_DIR/" 2>/dev/null || true

# Snapshot the fixture's own source files *before* trace_support.c is
# written below — trace_support.c is itself a ".c" file, so for the C
# case (ext=c) computing this glob afterward would capture it too,
# causing it to be compiled+linked twice (see #1914).
cd "$TMP_DIR"
local src_files
src_files="$(ls *."$ext" 2>/dev/null | tr '\n' ' ')"

# Create instrumentation support
cat > "$TMP_DIR/trace_support.c" <<'CTRACE'
#include <stdio.h>
Expand All @@ -222,6 +230,9 @@ typedef struct { char name[128]; char file[128]; } Frame;
static Frame call_stack[MAX_STACK];
static int stack_depth = 0;

static const char* extract_name(void* addr) __attribute__((no_instrument_function));
static const char* extract_file(void* addr) __attribute__((no_instrument_function));

static const char* extract_name(void* addr) {
Dl_info info;
if (dladdr(addr, &info) && info.dli_sname) {
Expand All @@ -239,6 +250,15 @@ static const char* extract_file(void* addr) {
return "unknown";
}

// g++ compiles this file as C++ (unlike gcc, which treats .c input as C),
// so without extern "C" these two hooks would be name-mangled and no longer
// match the unmangled __cyg_profile_func_enter/_exit symbols the compiler
// auto-inserts into every instrumented translation unit, including ones
// compiled from .cpp fixture files (see #1914).
#ifdef __cplusplus
extern "C" {
#endif

void __cyg_profile_func_enter(void* callee, void* caller)
__attribute__((no_instrument_function));
void __cyg_profile_func_exit(void* callee, void* caller)
Expand Down Expand Up @@ -279,6 +299,10 @@ void __cyg_profile_func_exit(void* callee, void* caller) {
if (stack_depth > 0) stack_depth--;
}

#ifdef __cplusplus
}
#endif

void __attribute__((destructor, no_instrument_function)) dump_trace() {
printf("{\n \"edges\": [\n");
for (int i = 0; i < edge_count; i++) {
Expand All @@ -293,18 +317,24 @@ void __attribute__((destructor, no_instrument_function)) dump_trace() {
}
CTRACE

cd "$TMP_DIR"
local src_files
src_files="$(ls *."$ext" 2>/dev/null | tr '\n' ' ')"
# trace_support.c is tracer scaffolding, not code under test, and must
# never itself be built with -finstrument-functions: g++ compiles .c
# input as C++, and instrumenting this translation unit's own compiler-
# generated bookkeeping crashes at runtime (see #1914). Compile it as a
# separate, uninstrumented object and link it against the instrumented
# fixture sources.
if ! $compiler -c trace_support.c -o trace_support.o 2>/dev/null; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Since trace_support.c is always pure C scaffolding, compiling it with $compiler (which is g++ for the C++ fixture) makes g++ treat the .c file as C++, requiring the #ifdef __cplusplus guards inside the heredoc to keep the hook symbols unmangled. Forcing C compilation with -x c (or always using gcc) removes that dependency and keeps the support file's compilation language-agnostic, regardless of which compiler is selected for the fixture.

Suggested change
if ! $compiler -c trace_support.c -o trace_support.o 2>/dev/null; then
if ! $compiler -x c -c trace_support.c -o trace_support.o 2>/dev/null; then

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

empty_result "$compiler trace_support.c compilation failed"
fi

if [[ "$compiler" == "gcc" || "$compiler" == "cc" ]]; then
if $compiler -finstrument-functions -rdynamic -ldl $src_files trace_support.c -o traced 2>/dev/null; then
if $compiler -finstrument-functions -rdynamic -ldl $src_files trace_support.o -o traced 2>/dev/null; then
./traced 2>/dev/null || echo '{"edges":[]}'
else
empty_result "$compiler compilation failed"
fi
else
if $compiler -finstrument-functions -rdynamic $src_files trace_support.c -o traced -ldl -lstdc++ 2>/dev/null; then
if $compiler -finstrument-functions -rdynamic $src_files trace_support.o -o traced -ldl -lstdc++ 2>/dev/null; then
./traced 2>/dev/null || echo '{"edges":[]}'
else
empty_result "$compiler compilation failed"
Expand Down
8 changes: 6 additions & 2 deletions tests/benchmarks/resolution/tracer/tracer-validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,12 @@ const SAME_FILE_THRESHOLDS: Record<string, number> = {
groovy: 0.5,

// Native — sed-injected trace support
c: 0.5,
cpp: 0.5,
// c/cpp use -finstrument-functions + dladdr() instead of sed injection,
// which can only resolve the compiled binary's own filename, never the
// originating source file — same-file recall is structurally stuck at 0%
// until that's fixed (see #2049).
c: 0.0,
cpp: 0.0,
go: 0.5,
rust: 0.5,
csharp: 0.5,
Expand Down
Loading