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