Skip to content

Add support for dynamic trice and triceS macro aliases#533

Merged
rokath merged 3 commits intorokath:masterfrom
srgg:master
Jun 1, 2025
Merged

Add support for dynamic trice and triceS macro aliases#533
rokath merged 3 commits intorokath:masterfrom
srgg:master

Conversation

@srgg
Copy link
Copy Markdown
Contributor

@srgg srgg commented May 27, 2025

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 --excludeSource feature added in #529.

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.

What This PR 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.

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);
 }

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

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

Introduced mechanisms to handle customizable macro aliases for trice() and triceS() through cli flags.
@rokath
Copy link
Copy Markdown
Owner

rokath commented May 28, 2025

Thanks a lot for your 2nd contribution @srgg ! I think the -alias CLI switch is a real good idea. Unfortunately the testAll.sh script fails currently. Would you please check.

@srgg
Copy link
Copy Markdown
Contributor Author

srgg commented May 28, 2025

@rokath I have checked and discovered that the CGO test somehow relies on CLEAN = 1, which includes triceOff.h (all stub macros) and at the same time trice.c, which compiles the real implementation of the functions stubbed by those TRICE_OFF macros:

if ((defined(TRICE_OFF) && TRICE_OFF == 1)) || ((defined(TRICE_CLEAN) && TRICE_CLEAN == 1))

...

#include "triceOff.h"

here

And in the trice.c:

#if !TRICE_OFF

So it is not excluded if CLEAN is defined. However, as I see it, the CGO test relies on triceInit() and other trice functions.

So I'm stuck here: the CGO tests, on the one hand, declare CLEAN=1 to insert IDs (which cause the inclusion of triceOff.h), but on the other hand, they require actual trice functions, not stubs.

What is strange is that I did not change anything related to this; moreover, when I attempted to exclude trice either from trice.c ot trice. I'm still getting numerous errors.

I'm affraid and I'm sorry for saying it. I would appreciate your help on this one.

Just in case:

gcc --version
Apple clang version 16.0.0 (clang-1600.0.26.4)
Target: arm64-apple-darwin24.5.0
Thread model: posix

@rokath
Copy link
Copy Markdown
Owner

rokath commented May 28, 2025

I will probably have time for this only next week. Please be patient.

@srgg
Copy link
Copy Markdown
Contributor Author

srgg commented May 29, 2025

@rokath I have pushed one more commit in this PR to fix the insert/clean commands behavior when the format string is not the first argument after the Trice ID.

Example of the issue:

CUSTOM_ASSERT(false, "CUSTOM_ASSERT false test: %s\n", str);

Before the fix, insert incorrectly produced:

CUSTOM_ASSERT(iD(1234), "CUSTOM_ASSERT false test: %s\n", str);

Expected result after fix:

CUSTOM_ASSERT(iD(1234), false, "CUSTOM_ASSERT false test: %s\n", str);

The issue was that the logic assumed the format string was always the first argument after Trice ID, but in custom macros like assert, the condition comes first.

@rokath
Copy link
Copy Markdown
Owner

rokath commented May 30, 2025

Hi @srgg, I tried to quickly check after your last update, but now already go test ./... fails. Is it failing in your copy as well? If yes and if it is possible for you to fix that, I would have it easier to go ahead with the CGO problem. Inbetween there are some smaller changes in the Trice main branch.

What I did:

git fetch origin pull/533/head:test-alias
...
git switch test-alias
ms@Mac trice % go test ./...
ok      github.com/rokath/trice/cmd/trice       (cached)
...
FAIL    github.com/rokath/trice/internal/id     3.475s
...
ok      github.com/rokath/trice/pkg/tst (cached)
FAIL

ms@MacBook-Pro trice % git log | head -n 20
commit 0cac155cc376052b417e86b0186a2ec0a16913aa
Author: srgg <srggal@gmail.com>
Date:   Thu May 29 16:16:18 2025 -0600

    Refactor alias handling logic, fixing custom macro handling where the format string isn't the first arg after Trice ID.

commit 5a04b94cb01bc13e58e6fdd573d539b5d195c87a
Author: srgg <srggal@gmail.com>
Date:   Thu May 22 10:37:38 2025 -0600

    Add support for dynamic trice and triceS macro aliases
    
    Introduced mechanisms to handle customizable macro aliases for trice() and triceS() through cli flags.

