CFSection9's Studio.

Translate Kernel dump_stack()

字数统计: 772阅读时长: 4 min
2018/10/21 Share

在分析kernel panic或者是自行在kernel的code中下dump_stack()的时候, 一个非常重要的信息就是它印出来的call trace,但是分析call trace有个比较麻烦的问题就是每次要找是哪个文件的哪行出了问题. 而现在这里有个脚本可以自动将stack当下所在的file与line number从vmlinux中捞出来.

使用方法

    1. 将translate_dump_stack.sh放到Kernel的根目录下
      e.g: ~/WorkSpace/Kernel/
    1. Build完Kernel, vmlinux會在根目录下(要确认与板子上的版本相同):
      e.g:~/WorkSpace/kernel/vmlinux
    1. 将板子上出现的dump stack复制到一个文件中, 例如dump.log
    1. ./translate_dump_stack.sh vmlinux dump.log

Orignial dump stack

1
2
3
4
5
6
[<c0011e0c>] (dump_backtrace+0x0/0x110) from [<c00129a0>] (show_stack+0x18/0x1c)
[<c0012988>] (show_stack+0x0/0x1c) from [<c04d5398>] (dump_stack+0x24/0x28)
[<c04d5374>] (Drv_XC_IRQ+0x0/0xf3c) from [<c036dee8>] (Drv_XC_Handler+0x10/0x30)
[<c036ded8>] (Drv_XC_Handler+0x0/0x30) from [<c007de88>] (irq_thread+0x1c/0x160)
[<c007dd7c>] (irq_thread+0x0/0x160) from [<c00448dc>] (kthread+0xcc/0xd0)
[<c0044810>] (kthread+0x0/0xd0) from [<c000e5d8>] (ret_from_fock+0x14/0x3c)

印出的信息格式如下
[name]+[offset]/[total length]
e.g: Drv_XC_IRQ+0x0/0xf3c

Translated dump stack

1
2
3
4
5
6
[<c0011e0c>] (dump_backtrace+0x0/0x110) from [<c00129a0>] (show_stack+0x18/0x1c) (arch/arm/kernel/traps.c:7000)
[<c0012988>] (show_stack+0x0/0x1c) from [<c04d5398>] (dump_stack+0x24/0x28) (lib/dump_stack.c:18)
[<c04d5374>] (Drv_XC_IRQ+0x0/0xf3c) from [<c036dee8>] (Drv_XC_Handler+0x10/0x30) (drivers/m/drv/xc/drvxc.c:1222)
[<c036ded8>] (Drv_XC_Handler+0x0/0x30) from [<c007de88>] (irq_thread+0x1c/0x160) (drivers/m/drv/xc/drvxc.c:242)
[<c007dd7c>] (irq_thread+0x0/0x160) from [<c00448dc>] (kthread+0xcc/0xd0) (kerenl/irq/manage.c:683 kernel/irq/manage.c:800 kernel/irq/manage.c:869)
[<c0044810>] (kthread+0x0/0xd0) from [<c000e5d8>] (ret_from_fock+0x14/0x3c) (arch/arm/kernel/entry-common.S:92)

以第3行为例,印出來的是drivers/m/drv/xc/drvxc.c:1222
而实际在Code里的第1221行下dump_stack(), 看起来会有几行的误差.

Notice

  • 请依据Kernel使用的Tool Chain, 自行更改sh中的CMD_Addr2Line

translate_dump_stack script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/bin/bash

if [ $# != "2" ]; then
echo "Usage:"
echo " $0 [vmlinux] [input log]"
exit 1
fi

vmlinux=$1
basepath=$(pwd)
input_log=$2
CMD_Addr2Line=arm-none-linux-gnueabi-addr2line

function parse_symbol() {
# The structure of symbol at this point is:
# [name]+[offset]/[total length]
#
# For example:
# do_basic_setup+0x9c/0xbf


# Strip the symbol name so that we could look it up
name=${symbol%+*}
#remove '('
name=${name##\(}

# Use 'nm vmlinux' to figure out the base address of said symbol.
# It's actually faster to call it every time than to load it
# all into bash.
base_addr=$(nm $vmlinux | grep " $name\$" | awk {'print $1'})

# Let's start doing the math to get the exact address into the
# symbol. First, strip out the symbol total length.
expr=${symbol%/*}
#remove '('
expr=${expr##\(}

# Now, replace the symbol name with the base address we found
# before.
expr=${expr/$name/0x$base_addr}

# Evaluate it to find the actual address
expr=$((expr))

# Pass it to addr2line to get filename and line number
code=`$CMD_Addr2Line -i -e $vmlinux $(printf "%x\n" $expr)`

# Strip out the base of the path
code=${code//$basepath\//""}

# In the case of inlines, move everything to same line
code=${code//$'\n'/' '}

# Replace old address with pretty line numbers
symbol=$(echo $symbol "("$code")")
}

function handle_line() {
line="$1"

# Tokenize
words=$(echo $line | tr "\r " "\n")

# Remove hex numbers. Do it ourselves until it happens in the
# kernel
for i in $words; do
if [[ $i =~ \[\<([^]]+)\>\] ]]; then
line=${line/" $i"/""}
fi
done

# The symbol is the last element, process it
symbol="$i"
parse_symbol

# Add up the line number to the symbol
line=${line/$i/$symbol}
echo "$line"
}

while read line; do
# Let's see if we have an address in the line
if [[ $line =~ \[\<([^]]+)\>\] ]]; then
# Translate address to line numbers
handle_line "$line"
else
# Nothing special in this line, show it as is
echo "$line"
fi
done < $input_log
CATALOG
  1. 1. 使用方法
  2. 2. Orignial dump stack
  3. 3. Translated dump stack
  4. 4. Notice
  5. 5. translate_dump_stack script: