mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-03 17:54:19 +02:00
Merge bitcoin/bitcoin#26593: tracing: Only prepare tracepoint arguments when actually tracing
0de3e96e33tracing: use bitcoind pid in bcc tracing examples (0xb10c)411c6cfc6ctracing: only prepare tracepoint args if attached (0xb10c)d524c1ec06tracing: dedup TRACE macros & rename to TRACEPOINT (0xb10c) Pull request description: Currently, if the tracepoints are compiled (e.g. in depends and release builds), we always prepare the tracepoint arguments regardless of the tracepoints being used or not. We made sure that the argument preparation is as cheap as possible, but we can almost completely eliminate any overhead for users not interested in the tracepoints (the vast majority), by gating the tracepoint argument preparation with an `if(something is attached to this tracepoint)`. To achieve this, we use the optional semaphore feature provided by SystemTap. The first commit simplifies and deduplicates our tracepoint macros from 13 TRACEx macros to a single TRACEPOINT macro. This makes them easier to use and also avoids more duplicate macro definitions in the second commit. The Linux tracing tools I'm aware of (bcc, bpftrace, libbpf, and systemtap) all support the semaphore gating feature. Thus, all existing tracepoints and their argument preparation is gated in the second commit. For details, please refer to the commit messages and the updated documentation in `doc/tracing.md`. Also adding unit tests that include all tracepoint macros to make sure there are no compiler problems with them (e.g. some varadiac extension not supported). Reviewers might want to check: - Do the tracepoints still work for you? Do the examples in `contrib/tracing/` run on your system (as bpftrace frequently breaks on every new version, please test master too if it should't work for you)? Do the CI interface tests still pass? - Is the new documentation clear? - The `TRACEPOINT_SEMAPHORE(event, context)` macros places global variables in our global namespace. Is this something we strictly want to avoid or maybe move to all `TRACEPOINT_SEMAPHORE`s to a separate .cpp file or even namespace? I like having the `TRACEPOINT_SEMAPHORE()` in same file as the `TRACEPOINT()`, but open for suggestion on alternative approaches. - Are newly added tracepoints in the unit tests visible when using `readelf -n build/src/test/test_bitcoin`? You can run the new unit tests with `./build/src/test/test_bitcoin --run_test=util_trace_tests* --log_level=all`. <details><summary>Two of the added unit tests demonstrate that we are only processing the tracepoint arguments when attached by having a test-failure condition in the tracepoint argument preparation. The following bpftrace script can be used to demonstrate that the tests do indeed fail when attached to the tracepoints.</summary> `fail_tests.bt`: ```c #!/usr/bin/env bpftrace usdt:./build/src/test/test_bitcoin:test:check_if_attached { printf("the 'check_if_attached' test should have failed\n"); } usdt:./build/src/test/test_bitcoin:test:expensive_section { printf("the 'expensive_section' test should have failed\n"); } ``` Run the unit tests with `./build/src/test/test_bitcoin` and start `bpftrace fail_tests.bt -p $(pidof test_bitcoin)` in a separate terminal. The unit tests should fail with: ``` Running 594 test cases... test/util_trace_tests.cpp(31): error: in "util_trace_tests/test_tracepoint_check_if_attached": check false has failed test/util_trace_tests.cpp(51): error: in "util_trace_tests/test_tracepoint_manual_tracepoint_active_check": check false has failed *** 2 failures are detected in the test module "Bitcoin Core Test Suite" ``` </details> These links might provide more contextual information for reviewers: - [How SystemTap Userspace Probes Work by eklitzke](https://eklitzke.org/how-sytemtap-userspace-probes-work) (actually an example on Bitcoin Core; mentions that with semaphores "the overhead for an untraced process is effectively zero.") - [libbpf comment on USDT semaphore handling](1596a09b5d/src/usdt.c (L83-L92)) (can recommend the whole comment for background on how the tracepoints and tracing tools work together) - https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation#Semaphore_Handling ACKs for top commit: willcl-ark: utACK0de3e96e33laanwj: re-ACK0de3e96e33jb55: utACK0de3e96e33vasild: ACK0de3e96e33Tree-SHA512: 0e5e0dc5e0353beaf5c446e4be03d447e64228b1be71ee9972fde1d6fac3fac71a9d73c6ce4fa68975f87db2b2bf6eee2009921a2a145e24d83a475d007a559b
This commit is contained in:
@@ -9,39 +9,46 @@
|
||||
|
||||
#ifdef ENABLE_TRACING
|
||||
|
||||
// Setting SDT_USE_VARIADIC lets systemtap (sys/sdt.h) know that we want to use
|
||||
// the optional variadic macros to define tracepoints.
|
||||
#define SDT_USE_VARIADIC 1
|
||||
|
||||
// Setting _SDT_HAS_SEMAPHORES let's systemtap (sys/sdt.h) know that we want to
|
||||
// use the optional semaphore feature for our tracepoints. This feature allows
|
||||
// us to check if something is attached to a tracepoint. We only want to prepare
|
||||
// some potentially expensive tracepoint arguments, if the tracepoint is being
|
||||
// used. Here, an expensive argument preparation could, for example, be
|
||||
// calculating a hash or serialization of a data structure.
|
||||
#define _SDT_HAS_SEMAPHORES 1
|
||||
|
||||
// Used to define a counting semaphore for a tracepoint. This semaphore is
|
||||
// automatically incremented by tracing frameworks (bpftrace, bcc, libbpf, ...)
|
||||
// upon attaching to the tracepoint and decremented when detaching. This needs
|
||||
// to be a global variable. It's placed in the '.probes' ELF section.
|
||||
#define TRACEPOINT_SEMAPHORE(context, event) \
|
||||
unsigned short context##_##event##_semaphore __attribute__((section(".probes")))
|
||||
|
||||
#include <sys/sdt.h>
|
||||
|
||||
#define TRACE(context, event) DTRACE_PROBE(context, event)
|
||||
#define TRACE1(context, event, a) DTRACE_PROBE1(context, event, a)
|
||||
#define TRACE2(context, event, a, b) DTRACE_PROBE2(context, event, a, b)
|
||||
#define TRACE3(context, event, a, b, c) DTRACE_PROBE3(context, event, a, b, c)
|
||||
#define TRACE4(context, event, a, b, c, d) DTRACE_PROBE4(context, event, a, b, c, d)
|
||||
#define TRACE5(context, event, a, b, c, d, e) DTRACE_PROBE5(context, event, a, b, c, d, e)
|
||||
#define TRACE6(context, event, a, b, c, d, e, f) DTRACE_PROBE6(context, event, a, b, c, d, e, f)
|
||||
#define TRACE7(context, event, a, b, c, d, e, f, g) DTRACE_PROBE7(context, event, a, b, c, d, e, f, g)
|
||||
#define TRACE8(context, event, a, b, c, d, e, f, g, h) DTRACE_PROBE8(context, event, a, b, c, d, e, f, g, h)
|
||||
#define TRACE9(context, event, a, b, c, d, e, f, g, h, i) DTRACE_PROBE9(context, event, a, b, c, d, e, f, g, h, i)
|
||||
#define TRACE10(context, event, a, b, c, d, e, f, g, h, i, j) DTRACE_PROBE10(context, event, a, b, c, d, e, f, g, h, i, j)
|
||||
#define TRACE11(context, event, a, b, c, d, e, f, g, h, i, j, k) DTRACE_PROBE11(context, event, a, b, c, d, e, f, g, h, i, j, k)
|
||||
#define TRACE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l) DTRACE_PROBE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l)
|
||||
// Returns true if something is attached to the tracepoint.
|
||||
#define TRACEPOINT_ACTIVE(context, event) (context##_##event##_semaphore > 0)
|
||||
|
||||
// A USDT tracepoint with one to twelve arguments. It's checked that the
|
||||
// tracepoint is active before preparing its arguments.
|
||||
#define TRACEPOINT(context, event, ...) \
|
||||
do { \
|
||||
if (TRACEPOINT_ACTIVE(context, event)) { \
|
||||
STAP_PROBEV(context, event __VA_OPT__(, ) __VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#else
|
||||
|
||||
#define TRACE(context, event)
|
||||
#define TRACE1(context, event, a)
|
||||
#define TRACE2(context, event, a, b)
|
||||
#define TRACE3(context, event, a, b, c)
|
||||
#define TRACE4(context, event, a, b, c, d)
|
||||
#define TRACE5(context, event, a, b, c, d, e)
|
||||
#define TRACE6(context, event, a, b, c, d, e, f)
|
||||
#define TRACE7(context, event, a, b, c, d, e, f, g)
|
||||
#define TRACE8(context, event, a, b, c, d, e, f, g, h)
|
||||
#define TRACE9(context, event, a, b, c, d, e, f, g, h, i)
|
||||
#define TRACE10(context, event, a, b, c, d, e, f, g, h, i, j)
|
||||
#define TRACE11(context, event, a, b, c, d, e, f, g, h, i, j, k)
|
||||
#define TRACE12(context, event, a, b, c, d, e, f, g, h, i, j, k, l)
|
||||
#define TRACEPOINT_SEMAPHORE(context, event)
|
||||
#define TRACEPOINT_ACTIVE(context, event) false
|
||||
#define TRACEPOINT(context, ...)
|
||||
|
||||
#endif
|
||||
#endif // ENABLE_TRACING
|
||||
|
||||
|
||||
#endif // BITCOIN_UTIL_TRACE_H
|
||||
|
||||
Reference in New Issue
Block a user