Skip to content

Commit 67b80be

Browse files
committed
merge bitcoin#15849: Thread names in logs and deadlock debug tools
1 parent a612ec5 commit 67b80be

19 files changed

+252
-93
lines changed

configure.ac

+17-2
Original file line numberDiff line numberDiff line change
@@ -865,8 +865,23 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
865865
}
866866
])],
867867
[
868-
AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
869-
AC_MSG_RESULT(yes)
868+
case $host in
869+
*mingw*)
870+
# mingw32's implementation of thread_local has also been shown to behave
871+
# erroneously under concurrent usage; see:
872+
# https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605
873+
AC_MSG_RESULT(no)
874+
;;
875+
*darwin*)
876+
# TODO enable thread_local on later versions of Darwin where it is
877+
# supported (per https://stackoverflow.com/a/29929949)
878+
AC_MSG_RESULT(no)
879+
;;
880+
*)
881+
AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
882+
AC_MSG_RESULT(yes)
883+
;;
884+
esac
870885
],
871886
[
872887
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
@@ -265,6 +265,7 @@ BITCOIN_CORE_H = \
265265
utilmoneystr.h \
266266
utilstring.h \
267267
utiltime.h \
268+
utilthreadnames.h \
268269
validation.h \
269270
validationinterface.h \
270271
versionbits.h \
@@ -591,6 +592,7 @@ libdash_util_a_SOURCES = \
591592
utilstrencodings.cpp \
592593
utiltime.cpp \
593594
utilstring.cpp \
595+
utilthreadnames.cpp \
594596
$(BITCOIN_CORE_H)
595597

596598
if GLIBC_BACK_COMPAT

src/Makefile.test.include

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ BITCOIN_TESTS =\
102102
test/streams_tests.cpp \
103103
test/subsidy_tests.cpp \
104104
test/sync_tests.cpp \
105+
test/util_threadnames_tests.cpp \
105106
test/timedata_tests.cpp \
106107
test/torcontrol_tests.cpp \
107108
test/transaction_tests.cpp \

src/dashd.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <httpserver.h>
2020
#include <httprpc.h>
2121
#include <utilstrencodings.h>
22+
#include <utilthreadnames.h>
2223
#include <walletinitinterface.h>
2324
#include <stacktraces.h>
2425

@@ -57,6 +58,8 @@ static bool AppInit(int argc, char* argv[])
5758
{
5859
bool fRet = false;
5960

61+
util::ThreadRename("init");
62+
6063
//
6164
// Parameters
6265
//

src/httpserver.cpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <compat.h>
1010
#include <util.h>
1111
#include <utilstrencodings.h>
12+
#include <utilthreadnames.h>
1213
#include <netbase.h>
1314
#include <rpc/protocol.h> // For HTTP status codes
1415
#include <sync.h>
@@ -18,7 +19,7 @@
1819
#include <memory>
1920
#include <stdio.h>
2021
#include <stdlib.h>
21-
#include <string.h>
22+
#include <string>
2223

2324
#include <sys/types.h>
2425
#include <sys/stat.h>
@@ -286,7 +287,7 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
286287
/** Event dispatcher thread */
287288
static bool ThreadHTTP(struct event_base* base, struct evhttp* http)
288289
{
289-
RenameThread("dash-http");
290+
util::ThreadRename("http");
290291
LogPrint(BCLog::HTTP, "Entering http event loop\n");
291292
event_base_dispatch(base);
292293
// Event loop will be interrupted by InterruptHTTPServer()
@@ -337,9 +338,9 @@ static bool HTTPBindAddresses(struct evhttp* http)
337338
}
338339

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

@@ -440,7 +441,7 @@ bool StartHTTPServer()
440441
threadHTTP = std::thread(ThreadHTTP, eventBase, eventHTTP);
441442

442443
for (int i = 0; i < rpcThreads; i++) {
443-
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
444+
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i);
444445
}
445446
return true;
446447
}

