Skip to content

Commit 235a67a

Browse files
fix: use fp unwinding mode when running go test
Go does not produce .eh_frame sections needed for dwarf unwinding. In dwarf mode, perf captures a stack snapshot that we can use to fall back to frame pointer unwinding via framehop. However on arm64, the snapshot is missing a key register (x29/FP) due to Go's non-standard function calling convention, so the framehop fallback fails. Using callgraph mode fp avoids this entirely: the kernel performs the unwinding itself with full access to the stack.
1 parent e1b20ac commit 235a67a

3 files changed

Lines changed: 20 additions & 1 deletion

File tree

src/executor/helpers/env.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ pub fn get_base_injected_env(
5050
env
5151
}
5252

53+
/// Set the env variable to not warn users about Go's perf unwinding mode when running Go benchmarks
54+
pub fn suppress_go_perf_unwinding_warning() {
55+
// Safety: no multithreading
56+
unsafe {
57+
std::env::set_var("CODSPEED_GO_SUPPRESS_PERF_UNWINDING_MODE_WARNING", "true");
58+
}
59+
}
60+
5361
pub fn is_codspeed_debug_enabled() -> bool {
5462
std::env::var("CODSPEED_LOG")
5563
.ok()

src/executor/helpers/introspected_golang/go.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ if [ $# -eq 0 ]; then
3232
exit $?
3333
fi
3434

35+
# On arm64, warn about setting CODSPEED_PERF_UNWINDING_MODE to "fp" for correct flamegraphs
36+
if [ "$(uname -m)" = "aarch64" ] && [ "${CODSPEED_GO_SUPPRESS_PERF_UNWINDING_MODE_WARNING:-}" != "true" ]; then
37+
echo "::warning::Go profiling on arm64 require frame pointer unwinding. Set CODSPEED_PERF_UNWINDING_MODE=fp for better profiling." >&2
38+
fi
39+
3540
# Route command based on first argument
3641
case "$1" in
3742
test)

src/executor/wall_time/perf/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::cli::UnwindingMode;
44
use crate::executor::ExecutorConfig;
55
use crate::executor::helpers::command::CommandBuilder;
66
use crate::executor::helpers::env::is_codspeed_debug_enabled;
7+
use crate::executor::helpers::env::suppress_go_perf_unwinding_warning;
78
use crate::executor::helpers::harvest_perf_maps_for_pids::harvest_perf_maps_for_pids;
89
use crate::executor::helpers::run_command_with_log_pipe::run_command_with_log_pipe_and_callback;
910
use crate::executor::helpers::run_with_sudo::run_with_sudo;
@@ -98,6 +99,8 @@ impl PerfRunner {
9899
(mode, None)
99100
} else if config.command.contains("cargo") {
100101
(UnwindingMode::Dwarf, None)
102+
} else if config.command.contains("go test") {
103+
(UnwindingMode::FramePointer, None)
101104
} else if config.command.contains("pytest")
102105
|| config.command.contains("uv")
103106
|| config.command.contains("python")
@@ -112,7 +115,10 @@ impl PerfRunner {
112115
};
113116

114117
let cg_mode = match cg_mode {
115-
UnwindingMode::FramePointer => "fp",
118+
UnwindingMode::FramePointer => {
119+
suppress_go_perf_unwinding_warning();
120+
"fp"
121+
}
116122
UnwindingMode::Dwarf => &format!("dwarf,{}", stack_size.unwrap_or(8192)),
117123
};
118124
debug!("Using call graph mode: {cg_mode:?}");

0 commit comments

Comments
 (0)