diff options
Diffstat (limited to 'scripts/decode_stacktrace.sh')
| -rwxr-xr-x | scripts/decode_stacktrace.sh | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh new file mode 100755 index 000000000000..515c4c00e957 --- /dev/null +++ b/scripts/decode_stacktrace.sh | |||
| @@ -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 | ||
