[PATCH 2/2] ccli: commands: Fix optimized `strlen()` calls
Ammar Faizi <[email protected]> Thu, 21 Aug 2025 04:14:34 +0700
| Newsgroups | org.kernel.vger.linux-trace-devel |
|---|---|
| Message-ID | <[email protected]> |
When compiling with clang, the following warning appears:
commands.c:291:6: warning: variable 'l' set but not used [-Wunused-but-set-variable]
291 | int l = 0;
| ^
Closer inspection shows that `l` is only used to accumulate the return
values of `strlen()` calls, but its value is never read. It turns out
that it indicates a deeper issue than a simple unused variable.
The purpose of the `strlen()` calls in this code is to ensure all
strings passed to `test_table()` are touched. However, `strlen()` is
declared with `__attribute_pure__`.
A pure function always produces the same result for the same input and
has no side effects.
If the result of `strlen()` is not used, the compiler is allowed to
remove the call entirely, which means the strings are never actually
touched.
For example, compiling with `-O3`:
#include <string.h>
void touch_string(char *s)
{
strlen(s);
}
produces only:
touch_string:
retq
Even at `-O0`, gcc still omits the `strlen()` call:
touch_string:
pushq %rbp
movq %rsp,%rbp
movq %rdi,-0x8(%rbp)
nop
popq %rbp
retq
Introduce a new macro, OPTIMIZER_HIDE_VAR(), to hide the variable from
the compiler's optimizer, keeping the strlen() calls intact. It also
cleans the clang warning.
Co-authored-by: Alviro Iskandar Setiawan <[email protected]>
Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
Signed-off-by: Ammar Faizi <[email protected]>
---
Giving more context for src/commands.c hunk for easier review.
src/ccli-local.h | 1 +
src/commands.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/ccli-local.h b/src/ccli-local.h
index 31d16de73095..d7fd8e71e482 100644
--- a/src/ccli-local.h
+++ b/src/ccli-local.h
@@ -15,6 +15,7 @@
#define __hidden __attribute__((visibility ("hidden")))
#define ISSPACE(c) isspace((unsigned char)(c))
+#define OPTIMIZER_HIDE_VAR(X) __asm__ volatile ("": "+r" (X))
struct line_buf {
char *line;
diff --git a/src/commands.c b/src/commands.c
--- a/src/commands.c
+++ b/src/commands.c
@@ -285,49 +285,50 @@ int ccli_execute(struct ccli *ccli, const char *line_str, bool hist)
static void test_table(const void *data)
{
const struct ccli_command_table *table = data;
const char *name = table->name;
int len = strlen(test_name);
int l = 0;
int i;
/* The root of the table does not need a name */
if (!name)
name = "root";
if (len)
strncat(test_name, "->", BUFSIZ - 1);
strncat(test_name, name, BUFSIZ - 1);
snprintf(test_message, BUFSIZ - 1,
"Command table missing name or 'subcommands' NULL terminator");
/* Touch all the names first */
for (i = 0; table->subcommands[i]; i++)
l += strlen(table->subcommands[i]->name);
snprintf(test_message, BUFSIZ - 1,
"Command table missing 'options' NULL terminator");
if (table->options) {
int clen;
/* Test the options too */
clen = strlen(test_name);
if (clen)
strncat(test_name, "->[options]", BUFSIZ - 1);
for (i = 0; table->options->options[i]; i++)
l += strlen(table->options->options[i]->name);
test_name[clen] = '\0';
}
/* Now recurse */
for (i = 0; table->subcommands[i]; i++)
test_table(table->subcommands[i]);
test_name[len] = '\0';
+ OPTIMIZER_HIDE_VAR(l);
}
--
Ammar Faizi