TI Utilities API
|
The Log module provides APIs to instrument source code. More...
Macros | |
#define | ti_utils_runtime_Log_USE_LTO 0 |
#define | ti_utils_runtime_Log_USE_PPO 1 |
#define | Log_DECLARE_MODULE(module) |
#define | Log_event(module, level, ...) _Log_GUARD_MACRO(_Log_event_B(module, level, __VA_ARGS__)) |
Raise a log event to the logger. More... | |
#define | Log_EVENT_CONSTRUCT(module, name, fmt) |
Construct a log event object. More... | |
#define | Log_buf(module, level, format, data, size) _Log_GUARD_MACRO(_Log_buf_B(module, level, format, data, size)) |
Log a continuous block of memory. More... | |
#define | Log_printf(module, level, ...) _Log_GUARD_MACRO(_Log_printf_B(module, level, __VA_ARGS__)) |
Emit a printf formatted log. More... | |
Typedefs | |
typedef enum Log_Level | Log_Level |
Enumerations | |
enum | Log_Level { Log_INFO1 = 1, Log_INFO2 = 2, Log_INFO3 = 4, Log_INFO4 = 8, Log_INFO5 = 16, Log_INFO6 = 32, Log_WARN = 64, Log_ERROR = 128 } |
The Log module provides APIs to instrument source code.
To access the LOG APIs, the application should include its header file as follows:
When the application is configured using syscfg, the generated ti_utils_build_linker command file will pull in the necessary libraries based on the options selected in the UI.
If syscfg is not used, the developer must add any relevant Logger transport libraries to the link line. An example is shown below for LoggerBuf which is a memory based log transport:
The logging ecosystem are to be considered beta quality. They are not recommended for use in production code by TI. APIs and behaviour will change in future releases. Please report issues or feedback to E2E.
The following terms are used throughout the log documentation.
Term | Definition |
---|---|
LogModule | A parameter passed to Log APIs to indicate which software module the log |
statement originated from. | | LogLevel
| The severity or importance of a given log statement. | | BackendDelegate
| Also simply called a logger. This is a transport specific logger implementation.
The Logging framework is flexible such that multiple backend delegates may exist in a single firmware image. | | CallSite
| A specific invocation of a Log API in a given file or program. | | LogSite
| The syscfg module that handles routing of LogModule
s to specific BackendDelegate
s. | | Link Time Optimization (LTO) | Conditional inclusion/exclusion of logs using link time optimization/dead code elimination.
This approach will push the decision of log inclusion to the linker.
In order to achieve good performance all code and libraries participating in logging must be compiled and linked at the highest optimization level. | | Preprocessor Optimization (PPO) | Conditional inclusion/exclusion of logs using the C preprocessor. The decision to include/exclude logs is made during preprocessing. |
When adding log statements to the target software, it is recommended to create a logging module for each software component in the image. Modules enable the reader to understand where the log record originated from. Some log visualizers may allow the reader to filter or sort log statements by module. It is also recommended to namespace modules.
For example, a good module name for the UART
driver that exists in source/ti/drivers
, would be ti_drivers_UART
.
Modules also control the routing of log records to a backend delegate. Routing is controlled via the LogSite panel in syscfg. The default log module is called ti_utils_runtime_LogMain
and is always available. All other modules must be created via LogSite. Enabling a module via LogSite will generate externs and definitions in ti_utils_runtime_config
. Each extern and definition is namespaced by the module.
An example of the definitions generated for the default log module is shown below:
In summary, each new module will instantiate a level bitmask and handle. It will also create externs of each log API that is prefixed with the module name. The extern definitions allow routing of different modules to different logger backends.
The routing of module –> backend is achieved at link time by remapping the externed symbol generated above to the actual backend logger API. Routing is achieved by using the symbol renaming function of the linker. The LogSite syscfg will generate a file called ti_utils_runtime_LogSite.cmd.genlibs
that does this mapping at link time. A sample of this for the TI ARM compiler is shown below. It will route the externs created above to the LoggerBuf backend.
Log levels are a way to indicate the severity or importance of the contents of a particular log call site. Each call site takes an argument that allows the user to specify the level. As with modules, log visualization tools allow the user to sort or filter on a given level. This can help the reader to find important or relevant log statements in visualization.
Log levels are also used to control the emission of logs. Each call site will check that the level is enabled before calling the underlying log API.
When using LTO log levels are configurable on a per module basis while when using PPO the levels are controlled via preprocessor definitions for example: ti_utils_runtime_Log_ENABLE_INFO1
. For PPO, all log levels of interest must be enabled in the preprocessor options. For LTO, they are controlled via syscfg.
For PPO:
For LTO:
Each time a Log API is invoked, a metadata string is placed in the .out file. This string contains information about the API type, file, line module, level, and other information associated with the log call site. Each call site emits a string to a specific memory section called .log_data
. In order to use logging, this section should be added to the linker command file. By default, this section points to a nonloadable region of memory. Meaning that the metadata will not be loaded on the target device. Instead, the various logging visualization tools such as wireshark and TI ROV2 will read the metadata from this section and properly decode the log statements. The benefit of this approach is that very little memory is consumed on target. Additionally, the log transport only needs to store or send pointers to this meta section when a log API is called.
In summary, this approach minimizes the amount of memory consumed on device and bytes sent over the transport. This section can be loaded on target if desired or if you are creating a custom logger. The design does not preclude this.
In order to use the logging framework, the log section must be added to the linker command file. Here is a sample for the TI linker. Other examples can be found in the TI provided linker files for each toolchain.
This section provides a basic usage summary and a set of examples in the form of commented code fragments. Detailed descriptions of the LOG APIs are provided in subsequent sections.
Log Event: The following example demonstrates how to create a log event object and use it in the code. There are two steps to using a log event: 1. instantiation and 2. call site(s). Instantiation creates the event and the necessary metadata, and call site is where the event is actually recorded by the logger framework. Because most of the metadata and arguments are statically created, the event is the most efficient log statement.
Later on, in the application, the count event is consumed. Note the log module must match between event creation and call site. In the code below, a LogEvent record is created for serialization or stage by the backend logger.
Log Printf: The following example demonstrates use of the Log printf API. in code. Printf will embed the format string in the call site and will take arguments using varadic arguments
Log Buf: The following example demonstrates use of the Log buf API. in code. Printf will embed the format string in the call site and will take the buffer as a pointer and length. Buffers are treated as arrays of bytes. The buffer API should only be used when it is necessary to log data that is only available at runtime. It will actually send or store the entire contents of the buffer, so this API should be used sparingly as it is costly in terms of runtime and memory overhead.
#define ti_utils_runtime_Log_USE_LTO 0 |
#define ti_utils_runtime_Log_USE_PPO 1 |
#define Log_DECLARE_MODULE | ( | module | ) |
Declare a log module other than the default (ti_utils_runtime_LogMain)
This macro will create the necessary externs used to route this module's traffic to a logger instance. It can be useful for statically linked libraries to forward declare their modules so that they can be routed at link time
This must be combined with a symbol remapping to the specific backend logger that will accept this modules log statements. This is discussed in the modules section above. If using syscfg, this macro is not required as the externs are generated by the LogSite module.
[in] | module | Log module to declare |
#define Log_event | ( | module, | |
level, | |||
... | |||
) | _Log_GUARD_MACRO(_Log_event_B(module, level, __VA_ARGS__)) |
Raise a log event to the logger.
This API will emit a log record with the user provided event arguments. This event is linked to the one constructed by Log_EVENT_CONSTRUCT through a pointer to the metadata location. Separate construct and consume APIs means that an event can be constructed in the code and reused multiple places in the code with different runtime arguments.
[in] | module | Log module that the event belongs to |
[in] | level | log level |
[in] | ... | Variadic arguments consisting of the following
|
#define Log_EVENT_CONSTRUCT | ( | module, | |
name, | |||
fmt | |||
) |
Construct a log event object.
Use this marco to define a log event object. The object is private to the current file. Use the object handle in a call to Log_event to raise that event to the logger.
[in] | module | Log module that the event belongs to |
[in] | name | Event variable name, to be passed to Log_event API |
[in] | fmt | Restricted format string. Note s is not supported. Supported format specifiers include: c , f , d , x |
#define Log_buf | ( | module, | |
level, | |||
format, | |||
data, | |||
size | |||
) | _Log_GUARD_MACRO(_Log_buf_B(module, level, format, data, size)) |
Log a continuous block of memory.
Use this marco to send out runtime data from the device. This API should be used when the data is non constant and can only be derived at runtime. It is the most intrusive in terms of record overhead and instructions used.
[in] | module | Log module that the buffer originated from |
[in] | level | log level of type Log_Level |
[in] | format | Restricted format string. |
[in] | data | Pointer to array of bytes (uint8_t *) |
[in] | size | Size in bytes of array to send |
#define Log_printf | ( | module, | |
level, | |||
... | |||
) | _Log_GUARD_MACRO(_Log_printf_B(module, level, __VA_ARGS__)) |
Emit a printf formatted log.
Use this marco to enable printf style logging. This API offers the most flexibility as the construction of the format string is embedded in the call site of the API. It also supports true variadic arguments.
[in] | module | Log module that the buffer originated from |
[in] | level | log level of type Log_Level |
[in] | ... | Variadic arguments consisting of the following
|
enum Log_Level |