Trice And Legacy User Code
Contents
- 1. Legacy User Code Option Separate Physical Output Channel
- 2. Legacy User Code Option Trice Adaptation Edits
- 3. Legacy User Code Option Print Buffer Wrapping and Framing
- 4. Legacy User Code Option Trice Aliases Adaptation
When it comes to use legacy sources together with Trice, there are several ways doing so, which do not exclude each other:
- Legacy User Code Option Separate Physical Output Channel
- Legacy User Code Option Trice Adaptation Edits
- Legacy User Code Option Print Buffer Wrapping and Framing
- Legacy User Code Option Trice Aliases Adaptation
1. Legacy User Code Option Separate Physical Output Channel
Advantages:
- No user code adaptation at all needed.
- Code can mix user prints and Trices.
Disadvantages:
- A 2nd physical output is needed.
- The log output goes to one or the other app, what may result in a partial sequence information loss.
- Suboptimal result for target image size and speed, because the legacy user code still prints and transmits strings.
Details:
- The legacy user code output drives a terminal app and the Trice output feeds the Trice binary data into the Trice tool.
2. Legacy User Code Option Trice Adaptation Edits
Advantages:
- This is the most straight forward method.
- Optimal result for target image size and speed.
Disadvantages:
- No mixed user prints and Trices.
- Legacy code gets changed, needs new testing and is not usable parallel in other existing projects anymore.
- Error prone, even when done KI supported.
- Max 12 integers/floats OR a single runtime generated string in one Trice possible, otherwise splitting into several Trices is needed.
float xvalues need wrapping withaFloat(x).double xneeds wrapping withaDouble(x).int64anddoubleneedtrice64instead oftriceor generally use 64-bit widthtrice.
- Not applicable for a large legacy code basis.
- It is (probably) already tested code.
- It is maybe used “as is” in other projects.
- It needs a lot of manual editing work. -> This nowadays is expected to be easier with AI.
Details:
- All exising user prints are replaced with appropriate Trice macros according chapter Trice Similarities and Differences to printf Usage.
- When using 64-bit as default Trice bit width, more RAM is used compared to 32-bit, but in combination with the default TCOBS compressing framing the transmitted Trice packets do not increase much compared to 32-bit width.
3. Legacy User Code Option Print Buffer Wrapping and Framing
Trice >= v1.1 feature, see also issue #550
Advantages:
- Code can mix user prints and Trices.
- Legacy code stays unchanged and is usable parallel in other existing projects.
Disadvantages:
- Suboptimal result for target image size and speed, because the legacy user code still prints and transmits strings.
- The reserved case, both Binary Encoding stamp selector bits are 0, is not available anymore for additional use cases.
- The log output may have a partial sequence information loss.
Details:
The Trice binary encoding uses states 1, 2, 3 of the 4 states, the 2 Binary Encoding stamp selector bits can have. They located in the starting uint16_t ID value to encode the Trice (time) stamp size. If both bits are zero (state 0), the Trice tool can interpret the incoming data buffer according to a passed CLI switch; in this special case just printing it as string.
If the Trice library and the user print both write to the same output, an easy modification would be, to prepend the user print output with a 2-byte count as long its size is < 16383, so that the 2 most significant bits are zero. Additionally, the this way counted buffer needs the same buffer framing as the Trice binary data.
4. Legacy User Code Option Trice Aliases Adaptation
Trice >= v1.1 feature, see also accepted pull requests #533 and #536
Advantages:
- Code can mix user prints and Trices.
- Legacy code stays unchanged or mainly unchanged and is usable parallel in other existing projects.
- Nearly optimal result for target image size and speed.
- No special wrapping and need to use the Binary Encoding stamp selector bits state 0 for this.
- Especially, when adapting user specific ASSERT macros with
-salias(see below), even their strings are compiled into the Target image, only in error cases the strings are printed and transmitted.
Disadvantages:
- The legacy user code could partially still print and transmit strings, especially when
floatordoubleare used.
Details:
This cool functionality was contributed by @srgg in pull requests (PR) #533 and #536 (to be considered as one PR only). It allows code integration containing user specific log statements into Trice instrumented projects without the need to rename the user specific log statements.
In the assumption, most user printi statements having only up to 12 integers, those user prints could get covered by adding -alias printi to the trice insert and trice clean commands.
The user printi statements containing floats, doubles, strings could get simply renamed into user prints and then -salias prints will cover them too. That, of course, is a legacy user code change, but it allows to use this slightly modified legacy user code parallel in other projects.
Yes, user printi and user prints need to be defined too. See ./_test/alias_dblB_de_tcobs_ua/triceConfig.h/triceConfig as a simple example and its usage in ./_test/alias_dblB_de_tcobs_ua/TargetActivity.c
This technique allows also to cover legacy user code specific ASSERT macros, as shown in ./_test/aliasassert_dblB_de_tcobs_ua/triceConfig.h and used in the tests ./_test/aliasassert_dblB_de_tcobs_ua/TargetActivity.c.
Despite of these 2 CGO tests the real-world example ./examples/G0B1_inst shows the usage too.
The following sub-chapters are mainly written by @srgg as accompanying documentation to its pull requests.
4.1. PR533 Doc
4.2. PR533 Summary
This PR introduces support for treating user-defined macros as aliases to trice and triceS within the Trice CLI toolchain. The goal is to enable project-specific logging macros to be processed just like built-in Trice macros — including ID generation, decoding, and binary format support — without requiring projects to directly call trice() or triceS() in their source code.
PR leverages the -exclude source feature added in #529.
4.3. PR533 Motivation
Trice uses a source-scanning and ID generation approach, where the toolchain scans for trice(...) and triceS(...) calls, injects numeric trace IDs, and builds a mapping database. However, it currently only supports built-in(hardcoded) macros and allows only global on/off control via compile-time flags.
This makes it difficult to:
- Adopt custom naming conventions (
DBG(),APP_LOG(),MON(), etc.). - Redirect trace/logging behavior to other backends (e.g., MicroSD, raw printf, no-op).
- Change behavior per module or configuration without losing Trice tooling support.
4.4. What This PR533 Adds
CLI-level aliasing: Developers can now declare custom macros to be treated as trice or triceS equivalents. These user-defined macros will be recognized during scanning, ID injection, and decoding.
4.5. PR533 Example
print_macro.h:
#ifndef TRICE_OFF
#define DEBUG_PRINT(...) trice(__VA_ARGS__)
#define DEBUG_PRINT_S(...) triceS(__VA_ARGS__)
#else
#define DEBUG_PRINT(...) Serial.println(__VA_ARGS__)
#define DEBUG_PRINT_S(...) Serial.printf(__VA_ARGS__)
#endif
example.c:
#include "trice.h"
#include "print_macro.h"
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(10);
}
// Add code here to initialize whatever Trice sender TCP/UDP/UART, etc.
// No argument
DEBUG_PRINT("DEBUG_PRINT test: no args\n");
char* str = "Test string";
DEBUG_PRINT_S("DEBUG_PRINT_S test: %s\n", str);
}
4.5.1. PR533 Check with Trice
Insert trice IDs:
trice insert -alias DEBUG_PRINT -salias DEBUG_PRINT_S -exclude ./print_macro.h -v
Flash the MCU and run the trice receiver on your host machine to receive probes (cli command is config and receiver dependent), for UDP4, it can be:
trice log -p UDP4 -v -pf none
4.5.2. PR533 Check without Trice:
Clean trice IDs, if any:
trice clean -alias DEBUG_PRINT -salias DEBUG_PRINT_S -exclude ./print_macro.h -v
Flash with -DTRICE_OFF.
4.6. PR536 Doc
4.6.1. What This PR536 Adds
This is a follow-up to #533. It enforces the consistent use of the “%s” format in all triceS aliases and fixes that behavior in the newly added test case.
The following simplified example reflects a real use case where custom macros wrap formatting logic:
#define CUSTOM_PRINT_S(id, fmt, ....) triceS(id, "%s", format_message(fmt, ##__VA_ARGS__))
4.6.2. PR536 - The Problem Statement
Determining the Strg argument reliably is challenging due to:
- The unrestricted flexibility of macros (which I would like to preserve and utilize).
- Trice core’s limited parsing, which relies on regular expressions.
For instance, custom macros like these show the variability:
CUSTOM_ASSERT(false, "Message: %s", msg);
CUSTOM_ASSERT(false, "Message without args");
CUSTOM_ASSERT(false);
Improving this would likely require Clang integration—adding complexity (e.g., full build flags, complete source context)—whereas Trice’s current regex-based approach remains lightweight and simple to use.
4.6.3. PR536 Implementation Details:
matchTrice() was re-implemented to improve robustness. It now:
- Locates the macro or alias name followed by (.
- Finds the matching closing parenthesis, correctly handling nested structures.
- Parses the arguments within.
This approach simplifies the logic and allows the parser to skip invalid or partial matches without aborting, enabling continued scanning of the file for valid constructs.
4.7. Alias Example Project
To use the Alias technique with examples/G0B1_inst the following adaptations were made:
- Copied file ./examples/G0B1_inst/Core/Inc/nanoprintf.h from https://github.com/charlesnicholson/nanoprintf
- Created file ./examples/G0B1_inst/Core/Inc/triceCustomAliases.h to cover the user code specific
CUSTUM_PRINTandCUSTUM_ASSERT. -
Core/Src/main.c:
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ - #include "trice.h" + #include "triceCustomAliases.h" #include <limits.h> // INT_MAX /* USER CODE END Includes */ ... /* USER CODE BEGIN 2 */ #if !TRICE_OFF LogTriceConfiguration(); SomeExampleTrices(3); - #endif + /* Some Custom Trice Alias Examples */ + const int theRightAnswer = 42; + const int theFaCEFoundAnswer = 24; + const char* theQuestion = "What could be the answer to the Ultimate Question of + Life, the Universe, and Everything?"; + + // Some Trice custom alias examples + CUSTOM_PRINT("CUSTOM_PRINT example: the right answer is: %d\n", theRightAnswer); + + // Assert with condition + CUSTOM_ASSERT(theFaCEFoundAnswer == theRightAnswer); + + // Assert with condition and a message: This works too, but triggers a clang + compiler warning, we cannot suppress. + CUSTOM_ASSERT(theFaCEFoundAnswer == theRightAnswer, (char*)theQuestion ); // + https://stackoverflow.com/questions/52692564/+ how-can-i-disable-format-security-error-with-clang + + // Assert with condition and a message and some extra message arguments + CUSTOM_ASSERT(theFaCEFoundAnswer == theRightAnswer, (char*)"'%s' Am, it is %d", + (char*)theQuestion, theRightAnswer); + #endif /* USER CODE END 2 */ - After flashing:
