wiki:Developer/Tracing/Trace_Linker

Version 9 (modified by Chris Johns, on Mar 16, 2015 at 2:06:16 AM) (diff)

INI formatting.

RTEMS Trace Linker

The RTEMS Trace Linker is a tool that is part of the RTEMS Tool Project and is central in the RTEMS Tracing framework.

The RTEMS Trace Linker is a post link tool that performs a link which produces a trace executable. A trace executable has been instrumented with additional code that provides software tracing capabilities. A key requirement of the trace process in RTEMS is to take existing code in a compiled format and to instrument it without rebuilding that code from source annotating it with trace code. The code being trace may be from a 3rd party or already certified or in the process of being certified and the trace testing is part of that process.

The RTEMS Source Builder builds and installs the RTEMS Tools package so the RTEMS Trace Linker is available for you to use.

Command Line

The current command line for the trace linker is:

$ rtems-tld -h
rtems-trace-ld [options] objects
Options and arguments:
 -h          : help (also --help)
 -V          : print linker version number and exit (also --version)
 -v          : verbose (trace import parts), can supply multiple times
               to increase verbosity (also --verbose)
 -w          : generate warnings (also --warn)
 -k          : keep temporary files (also --keep)
 -c compiler : target compiler is not standard (also --compiler)
 -l linker   : target linker is not standard (also --linker)
 -E prefix   : the RTEMS tool prefix (also --exec-prefix)
 -f cflags   : C compiler flags (also --cflags)
 -r path     : RTEMS path (also --rtems)
 -B bsp      : RTEMS arch/bsp (also --rtems-bsp)
 -W wrapper  : wrapper file name without ext (also --wrapper)
 -C ini      : user configuration INI file (also --config)

There are two parts to the command line passed to the trace linker. The first part controls the trace linker and provides the various options it needs and the second part is a standard linker command line you would use to link an RTEMS application. The first and second parts are separated by -- the command line option escape sequence.

The trace linker generate code that needs to be compiled and linked your standard executable so it needs to know the target compiler and CFLAGS. There are a couple of ways to do this. The simplest is to provide the path to RTEMS using the -r option and the architecture and BSP name in the standard RTEMS format of arch/bsp. The trace linker will extract the compiler and flags used to build RTEMS and will use them. If you require specific options you can use the -f, -c, -l and -E options to provide them.

The trace linker requires you provide a configuration file using the -C or --config option. This is an INI detailed in the Configuration section.

If you are working with new configuration files and you want to view the files the trace linker generates add the -k option to keep the temporary files, and -W to specify an explicit wrapper C file name.

Wrapping

The trace linker's main role is to wrap functions in the existing executable with trace code. The trace linker executable does not know about the trace code added. That is provided by the generator configuration. The wrapping function uses a GNU linker option called --wrap=symbol. The GNU Ld manual states:

Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to __wrap_symbol. Any undefined reference to __real_symbol will be resolved to symbol.

The trace linker generates C code with a wrapper for each function to be instrumented. The trace code generated is driven by the configuration INI files.

Function Signatures

A function signature is the function's declaration. It is the name of the function, the return value and the arguments. Tracing using function wrappers requires we have accurate function signatures and ideally we would like to determine the function signature from the data held in ELF files. ELF files can contain DWARF data, the ELF debugging data format. In time the trace project would like to support libdwarf so the DWARF data can be accessed and use to determine a function's signature. This work is planned but not scheduled to be done and so in the meantime we explicitly define the function signatures in the configuration files.

Configuration

The trace linker uses the INI file format for configuration files. Users provide a top level configuration that defines the trace executable created. The trace linker comes with a number of standard configurations that provide a range of functionality. A user can use those configurations or they can define a completely new set and produce a localised specific form or trace executable.

INI Files

INI files format consist of sections with a section name that groups keys A key has a name and value used as name=value. An example format is:

[section]
name1=value1
name2 = value2

The use of keys depends on the user of the INI file and in our case this is the trace linker. The trace linker can include other INI files using the include key name and a comma separated list of files to include:

include = rtems.ini, rtld-base.ini

The trace linker also uses values in key to specify other sections. In this example the functions name lists test-trace-funcs and that section a headers key that references a further section test-headers:

functions = test-trace-funcs, rtems-api

[test-trace-funcs]
; Parsed via the 'function-set', not parse as a 'trace'.
headers = test-headers

[test-headers] 
header = '#include "test-trace-1.h"'

The ability to include INI files and have key lists reference sections lets the trace linker provide base functionality a user can specialise.

Tracer Section

The top level section is [tracer]. The tracer section can contain the following keys:

name
A user defined name for the trace.
bsp
The BSP as an RTEMS standard arch/bsp pair. Currently not used.
options
Various trace linker defined options. Currently not used.
traces
List of trace sections. A trace section defines how functions are to be instrumented.
functions
List function sections. Function sections list the functions to be instrumented.

An example tracer section is:

;
; RTEMS Trace Linker Test Configuration.
;
; We must provide a top level trace section.
;
[tracer]
;
; Name of the trace.
;
name = RTEMS Trace Linker Test
;
; The BSP.
;
bsp = sparc/sis
;
; Options can be defined here or on the command line.
;
options = all-funcs, verbose
;
; Functions to trace.
;
traces = test-trace, test-trace-funcs, rtems-api-task
;
; Define the function sets. These are the function's that can be
; added to the trace lists.
;
functions = test-trace-funcs, rtems-api
;
; Include RTEMS Trace support.
;
include = rtems.ini, rtld-base.ini

