Skip to content

Commit c1059c9

Browse files
committed
Merge bitcoin/bitcoin#24770: Put lock logging behind DEBUG_LOCKCONTENTION preprocessor directive
4394733 Add DEBUG_LOCKCONTENTION documentation to the developer notes (Jon Atack) 39a34b6 Put lock logging behind DEBUG_LOCKCONTENTION preprocessor directive (Jon Atack) Pull request description: This is a more minimal, no-frills version of #24734 for backport. The other fixes and improvements in that pull can be done after. *Copy of the PR 24734 description:* PRs #22736, #22904 and #23223 changed lock contention logging from a `DEBUG_LOCKCONTENTION` compile-time preprocessor directive to a runtime `lock` log category and improved the logging output. This changed the locking from using `lock()` to `try_lock()`: - `void Mutex::UniqueLock::lock()` acquires the mutex and blocks until it gains access to it - `bool Mutex::UniqueLock::try_lock()` doesn't block but instead immediately returns whether it acquired the mutex; it may be used by `lock()` internally as part of the deadlock-avoidance algorithm In theory the cost of `try_lock` might be essentially the [same](https://www.erisian.com.au/bitcoin-core-dev/log-2022-03-31.html#l-697) relative to `lock`. The test-and-set logic of these calls is purported to be ~ constant time, optimised and light/quick if used carefully (i.e. no mutex convoying), compared to system calls, memory/cache coherency and fences, wait queues, and (particularly) lock contentions. See the discussion around bitcoin/bitcoin#22736 (comment) and after with respect to performance/cost aspects. However, there are reasonable concerns (see [here](bitcoin/bitcoin#22736 (comment)) and [here](https://www.erisian.com.au/bitcoin-core-dev/log-2022-03-31.html#l-620)) that `Base::try_lock()` may be potentially [costly](https://www.erisian.com.au/bitcoin-core-dev/log-2022-03-31.html#l-700) or [risky](bitcoin/bitcoin#22904 (comment)) compared to `Base::lock()` in this very frequently called code. One alternative to keep the run-time lock logging would be to gate the `try_lock` call behind the logging conditional, for example as proposed in bitcoin/bitcoin@ccd73de and ACKed [here](bitcoin/bitcoin#22736 (comment)). However, this would add the [cost](bitcoin/bitcoin#22736 (comment)) of `if (LogAcceptCategory(BCLog::LOCK))` to the hotspot, instead of replacing `lock` with `try_lock`, for the most frequent happy path (non-contention). It turns out we can keep the advantages of the runtime lock contention logging (the ability to turn it on/off at runtime) while out of prudence putting the `try_lock()` call and `lock` logging category behind a `DEBUG_LOCKCONTENTION` compile-time preprocessor directive, and also still retain the lock logging enhancements of the mentioned PRs, as suggested in bitcoin/bitcoin#24734 (comment) by W. J. van der Laan, in bitcoin/bitcoin#22736 (comment), and in the linked IRC discussion. Proposed here and for backport to v23. ACKs for top commit: laanwj: Code review ACK 4394733 Tree-SHA512: 89b1271cae1dca0eb251914b1a60fc5b68320aab4a3939c57eec3a33a3c8f01688f05d95dfc31f91d71a6ed80cfe2d67b77ff14742611cc206175e47b2e5d3b1
2 parents 38d3d0b + 4394733 commit c1059c9

File tree

7 files changed

+32
-6
lines changed

7 files changed

+32
-6
lines changed

