mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-09 11:27:28 -03:00
Merge bitcoin/bitcoin#26593: tracing: Only prepare tracepoint arguments when actually tracing
0de3e96e33
tracing: use bitcoind pid in bcc tracing examples (0xb10c)411c6cfc6c
tracing: only prepare tracepoint args if attached (0xb10c)d524c1ec06
tracing: 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: utACK0de3e96e33
laanwj: re-ACK0de3e96e33
jb55: utACK0de3e96e33
vasild: ACK0de3e96e33
Tree-SHA512: 0e5e0dc5e0353beaf5c446e4be03d447e64228b1be71ee9972fde1d6fac3fac71a9d73c6ce4fa68975f87db2b2bf6eee2009921a2a145e24d83a475d007a559b
This commit is contained in:
commit
19f277711e
16 changed files with 221 additions and 118 deletions
|
@ -36,13 +36,16 @@ if(USDT_INCLUDE_DIR)
|
|||
include(CheckCXXSourceCompiles)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${USDT_INCLUDE_DIR})
|
||||
check_cxx_source_compiles("
|
||||
// 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
|
||||
#include <sys/sdt.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
DTRACE_PROBE(context, event);
|
||||
STAP_PROBEV(context, event);
|
||||
int a, b, c, d, e, f, g;
|
||||
DTRACE_PROBE7(context, event, a, b, c, d, e, f, g);
|
||||
STAP_PROBEV(context, event, a, b, c, d, e, f, g);
|
||||
}
|
||||
" HAVE_USDT_H
|
||||
)
|
||||
|
|
|
@ -82,7 +82,7 @@ about the connection. Peers can be selected individually to view recent P2P
|
|||
messages.
|
||||
|
||||
```
|
||||
$ python3 contrib/tracing/p2p_monitor.py ./build/src/bitcoind
|
||||
$ python3 contrib/tracing/p2p_monitor.py $(pidof bitcoind)
|
||||
```
|
||||
|
||||
Lists selectable peers and traffic and connection information.
|
||||
|
@ -150,7 +150,7 @@ lost. BCC prints: `Possibly lost 2 samples` on lost messages.
|
|||
|
||||
|
||||
```
|
||||
$ python3 contrib/tracing/log_raw_p2p_msgs.py ./build/src/bitcoind
|
||||
$ python3 contrib/tracing/log_raw_p2p_msgs.py $(pidof bitcoind)
|
||||
```
|
||||
|
||||
```
|
||||
|
@ -241,7 +241,7 @@ A BCC Python script to log the UTXO cache flushes. Based on the
|
|||
`utxocache:flush` tracepoint.
|
||||
|
||||
```bash
|
||||
$ python3 contrib/tracing/log_utxocache_flush.py ./build/src/bitcoind
|
||||
$ python3 contrib/tracing/log_utxocache_flush.py $(pidof bitcoind)
|
||||
```
|
||||
|
||||
```
|
||||
|
@ -300,7 +300,7 @@ comprising a timestamp along with all event data available via the event's
|
|||
tracepoint.
|
||||
|
||||
```console
|
||||
$ python3 contrib/tracing/mempool_monitor.py ./build/src/bitcoind
|
||||
$ python3 contrib/tracing/mempool_monitor.py $(pidof bitcoind)
|
||||
```
|
||||
|
||||
```
|
||||
|
|
|
@ -132,8 +132,9 @@ def print_message(event, inbound):
|
|||
)
|
||||
|
||||
|
||||
def main(bitcoind_path):
|
||||
bitcoind_with_usdts = USDT(path=str(bitcoind_path))
|
||||
def main(pid):
|
||||
print(f"Hooking into bitcoind with pid {pid}")
|
||||
bitcoind_with_usdts = USDT(pid=int(pid))
|
||||
|
||||
# attaching the trace functions defined in the BPF program to the tracepoints
|
||||
bitcoind_with_usdts.enable_probe(
|
||||
|
@ -176,8 +177,8 @@ def main(bitcoind_path):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("USAGE:", sys.argv[0], "path/to/bitcoind")
|
||||
if len(sys.argv) != 2:
|
||||
print("USAGE:", sys.argv[0], "<pid of bitcoind>")
|
||||
exit()
|
||||
path = sys.argv[1]
|
||||
main(path)
|
||||
pid = sys.argv[1]
|
||||
main(pid)
|
||||
|
|
|
@ -70,8 +70,9 @@ def print_event(event):
|
|||
))
|
||||
|
||||
|
||||
def main(bitcoind_path):
|
||||
bitcoind_with_usdts = USDT(path=str(bitcoind_path))
|
||||
def main(pid):
|
||||
print(f"Hooking into bitcoind with pid {pid}")
|
||||
bitcoind_with_usdts = USDT(pid=int(pid))
|
||||
|
||||
# attaching the trace functions defined in the BPF program
|
||||
# to the tracepoints
|
||||
|
@ -99,9 +100,9 @@ def main(bitcoind_path):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("USAGE: ", sys.argv[0], "path/to/bitcoind")
|
||||
if len(sys.argv) != 2:
|
||||
print("USAGE: ", sys.argv[0], "<pid of bitcoind>")
|
||||
exit(1)
|
||||
|
||||
path = sys.argv[1]
|
||||
main(path)
|
||||
pid = sys.argv[1]
|
||||
main(pid)
|
||||
|
|
|
@ -114,8 +114,9 @@ int trace_replaced(struct pt_regs *ctx) {
|
|||
"""
|
||||
|
||||
|
||||
def main(bitcoind_path):
|
||||
bitcoind_with_usdts = USDT(path=str(bitcoind_path))
|
||||
def main(pid):
|
||||
print(f"Hooking into bitcoind with pid {pid}")
|
||||
bitcoind_with_usdts = USDT(pid=int(pid))
|
||||
|
||||
# attaching the trace functions defined in the BPF program
|
||||
# to the tracepoints
|
||||
|
@ -365,8 +366,8 @@ class Dashboard:
|
|||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("USAGE: ", sys.argv[0], "path/to/bitcoind")
|
||||
print("USAGE: ", sys.argv[0], "<pid of bitcoind>")
|
||||
exit(1)
|
||||
|
||||
path = sys.argv[1]
|
||||
main(path)
|
||||
pid = sys.argv[1]
|
||||
main(pid)
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
# outbound P2P messages. The eBPF program submits the P2P messages to
|
||||
# this script via a BPF ring buffer.
|
||||
|
||||
import sys
|
||||
import curses
|
||||
import os
|
||||
import sys
|
||||
from curses import wrapper, panel
|
||||
from bcc import BPF, USDT
|
||||
|
||||
|
@ -115,10 +116,10 @@ class Peer:
|
|||
self.total_outbound_msgs += 1
|
||||
|
||||
|
||||
def main(bitcoind_path):
|
||||
def main(pid):
|
||||
peers = dict()
|
||||
|
||||
bitcoind_with_usdts = USDT(path=str(bitcoind_path))
|
||||
print(f"Hooking into bitcoind with pid {pid}")
|
||||
bitcoind_with_usdts = USDT(pid=int(pid))
|
||||
|
||||
# attaching the trace functions defined in the BPF program to the tracepoints
|
||||
bitcoind_with_usdts.enable_probe(
|
||||
|
@ -245,9 +246,14 @@ def render(screen, peers, cur_list_pos, scroll, ROWS_AVALIABLE_FOR_LIST, info_pa
|
|||
(msg.msg_type, msg.size), curses.A_NORMAL)
|
||||
|
||||
|
||||
def running_as_root():
|
||||
return os.getuid() == 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("USAGE:", sys.argv[0], "path/to/bitcoind")
|
||||
if len(sys.argv) != 2:
|
||||
print("USAGE:", sys.argv[0], "<pid of bitcoind>")
|
||||
exit()
|
||||
path = sys.argv[1]
|
||||
main(path)
|
||||
if not running_as_root():
|
||||
print("You might not have the privileges required to hook into the tracepoints!")
|
||||
pid = sys.argv[1]
|
||||
main(pid)
|
||||
|
|
|
@ -265,42 +265,52 @@ Arguments passed:
|
|||
|
||||
## Adding tracepoints to Bitcoin Core
|
||||
|
||||
To add a new tracepoint, `#include <util/trace.h>` in the compilation unit where
|
||||
the tracepoint is inserted. Use one of the `TRACEx` macros listed below
|
||||
depending on the number of arguments passed to the tracepoint. Up to 12
|
||||
arguments can be provided. The `context` and `event` specify the names by which
|
||||
the tracepoint is referred to. Please use `snake_case` and try to make sure that
|
||||
the tracepoint names make sense even without detailed knowledge of the
|
||||
implementation details. Do not forget to update the tracepoint list in this
|
||||
document.
|
||||
Use the `TRACEPOINT` macro to add a new tracepoint. If not yet included, include
|
||||
`util/trace.h` (defines the tracepoint macros) with `#include <util/trace.h>`.
|
||||
Each tracepoint needs a `context` and an `event`. Please use `snake_case` and
|
||||
try to make sure that the tracepoint names make sense even without detailed
|
||||
knowledge of the implementation details. You can pass zero to twelve arguments
|
||||
to the tracepoint. Each tracepoint also needs a global semaphore. The semaphore
|
||||
gates the tracepoint arguments from being processed if we are not attached to
|
||||
the tracepoint. Add a `TRACEPOINT_SEMAPHORE(context, event)` with the `context`
|
||||
and `event` of your tracepoint in the top-level namespace at the beginning of
|
||||
the file. Do not forget to update the tracepoint list in this document.
|
||||
|
||||
```c
|
||||
#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)
|
||||
```
|
||||
|
||||
For example:
|
||||
For example, the `net:outbound_message` tracepoint in `src/net.cpp` with six
|
||||
arguments.
|
||||
|
||||
```C++
|
||||
TRACE6(net, inbound_message,
|
||||
pnode->GetId(),
|
||||
pnode->m_addr_name.c_str(),
|
||||
pnode->ConnectionTypeAsString().c_str(),
|
||||
sanitizedType.c_str(),
|
||||
msg.data.size(),
|
||||
msg.data.data()
|
||||
);
|
||||
// src/net.cpp
|
||||
TRACEPOINT_SEMAPHORE(net, outbound_message);
|
||||
…
|
||||
void CConnman::PushMessage(…) {
|
||||
…
|
||||
TRACEPOINT(net, outbound_message,
|
||||
pnode->GetId(),
|
||||
pnode->m_addr_name.c_str(),
|
||||
pnode->ConnectionTypeAsString().c_str(),
|
||||
sanitizedType.c_str(),
|
||||
msg.data.size(),
|
||||
msg.data.data()
|
||||
);
|
||||
…
|
||||
}
|
||||
```
|
||||
If needed, an extra `if (TRACEPOINT_ACTIVE(context, event)) {...}` check can be
|
||||
used to prepare somewhat expensive arguments right before the tracepoint. While
|
||||
the tracepoint arguments are only prepared when we attach something to the
|
||||
tracepoint, an argument preparation should never hang the process. Hashing and
|
||||
serialization of data structures is probably fine, a `sleep(10s)` not.
|
||||
|
||||
```C++
|
||||
// An example tracepoint with an expensive argument.
|
||||
|
||||
TRACEPOINT_SEMAPHORE(example, gated_expensive_argument);
|
||||
…
|
||||
if (TRACEPOINT_ACTIVE(example, gated_expensive_argument)) {
|
||||
expensive_argument = expensive_calulation();
|
||||
TRACEPOINT(example, gated_expensive_argument, expensive_argument);
|
||||
}
|
||||
```
|
||||
|
||||
### Guidelines and best practices
|
||||
|
@ -318,12 +328,6 @@ the tracepoint. See existing examples in [contrib/tracing/].
|
|||
|
||||
[contrib/tracing/]: ../contrib/tracing/
|
||||
|
||||
#### No expensive computations for tracepoints
|
||||
Data passed to the tracepoint should be inexpensive to compute. Although the
|
||||
tracepoint itself only has overhead when enabled, the code to compute arguments
|
||||
is always run - even if the tracepoint is not used. For example, avoid
|
||||
serialization and parsing.
|
||||
|
||||
#### Semi-stable API
|
||||
Tracepoints should have a semi-stable API. Users should be able to rely on the
|
||||
tracepoints for scripting. This means tracepoints need to be documented, and the
|
||||
|
@ -347,7 +351,7 @@ first six argument fields. Binary data can be placed in later arguments. The BCC
|
|||
supports reading from all 12 arguments.
|
||||
|
||||
#### Strings as C-style String
|
||||
Generally, strings should be passed into the `TRACEx` macros as pointers to
|
||||
Generally, strings should be passed into the `TRACEPOINT` macros as pointers to
|
||||
C-style strings (a null-terminated sequence of characters). For C++
|
||||
`std::strings`, [`c_str()`] can be used. It's recommended to document the
|
||||
maximum expected string size if known.
|
||||
|
@ -370,9 +374,9 @@ $ gdb ./build/src/bitcoind
|
|||
…
|
||||
(gdb) info probes
|
||||
Type Provider Name Where Semaphore Object
|
||||
stap net inbound_message 0x000000000014419e /build/src/bitcoind
|
||||
stap net outbound_message 0x0000000000107c05 /build/src/bitcoind
|
||||
stap validation block_connected 0x00000000002fb10c /build/src/bitcoind
|
||||
stap net inbound_message 0x000000000014419e 0x0000000000d29bd2 /build/src/bitcoind
|
||||
stap net outbound_message 0x0000000000107c05 0x0000000000d29bd0 /build/src/bitcoind
|
||||
stap validation block_connected 0x00000000002fb10c 0x0000000000d29bd8 /build/src/bitcoind
|
||||
…
|
||||
```
|
||||
|
||||
|
@ -388,7 +392,7 @@ Displaying notes found in: .note.stapsdt
|
|||
stapsdt 0x0000005d NT_STAPSDT (SystemTap probe descriptors)
|
||||
Provider: net
|
||||
Name: outbound_message
|
||||
Location: 0x0000000000107c05, Base: 0x0000000000579c90, Semaphore: 0x0000000000000000
|
||||
Location: 0x0000000000107c05, Base: 0x0000000000579c90, Semaphore: 0x0000000000d29bd0
|
||||
Arguments: -8@%r12 8@%rbx 8@%rdi 8@192(%rsp) 8@%rax 8@%rdx
|
||||
…
|
||||
```
|
||||
|
@ -407,7 +411,7 @@ between distributions. For example, on
|
|||
|
||||
```
|
||||
$ tplist -l ./build/src/bitcoind -v
|
||||
b'net':b'outbound_message' [sema 0x0]
|
||||
b'net':b'outbound_message' [sema 0xd29bd0]
|
||||
1 location(s)
|
||||
6 argument(s)
|
||||
…
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
#include <random.h>
|
||||
#include <util/trace.h>
|
||||
|
||||
TRACEPOINT_SEMAPHORE(utxocache, add);
|
||||
TRACEPOINT_SEMAPHORE(utxocache, spent);
|
||||
TRACEPOINT_SEMAPHORE(utxocache, uncache);
|
||||
|
||||
std::optional<Coin> CCoinsView::GetCoin(const COutPoint& outpoint) const { return std::nullopt; }
|
||||
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
||||
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
|
||||
|
@ -97,7 +101,7 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
|
|||
it->second.coin = std::move(coin);
|
||||
it->second.AddFlags(CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0), *it, m_sentinel);
|
||||
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
|
||||
TRACE5(utxocache, add,
|
||||
TRACEPOINT(utxocache, add,
|
||||
outpoint.hash.data(),
|
||||
(uint32_t)outpoint.n,
|
||||
(uint32_t)it->second.coin.nHeight,
|
||||
|
@ -131,7 +135,7 @@ bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
|
|||
CCoinsMap::iterator it = FetchCoin(outpoint);
|
||||
if (it == cacheCoins.end()) return false;
|
||||
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
|
||||
TRACE5(utxocache, spent,
|
||||
TRACEPOINT(utxocache, spent,
|
||||
outpoint.hash.data(),
|
||||
(uint32_t)outpoint.n,
|
||||
(uint32_t)it->second.coin.nHeight,
|
||||
|
@ -278,7 +282,7 @@ void CCoinsViewCache::Uncache(const COutPoint& hash)
|
|||
CCoinsMap::iterator it = cacheCoins.find(hash);
|
||||
if (it != cacheCoins.end() && !it->second.IsDirty() && !it->second.IsFresh()) {
|
||||
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
|
||||
TRACE5(utxocache, uncache,
|
||||
TRACEPOINT(utxocache, uncache,
|
||||
hash.hash.data(),
|
||||
(uint32_t)hash.n,
|
||||
(uint32_t)it->second.coin.nHeight,
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
TRACEPOINT_SEMAPHORE(net, outbound_message);
|
||||
|
||||
/** Maximum number of block-relay-only anchor connections */
|
||||
static constexpr size_t MAX_BLOCK_RELAY_ONLY_ANCHORS = 2;
|
||||
static_assert (MAX_BLOCK_RELAY_ONLY_ANCHORS <= static_cast<size_t>(MAX_BLOCK_RELAY_ONLY_CONNECTIONS), "MAX_BLOCK_RELAY_ONLY_ANCHORS must not exceed MAX_BLOCK_RELAY_ONLY_CONNECTIONS.");
|
||||
|
@ -3811,7 +3813,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
|
|||
CaptureMessage(pnode->addr, msg.m_type, msg.data, /*is_incoming=*/false);
|
||||
}
|
||||
|
||||
TRACE6(net, outbound_message,
|
||||
TRACEPOINT(net, outbound_message,
|
||||
pnode->GetId(),
|
||||
pnode->m_addr_name.c_str(),
|
||||
pnode->ConnectionTypeAsString().c_str(),
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
|
||||
using namespace util::hex_literals;
|
||||
|
||||
TRACEPOINT_SEMAPHORE(net, inbound_message);
|
||||
|
||||
/** Headers download timeout.
|
||||
* Timeout = base + per_header * (expected number of headers) */
|
||||
static constexpr auto HEADERS_DOWNLOAD_TIMEOUT_BASE = 15min;
|
||||
|
@ -4969,7 +4971,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
|
|||
CNetMessage& msg{poll_result->first};
|
||||
bool fMoreWork = poll_result->second;
|
||||
|
||||
TRACE6(net, inbound_message,
|
||||
TRACEPOINT(net, inbound_message,
|
||||
pfrom->GetId(),
|
||||
pfrom->m_addr_name.c_str(),
|
||||
pfrom->ConnectionTypeAsString().c_str(),
|
||||
|
|
|
@ -136,6 +136,7 @@ add_executable(test_bitcoin
|
|||
util_string_tests.cpp
|
||||
util_tests.cpp
|
||||
util_threadnames_tests.cpp
|
||||
util_trace_tests.cpp
|
||||
validation_block_tests.cpp
|
||||
validation_chainstate_tests.cpp
|
||||
validation_chainstatemanager_tests.cpp
|
||||
|
|
58
src/test/util_trace_tests.cpp
Normal file
58
src/test/util_trace_tests.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2023 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <test/util/setup_common.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <util/trace.h>
|
||||
|
||||
TRACEPOINT_SEMAPHORE(test, zero_args);
|
||||
TRACEPOINT_SEMAPHORE(test, one_arg);
|
||||
TRACEPOINT_SEMAPHORE(test, six_args);
|
||||
TRACEPOINT_SEMAPHORE(test, twelve_args);
|
||||
TRACEPOINT_SEMAPHORE(test, check_if_attached);
|
||||
TRACEPOINT_SEMAPHORE(test, expensive_section);
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(util_trace_tests, BasicTestingSetup)
|
||||
|
||||
// Tests the TRACEPOINT macro and that we can compile tracepoints with 0 to 12 args.
|
||||
BOOST_AUTO_TEST_CASE(test_tracepoints)
|
||||
{
|
||||
TRACEPOINT(test, zero_args);
|
||||
TRACEPOINT(test, one_arg, 1);
|
||||
TRACEPOINT(test, six_args, 1, 2, 3, 4, 5, 6);
|
||||
TRACEPOINT(test, twelve_args, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
int fail_test_if_executed()
|
||||
{
|
||||
BOOST_CHECK(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tracepoint_check_if_attached)
|
||||
{
|
||||
// TRACEPOINT should check if we are attaching to the tracepoint and only then
|
||||
// process arguments. This means, only if we are attached to the
|
||||
// `test:check_if_attached` tracepoint, fail_test_if_executed() is executed.
|
||||
// Since we don't attach to the tracepoint when running the test, it succeeds.
|
||||
TRACEPOINT(test, check_if_attached, fail_test_if_executed());
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_tracepoint_manual_tracepoint_active_check)
|
||||
{
|
||||
// We should be able to use the TRACEPOINT_ACTIVE() macro to only
|
||||
// execute an 'expensive' code section if we are attached to the
|
||||
// tracepoint.
|
||||
if (TRACEPOINT_ACTIVE(test, expensive_section)) {
|
||||
BOOST_CHECK(false); // expensive_function()
|
||||
TRACEPOINT(test, expensive_section);
|
||||
}
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -34,6 +34,9 @@
|
|||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
TRACEPOINT_SEMAPHORE(mempool, added);
|
||||
TRACEPOINT_SEMAPHORE(mempool, removed);
|
||||
|
||||
bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
@ -477,7 +480,7 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
|
|||
txns_randomized.emplace_back(newit->GetSharedTx());
|
||||
newit->idx_randomized = txns_randomized.size() - 1;
|
||||
|
||||
TRACE3(mempool, added,
|
||||
TRACEPOINT(mempool, added,
|
||||
entry.GetTx().GetHash().data(),
|
||||
entry.GetTxSize(),
|
||||
entry.GetFee()
|
||||
|
@ -497,7 +500,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
|||
// notification.
|
||||
m_opts.signals->TransactionRemovedFromMempool(it->GetSharedTx(), reason, mempool_sequence);
|
||||
}
|
||||
TRACE5(mempool, removed,
|
||||
TRACEPOINT(mempool, removed,
|
||||
it->GetTx().GetHash().data(),
|
||||
RemovalReasonToString(reason).c_str(),
|
||||
it->GetTxSize(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -108,6 +108,11 @@ const std::vector<std::string> CHECKLEVEL_DOC {
|
|||
* */
|
||||
static constexpr int PRUNE_LOCK_BUFFER{10};
|
||||
|
||||
TRACEPOINT_SEMAPHORE(validation, block_connected);
|
||||
TRACEPOINT_SEMAPHORE(utxocache, flush);
|
||||
TRACEPOINT_SEMAPHORE(mempool, replaced);
|
||||
TRACEPOINT_SEMAPHORE(mempool, rejected);
|
||||
|
||||
const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locator) const
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
@ -1284,7 +1289,7 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
|
|||
tx.GetWitnessHash().ToString(),
|
||||
entry->GetFee(),
|
||||
entry->GetTxSize());
|
||||
TRACE7(mempool, replaced,
|
||||
TRACEPOINT(mempool, replaced,
|
||||
it->GetTx().GetHash().data(),
|
||||
it->GetTxSize(),
|
||||
it->GetFee(),
|
||||
|
@ -1846,7 +1851,7 @@ MempoolAcceptResult AcceptToMemoryPool(Chainstate& active_chainstate, const CTra
|
|||
|
||||
for (const COutPoint& hashTx : coins_to_uncache)
|
||||
active_chainstate.CoinsTip().Uncache(hashTx);
|
||||
TRACE2(mempool, rejected,
|
||||
TRACEPOINT(mempool, rejected,
|
||||
tx->GetHash().data(),
|
||||
result.m_state.GetRejectReason().c_str()
|
||||
);
|
||||
|
@ -2718,7 +2723,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
Ticks<SecondsDouble>(m_chainman.time_index),
|
||||
Ticks<MillisecondsDouble>(m_chainman.time_index) / m_chainman.num_blocks_total);
|
||||
|
||||
TRACE6(validation, block_connected,
|
||||
TRACEPOINT(validation, block_connected,
|
||||
block_hash.data(),
|
||||
pindex->nHeight,
|
||||
block.vtx.size(),
|
||||
|
@ -2894,7 +2899,7 @@ bool Chainstate::FlushStateToDisk(
|
|||
}
|
||||
m_last_flush = nNow;
|
||||
full_flush_completed = true;
|
||||
TRACE5(utxocache, flush,
|
||||
TRACEPOINT(utxocache, flush,
|
||||
int64_t{Ticks<std::chrono::microseconds>(SteadyClock::now() - nNow)},
|
||||
(uint32_t)mode,
|
||||
(uint64_t)coins_count,
|
||||
|
|
|
@ -35,6 +35,11 @@ using common::TransactionErrorString;
|
|||
using interfaces::FoundBlock;
|
||||
using node::TransactionError;
|
||||
|
||||
TRACEPOINT_SEMAPHORE(coin_selection, selected_coins);
|
||||
TRACEPOINT_SEMAPHORE(coin_selection, normal_create_tx_internal);
|
||||
TRACEPOINT_SEMAPHORE(coin_selection, attempting_aps_create_tx);
|
||||
TRACEPOINT_SEMAPHORE(coin_selection, aps_create_tx_internal);
|
||||
|
||||
namespace wallet {
|
||||
static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100};
|
||||
|
||||
|
@ -1159,7 +1164,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
|||
return util::Error{err.empty() ?_("Insufficient funds") : err};
|
||||
}
|
||||
const SelectionResult& result = *select_coins_res;
|
||||
TRACE5(coin_selection, selected_coins,
|
||||
TRACEPOINT(coin_selection, selected_coins,
|
||||
wallet.GetName().c_str(),
|
||||
GetAlgorithmName(result.GetAlgo()).c_str(),
|
||||
result.GetTarget(),
|
||||
|
@ -1380,7 +1385,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
|
|||
LOCK(wallet.cs_wallet);
|
||||
|
||||
auto res = CreateTransactionInternal(wallet, vecSend, change_pos, coin_control, sign);
|
||||
TRACE4(coin_selection, normal_create_tx_internal,
|
||||
TRACEPOINT(coin_selection, normal_create_tx_internal,
|
||||
wallet.GetName().c_str(),
|
||||
bool(res),
|
||||
res ? res->fee : 0,
|
||||
|
@ -1389,7 +1394,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
|
|||
const auto& txr_ungrouped = *res;
|
||||
// try with avoidpartialspends unless it's enabled already
|
||||
if (txr_ungrouped.fee > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
|
||||
TRACE1(coin_selection, attempting_aps_create_tx, wallet.GetName().c_str());
|
||||
TRACEPOINT(coin_selection, attempting_aps_create_tx, wallet.GetName().c_str());
|
||||
CCoinControl tmp_cc = coin_control;
|
||||
tmp_cc.m_avoid_partial_spends = true;
|
||||
|
||||
|
@ -1401,7 +1406,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
|
|||
auto txr_grouped = CreateTransactionInternal(wallet, vecSend, change_pos, tmp_cc, sign);
|
||||
// if fee of this alternative one is within the range of the max fee, we use this one
|
||||
const bool use_aps{txr_grouped.has_value() ? (txr_grouped->fee <= txr_ungrouped.fee + wallet.m_max_aps_fee) : false};
|
||||
TRACE5(coin_selection, aps_create_tx_internal,
|
||||
TRACEPOINT(coin_selection, aps_create_tx_internal,
|
||||
wallet.GetName().c_str(),
|
||||
use_aps,
|
||||
txr_grouped.has_value(),
|
||||
|
|
Loading…
Reference in a new issue