1 /*
2  * Copyright (c) 2019-2024 Texas Instruments Incorporated - http://www.ti.com
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /**
34  *  @file  ti/log/Log.h
35  *
36  *  @addtogroup ti_log_LOG Log Interface
37  *
38  *  @brief      The Log module provides APIs to instrument source code
39  *
40  *  To access the LOG APIs, the application should include its header file as
41  *  follows:
42  *  @code
43  *  #include <ti/log/Log.h>
44  *  @endcode
45  *
46  *  ## Beta Disclaimer ##
47  *  The logging ecosystem are to be considered beta quality. They are not
48  *  recommended for use in production code by TI. APIs and behaviour will change
49  *  in future releases. Please report issues or feedback to [__E2E__][e2e].
50  *
51  *  [e2e]: https://e2e.ti.com/
52  *
53  *  ## Definitions ##
54  *
55  *  The following terms are used throughout the log documentation.
56  *
57  *  | Term                            | Definition                                                                                                                                                                                                                                                          |
58  *  |---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
59  *  | `LogModule`                     | A parameter passed to Log APIs to indicate which software module the log statement originated from. Modules also control the routing of logs to sinks.                                                                                                              |
60  *  | `LogLevel`                      | The severity or importance of a given log statement.                                                                                                                                                                                                                |
61  *  | `Sink`                          | Also simply called a logger. This is a transport specific logger implementation. <br> The Logging framework is flexible such that multiple sinks may exist in a single firmware image.                                                                              |
62  *  | `CallSite`                      | A specific invocation of a Log API in a given file or program.                                                                                                                                                                                                      |
63  *  | `Record`                        | The binary representation of a log when it is stored or transported by a given sink. The log record format varies slightly with each sink depending on their implementation and needs. However, they all convey the same information.                               |
64  *  | Link Time Optimization (LTO)    | A feature of some toolchains that can significantly reduce the code overhead of the log statements through a process called dead code elimination. In order to maximize the benefits of this, all static libraries and application files should have LTO enabled.   |
65  *
66  *  ## Summary ##
67  *
68  *  The following sections describe the usage of the TI logging system
69  *  implementation. This document will focus on the target (i.e. code that runs)
70  *  on the embedded device. For associated PC tooling, please see the
71  *  [README](../../../tools/log/tiutils/Readme.html) in the tools/log/tiutils/
72  *  folder.
73  *
74  *  Design Philosophy:
75  *
76  *  * Logs target code should be as efficient as possible.
77  *    * This means that Log APIs should minimize FLASH, RAM, and execution
78  *      overhead.
79  *  * Complexity should be pushed to host side tooling where possible.
80  *    *  Even if this means that PC setup/tooling requirements are more complex.
81  *  * Multiple log sink implementations shall be able to exist in a system.
82  *    *  Where applicable, multiple instances should be supported (e.g. multiple
83  *       circular buffers to collect logs)
84  *  * It shall be possible to remove logging entirely using the preprocessor
85  *  * Configuration of logging should be deferred to application compile and
86  *    link time.
87  *    * That means that the end application builder should make decisions about
88  *       the logging settings. This means that TI provided libraries are not
89  *       opinionated about what log levels should be enabled or how modules
90  *       should be routed to sinks.
91  *  * TI's logging system will leverage SysConfig out of the box, but it should
92  *    be possible to configure and use logging easily without the needing
93  *    SysConfig.
94  *
95  *  ## Stated Limitations ##
96  *
97  *  * It is not possible to control which log sink is used for each call site.
98  *    Routing of logs is controlled at a module level.
99  *  * A maximum of 8 arguments is supported for variadic APIs.
100  *
101  *  ## Anatomy of Log Statement ##
102  *
103  *  At the core of the logging implementation is heavy use of the C
104  *  preprocessor. When reading an application, the Log APIs may look like
105  *  function calls, but the preprocessor expands them heavily.
106  *
107  *  There are several ways in which the preprocessor is used.
108  *
109  *  ### Global ###
110  *  1. To enable/disable logs globally. If `ti_log_Log_ENABLE` is not defined,
111  *     all statements are removed by the preprocessor. This does not rely on LTO
112  *     or any other optimization. It removes any traces of logs from the
113  *     program.
114  *
115  *     This define is pushed to `ti_utils_build_compiler.opt` whenever any Log
116  *     module is enabled in SysConfig.
117  *
118  *  ### Module ###
119  *  1. To enable/disable logs by module. If
120  *     `ti_log_Log_ENABLE_<MyLogModuleName>=1` is not defined, all statements
121  *     using that Log module are removed by the preprocessor. This does not rely
122  *     on LTO or any other optimization. Removing the define removes all traces
123  *     of the log from the compiled code. Just defining the symbol name
124  *     `ti_log_Log_ENABLE_<MyLogModuleName>` without setting it to 1 will not
125  *     include Log statements during compilation.
126  *
127  *     These defines are automatically pushed to `ti_utils_build_compiler.opt`
128  *     for all modules configured in SysConfig.
129  *
130  *     Some TI libraries that have logging enabled also contain multiple log
131  *     modules. Enabling only a subset of Log modules via the preprocessor will
132  *     not cause the Log statements associated with the remaining Log modules to
133  *     be removed since this is a compile-time event. The Log statements
134  *     associated with individual modules can be removed from logging-enabled TI
135  *     libraries by recompiling those libraries without the module-level flags
136  *     in question.
137  *
138  *  ### Per Log Statement ###
139  *  1. (Level filtering): Insert the if statement that checks if the log level
140  *     of the statement has been enabled in its module configuration. If the log
141  *     level is not enabled, the process ends here.
142  *
143  *  2. (String declaration): Automate placement of constant strings, format
144  *     strings, and pointers to these strings in the the nonloadable metadata
145  *     section of the out file. This saves FLASH on the target. Each string
146  *     contains a large amount of data, including the following:
147  *
148  *     * File and line number of the log statement
149  *     * The log level and module of the log statement
150  *     * The format string
151  *     * The number of arguments
152  *
153  *  3. (Argument counting): Log APIs are variadic in nature, up to 8 arguments
154  *     are supported. However, at preprocess time, the number of arguments must
155  *     be known.
156  *
157  *  4. (Name spacing): Routing from module to sink. The module parameter of the
158  *     Log API controls which sink its log statements will be routed to. The
159  *     preprocessor does name expansion to look up the enabled log levels and
160  *     selected sink function pointers from the module's configuration
161  *     structure. NOTE: The used sink may require initialization. Please verify
162  *     with the specific sink documentation on how to initialize the sink.
163  *
164  *  5. (Sink API Invocation): With the names resolved and levels checked, the
165  *     logger is now ready to execute the sink function. This is done via
166  *     function pointer.
167  *
168  *  An simplified pseudo-C implementation of what `Log_printf(LogModule_App1,
169  *  Log_DEBUG, "Hello World!");` would expand to is shown below. This will not
170  *  compile and is not extensive, just for illustration.
171  *
172  *  @code
173  *  // Global log enable check, wrapped around each log site
174  *  #if defined(ti_log_Log_ENABLE)
175  *      #if ti_log_Log_ENABLE_LogModule_App1 == 1
176  *          // Check if the level of this specific log statement has been enabled by the module
177  *          if (LogMod_LogModule_App1.levels & level) {
178  *                // Note that ^^ is the record separator. Pack meta information into format string. This is stored off target.
179  *                const string logMeta = "LOG_OPCODE_FORMATED_TEXT^^"../../log.c"^^80^^Log_DEBUG^^LogMod_LogModule_App1^^"Hello World!"^^0";
180  *                // Route log to the selected sink implementation. This is done via function pointer.
181  *                // The 0 indicates no arguments. If runtime arguments were provided, they would follow.
182  *                LogMod_LogModule_App1.printf(pointerToModuleConfig, 0);
183  *          }
184  *      #endif
185  *  #endif
186  *  @endcode
187  *
188  *  From here, the logger has transferred control over to the sink
189  *  implementation, which varies based on the transport (e.g. circular buffer in
190  *  memory or UART).
191  *
192  *  ## Modules ##
193  *
194  *  When adding log statements to the target software, it is recommended to
195  *  create a logging module for each software component in the image. Modules
196  *  enable the reader to understand where the log record originated from. Some
197  *  log visualizers may allow the reader to filter or sort log statements by
198  *  module. It is also recommended to namespace modules.
199  *
200  *  For example, a good module name for the `UART` driver that exists in
201  *  `source/ti/drivers`, could be `ti_drivers_UART`.
202  *
203  *  Modules also control the routing of log records to a sink. Routing is
204  *  controlled via the LogModule panel in SysConfig, but can be changed in plain
205  *  C code using the macro @ref Log_MODULE_DEFINE and passing the sink specific
206  *  `Log_MODULE_INIT_` to the `init` parameter within the @ref Log_MODULE_DEFINE
207  *  macro. An example for the LogBuf sink is below, it will do the following
208  *
209  *  1. Create a module called `LogModule_App1`.
210  *  1. Initialize the module for use with the buffer based LogSink. Use buffer
211  *     instance called `CONFIG_ti_log_LogSinkBuf_0`.
212  *  1. Enable only the `Log_ERROR` level. Other logs will not be stored.
213  *
214  *  @code
215  *  #include <ti/log/Log.h>
216  *  #include <ti/log/LogSinkBuf.h>
217  *  Log_MODULE_DEFINE(LogModule_App1, Log_MODULE_INIT_SINK_BUF(CONFIG_ti_log_LogSinkBuf_0, Log_ERROR));
218  *  @endcode
219  *
220  *  TI created libraries will never use @ref Log_MODULE_DEFINE. This leaves the
221  *  choice of routing logs to their sinks to the end application writer. This is
222  *  recommended when creating any static libraries to defer the final logging
223  *  decisions to link time.
224  *
225  *  Each new module will instantiate a Log_Module structure with a `levels`
226  *  bitmap and pointers to the selected sink implementation and sink
227  *  configuration. See the @ref Log_Module structure for more information.
228  *
229  *  ## Levels ##
230  *  Log levels are a way to indicate the severity or importance of the contents
231  *  of a particular log call site. Each call site takes an argument that allows
232  *  the user to specify the level. As with modules, log visualization tools
233  *  allow the user to sort or filter on a given level. This can help the reader
234  *  to find important or relevant log statements in visualization.
235  *
236  *  Log levels are also used to control the emission of logs. Each call site
237  *  will check that the level is enabled before calling the underlying log API.
238  *
239  *  Depending on optimization, the check at each log statement for whether the
240  *  given level is enabled or not may end up being optimized away, and the
241  *  entire log statement may be optimized away if the log level is not enabled.
242  *
243  *  @code
244  *  if ((level) & module.levels) {// Call Log API
245  *  }
246  *  @endcode
247  *
248  *  Optimization level `-flto` for both the TICLANG toolchain and GCC will
249  *  typically be able to optimize the above statement.
250  *
251  *  @remark
252  *
253  *  ## Log Metadata ##
254  *
255  *  Each time a Log API is invoked, a metadata string is placed in the .out
256  *  file. This string contains information about the API type, file, line
257  *  module, level, and other information associated with the log call site. Each
258  *  call site emits a string to a specific memory section called `.log_data`. In
259  *  addition to this, a pointer to the string in .log_data is stored in another
260  *  section called `.log_ptr`. Because the .log_ptr section is always in the
261  *  same location, and each entry is the same size, an indexing-scheme can be
262  *  used to refer to each log-string. Entry 0 in .log_ptr would point to the
263  *  first string, entry 1 would point to the second string, etc. This Is
264  *  necessary on some devices where transmitting an entire 32-bit address as a
265  *  reference to the string is not possible, and instead an 8-bit index can be
266  *  transmitted across the Log sink implementation instead. In order to use
267  *  logging, this section should be added to the linker command file. By
268  *  default, this section points to a nonloadable region of memory. Meaning that
269  *  the metadata will not be loaded on the target device. Instead, the various
270  *  logging visualization tools such as wireshark and TI ROV2 will read the
271  *  metadata from this section and properly decode the log statements. The
272  *  benefit of this approach is that very little memory is consumed on target.
273  *  Additionally, the log transport only needs to store or send pointers to this
274  *  meta section when a log API is called.
275  *
276  *  This approach minimizes the amount of memory consumed on device and bytes
277  *  sent over the transport. This section can be loaded on target if desired or
278  *  if you are creating a custom logger. The design does not preclude this.
279  *
280  *  In order to use the logging framework, the log section must be added to the
281  *  linker command file. Here is a sample for the TI linker. Other examples can
282  *  be found in the TI provided linker files for each toolchain.
283  *
284  *  @code
285  *  MEMORY
286  *  {
287  *      // List other memory regions here
288  *      LOG_DATA (R) : origin = 0x90000000, length = 0x40000
289  *      LOG_PTR  (R) : origin = 0x94000008, length = 0x40000
290  *  }
291  *  SECTIONS
292  *  {
293  *       .log_data       :   > LOG_DATA, type = COPY
294  *       .log_ptr        : { *(.log_ptr*) } > LOG_PTR align 4, type = COPY
295  *  }
296  *  @endcode
297  *
298  *  ## Sinks ##
299  *
300  *  Sinks are responsible for storing or transporting the log record. In general
301  *  there are two categories of sinks:
302  *
303  *  1. Those that perform storage of logs.
304  *  2. Those that stream logs over a transport medium, and thus do not perform
305  *     storage.
306  *
307  *  Sinks may vary in their implementation based on the nature of the storage or
308  *  transport that they support, but they all have the following in common:
309  *
310  *  * Are named ti_log_LogSink<SinkName>. Where `<SinkName>` is the name of the
311  *    sink.
312  *  * Must implement the Log_printf and Log_buf APIs from this file.
313  *  * Must provide _USE, _INIT, and _DEFINE macros.
314  *
315  *  In addition, some sinks require initialization. This will be listed in the
316  *  documentation for the sink implementation. Sinks are closely tied to their
317  *  associated host side tooling. Since the log statements are not parsed at all
318  *  by the target code, this must be delegated to a program running on a PC.
319  *  While the binary format of log records may vary across sink implementations,
320  *  it is suggested that each log record contain:
321  *
322  *  1. Timestamp
323  *  1. Pointer to metadata string. This will be looked up by the PC side tooling
324  *     in the out file.
325  *  1. Runtime arguments
326  *
327  *  This is the minimum amount of information needed to decode a log statement.
328  *
329  *  # Usage #
330  *  This section provides a basic @ref ti_log_LOG_Synopsis "usage summary" and a
331  *  set of @ref ti_log_LOG_Examples "examples" in the form of commented code
332  *  fragments. Detailed descriptions of the LOG APIs are provided in subsequent
333  *  sections.
334  *
335  *  @anchor ti_log_LOG_Synopsis
336  *  ### Synopsis ###
337  *
338  *  @code
339  *  // Import the Log header
340  *  #include <ti/log/Log.h>
341  *
342  *  // Define your log module and log sink
343  *  // If using SysConfig, it will be done automatically, or it can be done manually:
344  *  // Use helper macro from <ti/log/LogSinkBuf.h> to make a sink instance (buffer + config) with 100 entries.
345  *  Log_SINK_BUF_DEFINE(MyBufferSink, LogSinkBuf_Type_CIRCULAR, 100);
346  *
347  *  // Use helper macro from <ti/log/Log.h> to make a module pointing at the new sink instance.
348  *  // This example will enable all log levels
349  *  Log_MODULE_DEFINE(MyModule, Log_MODULE_INIT_SINK_BUF(MyBufferSink, Log_ALL))
350  *
351  *  // Some log sinks may require special initialization to configure hardware. Refer to the documentation of
352  *  // the sink you wish to use. For example, LogSinkITM must be initialised like this before it can be used:
353  *  // LogSinkITM_init();
354  *
355  *  // Invoke one of the log APIs you want to use for either pre-defined events or formatted strings
356  *  Log_printf(MyModule, Log_DEBUG, "The answer is %d", 42);
357  *  uint8_t buffer[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
358  *  Log_buf(MyModule, Log_VERBOSE, buffer, sizeof(buffer));
359  *  @endcode
360  *
361  *
362  *
363  *  @anchor ti_log_LOG_Examples
364  *  ### Examples ###
365  *  * @ref ti_utils_LOG_Example_event "Log Event"
366  *  * @ref ti_utils_LOG_Example_printf "Log printf"
367  *  * @ref ti_utils_LOG_Example_buf "Logging buffers"
368  *
369  *  @anchor ti_utils_LOG_Example_event **Log Event**:
370  *
371  *  The following example demonstrates how to create a log event object and use
372  *  it in the code. There are two steps to using a log event: 1. instantiation
373  *  and 2. call site(s). Instantiation creates the event and the necessary
374  *  metadata, and call site is where the event is actually recorded by the
375  *  logger framework.
376  *
377  *  @code
378  *  // Create a log event data type called LogEvent_count
379  *  // The log module is MyModule
380  *  // The format string is "count=%d" -- this should describe what the event does
381  *
382  *  Log_EVENT_DEFINE(LogEvent_count, "count=%d");
383  *  @endcode
384  *
385  *  Later on, in the application, the count event is consumed. Note the log
386  *  module must match between event creation and call site. In the code below, a
387  *  LogEvent record is created for serialization or stage by the Log sink.
388  *
389  *  @code
390  *  Log_EVENT_USE(LogEvent_count); // If not defined in same file
391  *  // ...
392  *  Log_event(MyModule, Log_DEBUG, LogEvent_count, count++);
393  *  @endcode
394  *
395  *  @anchor ti_utils_LOG_Example_printf **Log Printf**:
396  *
397  *  The following example demonstrates use of the Log printf API. in code. Log
398  *  will embed the format string in the call site and will take arguments using
399  *  varadic arguments.
400  *
401  *  @code
402  *  Log_printf(MyModule, Log_DEBUG, "Hello World!");
403  *  @endcode
404  *
405  *  The arguments are type-casted into a uintptr_t, which is an unsigned integer
406  *  type. This limits the supported format specifiers to the following:
407  *      - Unsigned decimal integer: \%u
408  *      - Unsigned hexadecimal integer: \%x
409  *      - Unsigned hexadecimal integer (capital letters): \%X
410  *      - Character: \%c
411  *      - Signed decimal integer for positive values: \%i, \%d
412  *      - Signed octal for positive values: \%o
413  *
414  *  @anchor ti_utils_LOG_Example_buf **Log Buf**:
415  *
416  *  The following example demonstrates use of the Log buf API. in code.
417  *
418  *  Buf will embed the format string in the call site and will take the buffer
419  *  as a pointer and length. Buffers are treated as arrays of bytes. The buffer
420  *  API should only be used when it is necessary to log data that is only
421  *  available at runtime. It will actually send or store the entire contents of
422  *  the buffer, so this API should be used sparingly as it is costly in terms of
423  *  runtime and memory overhead.
424  *
425  *  @code
426  *  uint8_t bufferToLog[] = {0, 1, 2, 3, 4, 5};
427  *  Log_buf(ti_log_LogMain, Log_DEBUG, "The contents of bufferToLog are: ", bufferToLog, sizeof(bufferToLog));
428  *  @endcode
429  *
430  *  @anchor ti_utils_LOG_Example_guide **Log API usage**:
431  *
432  *  For a uniform experience with the logging tool, users are recommended to
433  *  follow certain guidelines regarding the Log API. Typical use-cases for each
434  *  API call is described below
435  *
436  *  #### Log_printf ####
437  *
438  *  Log_printf should be the default mechanism for emitting a log statement
439  *  within an application. Along with the Log-levels, Log_printf should be used
440  *  to communicate debug information as a formatted string, which accepts
441  *  variadic arguments. In this case, a pointer to the string and the arguments
442  *  themselves are transported by the Log sink.
443  *
444  *  @code
445  *  Log_printf(MyLibraryLogModule, Log_ERROR, "Library function received illegal argument: %d", arg);
446  *  @endcode
447  *
448  *  #### Log_event ####
449  *
450  *  Log_event is meant to represent more generic debug-information, and
451  *  typically something that can occur from anywhere in the application, as
452  *  opposed to being localized in a single library. Events can also be defined
453  *  once and referenced from anywhere in the application, so the same event can
454  *  be used by multiple libraries. A generic example would be an event such as
455  *  "Entering critical section"
456  *
457  *  @code
458  *  Log_EVENT_DEFINE(LogEvent_enterCritical, "Entering critical section");
459  *
460  *  Log_EVENT_USE(LogEvent_enterCritical); // If not defined in same file
461  *  // ...
462  *  Log_event(MyModule, Log_DEBUG, LogEvent_enterCritical);
463  *  @endcode
464  *
465  *  #### Log_buf ####
466  *
467  *  When the debug-information to be emitted is a large amount of dynamic data,
468  *  and is not suitable as an argument to printf, then Log_buf should be used.
469  *  Log_buf can transport the contents of large dynamic buffers, and as a
470  *  consequence has a larger overhead and should be used sparsely.
471  */
472 
473 #ifndef ti_log_Log__include
474 #define ti_log_Log__include
475 
476 /*! @ingroup ti_log_LOG */
477 /*@{*/
478 
479 /*
480  *  ======== Log.h ========
481  *  @brief Contains Log library APIs
482  */
483 #include <stdint.h>
484 #include <stddef.h>
485 
486 #if defined (__cplusplus)
487 extern "C" {
488 #endif
489 
490 /*
491  *  ======== ti_log_Log_ENABLE ========
492  *  Enable instrumentation using link-time optimization implementation
493  *
494  *  Define this symbol to add instrumentation at compile time.
495  *  It must be defined before including this header file.
496  */
497 #if ti_log_Log_ENABLE
498 /*
499  *  =============================
500  *  ======== Log Enabled ========
501  *  =============================
502  */
503 
504 #define Log_TI_LOG_VERSION 0.1.0
505 /**
506  *  @brief Defines a log module
507  *
508  *  Log modules are like namespaces for log statements, but also controls the
509  *  enabled log levels and decides where the log statement is redirected.
510  *
511  *  @param[in]  name       Name of the log module. Gets prefixed with `LogMod_`.
512  *  @param[in]  init       Initialization macro from the wanted sink
513  *
514  *  This is a helper to define `Log_Module LogMod_yourName` and initialize it
515  *  with the configuration and functions of the wanted log sink.
516  *
517  *  For example, you have already used the sink definition macros found in
518  *  LogSinkITM.h, and now you want to define a new module that uses this:
519  *
520  *   `Log_MODULE_DEFINE(MyDriver, Log_MODULE_INIT_SINK_ITM(Log_DEBUG | Log_ERROR))`
521  *
522  *  Perhaps you used the LogSinkBuf.h helper macro which needs a unique name
523  *  per instance and made a separate buffer for critical errors:
524  *
525  *   `Log_MODULE_DEFINE(MyCritical, Log_MODULE_INIT_SINK_BUF(criticalBuf, Log_ERROR)`
526  *
527  *  You would use this in your application via
528  *      `Log(MyCritical, Log_ERROR, "Oops")`
529  */
530 #define Log_MODULE_DEFINE(name, init) const Log_Module LogMod_ ## name = init
531 
532 /**
533  *  @brief Defines Log module as weak
534  *
535  *  If there are multiple modules containing Log statements per library,
536  *  special care must be taken not to create link-time failures.
537  *  Whether Log statements from a library are present in the final binary is
538  *  determined by the library configuration the application links against
539  *  (instrumented vs uninstrumented).
540  *  Each Log statement has a link-time dependency on its Log module. Enabling
541  *  only a subset of Log modules contained within the library will cause any
542  *  Log statements from other Log modules of that library to fail at link-time.
543  *  This is avoided by declaring a weak instance of each Log module in C code
544  *  that is compiled into the library. That way, the SysConfig-generated Log
545  *  module definitions will override the weak library ones but they are there
546  *  if SysConfig does not define that particular module.
547  *
548  *  @param[in]  name    Name of the log module. Gets prefixed with `LogMod_`.
549  *  @param[in]  init    Initialization value of the Log_Module struct.
550  */
551 #if defined(__IAR_SYSTEMS_ICC__)
552 #define Log_MODULE_DEFINE_WEAK(name, init) const __weak Log_Module LogMod_ ## name = init
553 #elif defined(__TI_COMPILER_VERSION__) || (defined(__clang__) && defined(__ti_version__)) || defined(__GNUC__)
554 #define Log_MODULE_DEFINE_WEAK(name, init) const Log_Module LogMod_ ## name __attribute__((weak)) = init
555 #else
556 #error "Incompatible compiler: Logging is currently supported by the following \
557 compilers: TI ARM Compiler, TI CLANG Compiler, GCC, IAR. Please migrate to a \
558 a supported compiler."
559 #endif
560 
561 /**
562  *  @brief Declares a reference to a log module
563  *
564  *  Declares that a log module is defined in another file so that it can be
565  *  used in the file with this macro in it.
566  *
567  *  @note This is done automatically for `Log` and `Log_buf` statements.
568  *
569  *  @param[in]  name Name of the log module. Gets prefixed with `LogMod_`.
570  */
571 #define Log_MODULE_USE(name) extern const Log_Module LogMod_ ## name
572 
573 /**
574  *  @brief Resolves to the symbol name of the log module
575  *
576  *  Provided for forward compatibility purposes should you have a need to
577  *  reference the log module symbol directly.
578  */
579 #define LOG_MODULE_SYM(name) LogMod_ ## name
580 
581 /** @cond NODOC */
582 
583 /* This macro protects against side effects of the C preprocessor expansion
584  * of log statements. Each log API should be guarded by it.
585  * An article explaining this behavior can be found here:
586  * https://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html
587  */
588 #define _Log_GUARD_MACRO(x) do{ x }while(0)
589 
590 /*
591  *
592  *  ======== Log Private Macros ========
593  *
594  *  The following macros are intended to be private to the log module and
595  *  are not intended for use by the user. Private macros will start with _Log.
596  *
597  *  In the case of multi level macros (macros that invoke other macros), a
598  *  letter is appended at the end of the definition. With each level of nesting,
599  *  the appended letter is incremented.
600  *
601  *  For example: _Log_test --> _Log_test_A --> _Log_test_B
602  */
603 /* Extracts the first/remaining argument from __VA_ARGS__ */
604 #define _Log_CAR_ARG(N, ...) N
605 #define _Log_CDR_ARG(N, ...) __VA_ARGS__
606 
607 
608 /*
609  *  ======== Meta string tokenization macros ========
610  */
611 /*  Helper macro to concatenate two symbols */
612 #define _Log_CONCAT2_A(x,y) x ## _ ## y
613 #define _Log_CONCAT2(x,y) _Log_CONCAT2_A(x,y)
614 #define _Log_CONCAT3(x,y,z) _Log_CONCAT2(x,_Log_CONCAT2(y,z))
615 
616 /*  Helper macro to concatenate two symbols */
617 #define _Log__TOKEN2STRING_A(x) #x
618 #define _Log_TOKEN2STRING(x) _Log__TOKEN2STRING_A(x)
619 
620 /* Macro to place meta string in a memory section separated by record separator */
621 #define _Log_APPEND_META_TO_FORMAT(opcode,                                     \
622                                     file,                                      \
623                                     line,                                      \
624                                     level,                                     \
625                                     module,                                    \
626                                     format,                                    \
627                                     nargs)                                     \
628                                     _Log_TOKEN2STRING(opcode)     "\x1e"       \
629                                     _Log_TOKEN2STRING(file)       "\x1e"       \
630                                     _Log_TOKEN2STRING(line)       "\x1e"       \
631                                     _Log_TOKEN2STRING(level)      "\x1e"       \
632                                     _Log_TOKEN2STRING(module)     "\x1e"       \
633                                     _Log_TOKEN2STRING(format)     "\x1e"       \
634                                     _Log_TOKEN2STRING(nargs)
635 
636 /* Place a string in trace format section named ".log_data" locally
637  * This section must exist in the linker file
638  */
639 #if defined(__IAR_SYSTEMS_ICC__)
640 #define _Log_PLACE_FORMAT_IN_SECTOR(name, opcode, level, module, format, nargs)\
641             __root static const char name[] @ ".log_data" =                    \
642             _Log_APPEND_META_TO_FORMAT(opcode,                                 \
643                                        __FILE__,                               \
644                                        __LINE__,                               \
645                                        level,                                  \
646                                        module,                                 \
647                                        format,                                 \
648                                        nargs);                                 \
649            __root static const char * const _Log_CONCAT2(Ptr, name) @ _Log_TOKEN2STRING(_Log_CONCAT2(.log_ptr, module)) = name;
650 #elif defined(__TI_COMPILER_VERSION__) || (defined(__clang__) && defined(__ti_version__)) || defined(__GNUC__)
651 #define _Log_PLACE_FORMAT_IN_SECTOR(name, opcode, level, module, format, nargs)\
652             static const char name[]                                           \
653             __attribute__((used,section(".log_data"))) =                       \
654             _Log_APPEND_META_TO_FORMAT(opcode,                                 \
655                                         __FILE__,                              \
656                                         __LINE__,                              \
657                                         level,                                 \
658                                         module,                                \
659                                         format,                                \
660                                         nargs);                                \
661             static const char * const _Log_CONCAT2(Ptr, name)                  \
662             __attribute__((used,section(_Log_TOKEN2STRING(_Log_CONCAT3(.log_ptr, __LINE__, module))))) = name;
663 #else
664 #error "Incompatible compiler: Logging is currently supported by the following \
665 compilers: TI ARM Compiler, TI CLANG Compiler, GCC, IAR. Please migrate to a \
666 a supported compiler."
667 #endif
668 
669 /*
670  *  ======== Variadic macro workaround ========
671  */
672 /*  Helper macro to count the number of arguments in __VA_ARGS_ */
673 #define _Log_NUMARGS(...) _Log_NUMARGS_A(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
674 #define _Log_NUMARGS_A(...) _Log_NUMARGS_B(__VA_ARGS__)
675 #define _Log_NUMARGS_B(_first, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
676 
677 /*
678  * Helper to select arg/noarg variant macro since empty va_arg fails
679  * when arguments are expected. Eg
680  *   Log_VARIANT(test, A, 7, "Hello") -> test__noarg(A, 7, "Hello")
681  *   Log_VARIANT(test, A, 7, "Hello %d", 42) -> test__arg1(A, 7, "Hello %d", 42)
682  */
683 #define _Log_VARIANT(x, module, level, ...) \
684     _Log_CONCAT2(x, _Log_NUMARGS_B(__VA_ARGS__, _arg8, _arg7, _arg6, _arg5, _arg4, _arg3, _arg2, _arg1, _noarg)) ( module, level, __VA_ARGS__ )
685 
686 /*
687  *  ======== Module-level preprocessor include macros ========
688  */
689 
690 /* Helper macro to extract the second argument of a variable number of input
691  * args
692  */
693 #define _Log_SECOND_ARG(x, y, ...) y
694 
695 /* Temporary token name.
696  * Name must end in "1" for preprocessor substitution below to work.
697  */
698 #define _Log_TOKEN_1 0,
699 
700 /* Helper macro to check whether a symbol is defined with a non-zero value.
701  * If x is a preprocessor define, the conversion below shows the macro output:
702  * x = 0 -> 0
703  * x = 1 -> 1
704  * x (no value) -> 0
705  * (undefined) -> 0
706  */
707 #define _Log_DEFINED(x)     _Log_DEFINED_A(x)
708 
709 /* If x is 1, _Log_TOKEN_##y turns into _Log_TOKEN_1 and is replaced with "0,"
710  * If x is anything else, _Log_TOKEN_##y turns into _Log_TOKEN_y.
711  */
712 #define _Log_DEFINED_A(y)   _Log_DEFINED_B(_Log_TOKEN_##y)
713 
714 /* If z is "0,", _Log_SECOND_ARG is called with the triplet "0, 1, 0" and
715  * selects the second item in it, 1.
716  * If z is anything else, _Log_SECOND_ARG is called with the tuple "z 1, 0" and
717  * selects the second item in it, 0.
718  */
719 #define _Log_DEFINED_B(z)   _Log_SECOND_ARG(z 1, 0)
720 
721 /* Empty Log buf macro to use when a log module is not enabled in the
722  * preprocessor during compilation
723  */
724 #define _Log_buf_C_0(module, level, format, data, size)
725 
726 /* Log_buf macro to use when a log module is enabled in the preprocessor during
727  * compilation.
728  */
729 #define _Log_buf_C_1(module, level, format, data, size)                                                 \
730     _Log_GUARD_MACRO(                                                                                   \
731         Log_MODULE_USE(module);                                                                         \
732             if ((Log_ENABLED & LogMod_ ## module.levels) &&  ((level) & LogMod_ ## module.levels)) {    \
733                 _Log_PLACE_FORMAT_IN_SECTOR(_Log_CONCAT2(LogSymbol, __LINE__),                          \
734                                             LOG_OPCODE_BUFFER,                                          \
735                                             level,                                                      \
736                                             LogMod_ ## module,                                          \
737                                             format,                                                     \
738                                             0);                                                         \
739                 LogMod_ ## module.buf(&LogMod_ ## module,                                               \
740                         (uint32_t)&_Log_CONCAT2(LogSymbol, __LINE__),                                   \
741                         (uint32_t)&_Log_CONCAT3(Ptr, LogSymbol, __LINE__),                              \
742                         data,                                                                           \
743                         size);                                                                          \
744             }                                                                                           \
745     )
746 
747 /* First level indirection macro for Log_buf that delegates between an empty
748  * implementation and the actual log emission based on whether a module is
749  * enabled in the preprocessor during compilation.
750  *
751  * The _Log_DEFINED() macro generates a token output of [0, 1] that is then
752  * concatenated with "_Log_buf_C" to form the correct delegate macro name.
753  *
754  * The expected module define name is ti_log_Log_ENABLE_ | <module> and must be
755  * set to 1. E.g. "-Dti_log_Log_ENABLE_MyLogModule=1". Just defining the symbol in
756  * the preprocessor will not emit any logs.
757  */
758 #define _Log_buf_B(module, level, format, data, size) \
759     _Log_CONCAT2(_Log_buf_C, _Log_DEFINED(ti_log_Log_ENABLE_ ## module))(module, level, format, data, size)
760 
761 /*
762  * Redirects to cast all printf arguments to uintptr_t to avoid surprises if
763  * passing wider values and the compiler silently allows it.
764  */
765 #define _Log_printf__arg1(module, level, fmt, a0)                              \
766     _Log_printf__arg(module, level, fmt, (uintptr_t)a0)
767 #define _Log_printf__arg2(module, level, fmt, a0, a1)                          \
768     _Log_printf__arg(module, level, fmt, (uintptr_t)a0,                        \
769                                          (uintptr_t)a1)
770 #define _Log_printf__arg3(module, level, fmt, a0, a1, a2)                      \
771     _Log_printf__arg(module, level, fmt, (uintptr_t)a0,                        \
772                                          (uintptr_t)a1,                        \
773                                          (uintptr_t)a2)
774 #define _Log_printf__arg4(module, level, fmt, a0, a1, a2, a3)                  \
775     _Log_printf__arg(module, level, fmt, (uintptr_t)a0,                        \
776                                          (uintptr_t)a1,                        \
777                                          (uintptr_t)a2,                        \
778                                          (uintptr_t)a3)
779 #define _Log_printf__arg5(module, level, fmt, a0, a1, a2, a3, a4)              \
780     _Log_printf__arg(module, level, fmt, (uintptr_t)a0,                        \
781                                          (uintptr_t)a1,                        \
782                                          (uintptr_t)a2,                        \
783                                          (uintptr_t)a3,                        \
784                                          (uintptr_t)a4)
785 #define _Log_printf__arg6(module, level, fmt, a0, a1, a2, a3, a4, a5)          \
786     _Log_printf__arg(module, level, fmt, (uintptr_t)a0,                        \
787                                          (uintptr_t)a1,                        \
788                                          (uintptr_t)a2,                        \
789                                          (uintptr_t)a3,                        \
790                                          (uintptr_t)a4,                        \
791                                          (uintptr_t)a5)
792 #define _Log_printf__arg7(module, level, fmt, a0, a1, a2, a3, a4, a5, a6)      \
793     _Log_printf__arg(module, level, fmt, (uintptr_t)a0,                        \
794                                          (uintptr_t)a1,                        \
795                                          (uintptr_t)a2,                        \
796                                          (uintptr_t)a3,                        \
797                                          (uintptr_t)a4,                        \
798                                          (uintptr_t)a5,                        \
799                                          (uintptr_t)a6)
800 #define _Log_printf__arg8(module, level, fmt, a0, a1, a2, a3, a4, a5, a6, a7)  \
801     _Log_printf__arg(module, level, fmt, (uintptr_t)a0,                        \
802                                          (uintptr_t)a1,                        \
803                                          (uintptr_t)a2,                        \
804                                          (uintptr_t)a3,                        \
805                                          (uintptr_t)a4,                        \
806                                          (uintptr_t)a5,                        \
807                                          (uintptr_t)a6,                        \
808                                          (uintptr_t)a7)
809 
810 #define _Log_printf__arg(module, level, ...)                                   \
811     module.printf(&module,                                                     \
812                        (uint32_t)&_Log_CONCAT2(LogSymbol, __LINE__),           \
813                        (uint32_t)&_Log_CONCAT3(Ptr, LogSymbol, __LINE__),      \
814                        _Log_NUMARGS(__VA_ARGS__),                              \
815                        _Log_CDR_ARG(__VA_ARGS__))
816 
817 #define _Log_printf__noarg(module, level, ...)                                 \
818     module.printf(&module,                                                     \
819                        (uint32_t)&_Log_CONCAT2(LogSymbol, __LINE__),           \
820                        (uint32_t)&_Log_CONCAT3(Ptr, LogSymbol, __LINE__),      \
821                        _Log_NUMARGS(__VA_ARGS__))
822 
823 /* Empty Log_printf macro to use when a log module is not enabled in the
824  * preprocessor during compilation
825  */
826 #define _Log_printf_C_0(opcode, module, level, ...)
827 
828 /* Log_printf macro to use when a log module is enabled in the preprocessor during
829  * compilation.
830  */
831 #define _Log_printf_C_1(opcode, module, level, ...)                            \
832     _Log_GUARD_MACRO(                                                          \
833         Log_MODULE_USE(module);                                                \
834         if ((Log_ENABLED & LogMod_ ## module.levels) &&                        \
835             ((level) & LogMod_ ## module.levels)) {                            \
836             _Log_PLACE_FORMAT_IN_SECTOR(_Log_CONCAT2(LogSymbol, __LINE__),     \
837                                         opcode,                                \
838                                         level,                                 \
839                                         LogMod_ ## module,                     \
840                                         _Log_CAR_ARG(__VA_ARGS__),             \
841                                         _Log_NUMARGS(__VA_ARGS__))             \
842             _Log_VARIANT(_Log_printf, LogMod_ ## module, level, __VA_ARGS__);  \
843         }                                                                      \
844     )
845 
846 /* First level indirection macro for Log_printf that delegates between an empty
847  * implementation and the actual log emission based on whether a module is
848  * enabled in the preprocessor during compilation.
849  *
850  * The _Log_DEFINED() macro generates a token output of [0, 1] that is then
851  * concatenated with "_Log_buf_C" to form the correct delegate macro name.
852  *
853  * The expected module define name is ti_log_Log_ENABLE_ | <module> and must be
854  * set to 1. E.g. "-Dti_log_Log_ENABLE_MyLogModule=1". Just defining the symbol in
855  * the preprocessor will not emit any logs.
856  */
857 #define _Log_printf_B(opcode, module, level, ...) \
858     _Log_CONCAT2(_Log_printf_C, _Log_DEFINED(ti_log_Log_ENABLE_ ## module))(opcode, module, level, __VA_ARGS__)
859 
860 /** @endcond */
861 
862 /**
863  *  @brief Construct a log event object
864  *
865  *  Use this marco to define a log event object. The object is global, and may
866  *  be used in other files by invoking Log_EVENT_USE(name) there.
867  *
868  *  @param[in]  name       Event variable name, to be passed to Log_event API
869  *  @param[in]  fmt        Restricted format string. Note `%s` is not supported.
870  *                         Supported format specifiers include: `%c`, `%f`,
871  *                         `%d`, `%x`
872  */
873 #if defined(__IAR_SYSTEMS_ICC__)
874 #define Log_EVENT_DEFINE(name, fmt)                                         \
875             __root const char LogSymbol_ ## name[] @ ".log_data" =          \
876             _Log_APPEND_META_TO_FORMAT(LOG_EVENT_CONSTRUCT,                 \
877                                         __FILE__,                           \
878                                         __LINE__,                           \
879                                         name,                               \
880                                         global,                             \
881                                         fmt,                                \
882                                         0)
883 
884 #elif defined(__TI_COMPILER_VERSION__) || (defined(__clang__) && defined(__ti_version__)) || defined(__GNUC__)
885 #define Log_EVENT_DEFINE(name, fmt)                                         \
886             const char LogSymbol_ ## name[]                                 \
887             __attribute__((used,section(".log_data"))) =                    \
888             _Log_APPEND_META_TO_FORMAT(LOG_EVENT_CONSTRUCT,                 \
889                                         __FILE__,                           \
890                                         __LINE__,                           \
891                                         name,                               \
892                                         global,                             \
893                                         fmt,                                \
894                                         0)
895 #else
896 #error "Incompatible compiler: Logging is currently supported by the following \
897 compilers: TI ARM Compiler, TI CLANG Compiler, GCC, IAR. Please migrate to a \
898 a supported compiler."
899 #endif
900 
901 /**
902  *  @brief Declare usage of a log event symbol defined elsewhere
903  *
904  *  Use this marco to declare a log event symbol for use. It's just a fancy
905  *  `extern` macro.
906  *
907  *  @param[in]  name       Event variable name, to be passed to Log_event API
908  */
909 #define Log_EVENT_USE(name) extern const char[] LogSymbol_ ## name;
910 
911 
912 /**
913  *  @brief Log a continuous block of memory
914  *
915  *  Use this macro to send out runtime data from the device. This API should be
916  *  used when the data is non constant and can only be derived at runtime. It
917  *  is the most intrusive in terms of record overhead and instructions used.
918  *
919  *  @param[in]  module     Log module that the buffer originated from
920  *  @param[in]  level      log level of type @ref Log_Level
921  *  @param[in]  format     Restricted format string.
922  *  @param[in]  data       Pointer to array of bytes (uint8_t *)
923  *  @param[in]  size       Size in bytes of array to send
924  *
925  */
926 #define Log_buf(module, level, format, data, size) _Log_buf_B(module, level, format, data, size)
927 
928 /**
929  *  @brief Emit a log event
930  *
931  *  Use this marco to enable printf style logging. This API offers the most
932  *  flexibility as the construction of the format string is embedded in the call
933  *  site of the API. It also supports true variadic arguments.
934  *
935  *  @param[in]  module     Log module that the buffer originated from
936  *  @param[in]  level      Log level of type @ref Log_Level
937  *  @param[in]  event      Event to be logged. Can be either a constructed
938  *                         Log_EVENT symbol, or a printf-like format-string
939  *  @param[in]  ...        Variable amount of arguments. Must match your
940  *                         event or format-string.
941  *
942  * Examples:
943  *   `Log_printf(MyTimingEvent, t.start)`, `Log_printf("Hello World")`, `Log_printf("Age: %d", 42)`
944  *
945  *  @note All arguments are treated as 32-bit wide and are promoted or
946  *        truncated accordingly.
947  */
948 #define Log_printf(module, level, ...) _Log_printf_B(LOG_OPCODE_FORMATED_TEXT, module, level, __VA_ARGS__)
949 
950 #define Log_event(module, level, ...) _Log_printf_B(LOG_OPCODE_EVENT, module, level, __VA_ARGS__)
951 
952 /* Macro for defining the version of the Log API */
953 
954 
955 
956 #if defined(__IAR_SYSTEMS_ICC__)
957 #define _Log_DEFINE_LOG_VERSION(module, version)                                                \
958     __root static const char _Log_CONCAT2(Log_ti_log_version, __COUNTER__)[] @ ".log_data" =    \
959     _Log_APPEND_META_TO_FORMAT(LOG_OPCODE_VERSION,                                              \
960                                                 module,                                         \
961                                                 version,                                        \
962                                                 0,                                              \
963                                                 0,                                              \
964                                                 0,                                              \
965                                                 0)
966 #elif defined(__TI_COMPILER_VERSION__) || (defined(__clang__) && defined(__ti_version__)) || defined(__GNUC__)
967 #define _Log_DEFINE_LOG_VERSION(module, version)                                \
968             static const char _Log_CONCAT2(Log_ti_log_version, __COUNTER__)[]   \
969             __attribute__((used,section(".log_data"))) =                        \
970             _Log_APPEND_META_TO_FORMAT(LOG_OPCODE_VERSION,                      \
971                                                 module,                         \
972                                                 version,                        \
973                                                 0,                              \
974                                                 0,                              \
975                                                 0,                              \
976                                                 0)
977 #else
978 #error "Incompatible compiler: Logging is currently supported by the following \
979 compilers: TI ARM Compiler, TI CLANG Compiler, GCC, IAR. Please migrate to a \
980 a supported compiler."
981 #endif
982 
983 /* Generate a symbol in the elf file that defines the version of the Log API */
984 _Log_DEFINE_LOG_VERSION(Log, Log_TI_LOG_VERSION);
985 
986 #else /* ti_log_Log_ENABLE */
987 
988 /*
989  *  =================================================
990  *  ======== Log Disabled (default behavior) ========
991  *  =================================================
992  */
993 
994 #define Log_MODULE_DEFINE(...)
995 #define Log_MODULE_DEFINE_WEAK(name, init)
996 #define Log_MODULE_USE(...)
997 #define Log_EVENT_DEFINE(name, fmt)
998 #define Log_EVENT_USE(name, fmt)
999 #define Log_printf(module, level, ...)
1000 #define Log_event(module, level, ...)
1001 #define Log_buf(module, level, ...)
1002 #define _Log_DEFINE_LOG_VERSION(module, version)
1003 
1004 #endif /* ti_log_Log_ENABLE */
1005 
1006 /*
1007  *  ======== Log_Level ========
1008  */
1009 typedef enum Log_Level {
1010     Log_DEBUG = 1,                          /*!< This should be the default level, reserved to be used by users to insert into applications for debugging. Exported libraries should avoid using this level. */
1011     Log_VERBOSE = 4,                        /*!< This level is recommended to be used in libraries to emit verbose information */
1012     Log_INFO = 16,                          /*!< This level is recommended to be used in libraries to emit simple information */
1013     Log_WARNING = 64,                       /*!< This level is recommended to be used in libraries to emit warnings. It is up to the library developer to decide what constitutes a warning, but it should typically indicate something unexpected, but not something that leads to system failure */
1014     Log_ERROR = 256,                        /*!< This level is recommended to be used in libraries to emit errors. Typically, this should be used when something has failed and the system is unable to continue correct operation */
1015     Log_ALL   = 1 + 4 + 16 + 64 + 256,      /*!< This enables all levels */
1016     Log_ENABLED = 512                       /*!< This is used to enable or disable the log module, independently of the log levels */
1017 } Log_Level;
1018 
1019 typedef const struct Log_Module Log_Module;
1020 
1021 typedef void (*Log_printf_fxn)(const Log_Module *handle,
1022                               uint32_t header,
1023                               uint32_t headerPtr,
1024                               uint32_t numArgs,
1025                               ...);
1026 
1027 typedef void (*Log_buf_fxn)(const Log_Module *handle,
1028                            uint32_t header,
1029                            uint32_t headerPtr,
1030                            uint8_t *data,
1031                            size_t size);
1032 
1033 struct Log_Module {
1034     void                 *sinkConfig;
1035     const Log_printf_fxn  printf;
1036     const Log_buf_fxn     buf;
1037     uint32_t              levels;
1038 };
1039 
1040 /*! @} */
1041 #if defined (__cplusplus)
1042 }
1043 #endif
1044 
1045 #endif // ti_log_Log__include
1046