Skip to content

Commit dbd1abb

Browse files
sashalevintorvalds
authored andcommitted
decode_stacktrace: make stack dump output useful again
Right now when people try to report issues in the kernel they send stack dumps to eachother, which looks something like this: [ 6.906437] [<ffffffff811f0e90>] ? backtrace_test_irq_callback+0x20/0x20 [ 6.907121] [<ffffffff84388ce8>] dump_stack+0x52/0x7f [ 6.907640] [<ffffffff811f0ec8>] backtrace_regression_test+0x38/0x110 [ 6.908281] [<ffffffff813596a0>] ? proc_create_data+0xa0/0xd0 [ 6.908870] [<ffffffff870a8040>] ? proc_modules_init+0x22/0x22 [ 6.909480] [<ffffffff810020c2>] do_one_initcall+0xc2/0x1e0 [...] However, most of the text you get is pure garbage. The only useful thing above is the function name. Due to the amount of different kernel code versions and various configurations being used, the kernel address and the offset into the function are not really helpful in determining where the problem actually occured. Too often the result of someone looking at a stack dump is asking the person who sent it for a translation for one or more 'addr2line' translations. Which slows down the entire process of debugging the issue (and really annoying). The decode_stacktrace script is an attempt to make the output more useful and easy to work with by translating all kernel addresses in the stack dump into line numbers. Which means that the stack dump would look like this: [ 635.148361] dump_stack (lib/dump_stack.c:52) [ 635.149127] warn_slowpath_common (kernel/panic.c:418) [ 635.150214] warn_slowpath_null (kernel/panic.c:453) [ 635.151031] _oalloc_pages_slowpath+0x6a/0x7d0 [ 635.152171] ? zone_watermark_ok (mm/page_alloc.c:1728) [ 635.152988] ? get_page_from_freelist (mm/page_alloc.c:1939) [ 635.154766] __alloc_pages_nodemask (mm/page_alloc.c:2766) It's pretty obvious why this is better than the previous stack dump before. Usage is pretty simple: ./decode_stacktrace.sh [vmlinux] [base path] Where vmlinux is the vmlinux to extract line numbers from and base path is the path that points to the root of the build tree, for example: ./decode_stacktrace.sh vmlinux /home/sasha/linux/ < input.log > output.log The stack trace should be piped through it (I, for example, just pipe the output of the serial console of my KVM test box through it). Signed-off-by: Sasha Levin <sasha.levin@oracle.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent d1e1cda commit dbd1abb

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

scripts/decode_stacktrace.sh

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/bin/bash
2+
# (c) 2014, Sasha Levin <sasha.levin@oracle.com>
3+
#set -x
4+
5+
if [[ $# != 2 ]]; then
6+
echo "Usage:"
7+
echo " $0 [vmlinux] [base path]"
8+
exit 1
9+
fi
10+
11+
vmlinux=$1
12+
basepath=$2
13+
declare -A cache
14+
15+
parse_symbol() {
16+
# The structure of symbol at this point is:
17+
# [name]+[offset]/[total length]
18+
#
19+
# For example:
20+
# do_basic_setup+0x9c/0xbf
21+
22+
23+
# Strip the symbol name so that we could look it up
24+
local name=${symbol%+*}
25+
26+
# Use 'nm vmlinux' to figure out the base address of said symbol.
27+
# It's actually faster to call it every time than to load it
28+
# all into bash.
29+
if [[ "${cache[$name]+isset}" == "isset" ]]; then
30+
local base_addr=${cache[$name]}
31+
else
32+
local base_addr=$(nm "$vmlinux" | grep -i ' t ' | awk "/ $name\$/ {print \$1}" | head -n1)
33+
cache["$name"]="$base_addr"
34+
fi
35+
# Let's start doing the math to get the exact address into the
36+
# symbol. First, strip out the symbol total length.
37+
local expr=${symbol%/*}
38+
39+
# Now, replace the symbol name with the base address we found
40+
# before.
41+
expr=${expr/$name/0x$base_addr}
42+
43+
# Evaluate it to find the actual address
44+
expr=$((expr))
45+
local address=$(printf "%x\n" "$expr")
46+
47+
# Pass it to addr2line to get filename and line number
48+
# Could get more than one result
49+
if [[ "${cache[$address]+isset}" == "isset" ]]; then
50+
local code=${cache[$address]}
51+
else
52+
local code=$(addr2line -i -e "$vmlinux" "$address")
53+
cache[$address]=$code
54+
fi
55+
56+
# addr2line doesn't return a proper error code if it fails, so
57+
# we detect it using the value it prints so that we could preserve
58+
# the offset/size into the function and bail out
59+
if [[ $code == "??:0" ]]; then
60+
return
61+
fi
62+
63+
# Strip out the base of the path
64+
code=${code//$basepath/""}
65+
66+
# In the case of inlines, move everything to same line
67+
code=${code//$'\n'/' '}
68+
69+
# Replace old address with pretty line numbers
70+
symbol="$name ($code)"
71+
}
72+
73+
decode_code() {
74+
local scripts=`dirname "${BASH_SOURCE[0]}"`
75+
76+
echo "$1" | $scripts/decodecode
77+
}
78+
79+
handle_line() {
80+
local words
81+
82+
# Tokenize
83+
read -a words <<<"$1"
84+
85+
# Remove hex numbers. Do it ourselves until it happens in the
86+
# kernel
87+
88+
# We need to know the index of the last element before we
89+
# remove elements because arrays are sparse
90+
local last=$(( ${#words[@]} - 1 ))
91+
92+
for i in "${!words[@]}"; do
93+
# Remove the address
94+
if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then
95+
unset words[$i]
96+
fi
97+
98+
# Format timestamps with tabs
99+
if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then
100+
unset words[$i]
101+
words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}")
102+
fi
103+
done
104+
105+
# The symbol is the last element, process it
106+
symbol=${words[$last]}
107+
unset words[$last]
108+
parse_symbol # modifies $symbol
109+
110+
# Add up the line number to the symbol
111+
echo "${words[@]}" "$symbol"
112+
}
113+
114+
while read line; do
115+
# Let's see if we have an address in the line
116+
if [[ $line =~ \[\<([^]]+)\>\] ]]; then
117+
# Translate address to line numbers
118+
handle_line "$line"
119+
# Is it a code line?
120+
elif [[ $line == *Code:* ]]; then
121+
decode_code "$line"
122+
else
123+
# Nothing special in this line, show it as is
124+
echo "$line"
125+
fi
126+
done

0 commit comments

Comments
 (0)