commit c0419731b5b1a6849a84602c2888cefc5a7074b1
Author: srgg <srggal@gmail.com>
Date:   Thu May 15 18:25:41 2025 -0600

    Add an IPv4 UDP receiver and introduce the '-exclude' flag to omit specified sources from scanning.

ms@MacBook-Pro trice % 
ms@MacBook-Pro trice % 
ms@MacBook-Pro trice % 
ms@MacBook-Pro trice % git checkout c0419731b5b1a6849a84602c2888cefc5a7074b1
Note: switching to 'c0419731b5b1a6849a84602c2888cefc5a7074b1'.

...

HEAD is now at c0419731 Add an IPv4 UDP receiver and introduce the '-exclude' flag to omit specified sources from scanning.
ms@MacBook-Pro trice % go test ./...
ok  	github.com/rokath/trice/cmd/trice	(cached)
...
ok  	github.com/rokath/trice/internal/id	(cached)
...
ok  	github.com/rokath/trice/pkg/tst	(cached)
ms@MacBook-Pro trice % git switch -
Previous HEAD position was c0419731 Add an IPv4 UDP receiver and introduce the '-exclude' flag to omit specified sources from scanning.
Switched to branch 'test-alias'

@srgg
Copy link
Copy Markdown
Contributor Author

srgg commented May 30, 2025

@rokath, oops, I'm sorry for that (means confirmed, fails on my side as well). My last commit broke it. I will check/fix? It today.

@srgg
Copy link
Copy Markdown
Contributor Author

srgg commented May 31, 2025

@rokath I've force-pushed my latest changes, which resolve all test failures except one (see below).

Additionally, I've added a trivial test case to TestMatchTrice to verify the correct matching of custom macros:

