3 # Copyright (C) - 2017 Francis Deslauriers <francis.deslauriers@efficios.com>
5 # This library is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as published by the
7 # Free Software Foundation; version 2.1 of the License.
9 # This library is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14 # You should have received a copy of the GNU Lesser General Public License
15 # along with this library; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 def addr2line(executable
, addr
):
25 Uses binutils' addr2line to get function containing a given address
29 cmd
+= ['-e', executable
]
31 # Print function names
32 cmd
+= ['--functions']
34 # Expand inlined functions
35 cmd
+= ['--addresses', addr
]
37 addr2line_output
= subprocess
.getoutput(' '.join(cmd
))
39 # Omit the last 2 lines as the caller of main can not be determine
40 fcts
= [addr2line_output
.split()[-2]]
42 fcts
= [ f
for f
in fcts
if '??' not in f
]
46 def extract_user_func_names(executable
, raw_callstack
):
48 Given a callstack from the Babeltrace CLI output, returns a set
49 containing the name of the functions. This assumes that the binary have
50 not changed since the execution.
52 recorded_callstack
= set()
54 # Remove commas and split on spaces
55 for index
, addr
in enumerate(raw_callstack
.replace(',', '').split(' ')):
56 # Consider only the elements starting with '0x' which are the
57 # addresses recorded in the callstack
59 funcs
= addr2line(executable
, addr
)
60 recorded_callstack
.update(funcs
)
62 return recorded_callstack
64 def extract_kernel_func_names(raw_callstack
):
66 Given a callstack from the Babeltrace CLI output, returns a set
67 containing the name of the functions.
68 Uses the /proc/kallsyms procfile to find the symbol associated with an
69 address. This function should only be used if the user is root or has
70 access to /proc/kallsyms.
72 recorded_callstack
= set()
75 # We read kallsyms file and save the output
76 with
open('/proc/kallsyms') as kallsyms_f
:
77 for line
in kallsyms_f
:
78 line_tokens
= line
.split()
80 symbol
= line_tokens
[2]
81 addresses
.append(int(addr
, 16))
82 syms
.append({'addr':int(addr
, 16), 'symbol':symbol
})
84 # Save the address and symbol in a sorted list of tupple
85 syms
= sorted(syms
, key
=lambda k
:k
['addr'])
86 # We save the list of addresses in a seperate sorted list to easily bisect
87 # the closer address of a symbol.
88 addresses
= sorted(addresses
)
90 # Remove commas and split on spaces
91 for addr
in raw_callstack
.replace(',', '').split(' '):
93 # Search the location of the address in the addresses list and
94 # deference this location in the syms list to add the associated
96 loc
= bisect
.bisect(addresses
, int(addr
, 16))
97 recorded_callstack
.add(syms
[loc
-1]['symbol'])
99 return recorded_callstack
101 # Regex capturing the callstack_user and callstack_kernel context
102 user_cs_rexp
='.*callstack_user\ \=\ \[(.*)\]\ .*\}, \{.*\}'
103 kernel_cs_rexp
='.*callstack_kernel\ \=\ \[(.*)\]\ .*\}, \{.*\}'
107 Reads a line from stdin and expect it to be a wellformed Babeltrace CLI
108 output containing containing a callstack context of the domain passed
111 expected_callstack
= set()
112 recorded_callstack
= set()
115 if len(sys
.argv
) <= 2:
117 raise ValueError('USAGE: ./{} (--kernel|--user EXE) FUNC-NAMES'.format(sys
.argv
[0]))
119 # If the `--user` option is passed, save the next argument as the path
123 if sys
.argv
[argc
] in '--kernel':
124 rexp
= kernel_cs_rexp
126 elif sys
.argv
[argc
] in '--user':
130 executable
= sys
.argv
[argc
]
132 raise Exception('Unknown domain')
136 # Extract the function names that are expected to be found call stack of
138 for func
in sys
.argv
[argc
:]:
139 expected_callstack
.add(func
)
141 # Read the tested line for STDIN
143 for line
in sys
.stdin
:
147 # Extract the userspace callstack context of the event
148 m
= re
.match(rexp
, event_line
)
150 # If there is no match, exit with error
152 raise re
.error('Callstack not found in event line')
154 raw_callstack
= str(m
.group(1))
155 if cs_type
in 'user':
156 recorded_callstack
=extract_user_func_names(executable
, raw_callstack
)
157 elif cs_type
in 'kernel':
158 recorded_callstack
=extract_kernel_func_names(raw_callstack
)
160 raise Exception('Unknown domain')
162 # Verify that all expected function are present in the callstack
163 for e
in expected_callstack
:
164 if e
not in recorded_callstack
:
165 raise Exception('Expected function name not found in recorded callstack')
169 if __name__
== '__main__':
This page took 0.031879 seconds and 4 git commands to generate.