hc
2023-11-06 9df731a176aab8e03b984b681b1bea01ccff6644
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# (c) 2014, Sasha Levin <sasha.levin@oracle.com>
#set -x
 
if [[ $# < 2 ]]; then
   echo "Usage:"
   echo "    $0 [vmlinux] [base path] [modules path]"
   exit 1
fi
 
vmlinux=$1
basepath=$2
modpath=$3
declare -A cache
declare -A modcache
 
parse_symbol() {
   # The structure of symbol at this point is:
   #   ([name]+[offset]/[total length])
   #
   # For example:
   #   do_basic_setup+0x9c/0xbf
 
   if [[ $module == "" ]] ; then
       local objfile=$vmlinux
   elif [[ "${modcache[$module]+isset}" == "isset" ]]; then
       local objfile=${modcache[$module]}
   else
       [[ $modpath == "" ]] && return
       local objfile=$(find "$modpath" -name $module.ko -print -quit)
       [[ $objfile == "" ]] && return
       modcache[$module]=$objfile
   fi
 
   # Remove the englobing parenthesis
   symbol=${symbol#\(}
   symbol=${symbol%\)}
 
   # Strip the symbol name so that we could look it up
   local name=${symbol%+*}
 
   # 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.
   if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then
       local base_addr=${cache[$module,$name]}
   else
       local base_addr=$(nm "$objfile" | grep -i ' t ' | awk "/ $name\$/ {print \$1}" | head -n1)
       cache[$module,$name]="$base_addr"
   fi
   # Let's start doing the math to get the exact address into the
   # symbol. First, strip out the symbol total length.
   local expr=${symbol%/*}
 
   # 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))
   local address=$(printf "%x\n" "$expr")
 
   # Pass it to addr2line to get filename and line number
   # Could get more than one result
   if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then
       local code=${cache[$module,$address]}
   else
       local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address")
       cache[$module,$address]=$code
   fi
 
   # addr2line doesn't return a proper error code if it fails, so
   # we detect it using the value it prints so that we could preserve
   # the offset/size into the function and bail out
   if [[ $code == "??:0" ]]; then
       return
   fi
 
   # Strip out the base of the path on each line
   code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code")
 
   # In the case of inlines, move everything to same line
   code=${code//$'\n'/' '}
 
   # Replace old address with pretty line numbers
   symbol="$name ($code)"
}
 
decode_code() {
   local scripts=`dirname "${BASH_SOURCE[0]}"`
 
   echo "$1" | $scripts/decodecode
}
 
handle_line() {
   local words
 
   # Tokenize
   read -a words <<<"$1"
 
   # Remove hex numbers. Do it ourselves until it happens in the
   # kernel
 
   # We need to know the index of the last element before we
   # remove elements because arrays are sparse
   local last=$(( ${#words[@]} - 1 ))
 
   for i in "${!words[@]}"; do
       # Remove the address
       if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then
           unset words[$i]
       fi
 
       # Format timestamps with tabs
       if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then
           unset words[$i]
           words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}")
       fi
   done
 
   if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
       module=${words[$last]}
       module=${module#\[}
       module=${module%\]}
       symbol=${words[$last-1]}
       unset words[$last-1]
   else
       # The symbol is the last element, process it
       symbol=${words[$last]}
       module=
   fi
 
   unset words[$last]
   parse_symbol # modifies $symbol
 
   # Add up the line number to the symbol
   echo "${words[@]}" "$symbol $module"
}
 
while read line; do
   # Let's see if we have an address in the line
   if [[ $line =~ \[\<([^]]+)\>\] ]] ||
      [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then
       # Translate address to line numbers
       handle_line "$line"
   # Is it a code line?
   elif [[ $line == *Code:* ]]; then
       decode_code "$line"
   else
       # Nothing special in this line, show it as is
       echo "$line"
   fi
done