f16ab8e404adddb41dec5a1754d353a3b794a585
3 # Copyright (C) 2017 Francis Deslauriers <francis.deslauriers@efficios.com>
5 # SPDX-License-Identifier: LGPL-2.1-only
14 def addr2line(executable
, addr
):
16 Uses binutils' addr2line to get function containing a given address
20 cmd
+= ["-e", executable
]
22 # Print function names
23 cmd
+= ["--functions"]
25 # Expand inlined functions
26 cmd
+= ["--addresses", addr
]
28 status
= subprocess
.run(cmd
, stdout
=subprocess
.PIPE
, check
=True)
30 addr2line_output
= status
.stdout
.decode("utf-8").splitlines()
31 # addr2line's output is made of 3-tuples:
35 if len(addr2line_output
) % 3 != 0:
37 "Unexpected addr2line output:\n\t{}".format("\n\t".join(addr2line_output
))
41 for address_line_number
in range(0, len(addr2line_output
), 3):
42 function_name
= addr2line_output
[address_line_number
+ 1]
44 # Filter-out unresolved functions
45 if "??" not in function_name
:
46 function_names
.append(addr2line_output
[address_line_number
+ 1])
51 def extract_user_func_names(executable
, raw_callstack
):
53 Given a callstack from the Babeltrace CLI output, returns a set
54 containing the name of the functions. This assumes that the binary have
55 not changed since the execution.
57 recorded_callstack
= set()
59 # Remove commas and split on spaces
60 for index
, addr
in enumerate(raw_callstack
.replace(",", "").split(" ")):
61 # Consider only the elements starting with '0x' which are the
62 # addresses recorded in the callstack
64 funcs
= addr2line(executable
, addr
)
65 recorded_callstack
.update(funcs
)
67 return recorded_callstack
70 def extract_kernel_func_names(raw_callstack
):
72 Given a callstack from the Babeltrace CLI output, returns a set
73 containing the name of the functions.
74 Uses the /proc/kallsyms procfile to find the symbol associated with an
75 address. This function should only be used if the user is root or has
76 access to /proc/kallsyms.
78 recorded_callstack
= set()
81 # We read kallsyms file and save the output
82 with
open("/proc/kallsyms") as kallsyms_f
:
83 for line
in kallsyms_f
:
84 line_tokens
= line
.split()
86 symbol
= line_tokens
[2]
87 addresses
.append(int(addr
, 16))
88 syms
.append({"addr": int(addr
, 16), "symbol": symbol
})
90 # Save the address and symbol in a sorted list of tupple
91 syms
= sorted(syms
, key
=lambda k
: k
["addr"])
92 # We save the list of addresses in a seperate sorted list to easily bisect
93 # the closer address of a symbol.
94 addresses
= sorted(addresses
)
96 # Remove commas and split on spaces
97 for addr
in raw_callstack
.replace(",", "").split(" "):
99 # Search the location of the address in the addresses list and
100 # deference this location in the syms list to add the associated
102 loc
= bisect
.bisect(addresses
, int(addr
, 16))
103 recorded_callstack
.add(syms
[loc
- 1]["symbol"])
105 return recorded_callstack
108 # Regex capturing the callstack_user and callstack_kernel context
109 user_cs_rexp
= ".*callstack_user\ \=\ \[(.*)\]\ .*\}, \{.*\}"
110 kernel_cs_rexp
= ".*callstack_kernel\ \=\ \[(.*)\]\ .*\}, \{.*\}"
115 Reads a line from stdin and expect it to be a wellformed Babeltrace CLI
116 output containing containing a callstack context of the domain passed
119 expected_callstack
= set()
120 recorded_callstack
= set()
123 if len(sys
.argv
) <= 2:
126 "USAGE: ./{} (--kernel|--user EXE) FUNC-NAMES".format(sys
.argv
[0])
129 # If the `--user` option is passed, save the next argument as the path
133 if sys
.argv
[argc
] in "--kernel":
134 rexp
= kernel_cs_rexp
136 elif sys
.argv
[argc
] in "--user":
140 executable
= sys
.argv
[argc
]
142 raise Exception("Unknown domain")
146 # Extract the function names that are expected to be found call stack of
148 for func
in sys
.argv
[argc
:]:
149 expected_callstack
.add(func
)
151 # Read the tested line for STDIN
153 for line
in sys
.stdin
:
157 # Extract the userspace callstack context of the event
158 m
= re
.match(rexp
, event_line
)
160 # If there is no match, exit with error
162 raise re
.error("Callstack not found in event line")
164 raw_callstack
= str(m
.group(1))
165 if cs_type
in "user":
166 recorded_callstack
= extract_user_func_names(executable
, raw_callstack
)
167 elif cs_type
in "kernel":
168 recorded_callstack
= extract_kernel_func_names(raw_callstack
)
170 raise Exception("Unknown domain")
172 # Verify that all expected function are present in the callstack
173 for e
in expected_callstack
:
174 if e
not in recorded_callstack
:
175 raise Exception("Expected function name not found in recorded callstack")
180 if __name__
== "__main__":
This page took 0.032306 seconds and 4 git commands to generate.