Since #15233 has been merged, the `lockdata` becomes a static local variable with a well-defined initialization order: https://github.com/bitcoin/bitcoin/blob/36c0abd8f61ba859d53b1e59014720282c97c143/src/sync.cpp#L93-L96

Its destructor `~LockData()` [is called at program exit](https://en.cppreference.com/w/cpp/utility/program/exit).

At the same time, "if a function-local ... static object was destroyed and then that function is called from the destructor..., the _behavior is undefined_."

It seems possible that at program exit the `lockdata` object is destroyed before a global `RecursiveMutex` destructor calls `DeleteLock()`, which in turn calls `GetLockData()` with UB.
std::string ret;
        for (char ch_in : str) {
            uint8_t ch = (uint8_t)ch_in;
            if ((ch >= 32 || ch == '\n') && ch != '\x7f') {
                ret += ch_in;
            } else {
                ret += strprintf("\\x%02x", ch);
            }
        }
        return ret;
    }
}
void BCLog::Logger::LogPrintStr(const std::string& str)
{
    std::lock_guard<std::mutex> scoped_lock(m_cs);
    std::string str_prefixed = LogEscapeMessage(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_buffering) {
        
        m_msgs_before_open.push_back(str_prefixed);
        return;