Trace Section

A trace section defines how trace wrapper functions are built. To build a trace function that wraps an existing function in an ELF object file or library archive we need to have the function's signature. A signature is the function's declaration with any types used. The the signature has specific types we need access to those types which means the wrapper code needs to include header files that define those types. There may also be specific defines needed to access those types.

generator
The generator defines the type of tracing being used.
headers
List of sections that contain header files keys.
header
A header key. Typically the include code.
defines
List of sections that contain defines.
define
A define key. Typically the define code.
signatures
List of function signature sections.
trace
Functions that are instrumented with trace code.

An example trace section is:

;
; User application trace example.
;
[test-trace]
generator = printf-generator
; Just here for testing.
trace = test_trace_3

[test-trace-funcs]
; Parsed via the 'function-set', not parse as a 'trace'.
headers = test-headers
header = '#include "test-trace-2.h"'
defines = test-defines
define = "#define TEST_TRACE_2 2"
signatures = test-signatures
; Parsed via the 'trace', not parsed as a function-set
trace = test_trace_1, test_trace_2

[test-headers]
header = '#include "test-trace-1.h"'

[test-defines]
define = "#define TEST_TRACE_1 1"

[test-signatures]
test_trace_1 = void, int
test_trace_2 = test_type_2, test_type_1
test_trace_3 = float, float*

A trace section can reference other trace sections of a specific type. This allows a trace sections to build on other trace sections.

Function signatures are specified with the function being the key's name and the key's value being the return value and a list of function arguments.

Generators

Generators sections specify how to generate trace wrapping code. The section's name specifies the generator and can be listed in a generator key in a tracer or trace section.

A generator specifies the code generated when:

  • Entering a trace function (entry-trace)
  • Entry argument of a trace function (arg-trace), called once per argument
  • Return value of a trace function (ret-trace), called if not void.
  • Exit value of a trace function (exit-trace)

The the generator is not interested in a specific phase it does not need to define and nothing will be generated. For example code to profile specific function may only provide the entry-trace and exit-trace code where a nano-second time stamp is taken.

The section supports:

headers
List of header sections.
header
A header key. Typically the include code. entry-trace: The code or call made on entry to a function.
arg-trace
The code or call made for each argument to a function.
ret-trace
The code or call made with the return value if not void.
exit-trace
The code or call made after the trace function has exited.
code
Code to be added into the trace file. The code is tagged between <<<CODE and CODE markers.

An example generator section is:

[printk-generator]
headers = printk-generator-headers
entry-trace = "rtld_pg_printk_entry(@FUNC_NAME@, (void*) &@FUNC_LABEL@);"
arg-trace = "rtld_pg_printk_arg(@ARG_NUM@, @ARG_TYPE@, @ARG_SIZE@, (void*) &@ARG_LABEL@);"
exit-trace = "rtld_pg_printk_exit(@FUNC_NAME@, (void*) &@FUNC_LABEL@);"
ret-trace = "rtld_pg_printk_ret(@RET_TYPE@, @RET_SIZE@, (void*) &@RET_LABEL@);"
code = <<<CODE
static inline void rtld_pg_printk_entry(const char* func_name,
                                        void*       func_addr)
{
  printk (">>> %s (0x%08x)\n", func_name, func_addr);
}
static inline void rtld_pg_printk_arg(int         arg_num,
                                     const char* arg_type,
                                     int         arg_size,
                                     void*       arg)
{
  const unsigned char* p = arg;
  int   i;
  printk (" %2d] %s(%d) = ", arg_num, arg_type, arg_size);
  for (i = 0; i < arg_size; ++i, ++p) printk ("%02x", (unsigned int) *p);
  printk ("\n");
}
static inline void rtld_pg_printk_exit(const char* func_name,
                                       void*       func_addr)
{
  printk ("<<< %s (0x%08x)\n", func_name, func_addr);
}
static inline void rtld_pg_printk_ret(const char* ret_type,
                                      int         ret_size,
                                      void*       ret)
{
  const unsigned char* p = ret;
  int   i;
  printk (" rt] %s(%d) = ", ret_type, ret_size);
  for (i = 0; i < ret_size; ++i, ++p) printk ("%02x", (unsigned int) *p);
  printk ("\n");
}
CODE

[printk-generator-headers]
header = "#include <stdio.h>"

The generator code can use a number of token's that are expanded when generating trace functions. They are:

@FUNC_NAME@
The name of the function being wrapped.
@FUNC_LABEL@
The C label of the function being wrapped. You can take the address of this label.
@ARG_NUM@
The argument number bring processed.
@ARG_TYPE@
The type of the argument.
@ARG_SIZE@
The size of the argument in bytes.
@ARG_LABEL@
The C label of the argument. You can take the address of this label.
@RET_TYPE@
The type of the return value.
@RET_SIZE@
The size of the return value in bytes.
@RET_LABEL@
The C label of the return value as held in the wrapping code.

Limitation

The trace linker and software tracing in this way currently has some limitations. The are:

  • Function signatures are held in configuration file and so a change in a signature will not be automatically handled.
  • Variable argument lists cannot be wrapped.
  • C++ is not supported. In time we would like to add C++ support via the demangler support in the RTEMS Toolkit, however there are C++ constructs that make wrapping difficult such as templates. C++ that results in explicit calls should be able to be wrapped.