func TestMatchTrice(t *testing.T) {
	// Register custom aliases
	triceAliasesPtr := &TriceAliases
	triceAliasesPtr.Set("MyAssert")
	ProcessAliases()

	var testSet = []struct {
		text, triceType, triceID, triceFmts string
	}{
		// Test case for assert-style custom macros w/o Trice ID
		{`...MyAssert( i<12, "%d+%3d=%u",
		(3), 4, (3+4) ); ,...`, `MyAssert`, ``, `i<12, "%d+%3d=%u"`},

		// Test case for assert-style custom macros with Trice ID
		{`...MyAssert( iD(42), i<12, "%d+%3d=%u",
		(3), 4, (3+4) ); ,...`, `MyAssert`, `iD(42)`, `i<12, "%d+%3d=%u"`},
		
		// Test case for built-in macros
...

There is only one remaining test failure:

=== RUN   TestInsert_On_valid_iCache_valid_cCache_clean_file_edited
TestInsert_On_valid_iCache_valid_cCache_clean_file_edited ...
    require.go:72: 
        	Error Trace:	/Users/srg/go/pkg/mod/github.com/tj/assert@v0.0.3/require.go:72
        	            				/Users/srg/src/srg-trice/trice/internal/id/cache_help_test.go:52
        	            				/Users/srg/src/srg-trice/trice/internal/id/cachedInsert_test.go:392
        	Error:      	Not equal: 
        	            	expected: "trice(iD(999), \"msg:value=%d\\n\", -2);"
        	            	actual  : "trice(iD(999), i\"msg:value=%d\\n\", -2);"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-trice(iD(999), "msg:value=%d\n", -2);
        	            	+trice(iD(999), i"msg:value=%d\n", -2);
        	Test:       	TestInsert_On_valid_iCache_valid_cCache_clean_file_edited
TestInsert_On_valid_iCache_valid_cCache_clean_file_edited ...done.
--- FAIL: TestInsert_On_valid_iCache_valid_cCache_clean_file_edited (0.01s)

Expected :trice(iD(999), "msg:value=%d\n", -2);
Actual   :trice(iD(999), i"msg:value=%d\n", -2);

This test previously passed because the matcher logic omitted everything between '<trice>(' and the format string. After changing the behavior to retain custom macro arguments before the format specifier (previously omitted by the legacy logic), this test now fails.

As soon as I remove the trailing "i" at line 384:

assertFileCreate(t, FSys, SFName, `trice("msg:value=%d\n", -2);`) // edit file

All go tests passed:

go test ./...
ok  	github.com/rokath/trice/cmd/trice	(cached)
ok  	github.com/rokath/trice/internal/args	(cached)
ok  	github.com/rokath/trice/internal/charDecoder	(cached)
ok  	github.com/rokath/trice/internal/com	(cached)
ok  	github.com/rokath/trice/internal/decoder	(cached)
?   	github.com/rokath/trice/internal/do	[no test files]
ok  	github.com/rokath/trice/internal/dumpDecoder	(cached)
ok  	github.com/rokath/trice/internal/emitter	(cached)
ok  	github.com/rokath/trice/internal/id	3.067s
ok  	github.com/rokath/trice/internal/keybcmd	(cached)
ok  	github.com/rokath/trice/internal/link	(cached)
ok  	github.com/rokath/trice/internal/receiver	0.159s
?   	github.com/rokath/trice/internal/translator	[no test files]
ok  	github.com/rokath/trice/internal/trexDecoder	(cached)
?   	github.com/rokath/trice/pkg/ant	[no test files]
ok  	github.com/rokath/trice/pkg/cipher	(cached)
ok  	github.com/rokath/trice/pkg/endian	(cached)
ok  	github.com/rokath/trice/pkg/msg	(cached)
ok  	github.com/rokath/trice/pkg/tst	(cached)

At this point, I'm not entirely sure what the purpose of this "i" is.
Let me know if you'd like me to remove it (as I’ve done locally) and push the change.

@rokath
Copy link
Copy Markdown
Owner

rokath commented May 31, 2025

The trailing "i" at line 384 is definitively a typo, so please remove it and go ahead. I expect to have on Monday, or maybe tomorrow time for yoPR.

… format string isn't the first arg after Trice ID.
@srgg
Copy link
Copy Markdown
Contributor Author

srgg commented May 31, 2025

Awesome, I have pushed it w/o "i":

git log -1 --oneline
bba67434 (HEAD -> master, origin/master, origin/HEAD) Refactor alias handling logic, fixing custom macro handling where the format string isn't the first arg after Trice ID.
    
    
go test ./...                  
go: downloading github.com/rokath/cobs v0.0.0-20230425030040-4ebbe9b903b9
go: downloading github.com/rokath/tcobs v0.9.1
go: downloading github.com/tj/assert v0.0.3
go: downloading github.com/spf13/afero v1.9.5
go: downloading go.bug.st/serial v1.6.0
go: downloading github.com/pkg/errors v0.9.1
go: downloading github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
go: downloading github.com/stretchr/testify v1.8.4
go: downloading github.com/kr/pretty v0.1.0
go: downloading golang.org/x/crypto v0.35.0
go: downloading github.com/fsnotify/fsnotify v1.6.0
go: downloading github.com/udhos/equalfile v0.3.0
go: downloading golang.org/x/sys v0.30.0
go: downloading github.com/kr/text v0.1.0
go: downloading golang.org/x/text v0.22.0
go: downloading github.com/mattn/go-colorable v0.1.13
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/creack/goselect v0.1.2
go: downloading github.com/mattn/go-isatty v0.0.19
ok  	github.com/rokath/trice/cmd/trice	1.306s
ok  	github.com/rokath/trice/internal/args	0.852s
ok  	github.com/rokath/trice/internal/charDecoder	0.575s
ok  	github.com/rokath/trice/internal/com	3.867s
ok  	github.com/rokath/trice/internal/decoder	0.689s
?   	github.com/rokath/trice/internal/do	[no test files]
ok  	github.com/rokath/trice/internal/dumpDecoder	0.799s
ok  	github.com/rokath/trice/internal/emitter	0.359s
ok  	github.com/rokath/trice/internal/id	3.146s
ok  	github.com/rokath/trice/internal/keybcmd	0.483s
ok  	github.com/rokath/trice/internal/link	0.119s
ok  	github.com/rokath/trice/internal/receiver	0.237s
?   	github.com/rokath/trice/internal/translator	[no test files]
ok  	github.com/rokath/trice/internal/trexDecoder	0.551s
?   	github.com/rokath/trice/pkg/ant	[no test files]
ok  	github.com/rokath/trice/pkg/cipher	0.663s
ok  	github.com/rokath/trice/pkg/endian	0.759s
ok  	github.com/rokath/trice/pkg/msg	0.867s
ok  	github.com/rokath/trice/pkg/tst	0.975s

…able evaluation for built-in trice macros only.
@srgg
Copy link
Copy Markdown
Contributor Author

srgg commented Jun 1, 2025

@rokath Added a commit skipping parameter count checks for aliases.

Custom macros can have arbitrary behavior, so checking format specifiers or argument count isn't reliable.

Example fails in evaluateTriceParameterCount(): "should have exactly one format specifier and not 0":

CUSTOM_ASSERT(false, "Whatever failure message");
// or it could be even
CUSTOM_ASSERT(false);

//  whereas:

  #define CUSTOM_ASSERT(id, condition, ...) \
    do { if (!(condition)) { triceS(id, "%s", format_message("ASSERTION FAILED: %s at %s:%d", #condition, __FILE__, __LINE__)); } } while(0)

And I have another issue in mind: with such flexibility of custom macros, how can Strg be determined?

// Question: What to use as Strg for custom macros when a format string isn't reliably at 1st or 2nd arg after Trice ID?
// Use "%s" for dynamic strings and assume a format string is the first arg after Trice ID for non-dynamic string macros
isAlias := t.Alias != ""
isSAlias := t.Type == "triceS"
if !isSAlias {
      t.Strg = rest[loc[5]+1 : loc[6]-1] // Now we have the complete trice t (Type and Strg). We remove the double quotes with +1 and -1.
} else {
      t.Strg = "%s"
}

Is it ok? The Default implementation for CUSTOM_ASSERT(false, "Whatever failure message"); generates:

"1111": {
      "Type": "triceS",
      "Strg": "alse   , \"Whatever failure message",
      "Alias": "CUSTOM_ASSERT"
}

@srgg srgg marked this pull request as draft June 1, 2025 07:43
@rokath rokath marked this pull request as ready for review June 1, 2025 20:26
@rokath rokath merged commit 63355ee into rokath:master Jun 1, 2025
1 check passed
@rokath
Copy link
Copy Markdown
Owner

rokath commented Jun 1, 2025

Hello @srgg, The CGO tests are OK, after commenting out in internal/args/handler.go around line 33:

	// Trim leading and trailing whitespace
	//for i := range args {
	//	args[i] = strings.TrimSpace(args[i])
	//}

Before I got:

ok  	github.com/rokath/trice/pkg/tst	1.350s
0 {59 time:feed3322default: Hello World!}
1 {61 time:feed3322default: info:This is a message without values and a 32-bit stamp.}
2 {62 time:    be16default: info:This is a message without values and a 16-bit stamp.}
3 {63 time:        default: info:This is a message without values and without stamp.}
--- FAIL: TestLogs (0.48s)
    require.go:72: 
        	Error Trace:	/Users/ms/go/pkg/mod/github.com/tj/assert@v0.0.3/require.go:72
        	            				/Users/ms/repos/trice/_test/be_dblB_de_tcobs_ua/generated_cgoPackage.go:184
        	            				/Users/ms/repos/trice/_test/be_dblB_de_tcobs_ua/cgo_test.go:24
        	Error:      	Not equal: 
        	            	expected: "time:        default: info:This is a message without values and without stamp."
        	            	actual  : "time:default: info:This is a message without values and without stamp."
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-time:        default: info:This is a message without values and without stamp.
        	            	+time:default: info:This is a message without values and without stamp.
        	Test:       	TestLogs
FAIL
FAIL	github.com/rokath/trice/_test/be_dblB_de_tcobs_ua	0.736s
FAIL

For example in _test/ringB_de_cobs_ua/cgo_test.go line 20 you can find "-ts0", "time: ",. The 8 spaces get trimmed then and causing the test failure.

I understand your intension trimming the aruments, but there are some cases, and not only test cases, where this causes issues. We could add a parameter -noargstrim but I have no good feeling with that. Is there any strong reason for the trimming?

Concerning your TRICE_CLEAN observation I do not see a problem right now.

The only thing I miss right now, are some example tests, but that I can add later by myself.

rokath added a commit that referenced this pull request Jun 1, 2025
@srgg
Copy link
Copy Markdown
Contributor Author

srgg commented Jun 1, 2025

@rokath I added the trim just to get the tests passing—it's fine to remove it if unnecessary.
That said, there were still alias-related issues, as noted in my earlier comment. It seems that part may have been missed:

I have another issue in mind: With such flexibility of custom macros, how can Strg be determined?

// Question: What to use as Strg for custom macros when a format string isn't reliably at 1st or 2nd arg after Trice ID?
// Use "%s" for dynamic strings and assume a format string is the first arg after Trice ID for non-dynamic string macros
isAlias := t.Alias != ""
isSAlias := t.Type == "triceS"
if !isSAlias {
      t.Strg = rest[loc[5]+1 : loc[6]-1] // Now we have the complete trice t (Type and Strg). We remove the double quotes with +1 and -1.
} else {
      t.Strg = "%s"
}

Is it ok? The Default implementation for CUSTOM_ASSERT(false, "Whatever failure message"); generates:

"1111": {
      "Type": "triceS",
      "Strg": "alse   , \"Whatever failure message",
      "Alias": "CUSTOM_ASSERT"
}

Is it possible to revert the PR so that everything can be merged together as one unit? Or would you prefer to handle it differently?

@rokath
Copy link
Copy Markdown
Owner

rokath commented Jun 2, 2025

PR leverages the --excludeSource feature added in #529.

In which way this PR influences the -exclude feature? I assume these changes are orthogonal.

I added the trim just to get the tests passing—it's fine to remove it if unnecessary.

Which tests caused a problem? Was it s.th. temporarily?

To be honest, I do not clearly understand your question "What to use as Strg for custom macros when a...".

Generally we should keeps things simple. I would not consider any possible custom macro signature and stick with the current known ones. If one day a new situation needs handling, we can react on it appropriate.

Why you need the "Alias": "CUSTOM_ASSERT" extension option in the til.json file? My assumption is that this is not nessecary, because this information exists already in the CLI switch. Once a user uses the -alias switch, it has to use it always (usually as part of a script). Also the ti.json file gets somehow a bit polluted this way, if you think about several developers using the same til.json file or if the developer later decides to get rid of the -alias usage.

Flash with -DTRICE_OFF

Compiling a project with -DTRICE_OFF will not exclude the custom macros code, right?

Custom macros can have arbitrary behavior, so checking format specifiers or argument count isn't reliable.

I agree to exclude them from parameter count checking.

I accepted the PR, assuming the existing PR changes are ok so far and also because in the main branch has already some additional changes, which I liked to see together with your PR to go forward with some additional test and the documentation. But probably I was to early. Sorry for that. I do not mind to revert the PR and to be patient if that is convenient for you. On the other hand, what speaks against a next PR from you? Lets do it the way you prefer.

@srgg, I really appreciate your ideas and your work!

EDIT: It seems not to be an easy and unproblematic going to revert a PR.

@srgg
Copy link
Copy Markdown
Contributor Author

srgg commented Jun 3, 2025

@rokath Thank you for your time and patience. Please see my answers below.

In which way this PR influences the -exclude feature? I assume these changes are orthogonal.

Please take a look at the example in the PR description. Macro definitions (print_macro.h) must be excluded from ID insertion process —otherwise, all the expanded macros will reuse the same hardcoded/inserted Trice ID.

Which tests caused a problem? Was it s.th. temporarily?

I'm having multiple issues running the CGO tests. Problems include func(void)-style declarations and name clashes between triceOff.h stubs and actual implementations in trice.c. As a result, I can't run these tests locally. If they pass on your side—with or without trailing cleanup—that's fine by me. I'll try to investigate further and open a ticket when I can, though I'm currently behind schedule on related features.

Why you need the "Alias": "CUSTOM_ASSERT" extension option in the til.json file?

The "Alias": "CUSTOM_ASSERT" option improves usability and debugging by preserving the original macro name. It is beneficial for developers to use aliases the first time.

Additionally, I have one more feature in mind (if I have time for that): customizable color mapping based on macro usage (e.g., asserts in red, warnings in purple). Unlike the current intrusive, hardcoded approach, this would allow non-intrusive styling via CLI/config files. Retaining aliases is crucial for enabling this functionality without modifying the source code.

It seems not to be an easy and unproblematic going to revert a PR.

No problem—I’ve opened a new PR #536. Please review it at your convenience.

In that PR, I’ve also tried to address your question about "what to use as Strg for custom macros by enforcing the proposed triceS alias format:. My earlier comment may have been unclear, but this new PR should provide us with a better place to collaborate on it.

-- Serhii

@rokath
Copy link
Copy Markdown
Owner

rokath commented Jun 4, 2025

Please see my reply in PR #536 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants