Re: [PATCH] decode_stacktrace: Support heuristic caller address search

From: Sasha Levin

Date: Thu Mar 05 2026 - 10:51:58 EST


On Thu, 5 Mar 2026 14:12:19 +0900, Masami Hiramatsu (Google) wrote:
Add -c option to search call address search to decode_stacktrace.
This tries to decode line info backwards, starting from 1byte before
the return address, and displays the first line info it founds as
the caller address.
If it tries up to 10bytes before (or the symbol address) and still
can not find it, it gives up and decodes the return address.

The commit message says "up to 10bytes" but the code passes $offset
(the function offset from the symbol) as the max iteration count to
search_call_site(). There's no 10-byte cap anywhere in the code?
$offset can easily be hundreds or thousands of bytes into a function.

+search_call_site() {
+ # Instead of using the return address, use the nearest line info
+ # address before given address.
+ local return_addr=${2}
+ local max=${3}
+ local i
+
+ for i in $(seq 1 ${max}); do
+ local expr=$((0x$return_addr-$i))
+ local address=$(printf "%x\n" "$expr")
+
+ local code=$(${ADDR2LINE} -i -e "${1}" "$address" 2>/dev/null)
+ local first=${code% *}
+ if [[ "$code" != "" && "$code" != ${UNKNOWN_LINE} && "${first#*:}" != "?" ]]; then

To also address Matthieu's question about performance: I think this
whole iterative search could be replaced by simply subtracting 1 from
the return address before passing it to addr2line.

DWARF line tables map address *ranges* to source lines, so any address
within the CALL instruction resolves to the correct source line.
return_addr-1 is guaranteed to land inside the CALL instruction (it's
the last byte of it), so a single addr2line call is sufficient.

This is exactly what the kernel itself does in sprint_backtrace()
(kernel/kallsyms.c:570): it passes symbol_offset=-1 to
__sprint_symbol(), which does `address += symbol_offset` before
lookup. GDB, perf, and libunwind all use the same addr-1 trick for
the same reason.

That would make this both correct and free.

+ if [[ "$code" != "" && "$code" != ${UNKNOWN_LINE} && "${first#*:}" != "?" ]]; then

Minor: ${UNKNOWN_LINE} is "??:0" -- when unquoted on the RHS of != inside
[[ ]], the ? characters are interpreted as glob wildcards (each matching
any single character). It happens to work here because ? also matches '?'
itself, but it should be quoted as "${UNKNOWN_LINE}" for correctness.
Same issue on the other != ${UNKNOWN_LINE} below.

--
Thanks,
Sasha