FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
suse_kernel/scripts/decode_stacktrace.sh at master · nvmochs/suse_kernel · GitHub
nvmochs
/
suse_kernel
Public
forked from
SUSE/kernel
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Pull requests
0
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
suse_kernel
/
scripts
/
decode_stacktrace.sh
Copy path
More file actions
More file actions
Latest commit
History
History
History
executable file
·
368 lines (319 loc) · 9.24 KB
Breadcrumbs
suse_kernel
/
scripts
/
decode_stacktrace.sh
Copy path
File metadata and controls
executable file
·
368 lines (319 loc) · 9.24 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
#!
/usr/bin/env bash
#
SPDX-License-Identifier: GPL-2.0
#
(c) 2014, Sasha Levin <sasha.levin@oracle.com>
#
set -x
usage
() {
echo
"
Usage:
"
echo
"
$0
[-R] -r <release>
"
echo
"
$0
[-R] [<vmlinux> [<base_path>|auto [<modules_path>]]]
"
echo
"
$0
-h
"
echo
"
Options:
"
echo
"
-R: decode return address instead of caller address.
"
}
#
Try to find a Rust demangler
if
type
llvm-cxxfilt
>
/dev/null
2>&1
;
then
cppfilt=llvm-cxxfilt
elif
type
c++filt
>
/dev/null
2>&1
;
then
cppfilt=c++filt
cppfilt_opts=-i
fi
UTIL_SUFFIX=
if
[[
-z
${LLVM
:-
}
]]
;
then
UTIL_PREFIX=
${CROSS_COMPILE
:-
}
else
UTIL_PREFIX=llvm-
if
[[
${LLVM}
==
*
/ ]]
;
then
UTIL_PREFIX=
${LLVM}${UTIL_PREFIX}
elif
[[
${LLVM}
==
-
*
]]
;
then
UTIL_SUFFIX=
${LLVM}
fi
fi
READELF=
${UTIL_PREFIX}
readelf
${UTIL_SUFFIX}
ADDR2LINE=
${UTIL_PREFIX}
addr2line
${UTIL_SUFFIX}
NM=
${UTIL_PREFIX}
nm
${UTIL_SUFFIX}
decode_retaddr=false
if
[[
$1
==
"
-h
"
]]
;
then
usage
exit
0
elif
[[
$1
==
"
-R
"
]]
;
then
decode_retaddr=true
shift
1
fi
if
[[
$1
==
"
-r
"
]]
;
then
vmlinux=
"
"
basepath=
"
auto
"
modpath=
"
"
release=
$2
for
fn
in
{,/usr/lib/debug}/boot/vmlinux-
$release
{,.debug} /lib/modules/
$release
{,/build}/vmlinux
;
do
if
[
-e
"
$fn
"
]
;
then
vmlinux=
$fn
break
fi
done
if
[[
$vmlinux
==
"
"
]]
;
then
echo
"
ERROR! vmlinux image for release
$release
is not found
"
>&2
usage
exit
2
fi
else
vmlinux=
$1
basepath=
${2-auto}
modpath=
$3
release=
"
"
debuginfod=
#
Can we use debuginfod-find?
if
type
debuginfod-find
>
/dev/null
2>&1
;
then
debuginfod=
${1-only}
fi
if
[[
$vmlinux
==
"
"
&&
-z
$debuginfod
]]
;
then
echo
"
ERROR! vmlinux image must be specified
"
>&2
usage
exit
1
fi
fi
declare
aarray_support=true
declare
-A cache
2>
/dev/null
if
[[
$?
!=
0 ]]
;
then
aarray_support=false
else
declare
-A modcache
fi
find_module
() {
if
[[
-n
$debuginfod
]]
;
then
if
[[
-n
$modbuildid
]]
;
then
debuginfod-find debuginfo
$modbuildid
&&
return
fi
#
Only using debuginfod so don't try to find vmlinux module path
if
[[
$debuginfod
==
"
only
"
]]
;
then
return
fi
fi
if
[
-z
$release
]
;
then
release=
$(
gdb -ex
'
print init_uts_ns.name.release
'
-ex
'
quit
'
-quiet -batch
"
$vmlinux
"
2>
/dev/null
|
sed -n
'
s/\$1 = "\(.*\)".*/\1/p
'
)
fi
if
[
-n
"
${release}
"
]
;
then
release_dirs=
"
/usr/lib/debug/lib/modules/
$release
/lib/modules/
$release
"
fi
found_without_debug_info=false
for
dir
in
"
$modpath
"
"
$(
dirname
"
$vmlinux
"
)
"
${release_dirs}
;
do
if
[
-n
"
${dir}
"
]
&&
[
-e
"
${dir}
"
]
;
then
for
fn
in
$(
find
"
$dir
"
-name
"
${module
//
_
/
[-_]}
.ko*
"
)
;
do
if
${READELF}
-WS
"
$fn
"
|
grep -qwF .debug_line
;
then
echo
$fn
return
fi
found_without_debug_info=true
done
fi
done
if
[[
${found_without_debug_info}
==
true
]]
;
then
echo
"
WARNING! No debugging info in module
${module}
, rebuild with DEBUG_KERNEL and DEBUG_INFO
"
>&2
else
echo
"
WARNING! Cannot find .ko for module
${module}
, please pass a valid module path
"
>&2
fi
return
1
}
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
[[
$aarray_support
==
true
&&
"
${modcache[$module]+isset}
"
==
"
isset
"
]]
;
then
local
objfile=
${modcache[$module]}
else
local
objfile=
$(
find_module
)
if
[[
$objfile
==
"
"
]]
;
then
return
fi
if
[[
$aarray_support
==
true
]]
;
then
modcache[
$module
]=
$objfile
fi
fi
#
Remove the englobing parenthesis
symbol=
${symbol
#
\(
}
symbol=
${symbol
%
\)
}
#
Strip segment
local
segment
if
[[
$symbol
==
*
:
*
]]
;
then
segment=
${symbol
%%:*
}
:
symbol=
${symbol
#*:
}
fi
#
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
[[
$aarray_support
==
true
&&
"
${cache[$module,$name]+isset}
"
==
"
isset
"
]]
;
then
local
base_addr=
${cache[$module,$name]}
else
local
base_addr=
$(
${NM}
"
$objfile
"
2>
/dev/null
|
awk
'
$3 == "
'
$name
'
" && ($2 == "t" || $2 == "T") {print $1; exit}
'
)
if
[[
$base_addr
==
"
"
]]
;
then
#
address not found
return
fi
if
[[
$aarray_support
==
true
]]
;
then
cache[
$module
,
$name
]=
"
$base_addr
"
fi
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
%/*
}
#
Also parse the offset from symbol.
local
offset=
${expr
#*
+}
offset=
$((
offset
))
#
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
#
The stack trace shows the return address, which is the next
#
instruction after the actual call, so as long as it's in the same
#
symbol, subtract one from that to point the call instruction.
if
[[
$decode_retaddr
==
false
&&
$offset
!=
0 ]]
;
then
expr=
$((
expr
-
1
))
else
expr=
$((
expr
))
fi
local
address=
$(
printf
"
%x\n
"
"
$expr
"
)
#
Pass it to addr2line to get filename and line number
#
Could get more than one result
if
[[
$aarray_support
==
true
&&
"
${cache[$module,$address]+isset}
"
==
"
isset
"
]]
;
then
local
code=
${cache[$module,$address]}
else
local
code=
$(
${ADDR2LINE}
-i -e
"
$objfile
"
"
$address
"
2>
/dev/null
)
if
[[
$aarray_support
==
true
]]
;
then
cache[
$module
,
$address
]=
$code
fi
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
'
/
'
'
}
#
Demangle if the name looks like a Rust symbol and if
#
we got a Rust demangler
if
[[
$name
=~
^_R
&&
$cppfilt
!=
"
"
]]
;
then
name=
$(
"
$cppfilt
"
"
$cppfilt_opts
"
"
$name
"
)
fi
#
Replace old address with pretty line numbers
symbol=
"
$segment$name
(
$code
)
"
}
debuginfod_get_vmlinux
() {
local
vmlinux_buildid=
${1
##*
}
if
[[
$vmlinux
!=
"
"
]]
;
then
return
fi
if
[[
$vmlinux_buildid
=~
^[0-9a-f]+ ]]
;
then
vmlinux=
$(
debuginfod-find debuginfo
$vmlinux_buildid
)
if
[[
$?
-ne
0 ]]
;
then
echo
"
ERROR! vmlinux image not found via debuginfod-find
"
>&2
usage
exit
2
fi
return
fi
echo
"
ERROR! Build ID for vmlinux not found. Try passing -r or specifying vmlinux
"
>&2
usage
exit
2
}
decode_code
() {
local
scripts=
`
dirname
"
${BASH_SOURCE[0]}
"
`
local
lim=
"
Code:
"
echo
-n
"
${1
%%
${lim}
*
}
"
echo
"
${lim}${1
##*
${lim}
}
"
|
$scripts
/decodecode
}
handle_line
() {
if
[[
$basepath
==
"
auto
"
&&
$vmlinux
!=
"
"
]]
;
then
module=
"
"
symbol=
"
kernel_init+0x0/0x0
"
parse_symbol
basepath=
${symbol
#
kernel_init (}
basepath=
${basepath
%/
init
/
main.c
:*
)}
fi
local
words spaces
#
Tokenize: words and spaces to preserve the alignment
read
-ra words
<<<
"
$1
"
IFS=
'
#
'
read
-ra spaces
<<<
"
$(shopt -s extglob; echo
"
${1
//
+([^[:space:]])
/#
}
"
)
"
#
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
] spaces[
$i
]
fi
done
#
Extract info after the symbol if present. E.g.:
#
func_name+0x54/0x80 (P)
#
^^^
#
The regex assumes only uppercase letters will be used. To be
#
extended if needed.
local
info_str=
"
"
if
[[
${words[$last]}
=~
\(
[A-Z]
*
\)
]]
;
then
info_str=
${words[$last]}
unset
words[
$last
] spaces[
$last
]
last=
$((
$last
-
1
))
fi
#
Join module name with its build id if present, as these were
#
split during tokenization (e.g. "[module" and "modbuildid]").
if
[[
${words[$last]}
=~
^[0-9a-f]+
\]
]]
;
then
words[
$last
-1]=
"
${words[$last-1]}
${words[$last]}
"
unset
words[
$last
] spaces[
$last
]
last=
$((
$last
-
1
))
fi
if
[[
${words[$last]}
=~
\[
([^]]+)
\]
]]
;
then
module=
${words[$last]}
#
some traces format is "(%pS)", which like "(foo+0x0/0x1 [bar])"
#
so $module may like "[bar])". Strip the right parenthesis firstly
module=
${module
%
\)
}
module=
${module
#
\[
}
module=
${module
%
\]
}
modbuildid=
${module
#*
}
module=
${module
%
*
}
if
[[
$modbuildid
==
$module
]]
;
then
modbuildid=
fi
symbol=
${words[$last-1]}
unset
words[
$last
-1] spaces[
$last
-1]
else
#
The symbol is the last element, process it
symbol=
${words[$last]}
module=
modbuildid=
fi
unset
words[
$last
]
parse_symbol
#
modifies $symbol
#
Add up the line number to the symbol
for
i
in
"
${
!
words[@]}
"
;
do
echo
-n
"
${spaces[i]}${words[i]}
"
done
echo
"
${spaces[$last]}${symbol}${module
:
+
${module}
}${info_str
:
+
${info_str}
}
"
}
while
read
line
;
do
#
Strip unexpected carriage return at end of line
line=
${line
%
$'
\r
'
}
#
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
"
#
Is it a version line?
elif
[[
-n
$debuginfod
&&
$line
=~
PID:
\
[0-9]+
\
Comm: ]]
;
then
debuginfod_get_vmlinux
"
$line
"
else
#
Nothing special in this line, show it as is
echo
"
$line
"
fi
done
Back
|
FazBrowse Home
|
New Git URL