Merge #15849: Thread names in logs and deadlock debug tools
8722e54e56
threads: add thread names to deadlock debugging message (James O'Beirne)383b186c28
threads: prefix log messages with thread names (James O'Beirne)ddd95ccb80
tests: add threadutil tests (James O'Beirne)ae5f2b6a6c
threads: introduce util/threadnames, refactor thread naming (James O'Beirne)188ca75e5f
disable HAVE_THREAD_LOCAL on unreliable platforms (James O'Beirne) Pull request description: I'm resurrecting this one (from #13168) because I need it to make progress on #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: utACK8722e54e56
jnewbery: utACK8722e54e56
MarcoFalke: re-utACK8722e54e56
(Only change since my previous review is DEFAULT_LOGTHREADNAMES=false and stylistic updates Tree-SHA512: 50af992708295b8d680cf10025262dd964e599a356bdfc1dfc84fb18c00afabcb34d3d12d551b0677ff81f8fccad0e17c1d5b24dfecb953a913bc77fdd1a4577
This commit is contained in:
commit
2c35fe6238
19 changed files with 239 additions and 58 deletions
15
configure.ac
15
configure.ac
|
@ -836,8 +836,23 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||||
}
|
}
|
||||||
])],
|
])],
|
||||||
[
|
[
|
||||||
|
case $host in
|
||||||
|
*mingw*)
|
||||||
|
# mingw32's implementation of thread_local has also been shown to behave
|
||||||
|
# erroneously under concurrent usage; see:
|
||||||
|
# https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
;;
|
||||||
|
*darwin*)
|
||||||
|
# TODO enable thread_local on later versions of Darwin where it is
|
||||||
|
# supported (per https://stackoverflow.com/a/29929949)
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
|
AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
|
||||||
AC_MSG_RESULT(yes)
|
AC_MSG_RESULT(yes)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
AC_MSG_RESULT(no)
|
AC_MSG_RESULT(no)
|
||||||
|
|
6
doc/release-notes-15849.md
Normal file
6
doc/release-notes-15849.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Thread names in logs
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
On platforms supporting `thread_local`, log lines can be prefixed with the name
|
||||||
|
of the thread that caused the log. To enable this behavior, use
|
||||||
|
`-logthreadnames=1`.
|
|
@ -209,6 +209,7 @@ BITCOIN_CORE_H = \
|
||||||
util/memory.h \
|
util/memory.h \
|
||||||
util/moneystr.h \
|
util/moneystr.h \
|
||||||
util/rbf.h \
|
util/rbf.h \
|
||||||
|
util/threadnames.h \
|
||||||
util/time.h \
|
util/time.h \
|
||||||
util/url.h \
|
util/url.h \
|
||||||
util/validation.h \
|
util/validation.h \
|
||||||
|
@ -489,6 +490,7 @@ libbitcoin_util_a_SOURCES = \
|
||||||
util/system.cpp \
|
util/system.cpp \
|
||||||
util/moneystr.cpp \
|
util/moneystr.cpp \
|
||||||
util/rbf.cpp \
|
util/rbf.cpp \
|
||||||
|
util/threadnames.cpp \
|
||||||
util/strencodings.cpp \
|
util/strencodings.cpp \
|
||||||
util/time.cpp \
|
util/time.cpp \
|
||||||
util/url.cpp \
|
util/url.cpp \
|
||||||
|
|
|
@ -138,6 +138,7 @@ BITCOIN_TESTS =\
|
||||||
test/skiplist_tests.cpp \
|
test/skiplist_tests.cpp \
|
||||||
test/streams_tests.cpp \
|
test/streams_tests.cpp \
|
||||||
test/sync_tests.cpp \
|
test/sync_tests.cpp \
|
||||||
|
test/util_threadnames_tests.cpp \
|
||||||
test/timedata_tests.cpp \
|
test/timedata_tests.cpp \
|
||||||
test/torcontrol_tests.cpp \
|
test/torcontrol_tests.cpp \
|
||||||
test/transaction_tests.cpp \
|
test/transaction_tests.cpp \
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <httpserver.h>
|
#include <httpserver.h>
|
||||||
#include <httprpc.h>
|
#include <httprpc.h>
|
||||||
|
#include <util/threadnames.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <walletinitinterface.h>
|
#include <walletinitinterface.h>
|
||||||
|
|
||||||
|
@ -64,6 +65,8 @@ static bool AppInit(int argc, char* argv[])
|
||||||
|
|
||||||
bool fRet = false;
|
bool fRet = false;
|
||||||
|
|
||||||
|
util::ThreadRename("init");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Parameters
|
// Parameters
|
||||||
//
|
//
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <chainparamsbase.h>
|
#include <chainparamsbase.h>
|
||||||
#include <compat.h>
|
#include <compat.h>
|
||||||
|
#include <util/threadnames.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <netbase.h>
|
#include <netbase.h>
|
||||||
|
@ -17,7 +18,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -284,7 +285,7 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
|
||||||
/** Event dispatcher thread */
|
/** Event dispatcher thread */
|
||||||
static bool ThreadHTTP(struct event_base* base)
|
static bool ThreadHTTP(struct event_base* base)
|
||||||
{
|
{
|
||||||
RenameThread("bitcoin-http");
|
util::ThreadRename("http");
|
||||||
LogPrint(BCLog::HTTP, "Entering http event loop\n");
|
LogPrint(BCLog::HTTP, "Entering http event loop\n");
|
||||||
event_base_dispatch(base);
|
event_base_dispatch(base);
|
||||||
// Event loop will be interrupted by InterruptHTTPServer()
|
// Event loop will be interrupted by InterruptHTTPServer()
|
||||||
|
@ -335,9 +336,9 @@ static bool HTTPBindAddresses(struct evhttp* http)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Simple wrapper to set thread name and run work queue */
|
/** Simple wrapper to set thread name and run work queue */
|
||||||
static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue)
|
static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
|
||||||
{
|
{
|
||||||
RenameThread("bitcoin-httpworker");
|
util::ThreadRename(strprintf("httpworker.%i", worker_num));
|
||||||
queue->Run();
|
queue->Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +431,7 @@ void StartHTTPServer()
|
||||||
threadHTTP = std::thread(ThreadHTTP, eventBase);
|
threadHTTP = std::thread(ThreadHTTP, eventBase);
|
||||||
|
|
||||||
for (int i = 0; i < rpcThreads; i++) {
|
for (int i = 0; i < rpcThreads; i++) {
|
||||||
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
|
g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <script/sigcache.h>
|
#include <script/sigcache.h>
|
||||||
#include <scheduler.h>
|
#include <scheduler.h>
|
||||||
#include <shutdown.h>
|
#include <shutdown.h>
|
||||||
|
#include <util/threadnames.h>
|
||||||
#include <timedata.h>
|
#include <timedata.h>
|
||||||
#include <txdb.h>
|
#include <txdb.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
@ -206,7 +207,7 @@ void Shutdown(InitInterfaces& interfaces)
|
||||||
/// for example if the data directory was found to be locked.
|
/// for example if the data directory was found to be locked.
|
||||||
/// Be sure that anything that writes files or flushes caches only does this if the respective
|
/// Be sure that anything that writes files or flushes caches only does this if the respective
|
||||||
/// module was initialized.
|
/// module was initialized.
|
||||||
RenameThread("bitcoin-shutoff");
|
util::ThreadRename("shutoff");
|
||||||
mempool.AddTransactionsUpdated(1);
|
mempool.AddTransactionsUpdated(1);
|
||||||
|
|
||||||
StopHTTPRPC();
|
StopHTTPRPC();
|
||||||
|
@ -506,6 +507,7 @@ void SetupServerArgs()
|
||||||
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);
|
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);
|
||||||
gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
|
gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
|
||||||
gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
|
gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
|
||||||
|
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);
|
||||||
gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
|
gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
|
||||||
gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST);
|
gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST);
|
||||||
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);
|
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()
|
||||||
static void ThreadImport(std::vector<fs::path> vImportFiles)
|
static void ThreadImport(std::vector<fs::path> vImportFiles)
|
||||||
{
|
{
|
||||||
const CChainParams& chainparams = Params();
|
const CChainParams& chainparams = Params();
|
||||||
RenameThread("bitcoin-loadblk");
|
util::ThreadRename("loadblk");
|
||||||
ScheduleBatchPriority();
|
ScheduleBatchPriority();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -862,6 +864,7 @@ void InitLogging()
|
||||||
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
|
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
|
||||||
LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
|
LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
|
||||||
LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
|
LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
|
||||||
|
LogInstance().m_log_threadnames = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
|
||||||
|
|
||||||
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
|
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
|
||||||
|
|
||||||
|
@ -1286,7 +1289,7 @@ bool AppInitMain(InitInterfaces& interfaces)
|
||||||
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
|
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
|
||||||
if (nScriptCheckThreads) {
|
if (nScriptCheckThreads) {
|
||||||
for (int i=0; i<nScriptCheckThreads-1; i++)
|
for (int i=0; i<nScriptCheckThreads-1; i++)
|
||||||
threadGroup.create_thread(&ThreadScriptCheck);
|
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the lightweight task scheduler thread
|
// Start the lightweight task scheduler thread
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
|
#include <util/threadnames.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
|
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
|
||||||
|
|
||||||
BCLog::Logger& LogInstance()
|
BCLog::Logger& LogInstance()
|
||||||
|
@ -174,7 +177,7 @@ std::vector<CLogCategoryActive> ListActiveLogCategories()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BCLog::Logger::LogTimestampStr(const std::string &str)
|
std::string BCLog::Logger::LogTimestampStr(const std::string& str)
|
||||||
{
|
{
|
||||||
std::string strStamped;
|
std::string strStamped;
|
||||||
|
|
||||||
|
@ -196,21 +199,24 @@ std::string BCLog::Logger::LogTimestampStr(const std::string &str)
|
||||||
} else
|
} else
|
||||||
strStamped = str;
|
strStamped = str;
|
||||||
|
|
||||||
if (!str.empty() && str[str.size()-1] == '\n')
|
|
||||||
m_started_new_line = true;
|
|
||||||
else
|
|
||||||
m_started_new_line = false;
|
|
||||||
|
|
||||||
return strStamped;
|
return strStamped;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BCLog::Logger::LogPrintStr(const std::string &str)
|
void BCLog::Logger::LogPrintStr(const std::string &str)
|
||||||
{
|
{
|
||||||
std::string strTimestamped = LogTimestampStr(str);
|
std::string str_prefixed = str;
|
||||||
|
|
||||||
|
if (m_log_threadnames && m_started_new_line) {
|
||||||
|
str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
str_prefixed = LogTimestampStr(str_prefixed);
|
||||||
|
|
||||||
|
m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
|
||||||
|
|
||||||
if (m_print_to_console) {
|
if (m_print_to_console) {
|
||||||
// print to console
|
// print to console
|
||||||
fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
|
fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
if (m_print_to_file) {
|
if (m_print_to_file) {
|
||||||
|
@ -218,7 +224,7 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
|
||||||
|
|
||||||
// buffer if we haven't opened the log yet
|
// buffer if we haven't opened the log yet
|
||||||
if (m_fileout == nullptr) {
|
if (m_fileout == nullptr) {
|
||||||
m_msgs_before_open.push_back(strTimestamped);
|
m_msgs_before_open.push_back(str_prefixed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -232,7 +238,7 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
|
||||||
m_fileout = new_fileout;
|
m_fileout = new_fileout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileWriteStr(strTimestamped, m_fileout);
|
FileWriteStr(str_prefixed, m_fileout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
static const bool DEFAULT_LOGTIMEMICROS = false;
|
static const bool DEFAULT_LOGTIMEMICROS = false;
|
||||||
static const bool DEFAULT_LOGIPS = false;
|
static const bool DEFAULT_LOGIPS = false;
|
||||||
static const bool DEFAULT_LOGTIMESTAMPS = true;
|
static const bool DEFAULT_LOGTIMESTAMPS = true;
|
||||||
|
static const bool DEFAULT_LOGTHREADNAMES = false;
|
||||||
extern const char * const DEFAULT_DEBUGLOGFILE;
|
extern const char * const DEFAULT_DEBUGLOGFILE;
|
||||||
|
|
||||||
extern bool fLogIPs;
|
extern bool fLogIPs;
|
||||||
|
@ -81,6 +82,7 @@ namespace BCLog {
|
||||||
|
|
||||||
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
|
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
|
||||||
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
|
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
|
||||||
|
bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
|
||||||
|
|
||||||
fs::path m_file_path;
|
fs::path m_file_path;
|
||||||
std::atomic<bool> m_reopen_file{false};
|
std::atomic<bool> m_reopen_file{false};
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <interfaces/handler.h>
|
#include <interfaces/handler.h>
|
||||||
#include <interfaces/node.h>
|
#include <interfaces/node.h>
|
||||||
#include <noui.h>
|
#include <noui.h>
|
||||||
|
#include <util/threadnames.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <ui_interface.h>
|
#include <ui_interface.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
@ -149,6 +150,7 @@ void BitcoinCore::initialize()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
qDebug() << __func__ << ": Running initialization in thread";
|
qDebug() << __func__ << ": Running initialization in thread";
|
||||||
|
util::ThreadRename("qt-init");
|
||||||
bool rv = m_node.appInitMain();
|
bool rv = m_node.appInitMain();
|
||||||
Q_EMIT initializeResult(rv);
|
Q_EMIT initializeResult(rv);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -423,6 +425,7 @@ int GuiMain(int argc, char* argv[])
|
||||||
std::tie(argc, argv) = winArgs.get();
|
std::tie(argc, argv) = winArgs.get();
|
||||||
#endif
|
#endif
|
||||||
SetupEnvironment();
|
SetupEnvironment();
|
||||||
|
util::ThreadRename("main");
|
||||||
|
|
||||||
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
|
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
|
||||||
|
|
||||||
|
|
29
src/sync.cpp
29
src/sync.cpp
|
@ -3,9 +3,11 @@
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
|
#include <tinyformat.h>
|
||||||
|
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
#include <util/threadnames.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
@ -37,23 +39,30 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
|
||||||
//
|
//
|
||||||
|
|
||||||
struct CLockLocation {
|
struct CLockLocation {
|
||||||
CLockLocation(const char* pszName, const char* pszFile, int nLine, bool fTryIn)
|
CLockLocation(
|
||||||
{
|
const char* pszName,
|
||||||
mutexName = pszName;
|
const char* pszFile,
|
||||||
sourceFile = pszFile;
|
int nLine,
|
||||||
sourceLine = nLine;
|
bool fTryIn,
|
||||||
fTry = fTryIn;
|
const std::string& thread_name)
|
||||||
}
|
: fTry(fTryIn),
|
||||||
|
mutexName(pszName),
|
||||||
|
sourceFile(pszFile),
|
||||||
|
m_thread_name(thread_name),
|
||||||
|
sourceLine(nLine) {}
|
||||||
|
|
||||||
std::string ToString() const
|
std::string ToString() const
|
||||||
{
|
{
|
||||||
return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : "");
|
return tfm::format(
|
||||||
|
"%s %s:%s%s (in thread %s)",
|
||||||
|
mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool fTry;
|
bool fTry;
|
||||||
std::string mutexName;
|
std::string mutexName;
|
||||||
std::string sourceFile;
|
std::string sourceFile;
|
||||||
|
const std::string& m_thread_name;
|
||||||
int sourceLine;
|
int sourceLine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,7 +134,7 @@ static void push_lock(void* c, const CLockLocation& locklocation)
|
||||||
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
|
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
|
||||||
if (lockdata.lockorders.count(p1))
|
if (lockdata.lockorders.count(p1))
|
||||||
continue;
|
continue;
|
||||||
lockdata.lockorders[p1] = g_lockstack;
|
lockdata.lockorders.emplace(p1, g_lockstack);
|
||||||
|
|
||||||
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
|
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
|
||||||
lockdata.invlockorders.insert(p2);
|
lockdata.invlockorders.insert(p2);
|
||||||
|
@ -141,7 +150,7 @@ static void pop_lock()
|
||||||
|
|
||||||
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
|
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
|
||||||
{
|
{
|
||||||
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry));
|
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeaveCritical()
|
void LeaveCritical()
|
||||||
|
|
|
@ -94,7 +94,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
|
||||||
|
|
||||||
nScriptCheckThreads = 3;
|
nScriptCheckThreads = 3;
|
||||||
for (int i = 0; i < nScriptCheckThreads - 1; i++)
|
for (int i = 0; i < nScriptCheckThreads - 1; i++)
|
||||||
threadGroup.create_thread(&ThreadScriptCheck);
|
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
|
||||||
|
|
||||||
g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
|
g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
|
||||||
g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
|
g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
|
||||||
|
|
73
src/test/util_threadnames_tests.cpp
Normal file
73
src/test/util_threadnames_tests.cpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright (c) 2018 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 <util/threadnames.h>
|
||||||
|
#include <test/setup_common.h>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#if defined(HAVE_CONFIG_H)
|
||||||
|
#include <config/bitcoin-config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(util_threadnames_tests, BasicTestingSetup)
|
||||||
|
|
||||||
|
const std::string TEST_THREAD_NAME_BASE = "test_thread.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a bunch of threads to all call util::ThreadRename.
|
||||||
|
*
|
||||||
|
* @return the set of name each thread has after attempted renaming.
|
||||||
|
*/
|
||||||
|
std::set<std::string> RenameEnMasse(int num_threads)
|
||||||
|
{
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
std::set<std::string> names;
|
||||||
|
std::mutex lock;
|
||||||
|
|
||||||
|
auto RenameThisThread = [&](int i) {
|
||||||
|
util::ThreadRename(TEST_THREAD_NAME_BASE + std::to_string(i));
|
||||||
|
std::lock_guard<std::mutex> guard(lock);
|
||||||
|
names.insert(util::ThreadGetInternalName());
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < num_threads; ++i) {
|
||||||
|
threads.push_back(std::thread(RenameThisThread, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::thread& thread : threads) thread.join();
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename a bunch of threads with the same basename (expect_multiple=true), ensuring suffixes are
|
||||||
|
* applied properly.
|
||||||
|
*/
|
||||||
|
BOOST_AUTO_TEST_CASE(util_threadnames_test_rename_threaded)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(util::ThreadGetInternalName(), "");
|
||||||
|
|
||||||
|
#if !defined(HAVE_THREAD_LOCAL)
|
||||||
|
// This test doesn't apply to platforms where we don't have thread_local.
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::set<std::string> names = RenameEnMasse(100);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(names.size(), 100);
|
||||||
|
|
||||||
|
// Names "test_thread.[n]" should exist for n = [0, 99]
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
BOOST_CHECK(names.find(TEST_THREAD_NAME_BASE + std::to_string(i)) != names.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -60,10 +60,6 @@
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SYS_PRCTL_H
|
|
||||||
#include <sys/prctl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_MALLOPT_ARENA_MAX
|
#ifdef HAVE_MALLOPT_ARENA_MAX
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -1137,22 +1133,6 @@ void runCommand(const std::string& strCommand)
|
||||||
LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
|
LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenameThread(const char* name)
|
|
||||||
{
|
|
||||||
#if defined(PR_SET_NAME)
|
|
||||||
// Only the first 15 characters are used (16 - NUL terminator)
|
|
||||||
::prctl(PR_SET_NAME, name, 0, 0, 0);
|
|
||||||
#elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
|
|
||||||
pthread_set_name_np(pthread_self(), name);
|
|
||||||
|
|
||||||
#elif defined(MAC_OSX)
|
|
||||||
pthread_setname_np(name);
|
|
||||||
#else
|
|
||||||
// Prevent warnings for unused parameters...
|
|
||||||
(void)name;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetupEnvironment()
|
void SetupEnvironment()
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MALLOPT_ARENA_MAX
|
#ifdef HAVE_MALLOPT_ARENA_MAX
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
|
#include <util/threadnames.h>
|
||||||
#include <tinyformat.h>
|
#include <tinyformat.h>
|
||||||
#include <util/memory.h>
|
#include <util/memory.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
|
@ -325,15 +326,12 @@ std::string HelpMessageOpt(const std::string& option, const std::string& message
|
||||||
*/
|
*/
|
||||||
int GetNumCores();
|
int GetNumCores();
|
||||||
|
|
||||||
void RenameThread(const char* name);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* .. and a wrapper that just calls func once
|
* .. and a wrapper that just calls func once
|
||||||
*/
|
*/
|
||||||
template <typename Callable> void TraceThread(const char* name, Callable func)
|
template <typename Callable> void TraceThread(const char* name, Callable func)
|
||||||
{
|
{
|
||||||
std::string s = strprintf("bitcoin-%s", name);
|
util::ThreadRename(name);
|
||||||
RenameThread(s.c_str());
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LogPrintf("%s thread start\n", name);
|
LogPrintf("%s thread start\n", name);
|
||||||
|
|
57
src/util/threadnames.cpp
Normal file
57
src/util/threadnames.cpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright (c) 2018 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#if defined(HAVE_CONFIG_H)
|
||||||
|
#include <config/bitcoin-config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <util/threadnames.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_PRCTL_H
|
||||||
|
#include <sys/prctl.h> // For prctl, PR_SET_NAME, PR_GET_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! Set the thread's name at the process level. Does not affect the
|
||||||
|
//! internal name.
|
||||||
|
static void SetThreadName(const char* name)
|
||||||
|
{
|
||||||
|
#if defined(PR_SET_NAME)
|
||||||
|
// Only the first 15 characters are used (16 - NUL terminator)
|
||||||
|
::prctl(PR_SET_NAME, name, 0, 0, 0);
|
||||||
|
#elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
|
||||||
|
pthread_set_name_np(pthread_self(), name);
|
||||||
|
#elif defined(MAC_OSX)
|
||||||
|
pthread_setname_np(name);
|
||||||
|
#else
|
||||||
|
// Prevent warnings for unused parameters...
|
||||||
|
(void)name;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have thread_local, just keep thread ID and name in a thread_local
|
||||||
|
// global.
|
||||||
|
#if defined(HAVE_THREAD_LOCAL)
|
||||||
|
|
||||||
|
static thread_local std::string g_thread_name;
|
||||||
|
const std::string& util::ThreadGetInternalName() { return g_thread_name; }
|
||||||
|
//! Set the in-memory internal name for this thread. Does not affect the process
|
||||||
|
//! name.
|
||||||
|
static void SetInternalName(std::string name) { g_thread_name = std::move(name); }
|
||||||
|
|
||||||
|
// Without thread_local available, don't handle internal name at all.
|
||||||
|
#else
|
||||||
|
|
||||||
|
static const std::string empty_string;
|
||||||
|
const std::string& util::ThreadGetInternalName() { return empty_string; }
|
||||||
|
static void SetInternalName(std::string name) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void util::ThreadRename(std::string&& name)
|
||||||
|
{
|
||||||
|
SetThreadName(("bitcoin-" + name).c_str());
|
||||||
|
SetInternalName(std::move(name));
|
||||||
|
}
|
21
src/util/threadnames.h
Normal file
21
src/util/threadnames.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) 2018 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_UTIL_THREADNAMES_H
|
||||||
|
#define BITCOIN_UTIL_THREADNAMES_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
//! Rename a thread both in terms of an internal (in-memory) name as well
|
||||||
|
//! as its system thread name.
|
||||||
|
void ThreadRename(std::string&&);
|
||||||
|
|
||||||
|
//! Get the thread's internal (in-memory) name; used e.g. for identification in
|
||||||
|
//! logging.
|
||||||
|
const std::string& ThreadGetInternalName();
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
#endif // BITCOIN_UTIL_THREADNAMES_H
|
|
@ -48,6 +48,7 @@
|
||||||
|
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
@ -1669,8 +1670,8 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState&
|
||||||
|
|
||||||
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
|
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
|
||||||
|
|
||||||
void ThreadScriptCheck() {
|
void ThreadScriptCheck(int worker_num) {
|
||||||
RenameThread("bitcoin-scriptch");
|
util::ThreadRename(strprintf("scriptch.%i", worker_num));
|
||||||
scriptcheckqueue.Thread();
|
scriptcheckqueue.Thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -247,7 +247,7 @@ bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_m
|
||||||
/** Unload database information */
|
/** Unload database information */
|
||||||
void UnloadBlockIndex();
|
void UnloadBlockIndex();
|
||||||
/** Run an instance of the script checking thread */
|
/** Run an instance of the script checking thread */
|
||||||
void ThreadScriptCheck();
|
void ThreadScriptCheck(int worker_num);
|
||||||
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
||||||
bool IsInitialBlockDownload();
|
bool IsInitialBlockDownload();
|
||||||
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
|
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
|
||||||
|
|
Loading…
Reference in a new issue