Blame view

kernel/linux-rt-4.4.41/scripts/decode_stacktrace.sh 3.09 KB
5113f6f70   김현기   kernel add
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
  #!/bin/bash
  # (c) 2014, Sasha Levin <sasha.levin@oracle.com>
  #set -x
  
  if [[ $# != 2 ]]; then
  	echo "Usage:"
  	echo "	$0 [vmlinux] [base path]"
  	exit 1
  fi
  
  vmlinux=$1
  basepath=$2
  declare -A cache
  
  parse_symbol() {
  	# The structure of symbol at this point is:
  	#   ([name]+[offset]/[total length])
  	#
  	# For example:
  	#   do_basic_setup+0x9c/0xbf
  
  	# 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[$name]+isset}" == "isset" ]]; then
  		local base_addr=${cache[$name]}
  	else
  		local base_addr=$(nm "$vmlinux" | grep -i ' t ' | awk "/ $name\$/ {print \$1}" | head -n1)
  		cache["$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
  " "$expr")
  
  	# Pass it to addr2line to get filename and line number
          # Could get more than one result
  	if [[ "${cache[$address]+isset}" == "isset" ]]; then
  		local code=${cache[$address]}
  	else
  		local code=$(addr2line -i -e "$vmlinux" "$address")
  		cache[$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
  	code=${code//$basepath/""}
  
  	# In the case of inlines, move everything to same line
  	code=${code//$'
  '/' '}
  
  	# 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
  " "${words[$i+1]}")
  		fi
  	done
  
  	# The symbol is the last element, process it
  	symbol=${words[$last]}
  	unset words[$last]
  	parse_symbol # modifies $symbol
  
  	# Add up the line number to the symbol
  	echo "${words[@]}" "$symbol"
  }
  
  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"
  	# 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