Skip to content

Commit 2c35fe6

Browse files
author
MarcoFalke
committed
Merge bitcoin#15849: Thread names in logs and deadlock debug tools
8722e54 threads: add thread names to deadlock debugging message (James O'Beirne) 383b186 threads: prefix log messages with thread names (James O'Beirne) ddd95cc tests: add threadutil tests (James O'Beirne) ae5f2b6 threads: introduce util/threadnames, refactor thread naming (James O'Beirne) 188ca75 disable HAVE_THREAD_LOCAL on unreliable platforms (James O'Beirne) Pull request description: I'm resurrecting this one (from bitcoin#13168) because I need it to make progress on bitcoin#15735. It's now off by default and can be turned on with `-logthreadnames=1`. Ran some benchmarks (IBD from local peer from 500_000 -> 504_000) and it's within spitting distance either on or off: ### threadnames off (default) #### 2018-05-threadnames.3 vs. master (absolute) | name | iterations | 2018-05-threadnames.3 | master | |------------------------------------------------|-----------:|----------------------------|----------------------------| | ibd.local.500000.504000.dbcache=2048 | 3 | 376.1584 (± 9.2944) | 392.3414 (± 13.4238) | | ibd.local.500000.504000.dbcache=2048.mem-usage | 3 | 2236117.3333 (± 1845.9623) | 2238690.6667 (± 2669.3487) | #### 2018-05-threadnames.3 vs. master (relative) | name | iterations | 2018-05-threadnames.3 | master | |------------------------------------------------|-----------:|----------------------:|-------:| | ibd.local.500000.504000.dbcache=2048 | 3 | 1 | 1.043 | | ibd.local.500000.504000.dbcache=2048.mem-usage | 3 | 1 | 1.001 | ### threadnames on #### 2018-05-threadnames-take-2 vs. master (absolute) | name | iterations | 2018-05-threadnames-take-2 | master | |------------------------------------------------|-----------:|----------------------------|----------------------------| | ibd.local.500000.504000.dbcache=2048 | 3 | 367.6861 (± 0.3941) | 364.1667 (± 0.9776) | | ibd.local.500000.504000.dbcache=2048.mem-usage | 3 | 2238461.3333 (± 3697.8730) | 2237014.6667 (± 3307.6966) | #### 2018-05-threadnames-take-2 vs. master (relative) | name | iterations | 2018-05-threadnames-take-2 | master | |------------------------------------------------|-----------:|---------------------------:|-------:| | ibd.local.500000.504000.dbcache=2048 | 3 | 1.010 | 1.00 | | ibd.local.500000.504000.dbcache=2048.mem-usage | 3 | 1.001 | 1.00 | ``` ACKs for commit 8722e5: Empact: utACK bitcoin@8722e54 jnewbery: utACK 8722e54 MarcoFalke: re-utACK 8722e54 (Only change since my previous review is DEFAULT_LOGTHREADNAMES=false and stylistic updates Tree-SHA512: 50af992708295b8d680cf10025262dd964e599a356bdfc1dfc84fb18c00afabcb34d3d12d551b0677ff81f8fccad0e17c1d5b24dfecb953a913bc77fdd1a4577
2 parents 10ed4df + 8722e54 commit 2c35fe6

19 files changed

+239
-58
lines changed

configure.ac

+17-2
Original file line numberDiff line numberDiff line change
@@ -836,8 +836,23 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
836836
}
837837
])],
838838
[
839-
AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
840-
AC_MSG_RESULT(yes)
839+
case $host in
840+
*mingw*)
841+
# mingw32's implementation of thread_local has also been shown to behave
842+
# erroneously under concurrent usage; see:
843+
# https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605
844+
AC_MSG_RESULT(no)
845+
;;
846+
*darwin*)
847+
# TODO enable thread_local on later versions of Darwin where it is
848+
# supported (per https://stackoverflow.com/a/29929949)
849+
AC_MSG_RESULT(no)
850+
;;
851+
*)
852+
AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
853+
AC_MSG_RESULT(yes)
854+
;;
855+
esac
841856
],
842857
[
843858
AC_MSG_RESULT(no)

doc/release-notes-15849.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Thread names in logs
2+
--------------------
3+
4+
On platforms supporting `thread_local`, log lines can be prefixed with the name
5+
of the thread that caused the log. To enable this behavior, use
6+
`-logthreadnames=1`.

src/Makefile.am

+2
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ BITCOIN_CORE_H = \
209209
util/memory.h \
210210
util/moneystr.h \
211211
util/rbf.h \
212+
util/threadnames.h \
212213
util/time.h \
213214
util/url.h \
214215
util/validation.h \
@@ -489,6 +490,7 @@ libbitcoin_util_a_SOURCES = \
489490
util/system.cpp \
490491
util/moneystr.cpp \
491492
util/rbf.cpp \
493+
util/threadnames.cpp \
492494
util/strencodings.cpp \
493495
util/time.cpp \
494496
util/url.cpp \

src/Makefile.test.include

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ BITCOIN_TESTS =\
138138
test/skiplist_tests.cpp \
139139
test/streams_tests.cpp \
140140
test/sync_tests.cpp \
141+
test/util_threadnames_tests.cpp \
141142
test/timedata_tests.cpp \
142143
test/torcontrol_tests.cpp \
143144
test/transaction_tests.cpp \

src/bitcoind.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <util/system.h>
2020
#include <httpserver.h>
2121
#include <httprpc.h>
22+
#include <util/threadnames.h>
2223
#include <util/strencodings.h>
2324
#include <walletinitinterface.h>
2425

@@ -64,6 +65,8 @@ static bool AppInit(int argc, char* argv[])
6465

6566
bool fRet = false;
6667

68+
util::ThreadRename("init");
69+
6770
//
6871
// Parameters
6972
//

src/httpserver.cpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <chainparamsbase.h>
88
#include <compat.h>
9+
#include <util/threadnames.h>
910
#include <util/system.h>
1011
#include <util/strencodings.h>
1112
#include <netbase.h>
@@ -17,7 +18,7 @@
1718
#include <memory>
1819
#include <stdio.h>
1920
#include <stdlib.h>
20-
#include <string.h>
21+
#include <string>
2122

2223
#include <sys/types.h>
2324
#include <sys/stat.h>
@@ -284,7 +285,7 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
284285
/** Event dispatcher thread */
285286
static bool ThreadHTTP(struct event_base* base)
286287
{
287-
RenameThread("bitcoin-http");
288+
util::ThreadRename("http");
288289
LogPrint(BCLog::HTTP, "Entering http event loop\n");
289290
event_base_dispatch(base);
290291
// Event loop will be interrupted by InterruptHTTPServer()
@@ -335,9 +336,9 @@ static bool HTTPBindAddresses(struct evhttp* http)
335336
}
336337

337338
/** Simple wrapper to set thread name and run work queue */
338-
static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue)
339+
static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
339340
{
340-
RenameThread("bitcoin-httpworker");
341+
util::ThreadRename(strprintf("httpworker.%i", worker_num));
341342
queue->Run();
342343
}
343344

