diff --git a/src/batchedlogger.cpp b/src/batchedlogger.cpp index 8af506fb06..a4204e86f4 100644 --- a/src/batchedlogger.cpp +++ b/src/batchedlogger.cpp @@ -4,8 +4,11 @@ #include -CBatchedLogger::CBatchedLogger(BCLog::LogFlags _category, const std::string& _header) : - accept(LogAcceptCategory(_category)), header(_header) +CBatchedLogger::CBatchedLogger(BCLog::LogFlags _category, const std::string& logging_function, const std::string& source_file, int source_line) : + accept(LogAcceptCategory(_category)), + m_logging_function(logging_function), + m_source_file(source_file), + m_source_line(source_line) { } @@ -19,6 +22,6 @@ void CBatchedLogger::Flush() if (!accept || msg.empty()) { return; } - LogInstance().LogPrintStr(strprintf("%s:\n%s", header, msg)); + LogInstance().LogPrintStr(msg, m_logging_function, m_source_file, m_source_line); msg.clear(); } diff --git a/src/batchedlogger.h b/src/batchedlogger.h index 031e55ad39..c24ca84fe5 100644 --- a/src/batchedlogger.h +++ b/src/batchedlogger.h @@ -11,10 +11,12 @@ class CBatchedLogger { private: bool accept; - std::string header; + std::string m_logging_function;; + std::string m_source_file; + const int m_source_line; std::string msg; public: - CBatchedLogger(BCLog::LogFlags _category, const std::string& _header); + CBatchedLogger(BCLog::LogFlags _category, const std::string& logging_function, const std::string& m_source_file, int m_source_line); virtual ~CBatchedLogger(); template diff --git a/src/init.cpp b/src/init.cpp index 64ee07832e..856c3e5075 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -719,6 +719,7 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-debugexclude=", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except the specified category. This option can be specified multiple times to exclude multiple categories."), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-disablegovernance", strprintf("Disable governance validation (0-1, default: %u)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-logsourcelocations", strprintf("Prepend debug output with name of the originating source location (source file, line number and function name) (default: %u)", DEFAULT_LOGSOURCELOCATIONS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); #ifdef HAVE_THREAD_LOCAL argsman.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); @@ -1084,6 +1085,8 @@ void InitLogging(const ArgsManager& args) #ifdef HAVE_THREAD_LOCAL LogInstance().m_log_threadnames = args.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES); #endif + LogInstance().m_log_sourcelocations = args.GetBoolArg("-logsourcelocations", DEFAULT_LOGSOURCELOCATIONS); + fLogIPs = args.GetBoolArg("-logips", DEFAULT_LOGIPS); std::string version_string = FormatFullVersion(); diff --git a/src/llmq/commitment.cpp b/src/llmq/commitment.cpp index b6a8f82951..258fe8e479 100644 --- a/src/llmq/commitment.cpp +++ b/src/llmq/commitment.cpp @@ -27,18 +27,11 @@ CFinalCommitment::CFinalCommitment(const Consensus::LLMQParams& params, const ui { } -template -void LogPrintfFinalCommitment(Types... out) { - if (LogAcceptCategory(BCLog::LLMQ)) { - LogInstance().LogPrintStr(strprintf("CFinalCommitment::%s -- %s", __func__, tinyformat::format(out...))); - } -} - bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, gsl::not_null pQuorumBaseBlockIndex, bool checkSigs) const { const auto& llmq_params_opt = Params().GetLLMQ(llmqType); if (!llmq_params_opt.has_value()) { - LogPrintfFinalCommitment("q[%s] invalid llmqType=%d\n", quorumHash.ToString(), ToUnderlying(llmqType)); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid llmqType=%d\n", quorumHash.ToString(), ToUnderlying(llmqType)); return false; } const auto& llmq_params = llmq_params_opt.value(); @@ -46,17 +39,17 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, gsl::not_nullGetBlockHash() != quorumHash) { - LogPrintfFinalCommitment("q[%s] invalid quorumHash\n", quorumHash.ToString()); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumHash\n", quorumHash.ToString()); return false; } if ((pQuorumBaseBlockIndex->nHeight % llmq_params.dkgInterval) != quorumIndex) { - LogPrintfFinalCommitment("q[%s] invalid quorumIndex=%d\n", quorumHash.ToString(), quorumIndex); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumIndex=%d\n", quorumHash.ToString(), quorumIndex); return false; } @@ -65,27 +58,27 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, gsl::not_nullproTxHash.ToString().substr(0, 4) << " | "; } - LogPrintfFinalCommitment("CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n", + LogPrint(BCLog::LLMQ, "CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n", __func__, ss3.str(), quorumPublicKey.ToString(), commitmentHash.ToString()); } std::vector memberPubKeys; @@ -130,17 +123,17 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, gsl::not_null(tx); if (!opt_qcTx) { - LogPrintfFinalCommitment("h[%d] GetTxPayload failed\n", pindexPrev->nHeight); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] GetTxPayload LLMQCommitment failed\n", pindexPrev->nHeight); return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-payload"); } auto& qcTx = *opt_qcTx; const auto& llmq_params_opt = Params().GetLLMQ(qcTx.commitment.llmqType); if (!llmq_params_opt.has_value()) { - LogPrintfFinalCommitment("h[%d] GetLLMQ failed for llmqType[%d]\n", pindexPrev->nHeight, ToUnderlying(qcTx.commitment.llmqType)); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] GetLLMQ failed for llmqType[%d]\n", pindexPrev->nHeight, ToUnderlying(qcTx.commitment.llmqType)); return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-commitment-type"); } @@ -193,17 +186,17 @@ bool CheckLLMQCommitment(CDeterministicMNManager& dmnman, const CTransaction& tx for (const auto i: irange::range(llmq_params_opt->size)) { ss << "v[" << i << "]=" << qcTx.commitment.validMembers[i]; } - LogPrintfFinalCommitment("%s llmqType[%d] validMembers[%s] signers[]\n", __func__, + LogPrint(BCLog::LLMQ, "CFinalCommitment -- %s llmqType[%d] validMembers[%s] signers[]\n", __func__, int(qcTx.commitment.llmqType), ss.str()); } if (qcTx.nVersion == 0 || qcTx.nVersion > CFinalCommitmentTxPayload::CURRENT_VERSION) { - LogPrintfFinalCommitment("h[%d] invalid qcTx.nVersion[%d]\n", pindexPrev->nHeight, qcTx.nVersion); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.nVersion[%d]\n", pindexPrev->nHeight, qcTx.nVersion); return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-version"); } if (qcTx.nHeight != uint32_t(pindexPrev->nHeight + 1)) { - LogPrintfFinalCommitment("h[%d] invalid qcTx.nHeight[%d]\n", pindexPrev->nHeight, qcTx.nHeight); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.nHeight[%d]\n", pindexPrev->nHeight, qcTx.nHeight); return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-height"); } @@ -220,18 +213,18 @@ bool CheckLLMQCommitment(CDeterministicMNManager& dmnman, const CTransaction& tx if (qcTx.commitment.IsNull()) { if (!qcTx.commitment.VerifyNull()) { - LogPrintfFinalCommitment("h[%d] invalid qcTx.commitment[%s] VerifyNull failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString()); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.commitment[%s] VerifyNull failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString()); return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid-null"); } return true; } if (!qcTx.commitment.Verify(dmnman, pQuorumBaseBlockIndex, false)) { - LogPrintfFinalCommitment("h[%d] invalid qcTx.commitment[%s] Verify failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString()); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.commitment[%s] Verify failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString()); return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid"); } - LogPrintfFinalCommitment("h[%d] CheckLLMQCommitment VALID\n", pindexPrev->nHeight); + LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] CheckLLMQCommitment VALID\n", pindexPrev->nHeight); return true; } diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index 8b4345940c..2aa46c2e4e 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -35,10 +35,10 @@ namespace llmq class CDKGLogger : public CBatchedLogger { public: - CDKGLogger(const CDKGSession& _quorumDkg, std::string_view _func) : - CDKGLogger(_quorumDkg.params.name, _quorumDkg.quorumIndex, _quorumDkg.m_quorum_base_block_index->GetBlockHash(), _quorumDkg.m_quorum_base_block_index->nHeight, _quorumDkg.AreWeMember(), _func){}; - CDKGLogger(std::string_view _llmqTypeName, int _quorumIndex, const uint256& _quorumHash, int _height, bool _areWeMember, std::string_view _func) : - CBatchedLogger(BCLog::LLMQ_DKG, strprintf("QuorumDKG(type=%s, quorumIndex=%d, height=%d, member=%d, func=%s)", _llmqTypeName, _quorumIndex, _height, _areWeMember, _func)){}; + CDKGLogger(const CDKGSession& _quorumDkg, std::string_view _func, int source_line) : + CDKGLogger(_quorumDkg.params.name, _quorumDkg.quorumIndex, _quorumDkg.m_quorum_base_block_index->GetBlockHash(), _quorumDkg.m_quorum_base_block_index->nHeight, _quorumDkg.AreWeMember(), _func, source_line){}; + CDKGLogger(std::string_view _llmqTypeName, int _quorumIndex, const uint256& _quorumHash, int _height, bool _areWeMember, std::string_view _func, int source_line) : + CBatchedLogger(BCLog::LLMQ_DKG, strprintf("QuorumDKG(type=%s, qIndex=%d, h=%d, member=%d)", _llmqTypeName, _quorumIndex, _height, _areWeMember), __FILE__, source_line){}; }; static std::array, ToUnderlying(DKGError::type::_COUNT)> simDkgErrorMap{}; @@ -100,7 +100,7 @@ bool CDKGSession::Init(gsl::not_null _pQuorumBaseBlockIndex, } } - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); if (LogAcceptCategory(BCLog::LLMQ) && IsQuorumRotationEnabled(params, m_quorum_base_block_index)) { int cycleQuorumBaseHeight = m_quorum_base_block_index->nHeight - quorumIndex; @@ -140,7 +140,7 @@ bool CDKGSession::Init(gsl::not_null _pQuorumBaseBlockIndex, void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages) { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); if (!AreWeMember()) { return; @@ -161,7 +161,7 @@ void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages) void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages) { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); assert(AreWeMember()); @@ -214,7 +214,7 @@ void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages) // only performs cheap verifications, but not the signature of the message. this is checked with batched verification bool CDKGSession::PreVerifyMessage(const CDKGContribution& qc, bool& retBan) const { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); retBan = false; @@ -262,7 +262,7 @@ void CDKGSession::ReceiveMessage(const CDKGContribution& qc, bool& retBan) { LOCK(cs_pending); - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); retBan = false; @@ -357,7 +357,7 @@ void CDKGSession::VerifyPendingContributions() { AssertLockHeld(cs_pending); - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); cxxtimer::Timer t1(true); @@ -418,7 +418,7 @@ void CDKGSession::VerifyAndComplain(CDKGPendingMessages& pendingMessages) VerifyPendingContributions(); } - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); // we check all members if they sent us their contributions // we consider members as bad if they missed to send anything or if they sent multiple @@ -454,7 +454,7 @@ void CDKGSession::VerifyConnectionAndMinProtoVersions() const return; } - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); std::unordered_map protoMap; connman.ForEachNode([&](const CNode* pnode) { @@ -489,7 +489,7 @@ void CDKGSession::VerifyConnectionAndMinProtoVersions() const void CDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages) { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); assert(AreWeMember()); @@ -532,7 +532,7 @@ void CDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages) // only performs cheap verifications, but not the signature of the message. this is checked with batched verification bool CDKGSession::PreVerifyMessage(const CDKGComplaint& qc, bool& retBan) const { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); retBan = false; @@ -574,7 +574,7 @@ bool CDKGSession::PreVerifyMessage(const CDKGComplaint& qc, bool& retBan) const void CDKGSession::ReceiveMessage(const CDKGComplaint& qc, bool& retBan) { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); retBan = false; @@ -641,7 +641,7 @@ void CDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages) return; } - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); std::set justifyFor; @@ -678,7 +678,7 @@ void CDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages) void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, const std::set& forMembers) { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); assert(AreWeMember()); @@ -727,7 +727,7 @@ void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, const // only performs cheap verifications, but not the signature of the message. this is checked with batched verification bool CDKGSession::PreVerifyMessage(const CDKGJustification& qj, bool& retBan) const { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); retBan = false; @@ -785,7 +785,7 @@ bool CDKGSession::PreVerifyMessage(const CDKGJustification& qj, bool& retBan) co void CDKGSession::ReceiveMessage(const CDKGJustification& qj, bool& retBan) { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); retBan = false; @@ -883,7 +883,7 @@ void CDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages) return; } - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); std::vector badMembers; badMembers.reserve(members.size()); @@ -924,7 +924,7 @@ void CDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages) void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages) { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); assert(AreWeMember()); @@ -1039,7 +1039,7 @@ void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages) // only performs cheap verifications, but not the signature of the message. this is checked with batched verification bool CDKGSession::PreVerifyMessage(const CDKGPrematureCommitment& qc, bool& retBan) const { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); retBan = false; @@ -1100,7 +1100,7 @@ bool CDKGSession::PreVerifyMessage(const CDKGPrematureCommitment& qc, bool& retB void CDKGSession::ReceiveMessage(const CDKGPrematureCommitment& qc, bool& retBan) { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); retBan = false; @@ -1183,7 +1183,7 @@ std::vector CDKGSession::FinalizeCommitments() return {}; } - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); using Key = std::vector; std::map> commitmentsMap; @@ -1309,7 +1309,7 @@ void CDKGSession::MarkBadMember(size_t idx) void CDKGSession::RelayInvToParticipants(const CInv& inv) const { - CDKGLogger logger(*this, __func__); + CDKGLogger logger(*this, __func__, __LINE__); std::stringstream ss; for (const auto& r : relayMembers) { ss << r.ToString().substr(0, 4) << " | "; diff --git a/src/logging.cpp b/src/logging.cpp index 0409fa7fa1..3f70d0c22a 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -269,11 +270,15 @@ namespace BCLog { } } // namespace BCLog -void BCLog::Logger::LogPrintStr(const std::string& str) +void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line) { StdLockGuard scoped_lock(m_cs); std::string str_prefixed = LogEscapeMessage(str); + if (m_log_sourcelocations && m_started_new_line) { + str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] "); + } + if (m_log_threadnames && m_started_new_line) { // 16 chars total, "dash-" is 5 of them and another 1 is a NUL terminator str_prefixed.insert(0, "[" + strprintf("%10s", util::ThreadGetInternalName()) + "] "); diff --git a/src/logging.h b/src/logging.h index e5b446926f..6cf696f3d0 100644 --- a/src/logging.h +++ b/src/logging.h @@ -22,6 +22,7 @@ static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; static const bool DEFAULT_LOGTHREADNAMES = false; +static const bool DEFAULT_LOGSOURCELOCATIONS = false; extern const char * const DEFAULT_DEBUGLOGFILE; extern bool fLogThreadNames; @@ -117,12 +118,13 @@ namespace BCLog { bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS; bool m_log_time_micros = DEFAULT_LOGTIMEMICROS; bool m_log_threadnames = DEFAULT_LOGTHREADNAMES; + bool m_log_sourcelocations = DEFAULT_LOGSOURCELOCATIONS; fs::path m_file_path; std::atomic m_reopen_file{false}; /** Send a string to the log output */ - void LogPrintStr(const std::string& str); + void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line); /** Returns whether logs will be written to any output */ bool Enabled() const @@ -203,7 +205,7 @@ std::string SafeStringFormat(const std::string& fmt, const Args&... args) // peer can fill up a user's disk with debug.log entries. template -static inline void LogPrintf(const char* fmt, const Args&... args) +static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const char* fmt, const Args&... args) { if (LogInstance().Enabled()) { std::string log_msg; @@ -213,10 +215,12 @@ static inline void LogPrintf(const char* fmt, const Args&... args) /* Original format string will have newline so don't add one here */ log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt; } - LogInstance().LogPrintStr(log_msg); + LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line); } } +#define LogPrintf(...) LogPrintf_(__func__, __FILE__, __LINE__, __VA_ARGS__) + // Use a macro instead of a function for conditional logging to prevent // evaluating arguments when logging for the category is not enabled. #define LogPrint(category, ...) \ diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp index 6c618c51f0..4710f7b996 100644 --- a/src/test/fuzz/string.cpp +++ b/src/test/fuzz/string.cpp @@ -161,6 +161,7 @@ FUZZ_TARGET(string) (void)ParseNonRFCJSONValue(random_string_1); } catch (const std::runtime_error&) { } + (void)RemovePrefix(random_string_1, random_string_2); (void)ResolveErrMsg(random_string_1, random_string_2); try { (void)RPCConvertNamedValues(random_string_1, random_string_vector); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index d40b0ecb7a..edb981e729 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -142,6 +142,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve { "dummy", "-printtoconsole=0", + "-logsourcelocations", "-logtimemicros", "-logthreadnames", "-debug", diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index d5eafe2824..ee5774e981 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -2635,4 +2635,17 @@ BOOST_AUTO_TEST_CASE(message_hash) BOOST_CHECK_NE(message_hash1, signature_hash); } +BOOST_AUTO_TEST_CASE(remove_prefix) +{ + BOOST_CHECK_EQUAL(RemovePrefix("./util/system.h", "./"), "util/system.h"); + BOOST_CHECK_EQUAL(RemovePrefix("foo", "foo"), ""); + BOOST_CHECK_EQUAL(RemovePrefix("foo", "fo"), "o"); + BOOST_CHECK_EQUAL(RemovePrefix("foo", "f"), "oo"); + BOOST_CHECK_EQUAL(RemovePrefix("foo", ""), "foo"); + BOOST_CHECK_EQUAL(RemovePrefix("fo", "foo"), "fo"); + BOOST_CHECK_EQUAL(RemovePrefix("f", "foo"), "f"); + BOOST_CHECK_EQUAL(RemovePrefix("", "foo"), ""); + BOOST_CHECK_EQUAL(RemovePrefix("", ""), ""); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/util/string.h b/src/util/string.h index 5ff21fbbd6..72fea9fae8 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -39,6 +39,14 @@ void ReplaceAll(std::string& in_out, const std::string& search, const std::strin return str.substr(front, end - front + 1); } +[[nodiscard]] inline std::string RemovePrefix(const std::string& str, const std::string& prefix) +{ + if (str.substr(0, prefix.size()) == prefix) { + return str.substr(prefix.size()); + } + return str; +} + /** * Join a list of items * diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 355a2d2ae8..441cba00f1 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -118,6 +118,8 @@ class TestNode(): if self.version_is_at_least(120100): self.args.append("-logthreadnames") + if self.version_is_at_least(21000000): + self.args.append("-logsourcelocations") self.cli = TestNodeCLI(bitcoin_cli, self.datadir) self.use_cli = use_cli