Bug #1435
openProblematic tracepoint provider header files in LTTng-UST test suite
0%
Description
Dear LTTng developers,
We are long time happy users of LTTng-UST technology. Thank you for developing and maintaining it.
TLDR: We have stumbled upon build problem involving modern Clang versions and tracepoint provider header files in the LTTng-UST test suite, which I believe is coding error in those files. The fix appears to be easy: just remove some offending include directives from affected files.
Recently we have upgraded our toolchain to LLVM 20 + libstdc++ from GCC 15. We are also evaluating upgrade of our LTTng dependencies to latest versions. By default we provide -std=gnu++23 to CXXFLAGS building all components. Rebuilding LTTng-UST v2.13.9 and also from stable-2.15 branch, I've stumbled on compilation error in one of the tests. Here is a Dockerfile for convenience so you can reproduce it yourselves:
FROM quay.io/centos/centos:stream9
RUN dnf -y group install "Development Tools" && \
dnf -y install llvm clang lld
ARG WORKDIR=/work
ARG DISTDIR=/work/dist
ARG URCU_BRANCH=stable-0.15
ARG LTTNG_UST_BRANCH=stable-2.15
RUN mkdir $WORKDIR && cd $WORKDIR && \
git clone -b $URCU_BRANCH https://github.com/urcu/userspace-rcu.git && \
git clone -b $LTTNG_UST_BRANCH https://github.com/lttng/lttng-ust.git
WORKDIR $WORKDIR
ARG CC=clang CXX=clang++
ARG CXX_STANDARD=20
ENV CC=$CC CXX=$CXX
ENV CXXFLAGS="-std=gnu++${CXX_STANDARD}"
ENV PKG_CONFIG_PATH=$DISTDIR/lib/pkgconfig
RUN <<EOF
#!/bin/sh
set -e
set -x
cd $WORKDIR/userspace-rcu
autoreconf -vi
./configure --prefix=$DISTDIR --disable-static
make -j8 V=1
make check
make install
EOF
# Save script for manual invocations with different parameters
COPY --chmod=0755 <<EOF $WORKDIR/build-lttng-ust.sh
#!/bin/sh
set -e
set -x
cd $WORKDIR/lttng-ust
autoreconf -vi
./configure --prefix=$DISTDIR --disable-static \
--disable-man-pages --without-sdt --disable-numa --disable-examples
make -j8 V=1
make check
EOF
RUN /work/build-lttng-ust.sh
When executed with Podman build command, it would fail like this:
podman build .
...
In file included from tp-cpp.cpp:8:
In file included from ./ust_tests_hello.h:62:
In file included from ../../../../include/lttng/tracepoint-event.h:67:
In file included from ../../../../include/lttng/ust-tracepoint-event.h:1189:
In file included from ./ust_tests_hello.h:14:
In file included from /usr/bin/../lib/clang/21/include/stddef.h:88:
/usr/bin/../lib/clang/21/include/__stddef_ptrdiff_t.h:18:1: error: expected expression
18 | typedef __PTRDIFF_TYPE__ ptrdiff_t;
| ^
In file included from tp-cpp.cpp:8:
In file included from ./ust_tests_hello.h:62:
In file included from ../../../../include/lttng/tracepoint-event.h:67:
../../../../include/lttng/ust-tracepoint-event.h:1204:15: error: invalid application of 'sizeof' to an incomplete type 'const struct lttng_ust_event_desc *const[]'
1204 | .nr_events = LTTNG_UST__TP_ARRAY_SIZE(LTTNG_UST__TP_COMBINE_TOKENS(lttng_ust__provider_event_desc___, LTTNG_UST_TRACEPOINT_PROVIDER)) - 1,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../../../../include/lttng/ust-tracepoint-event.h:97:46: note: expanded from macro 'LTTNG_UST__TP_ARRAY_SIZE'
97 | #define LTTNG_UST__TP_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
| ^~~~~
This is due to include directive inside array initializer list in https://github.com/lttng/lttng-ust/blob/v2.13.9/include/lttng/ust-tracepoint-event.h#L1181
I was able to minimize repro case to this:
$ cat minrepro.cpp
#include <stddef.h>
[[maybe_unused]] static const int arr[] = {
#include <stddef.h>
0,
};
[[maybe_unused]] static const int len = (sizeof(arr) / sizeof((arr)[0]));
It happens to work with GCC, and it worked with older Clang versions thanks to #pragma once or equivalent guard:
$ clang++ -std=c++17 -Wall -Wextra -pedantic -c minrepro.cpp && echo OK OK $ g++ -std=c++23 -Wall -Wextra -pedantic -c minrepro.cpp && echo OK OK
But it fails to be compiled with modern Clang in modern C++ mode:
$ clang++ -std=c++20 -Wall -Wextra -pedantic -c minrepro.cpp && echo OK
In file included from minrepro.cpp:4:
In file included from /usr/bin/../lib/clang/21/include/stddef.h:88:
/usr/bin/../lib/clang/21/include/__stddef_ptrdiff_t.h:18:1: error: expected expression
18 | typedef __PTRDIFF_TYPE__ ptrdiff_t;
| ^
minrepro.cpp:8:48: error: invalid application of 'sizeof' to an incomplete type 'const int[]'
8 | [[maybe_unused]] static const int len = (sizeof(arr) / sizeof((arr)[0]));
| ^~~~~
2 errors generated.
This is because modern Clang implements magic of Clang Modules:
- https://clang.llvm.org/docs/Modules.html
- https://reviews.llvm.org/D159064
- https://github.com/llvm/llvm-project/blob/llvmorg-20.1.7/clang/lib/Headers/__stddef_ptrdiff_t.h
The repro can be further minimized to more clearly demonstrate that specific Clang trouble:
$ cat minrepro2.cpp
[[maybe_unused]] static const int arr[] = {
typedef int foo;
0,
};
Of course, for building LTTng-UST we can use older C++ standard like -std=c++17, or let LTTng configure default to c++11.
Cleaner solution would be avoid including anything in tracepoint provider header files:
$ git diff -- tests/compile/api0/hello.cxx/
diff --git a/tests/compile/api0/hello.cxx/ust_tests_hello.h b/tests/compile/api0/hello.cxx/ust_tests_hello.h
index 1963e737..a968cd8f 100644
--- a/tests/compile/api0/hello.cxx/ust_tests_hello.h
+++ b/tests/compile/api0/hello.cxx/ust_tests_hello.h
@@ -11,7 +11,6 @@
#define _TRACEPOINT_UST_TESTS_HELLO_H
#include <lttng/tracepoint.h>
-#include <stddef.h>
TRACEPOINT_ENUM(ust_tests_hello, my_enum,
TP_ENUM_VALUES(
I was able to build stable-2.15 by patching tracepoint provider header files in tests to remove all includes like stddef.h stdbool.h and the like -- basically all except strictly necessary lttng ones:
$ git status --short M tests/compile/api0/hello-many/ust_tests_hello_many.h M tests/compile/api0/hello.cxx/ust_tests_hello.h M tests/compile/api0/hello/ust_tests_hello.h M tests/compile/api1/hello-many/ust_tests_hello_many.h M tests/compile/api1/hello.cxx/ust_tests_hello.h M tests/compile/api1/hello/ust_tests_hello.h M tests/compile/api1/test-app-ctx/ust_tests_hello.h M tests/regression/abi0-conflict/ust_tests_hello.h $
Thankfully, example in the official documentation is clean in this regard: https://lttng.org/docs/v2.15/#doc-tracing-your-own-user-application
MD Updated by Mathieu Desnoyers 4 months ago
If I get this right, Clang Modules replaces the typical preprocessor include guards with a clever scheme.
As a result, with Clang Modules, we don't have those preprocessor include guards which guarantee that the content of the header is only ever expanded in the first include.
I suspect that we are hitting this issue only with stddef.h now, but it may extend to other headers in the future as well.
Note that removing the include from the tracepoint declaration header requires all users of the tracepoint declaration header to include the proper header dependencies, which is error prone. So I don't think it's a good solution.
One alternative would be to add explicit include guards around the includes within the tracepoint declaration header, e.g.:
/* lttng/tracepoint.h needs to be included in each inclusion pass. */
#include <lttng/tracepoint.h>
/* Guard includes with explicit include guards to support Clang Modules. */
#ifndef _TRACEPOINT_UST_TESTS_HELLO_ONCE
# define _TRACEPOINT_UST_TESTS_HELLO_ONCE
# include <stdbool.h>
# include <stddef.h>
#endif
Would this approach work for you ?
AS Updated by Anton Smyk 4 months ago
Hi Mathieu, thanks for your prompt reply!
On our side, this is not much of a problem. Since we build LTTng internally, we can apply whatever patch is necessary. I just wanted to draw your attention to this issue.
In my report I should have provided link to this Clang commit: https://github.com/llvm/llvm-project/commit/f50d3582b4844b86ad86372028e44b52c560ec7d#diff-3fb2ec80382fb787a092d53b056f3b4f6cfd8bea677e66a72720c8752b0d2ada which replaced nice oldschool include guard by some modules mumble and caused this issue. I don't really understand the Apple trouble in https://github.com/llvm/llvm-project/pull/84127 that appears to drive these changes. Perhaps in the future, Clang developers will update those files again to use simple include guards, like they tried to already in https://github.com/llvm/llvm-project/commit/9a7a6dd3c358ca7becef75c0a9581dcfa3e6b5f4#diff-3fb2ec80382fb787a092d53b056f3b4f6cfd8bea677e66a72720c8752b0d2ada before rolling it back.
I've looked some more into it, and found that using proper C++ includes instead of C ones also helps:
$ cat minrepro1b.cpp
#include <cstddef>
[[maybe_unused]] static const int arr[] = {
#include <cstddef>
0,
};
[[maybe_unused]] static const int len = (sizeof(arr) / sizeof((arr)[0]));
$ clang++ -std=c++20 -Wall -Wextra -pedantic -c minrepro1b.cpp && echo OK
OK
Also reproduced the same issue with Clang's libc++ on Fedora 43 which provides libcxx-devel package, while CentOS does not:
$ clang++ -std=c++20 -stdlib=libc++ -Wall -Wextra -pedantic -c minrepro1a.cpp && echo OK
In file included from minrepro1a.cpp:4:
In file included from /usr/bin/../include/c++/v1/stddef.h:38:
In file included from /usr/bin/../lib/clang/21/include/stddef.h:88:
/usr/bin/../lib/clang/21/include/__stddef_ptrdiff_t.h:18:1: error: expected expression
18 | typedef __PTRDIFF_TYPE__ ptrdiff_t;
| ^
minrepro1a.cpp:8:48: error: invalid application of 'sizeof' to an incomplete type 'const int[]'
8 | [[maybe_unused]] static const int len = (sizeof(arr) / sizeof((arr)[0]));
| ^~~~~
2 errors generated.
$ clang++ -std=c++20 -stdlib=libc++ -Wall -Wextra -pedantic -c minrepro1b.cpp && echo OK
OK
I can't say I understand Clang source code well, but this seems relevant: https://github.com/llvm/llvm-project/blob/llvmorg-20.1.7/clang/lib/Lex/ModuleMap.cpp#L254-L291 and https://github.com/llvm/llvm-project/blob/llvmorg-20.1.7/clang/lib/Lex/ModuleMap.cpp#L2529-L2530
Can't say I understand C++ standardese either, but C++20 draft https://isocpp.org/files/papers/N4860.pdf in section D.9 seems to imply that includes like stddef.h are deprecated.
So, using C++ includes it is!
To build the lttng-ust project, the only required fix was to update these two files:
$ git diff
diff --git a/tests/compile/api0/hello.cxx/ust_tests_hello.h b/tests/compile/api0/hello.cxx/ust_tests_hello.h
index 1963e737..26ce8304 100644
--- a/tests/compile/api0/hello.cxx/ust_tests_hello.h
+++ b/tests/compile/api0/hello.cxx/ust_tests_hello.h
@@ -11,7 +11,7 @@
#define _TRACEPOINT_UST_TESTS_HELLO_H
#include <lttng/tracepoint.h>
-#include <stddef.h>
+#include <cstddef>
TRACEPOINT_ENUM(ust_tests_hello, my_enum,
TP_ENUM_VALUES(
diff --git a/tests/compile/api1/hello.cxx/ust_tests_hello.h b/tests/compile/api1/hello.cxx/ust_tests_hello.h
index 9088aaf1..7d815f02 100644
--- a/tests/compile/api1/hello.cxx/ust_tests_hello.h
+++ b/tests/compile/api1/hello.cxx/ust_tests_hello.h
@@ -11,7 +11,7 @@
#define _TRACEPOINT_UST_TESTS_HELLO_H
#include <lttng/tracepoint.h>
-#include <stddef.h>
+#include <cstddef>
LTTNG_UST_TRACEPOINT_ENUM(ust_tests_hello, my_enum,
LTTNG_UST_TP_ENUM_VALUES(
That was fun! :)
MD Updated by Mathieu Desnoyers 4 months ago
I have opened an issue against llvm: https://github.com/llvm/llvm-project/issues/181361