@@ -430,7 +431,7 @@ void StartHTTPServer()
430431
threadHTTP = std::thread(ThreadHTTP, eventBase);
431432

432433
for (int i = 0; i < rpcThreads; i++) {
433-
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
434+
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i);
434435
}
435436
}
436437

src/init.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include <script/sigcache.h>
4242
#include <scheduler.h>
4343
#include <shutdown.h>
44+
#include <util/threadnames.h>
4445
#include <timedata.h>
4546
#include <txdb.h>
4647
#include <txmempool.h>
@@ -206,7 +207,7 @@ void Shutdown(InitInterfaces& interfaces)
206207
/// for example if the data directory was found to be locked.
207208
/// Be sure that anything that writes files or flushes caches only does this if the respective
208209
/// module was initialized.
209-
RenameThread("bitcoin-shutoff");
210+
util::ThreadRename("shutoff");
210211
mempool.AddTransactionsUpdated(1);
211212

212213
StopHTTPRPC();
@@ -506,6 +507,7 @@ void SetupServerArgs()
506507
gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), false, OptionsCategory::DEBUG_TEST);
507508
gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
508509
gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
510+
gArgs.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), false, OptionsCategory::DEBUG_TEST);
509511
gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
510512
gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST);
511513
gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST);
@@ -666,7 +668,7 @@ static void CleanupBlockRevFiles()
666668
static void ThreadImport(std::vector<fs::path> vImportFiles)
667669
{
668670
const CChainParams& chainparams = Params();
669-
RenameThread("bitcoin-loadblk");
671+
util::ThreadRename("loadblk");
670672
ScheduleBatchPriority();
671673

672674
{
@@ -862,6 +864,7 @@ void InitLogging()
862864
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
863865
LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
864866
LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
867+
LogInstance().m_log_threadnames = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
865868

866869
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
867870

@@ -1286,7 +1289,7 @@ bool AppInitMain(InitInterfaces& interfaces)
12861289
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
12871290
if (nScriptCheckThreads) {
12881291
for (int i=0; i<nScriptCheckThreads-1; i++)
1289-
threadGroup.create_thread(&ThreadScriptCheck);
1292+
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
12901293
}
12911294

12921295
// Start the lightweight task scheduler thread

src/logging.cpp

+16-10
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

66
#include <logging.h>
7+
#include <util/threadnames.h>
78
#include <util/time.h>
89

10+
#include <mutex>
11+
912
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
1013

1114
BCLog::Logger& LogInstance()
@@ -174,7 +177,7 @@ std::vector<CLogCategoryActive> ListActiveLogCategories()
174177
return ret;
175178
}
176179

177-
std::string BCLog::Logger::LogTimestampStr(const std::string &str)
180+
std::string BCLog::Logger::LogTimestampStr(const std::string& str)
178181
{
179182
std::string strStamped;
180183

@@ -196,29 +199,32 @@ std::string BCLog::Logger::LogTimestampStr(const std::string &str)
196199
} else
197200
strStamped = str;
198201

199-
if (!str.empty() && str[str.size()-1] == '\n')
200-
m_started_new_line = true;
201-
else
202-
m_started_new_line = false;
203-
204202
return strStamped;
205203
}
206204