src/init.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <script/standard.h>
4040
#include <script/sigcache.h>
4141
#include <scheduler.h>
42+
#include <utilthreadnames.h>
4243
#include <timedata.h>
4344
#include <txdb.h>
4445
#include <txmempool.h>
@@ -243,7 +244,7 @@ void PrepareShutdown()
243244
/// for example if the data directory was found to be locked.
244245
/// Be sure that anything that writes files or flushes caches only does this if the respective
245246
/// module was initialized.
246-
RenameThread("dash-shutoff");
247+
util::ThreadRename("shutoff");
247248
mempool.AddTransactionsUpdated(1);
248249
StopHTTPRPC();
249250
StopREST();
@@ -606,6 +607,7 @@ void SetupServerArgs()
606607
gArgs.AddArg("-llmqinstantsend=<quorum name>", strprintf("Override the default LLMQ type used for InstantSend on a devnet. Allows using InstantSend with smaller LLMQs. (default: %s)", devnetConsensus.llmqs.at(devnetConsensus.llmqTypeInstantSend).name), false, OptionsCategory::DEBUG_TEST);
607608
gArgs.AddArg("-llmqtestparams=<size:threshold>", strprintf("Override the default LLMQ size for the LLMQ_TEST quorum (default: %u:%u)", regtestLLMQ.size, regtestLLMQ.threshold), false, OptionsCategory::DEBUG_TEST);
608609
gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
610+
gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
609611
gArgs.AddArg("-logthreadnames", strprintf("Add thread names to debug messages (default: %u)", DEFAULT_LOGTHREADNAMES), true, OptionsCategory::DEBUG_TEST);
610612
gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
611613
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);
@@ -774,7 +776,7 @@ static void CleanupBlockRevFiles()
774776
static void ThreadImport(std::vector<fs::path> vImportFiles)
775777
{
776778
const CChainParams& chainparams = Params();
777-
RenameThread("dash-loadblk");
779+
util::ThreadRename("loadblk");
778780
ScheduleBatchPriority();
779781

780782
{
@@ -1092,7 +1094,7 @@ void InitLogging()
10921094
g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
10931095
g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
10941096
g_logger->m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
1095-
g_logger->m_log_thread_names = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
1097+
g_logger->m_log_threadnames = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
10961098

10971099
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
10981100

@@ -1731,7 +1733,7 @@ bool AppInitMain()
17311733
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
17321734
if (nScriptCheckThreads) {
17331735
for (int i=0; i<nScriptCheckThreads-1; i++)
1734-
threadGroup.create_thread(&ThreadScriptCheck);
1736+
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
17351737
}
17361738

17371739
std::vector<std::string> vSporkAddresses;

src/logging.cpp

+30-42
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#include <logging.h>
77
#include <util.h>
88
#include <utiltime.h>
9+
#include <utilthreadnames.h>
10+
11+
#include <mutex>
912

1013
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
1114

@@ -219,7 +222,7 @@ std::string ListActiveLogCategoriesString()
219222
return ret;
220223
}
221224

222-
std::string BCLog::Logger::LogTimestampStr(const std::string &str)
225+
std::string BCLog::Logger::LogTimestampStr(const std::string& str)
223226
{
224227
std::string strStamped;
225228

@@ -244,59 +247,44 @@ std::string BCLog::Logger::LogTimestampStr(const std::string &str)
244247
return strStamped;
245248
}
246249

247-
std::string BCLog::Logger::LogThreadNameStr(const std::string &str)
250+
void BCLog::Logger::LogPrintStr(const std::string &str)
248251
{
249-
std::string strThreadLogged;
250-
251-
if (!m_log_thread_names)
252-
return str;
252+
std::string str_prefixed = str;
253253

254-
std::string strThreadName = GetThreadName();
255-
256-
if (m_started_new_line)
257-
strThreadLogged = strprintf("%16s | %s", strThreadName.c_str(), str.c_str());
258-
else
259-
strThreadLogged = str;
254+
if (m_log_threadnames && m_started_new_line) {
255+
str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
256+
}
260257

261-
return strThreadLogged;
262-
}
258+
str_prefixed = LogTimestampStr(str_prefixed);
263259

264-
void BCLog::Logger::LogPrintStr(const std::string& str)
265-
{
266-
StdLockGuard scoped_lock(m_cs);
267-
std::string strThreadLogged = LogThreadNameStr(str);
268-
std::string strTimestamped = LogTimestampStr(strThreadLogged);
269-
270-
if (!str.empty() && str[str.size()-1] == '\n')
271-
m_started_new_line = true;
272-
else
273-
m_started_new_line = false;
274-
275-
if (m_buffering) {
276-
// buffer if we haven't started logging yet
277-
m_msgs_before_open.push_back(strTimestamped);
278-
return;
279-
}
260+
m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
280261

281262
if (m_print_to_console) {
282263
// print to console
283-
fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
264+
fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
284265
fflush(stdout);
285266
}
286267
if (m_print_to_file) {
287-
assert(m_fileout != nullptr);
288-
289-
// reopen the log file, if requested
290-
if (m_reopen_file) {
291-
m_reopen_file = false;
292-
FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
293-
if (new_fileout) {
294-
setbuf(new_fileout, nullptr); // unbuffered
295-
fclose(m_fileout);
296-
m_fileout = new_fileout;
268+
std::lock_guard<std::mutex> scoped_lock(m_cs);
269+
270+
// buffer if we haven't opened the log yet
271+
if (m_fileout == nullptr) {
272+
m_msgs_before_open.push_back(str_prefixed);
273+
}
274+
else
275+
{
276+
// reopen the log file, if requested
277+
if (m_reopen_file) {
278+
m_reopen_file = false;
279+
FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
280+
if (new_fileout) {
281+
setbuf(new_fileout, nullptr); // unbuffered
282+
fclose(m_fileout);
283+
m_fileout = new_fileout;
284+
}
297285
}
286+
FileWriteStr(str_prefixed, m_fileout);
298287
}
299-
FileWriteStr(strTimestamped, m_fileout);
300288
}
301289
}
302290

src/logging.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ namespace BCLog {
108108

109109
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
110110
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
111-
bool m_log_thread_names = DEFAULT_LOGTHREADNAMES;
111+
bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
112112

113113
fs::path m_file_path;
114114
std::atomic<bool> m_reopen_file{false};

src/qt/dash.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ void BitcoinCore::initialize()
257257
try
258258
{
259259
qDebug() << __func__ << ": Running initialization in thread";
260+
util::ThreadRename("qt-init");
260261
bool rv = m_node.appInitMain();
261262
Q_EMIT initializeResult(rv);
262263
} catch (...) {
@@ -577,6 +578,7 @@ int main(int argc, char *argv[])
577578
RegisterPrettySignalHandlers();
578579

579580
SetupEnvironment();
581+
util::ThreadRename("main");
580582

581583
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
582584

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 <utilstrencodings.h>
10+
#include <utilthreadnames.h>
911

1012
#include <stdio.h>
1113

@@ -36,23 +38,30 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
3638
//
3739

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

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

5260
private:
5361
bool fTry;
5462
std::string mutexName;
5563
std::string sourceFile;
64+
const std::string& m_thread_name;
5665
int sourceLine;
5766
};
5867

@@ -124,7 +133,7 @@ static void push_lock(void* c, const CLockLocation& locklocation)
124133
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
125134
if (lockdata.lockorders.count(p1))
126135
continue;
127-
lockdata.lockorders[p1] = g_lockstack;
136+
lockdata.lockorders.emplace(p1, g_lockstack);
128137

129138
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
130139
lockdata.invlockorders.insert(p2);
@@ -140,7 +149,7 @@ static void pop_lock()
140149

141150
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
142151
{
143-
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry));
152+
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
144153
}
145154

146155
void LeaveCritical()

src/test/test_dash.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
128128
}
129129
nScriptCheckThreads = 3;
130130
for (int i=0; i < nScriptCheckThreads-1; i++)
131-
threadGroup.create_thread(&ThreadScriptCheck);
131+
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
132132
peerLogic.reset(new PeerLogicValidation(connman, scheduler, /*enable_bip61=*/true));
133133
}
134134

0 commit comments

Comments
 (0)