doc/developer-notes.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Developer Notes
1717
- [`debug.log`](#debuglog)
1818
- [Signet, testnet, and regtest modes](#signet-testnet-and-regtest-modes)
1919
- [DEBUG_LOCKORDER](#debug_lockorder)
20+
- [DEBUG_LOCKCONTENTION](#debug_lockcontention)
2021
- [Valgrind suppressions file](#valgrind-suppressions-file)
2122
- [Compiling for test coverage](#compiling-for-test-coverage)
2223
- [Performance profiling with perf](#performance-profiling-with-perf)
@@ -362,6 +363,19 @@ configure option adds `-DDEBUG_LOCKORDER` to the compiler flags. This inserts
362363
run-time checks to keep track of which locks are held and adds warnings to the
363364
`debug.log` file if inconsistencies are detected.
364365

366+
### DEBUG_LOCKCONTENTION
367+
368+
Defining `DEBUG_LOCKCONTENTION` adds a "lock" logging category to the logging
369+
RPC that, when enabled, logs the location and duration of each lock contention
370+
to the `debug.log` file.
371+
372+
To enable it, run configure with `-DDEBUG_LOCKCONTENTION` added to your
373+
CPPFLAGS, e.g. `CPPFLAGS="-DDEBUG_LOCKCONTENTION"`, then build and run bitcoind.
374+
375+
You can then use the `-debug=lock` configuration option at bitcoind startup or
376+
`bitcoin-cli logging '["lock"]'` at runtime to turn on lock contention logging.
377+
It can be toggled off again with `bitcoin-cli logging [] '["lock"]'`.
378+
365379
### Assertions and Checks
366380

367381
The util file `src/util/check.h` offers helpers to protect against coding and

src/logging.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,9 @@ const CLogCategoryDesc LogCategories[] =
160160
{BCLog::VALIDATION, "validation"},
161161
{BCLog::I2P, "i2p"},
162162
{BCLog::IPC, "ipc"},
163+
#ifdef DEBUG_LOCKCONTENTION
163164
{BCLog::LOCK, "lock"},
165+
#endif
164166
{BCLog::UTIL, "util"},
165167
{BCLog::BLOCKSTORE, "blockstorage"},
166168
{BCLog::ALL, "1"},

src/logging.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ namespace BCLog {
6060
VALIDATION = (1 << 21),
6161
I2P = (1 << 22),
6262
IPC = (1 << 23),
63+
#ifdef DEBUG_LOCKCONTENTION
6364
LOCK = (1 << 24),
65+
#endif
6466
UTIL = (1 << 25),
6567
BLOCKSTORE = (1 << 26),
6668
ALL = ~(uint32_t)0,

src/net.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <crypto/siphash.h>
1414
#include <hash.h>
1515
#include <i2p.h>
16+
#include <logging.h>
1617
#include <net_permissions.h>
1718
#include <netaddress.h>
1819
#include <netbase.h>
@@ -32,6 +33,7 @@
3233
#include <cstdint>
3334
#include <deque>
3435
#include <functional>
36+
#include <list>
3537
#include <map>
3638
#include <memory>
3739
#include <optional>

src/sync.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
#ifndef BITCOIN_SYNC_H
77
#define BITCOIN_SYNC_H
88

9+
#ifdef DEBUG_LOCKCONTENTION
910
#include <logging.h>
1011
#include <logging/timer.h>
12+
#endif
13+
1114
#include <threadsafety.h>
1215
#include <util/macros.h>
1316

@@ -136,8 +139,10 @@ class SCOPED_LOCKABLE UniqueLock : public Base
136139
void Enter(const char* pszName, const char* pszFile, int nLine)
137140
{
138141
EnterCritical(pszName, pszFile, nLine, Base::mutex());
142+
#ifdef DEBUG_LOCKCONTENTION
139143
if (Base::try_lock()) return;
140144
LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
145+
#endif
141146
Base::lock();
142147
}
143148

src/test/checkqueue_tests.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919
#include <vector>
2020

2121
/**
22-
* Identical to TestingSetup but excludes lock contention logging, as some of
23-
* these tests are designed to be heavily contested to trigger race conditions
24-
* or other issues.
22+
* Identical to TestingSetup but excludes lock contention logging if
23+
* `DEBUG_LOCKCONTENTION` is defined, as some of these tests are designed to be
24+
* heavily contested to trigger race conditions or other issues.
2525
*/
2626
struct NoLockLoggingTestingSetup : public TestingSetup {
2727
NoLockLoggingTestingSetup()
28+
#ifdef DEBUG_LOCKCONTENTION
2829
: TestingSetup{CBaseChainParams::MAIN, /*extra_args=*/{"-debugexclude=lock"}} {}
30+
#else
31+
: TestingSetup{CBaseChainParams::MAIN} {}
32+
#endif
2933
};
3034

3135
BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, NoLockLoggingTestingSetup)

test/functional/rpc_misc.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ def run_test(self):
5656

5757
self.log.info("test logging rpc and help")
5858

59-
# Test logging RPC returns the expected number of logging categories.
60-
assert_equal(len(node.logging()), 27)
61-
6259
# Test toggling a logging category on/off/on with the logging RPC.
6360
assert_equal(node.logging()['qt'], True)
6461
node.logging(exclude=['qt'])

0 commit comments

Comments
 (0)