From 81b4fdcadcf43a2aa3f6f723c780bacf2abf91b3 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 13:49:20 +0300 Subject: [PATCH] builder: addr2line: 'v': No such file, instead of meaningful output, when build fails due to internal bug (fixes #20768) --- vlib/builtin/backtraces_nix.c.v | 28 +++++++++++++- vlib/v/tests/backtrace_addr2line_path_test.v | 39 ++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 vlib/v/tests/backtrace_addr2line_path_test.v diff --git a/vlib/builtin/backtraces_nix.c.v b/vlib/builtin/backtraces_nix.c.v index c1dfdf952..529813aa1 100644 --- a/vlib/builtin/backtraces_nix.c.v +++ b/vlib/builtin/backtraces_nix.c.v @@ -38,6 +38,29 @@ fn print_backtrace_skipping_top_frames_bsd(skipframes int) bool { } fn C.tcc_backtrace(fmt &char) i32 + +fn backtrace_current_executable_name() string { + args := arguments() + if args.len == 0 { + return '' + } + return args[0] +} + +fn backtrace_addr2line_executable(executable string, current_executable_name string) string { + if executable.len == 0 { + return '/proc/self/exe' + } + if executable.contains('/') { + return executable + } + if current_executable_name.len > 0 + && executable.all_after_last('/') == current_executable_name.all_after_last('/') { + return '/proc/self/exe' + } + return executable +} + @[direct_array_access] fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { $if android { @@ -60,6 +83,7 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { eprintln('Some libc implementations like musl simply do not provide it.') return false } $else { + current_executable_name := backtrace_current_executable_name() buffer := [100]voidptr{} nr_ptrs := C.backtrace(&buffer[0], 100) if nr_ptrs < 2 { @@ -72,9 +96,11 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { for i in 0 .. nr_actual_frames { sframe := unsafe { tos2(&u8(csymbols[i])) } executable := sframe.all_before('(') + addr2line_executable := backtrace_addr2line_executable(executable, + current_executable_name) addr := sframe.all_after('[').all_before(']') beforeaddr := sframe.all_before('[') - cmd := 'addr2line -e ' + executable + ' ' + addr + cmd := 'addr2line -e ' + addr2line_executable + ' ' + addr // taken from os, to avoid depending on the os module inside builtin.v f := C.popen(&char(cmd.str), c'r') if f == unsafe { nil } { diff --git a/vlib/v/tests/backtrace_addr2line_path_test.v b/vlib/v/tests/backtrace_addr2line_path_test.v new file mode 100644 index 000000000..6551b95b0 --- /dev/null +++ b/vlib/v/tests/backtrace_addr2line_path_test.v @@ -0,0 +1,39 @@ +import os + +const vexe = @VEXE + +fn test_print_backtrace_does_not_pass_a_bare_main_binary_name_to_addr2line() { + base_dir := os.join_path(os.vtmp_dir(), 'backtrace_addr2line_path_test_${os.getpid()}') + bin_dir := os.join_path(base_dir, 'bin') + run_dir := os.join_path(base_dir, 'run') + source_path := os.join_path(base_dir, 'backtrace_addr2line_probe.v') + exe_name := 'backtrace_addr2line_probe' + exe_path := os.join_path(bin_dir, exe_name) + old_path := os.getenv('PATH') + old_wd := os.getwd() + os.rmdir_all(base_dir) or {} + os.mkdir_all(bin_dir)! + os.mkdir_all(run_dir)! + defer { + os.chdir(old_wd) or {} + os.setenv('PATH', old_path, true) + os.rmdir_all(base_dir) or {} + } + helper_source := [ + 'fn main() {', + "\teprintln('backtrace_addr2line_probe')", + '\tprint_backtrace()', + '}', + ].join_lines() + os.write_file(source_path, helper_source)! + compile_cmd := '${os.quoted_path(vexe)} -g -o ${os.quoted_path(exe_path)} ${os.quoted_path(source_path)}' + compile_res := os.execute(compile_cmd) + assert compile_res.exit_code == 0, 'compilation failed: ${compile_res.output}' + os.setenv('PATH', '${bin_dir}${os.path_delimiter}${old_path}', true) + os.chdir(run_dir)! + run_res := os.execute('${exe_name} 2>&1') + assert run_res.exit_code == 0, 'execution failed: ${run_res.output}' + assert run_res.output.contains('backtrace_addr2line_probe') + assert !run_res.output.contains('addr2line:'), run_res.output + assert !run_res.output.contains('No such file'), run_res.output +} -- 2.39.5