Function Call Tracing¶
Function Instrumentation¶
The gcc
option -finstrument_functions
allows defining two
functions that will be injected at the entry and at the exit of
functions compiled with that compiler option.
One application of this compiler option is a function call tracing made with standard output.
Debug Functions¶
TRENTOS provides basic implementations of the instrumentation functions
__cyg_profile_func_enter()
and __cyg_profile_func_exit()
in
instrument_functions.c
. When including lib_debug
those functions
are included automatically into the project.
The functions print an entry marker or an exit marker together with the function address when entering or exiting functions compiled with the compiler option.
If a user wants to provide its own implementations, the default
implementations can be deactivated in the configuration of
lib_debug
by defining LIB_DEBUG_INSTRUMENT_FUNCTIONS 0
.
Compiler Option¶
Usually one wants to debug certain components or libraries and the
gcc
option -finstrument_functions
has to be set for the
corresponding source files.
WARNING: It is not possible to set the compiler option globally to debug the full system stack because the used standard output via UART is not always available and its throughput is limited.
The activation of the compiler option for a component is straightforward
by setting the COMPILE_OPTIONS
property for all relevant source
files. For a library of type INTERFACE
it is easily possible as
well without changing the library because it gets compiled together with
the component. Otherwise, a modification of the library would be
necessary.
Below is an example of how the source files of a component and the
source files of a specific library can be compiled with the compiler
option. In the example, the source files of the component are available
in a CMake variable and the source files of the INTERFACE
library are
collected into a CMake variable. For each source file, the COMPILE_OPTIONS
property is modified to have the -finstrument_functions
flag.
DeclareCAmkESComponent(
MyComponent
SOURCES
${MY_COMPONENT_SOURCES}
LIBS
lib_debug # required for instrument_functions.c
my_library
)
# Set gcc option for component source files
set_source_files_properties(${MY_COMPONENT_SOURCES}
PROPERTIES
COMPILE_OPTIONS "-finstrument-functions"
)
# Get source files of INTERFACE library "my_library"
get_target_property(MY_LIBRARY_SOURCES
my_library INTERFACE_SOURCES
)
# Set gcc option for library source files
set_source_files_properties(${MY_LIBRARY_SOURCES}
PROPERTIES
COMPILE_OPTIONS "-finstrument-functions"
)
Backtrace Function Calls¶
The gcc
option -finstrument_functions
is a really powerful
feature. Its only drawback is that it is not possible to know the name
of the functions at runtime. The arguments
of __cyg_profile_func_enter()
and __cyg_profile_func_exit()
are
just the addresses of the current function and its caller.
TRENTOS offers the script sdk/scripts/debug/backtrace.py
to have a pretty and
helpful format with function names.
Backtrace Script¶
The script will display the function names with their nesting level together with the usual standard output logs.
Therefore the script requires the standard output of the system to be
redirected to a file. If the system is for example run in QEMU, the QEMU
output has to be redirected to a file like qemu_stdout.txt
. For
the demos in the SDK, only the error output is typically redirected to a
file qemu_stderr.txt
through the run_demo.sh
scripts and
they, therefore, need to be adapted to use the script.
In addition to the standard output file of the system, the script is called with the symbol file created when building the system. So the script is able to convert the printed addresses into the function names.
Since the script opens the standard output file in a non-blocking way,
it is even possible to use it while the system is running. Then the optional
timeout
parameter should be considered to set an appropriate timeout the
script keeps waiting for new input.
Usage¶
The usage of the script is given in its Python documentation:
Backtraces -finstrument-functions markers with function hex addresses into
function names.
The script expects as arguments:
--stdout_file: An output file of a system (either still running or not)
containing the markers produced by enabling the gcc flag
-fintstument-functions
--symbols_file: A .lst map file containing the symbols supposed to match the
hex addresses
--timeout: A timeout (in secs) parameter to exit the script when
waiting for new input. Default value is 0.
It will print out the same content of stdout_file but with the markers (with hex
strings inside corresponding to the function addresses) resolved to symbols
(names of the functions) in a human readable format with a layout that
emphasizes the function calls nesting.
E.g.:
Call the script:
./backtrace.py --stdout_file qemu_stdout.txt --symbols_file
build-zynq7000-Debug-httpd/os_system/httpServer.instance.bin.lst
Example Input¶
With activated function instrumentation, the standard output of the
system includes the entry or exit markers and the function addresses
(e.g. “0x25a00 {
” or “0x25a00 }
”) and is used as an
input for the script:
Booting all finished, dropped to user space
main@main.c:2125 Starting CapDL Loader...
main@main.c:2127 CapDL Loader done, suspending...
0x25a00 {
0x25950 {
0x2e35c {
INFO: /host/trentos_sdk/components/UART/Uart.c:235: initialize UART
INFO: /host/trentos_sdk/components/UART/Uart.c:295: initialize UART ok
INFO: /host/httpd/components/Ticker/src/Ticker.c:14: Ticker running
INFO: /host/httpd/components/NwStack/src/NwStack.c:59: [NwStack 'nwStack'] starting
INFO: /host/trentos_sdk/components/NIC_ChanMux/driver.c:19: [NIC 'nwDriver'] post_init()
INFO: /host/trentos_sdk/components/NIC_ChanMux/driver.c:67: [NIC 'nwDriver'] starting driver
INFO: /host/trentos_sdk/libs/chanmux_nic_driver/src/chanmux_nic_drv_cfg.c:167: network driver init
INFO: /host/trentos_sdk/libs/chanmux_nic_driver/src/chanmux_nic_drv_cfg.c:182: ChanMUX channels: ctrl=4, data=5
0x2e35c }
0x30fe0 {
0x30fe0 }
0x2e53c {
0x2e53c }
0x25950 }
0x25a00 }
Example Output¶
An exemplary output of the backtrace.py
script shows the function
names with their nesting level together with the usual standard output:
Booting all finished, dropped to user space
main@main.c:2125 Starting CapDL Loader...
main@main.c:2127 ESC[0mESC[32mCapDL Loader done, suspending...
_GNUC_init_helper_MHD_init() {
| MHD_init() {
| | MHD_monotonic_sec_counter_init() {
INFO: /host/trentos_sdk/components/UART/Uart.c:235: initialize UART
INFO: /host/trentos_sdk/components/UART/Uart.c:295: initialize UART ok
INFO: /host/httpd/components/Ticker/src/Ticker.c:14: Ticker running
INFO: /host/httpd/components/NwStack/src/NwStack.c:59: [NwStack 'nwStack'] starting
INFO: /host/trentos_sdk/components/NIC_ChanMux/driver.c:19: [NIC 'nwDriver'] post_init()
INFO: /host/trentos_sdk/components/NIC_ChanMux/driver.c:67: [NIC 'nwDriver'] starting driver
INFO: /host/trentos_sdk/libs/chanmux_nic_driver/src/chanmux_nic_drv_cfg.c:167: network driver init
INFO: /host/trentos_sdk/libs/chanmux_nic_driver/src/chanmux_nic_drv_cfg.c:182: ChanMUX channels: ctrl=4, data=5
| | MHD_monotonic_sec_counter_init() }
| | MHD_send_init_static_vars_() {
| | MHD_send_init_static_vars_() }
| | MHD_init_mem_pools_() {
| | MHD_init_mem_pools_() }
| MHD_init()
_GNUC_init_helper_MHD_init() }