207205
void BCLog::Logger::LogPrintStr(const std::string &str)
208206
{
209-
std::string strTimestamped = LogTimestampStr(str);
207+
std::string str_prefixed = str;
208+
209+
if (m_log_threadnames && m_started_new_line) {
210+
str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
211+
}
212+
213+
str_prefixed = LogTimestampStr(str_prefixed);
214+
215+
m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
210216

211217
if (m_print_to_console) {
212218
// print to console
213-
fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
219+
fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
214220
fflush(stdout);
215221
}
216222
if (m_print_to_file) {
217223
std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
218224

219225
// buffer if we haven't opened the log yet
220226
if (m_fileout == nullptr) {
221-
m_msgs_before_open.push_back(strTimestamped);
227+
m_msgs_before_open.push_back(str_prefixed);
222228
}
223229
else
224230
{
@@ -232,7 +238,7 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
232238
m_fileout = new_fileout;
233239
}
234240
}
235-
FileWriteStr(strTimestamped, m_fileout);
241+
FileWriteStr(str_prefixed, m_fileout);
236242
}
237243
}
238244
}

src/logging.h

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
static const bool DEFAULT_LOGTIMEMICROS = false;
2020
static const bool DEFAULT_LOGIPS = false;
2121
static const bool DEFAULT_LOGTIMESTAMPS = true;
22+
static const bool DEFAULT_LOGTHREADNAMES = false;
2223
extern const char * const DEFAULT_DEBUGLOGFILE;
2324

2425
extern bool fLogIPs;
@@ -81,6 +82,7 @@ namespace BCLog {
8182

8283
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
8384
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
85+
bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
8486

8587
fs::path m_file_path;
8688
std::atomic<bool> m_reopen_file{false};

src/qt/bitcoin.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <interfaces/handler.h>
3131
#include <interfaces/node.h>
3232
#include <noui.h>
33+
#include <util/threadnames.h>
3334
#include <rpc/server.h>
3435
#include <ui_interface.h>
3536
#include <uint256.h>
@@ -149,6 +150,7 @@ void BitcoinCore::initialize()
149150
try
150151
{
151152
qDebug() << __func__ << ": Running initialization in thread";
153+
util::ThreadRename("qt-init");
152154
bool rv = m_node.appInitMain();
153155
Q_EMIT initializeResult(rv);
154156
} catch (const std::exception& e) {
@@ -423,6 +425,7 @@ int GuiMain(int argc, char* argv[])
423425
std::tie(argc, argv) = winArgs.get();
424426
#endif
425427
SetupEnvironment();
428+
util::ThreadRename("main");
426429

427430
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
428431

src/sync.cpp

+19-10
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <sync.h>
6+
#include <tinyformat.h>
67

78
#include <logging.h>
89
#include <util/strencodings.h>
10+
#include <util/threadnames.h>
911

1012
#include <stdio.h>
1113

@@ -37,23 +39,30 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
3739
//
3840

3941
struct CLockLocation {
40-
CLockLocation(const char* pszName, const char* pszFile, int nLine, bool fTryIn)
41-
{
42-
mutexName = pszName;
43-
sourceFile = pszFile;
44-
sourceLine = nLine;
45-
fTry = fTryIn;
46-
}
42+
CLockLocation(
43+
const char* pszName,
44+
const char* pszFile,
45+
int nLine,
46+
bool fTryIn,
47+
const std::string& thread_name)
48+
: fTry(fTryIn),
49+
mutexName(pszName),
50+
sourceFile(pszFile),
51+
m_thread_name(thread_name),
52+
sourceLine(nLine) {}
4753

4854
std::string ToString() const
4955
{
50-
return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : "");
56+
return tfm::format(
57+
"%s %s:%s%s (in thread %s)",
58+
mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
5159
}
5260

5361
private:
5462
bool fTry;
5563
std::string mutexName;
5664
std::string sourceFile;
65+
const std::string& m_thread_name;
5766
int sourceLine;
5867
};
5968

@@ -125,7 +134,7 @@ static void push_lock(void* c, const CLockLocation& locklocation)
125134
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
126135
if (lockdata.lockorders.count(p1))
127136
continue;
128-
lockdata.lockorders[p1] = g_lockstack;
137+
lockdata.lockorders.emplace(p1, g_lockstack);
129138

130139
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
131140
lockdata.invlockorders.insert(p2);
@@ -141,7 +150,7 @@ static void pop_lock()
141150

142151
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
143152
{
144-
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry));
153+
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
145154
}
146155

147156
void LeaveCritical()

src/test/setup_common.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
9494

9595
nScriptCheckThreads = 3;
9696
for (int i = 0; i < nScriptCheckThreads - 1; i++)
97-
threadGroup.create_thread(&ThreadScriptCheck);
97+
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
9898

9999
g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
100100
g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.

0 commit comments

Comments
 (0)