diff options
author | babenko <babenko@yandex-team.com> | 2025-02-01 22:00:57 +0300 |
---|---|---|
committer | babenko <babenko@yandex-team.com> | 2025-02-01 22:16:41 +0300 |
commit | a0eafa50f69b88e2c366b50bc97196686ec4d8e8 (patch) | |
tree | f0e83688395774d6f0b940d346e815d248258ab0 | |
parent | 8cb823cc499768ee83daaba6fb81de16054071ad (diff) | |
download | ydb-a0eafa50f69b88e2c366b50bc97196686ec4d8e8.tar.gz |
Refactor and improve crash handler stderr dumps
1. Prevent concurrent crashes to interleave in stderr
2. Cleaner and more compact formatting of registers dump
3. Omit `OLDMASK` (nobody seems to know what is it for)
4. Don't print the top backtrace frame twice
5. Code cosmetics
commit_hash:f7a4c960b3400d6dfb0f8f60317aa524611d2fd0
-rw-r--r-- | library/cpp/yt/backtrace/symbolizers/dummy/dummy_symbolizer.cpp | 4 | ||||
-rw-r--r-- | library/cpp/yt/backtrace/symbolizers/dynload/dynload_symbolizer.cpp | 4 | ||||
-rw-r--r-- | library/cpp/yt/string/raw_formatter.h | 27 | ||||
-rw-r--r-- | yt/yt/core/misc/assert.cpp | 65 | ||||
-rw-r--r-- | yt/yt/core/misc/crash_handler.cpp | 179 | ||||
-rw-r--r-- | yt/yt/core/ya.make | 3 |
6 files changed, 123 insertions, 159 deletions
diff --git a/library/cpp/yt/backtrace/symbolizers/dummy/dummy_symbolizer.cpp b/library/cpp/yt/backtrace/symbolizers/dummy/dummy_symbolizer.cpp index 19cb41e795..3b1d37aa02 100644 --- a/library/cpp/yt/backtrace/symbolizers/dummy/dummy_symbolizer.cpp +++ b/library/cpp/yt/backtrace/symbolizers/dummy/dummy_symbolizer.cpp @@ -13,8 +13,8 @@ void SymbolizeBacktrace( for (int index = 0; index < std::ssize(backtrace); ++index) { TRawFormatter<1024> formatter; formatter.AppendNumber(index + 1, 10, 2); - formatter.AppendString(". "); - formatter.AppendNumberAsHexWithPadding(reinterpret_cast<uintptr_t>(backtrace[index]), 12); + formatter.AppendString(". 0x"); + formatter.AppendNumber(reinterpret_cast<uintptr_t>(backtrace[index]), /*radix*/ 16, /*width*/ 12); formatter.AppendString("\n"); frameCallback(formatter.GetBuffer()); } diff --git a/library/cpp/yt/backtrace/symbolizers/dynload/dynload_symbolizer.cpp b/library/cpp/yt/backtrace/symbolizers/dynload/dynload_symbolizer.cpp index 37ebda8e48..ca4b157045 100644 --- a/library/cpp/yt/backtrace/symbolizers/dynload/dynload_symbolizer.cpp +++ b/library/cpp/yt/backtrace/symbolizers/dynload/dynload_symbolizer.cpp @@ -75,10 +75,10 @@ int GetSymbolInfo(const void* pc, char* buffer, int length) void DumpStackFrameInfo(TBaseFormatter* formatter, const void* pc) { - formatter->AppendString("@ "); + formatter->AppendString("@ 0x"); const int width = (sizeof(void*) == 8 ? 12 : 8) + 2; // +2 for "0x"; 12 for x86_64 because higher bits are always zeroed. - formatter->AppendNumberAsHexWithPadding(reinterpret_cast<uintptr_t>(pc), width); + formatter->AppendNumber(reinterpret_cast<uintptr_t>(pc), 16, width); formatter->AppendString(" "); // Get the symbol from the previous address of PC, // because PC may be in the next function. diff --git a/library/cpp/yt/string/raw_formatter.h b/library/cpp/yt/string/raw_formatter.h index 6956330883..23ea7f9af0 100644 --- a/library/cpp/yt/string/raw_formatter.h +++ b/library/cpp/yt/string/raw_formatter.h @@ -84,11 +84,12 @@ public: } } - //! Appends a single character and updates the internal cursor. - void AppendChar(char ch) + //! Appends a single character given number of times and updates the internal cursor. + void AppendChar(char ch, int count = 1) { - if (Cursor_ < End_) { + while (Cursor_ < End_ && count > 0) { *Cursor_++ = ch; + count--; } } @@ -97,6 +98,8 @@ public: { int digits = 0; + width = std::min(width, GetBytesRemaining()); + if (radix == 16) { // Optimize output of hex numbers. @@ -140,22 +143,6 @@ public: } } - //! Formats |number| as hexadecimal number and updates the internal cursor. - //! Padding will be added in front if needed. - void AppendNumberAsHexWithPadding(uintptr_t number, int width) - { - char* begin = Cursor_; - AppendString("0x"); - AppendNumber(number, 16); - // Move to right and add padding in front if needed. - if (Cursor_ < begin + width) { - auto delta = begin + width - Cursor_; - std::copy(begin, Cursor_, begin + delta); - std::fill(begin, begin + delta, ' '); - Cursor_ = begin + width; - } - } - //! Formats |guid| and updates the internal cursor. void AppendGuid(TGuid guid) { @@ -185,7 +172,6 @@ private: char* const Begin_; char* Cursor_; char* const End_; - }; template <size_t N> @@ -203,7 +189,6 @@ public: private: char Buffer_[N]; - }; //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/misc/assert.cpp b/yt/yt/core/misc/assert.cpp deleted file mode 100644 index 3fe7a2863d..0000000000 --- a/yt/yt/core/misc/assert.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "assert.h" - -#include "proc.h" - -#include <yt/yt/core/logging/log_manager.h> - -#include <library/cpp/yt/assert/assert.h> - -#include <library/cpp/yt/string/raw_formatter.h> - -#include <library/cpp/yt/system/handle_eintr.h> - -#ifdef _win_ - #include <io.h> -#else - #include <unistd.h> -#endif - -#include <errno.h> - -namespace NYT::NDetail { - -using namespace NConcurrency; - -//////////////////////////////////////////////////////////////////////////////// - -Y_WEAK void MaybeThrowSafeAssertionException(const char* /*message*/, int /*length*/) -{ - // A default implementation has no means of safety. - // Actual implementation lives in yt/yt/library/safe_assert. -} - -void AssertTrapImpl( - TStringBuf trapType, - TStringBuf expr, - TStringBuf file, - int line, - TStringBuf function) -{ - TRawFormatter<1024> formatter; - formatter.AppendString(trapType); - formatter.AppendString("("); - formatter.AppendString(expr); - formatter.AppendString(") at "); - formatter.AppendString(file); - formatter.AppendString(":"); - formatter.AppendNumber(line); - if (function) { - formatter.AppendString(" in "); - formatter.AppendString(function); - formatter.AppendString("\n"); - } - - MaybeThrowSafeAssertionException(formatter.GetData(), formatter.GetBytesWritten()); - - HandleEintr(::write, 2, formatter.GetData(), formatter.GetBytesWritten()); - - NLogging::TLogManager::Get()->Shutdown(); - - YT_BUILTIN_TRAP(); -} - -//////////////////////////////////////////////////////////////////////////////// - -} // namespace NYT::NDetail diff --git a/yt/yt/core/misc/crash_handler.cpp b/yt/yt/core/misc/crash_handler.cpp index 80e657531e..fcc4d356c3 100644 --- a/yt/yt/core/misc/crash_handler.cpp +++ b/yt/yt/core/misc/crash_handler.cpp @@ -20,6 +20,8 @@ #include <library/cpp/yt/system/handle_eintr.h> +#include <library/cpp/yt/misc/tls.h> + #ifdef _unix_ #include <library/cpp/yt/backtrace/cursors/libunwind/libunwind_cursor.h> #else @@ -76,6 +78,23 @@ void WriteToStderr(const char* buffer) namespace NDetail { +constinit NThreading::TSpinLock CrashLock; +constinit YT_DEFINE_THREAD_LOCAL(bool, CrashLockAcquiredByCurrentThread); + +void AcquireCrashLock() +{ + if (!std::exchange(CrashLockAcquiredByCurrentThread(), true)) { + CrashLock.Acquire(); + } +} + +void ReleaseCrashLock() +{ + if (std::exchange(CrashLockAcquiredByCurrentThread(), false)) { + CrashLock.Release(); + } +} + Y_NO_INLINE TStackTrace GetStackTrace(TStackTraceBuffer* buffer) { #ifdef _unix_ @@ -264,7 +283,7 @@ const char* GetSignalCodeName(int signo, int code) // From include/asm/traps.h [[maybe_unused]] -const char* GetTrapName(int trapno) +const char* FindTrapName(int trapno) { #define XX(name, value, message) case value: return #name " (" message ")"; @@ -309,7 +328,8 @@ void FormatErrorCodeName(TBaseFormatter* formatter, int codeno) * bit 4 == 1: fault was an instruction fetch * bit 5 == 1: protection keys block access */ - enum x86_pf_error_code { + enum x86_pf_error_code + { X86_PF_PROT = 1 << 0, X86_PF_WRITE = 1 << 1, X86_PF_USER = 1 << 2, @@ -343,7 +363,6 @@ void DumpSignalInfo(siginfo_t* si) { TFormatter formatter; - formatter.AppendString("*** "); if (const char* name = GetSignalName(si->si_signo)) { formatter.AppendString(name); } else { @@ -354,20 +373,20 @@ void DumpSignalInfo(siginfo_t* si) } formatter.AppendString(" (@0x"); - formatter.AppendNumber(reinterpret_cast<uintptr_t>(si->si_addr), 16); + formatter.AppendNumber(reinterpret_cast<uintptr_t>(si->si_addr), /*radix*/ 16); formatter.AppendString(")"); formatter.AppendString(" received by PID "); formatter.AppendNumber(getpid()); formatter.AppendString(" (FID 0x"); - formatter.AppendNumber(NConcurrency::GetCurrentFiberId(), 16); + formatter.AppendNumber(NConcurrency::GetCurrentFiberId(), /*radix*/ 16); formatter.AppendString(" TID 0x"); // We assume pthread_t is an integral number or a pointer, rather // than a complex struct. In some environments, pthread_self() // returns an uint64 but in some other environments pthread_self() // returns a pointer. Hence we use C-style cast here, rather than // reinterpret/static_cast, to support both types of environments. - formatter.AppendNumber((uintptr_t)pthread_self(), 16); + formatter.AppendNumber(reinterpret_cast<uintptr_t>(pthread_self()), /*radix*/ 16); formatter.AppendString(") "); // Only linux has the PID of the signal sender in si_pid. #ifdef _unix_ @@ -381,10 +400,8 @@ void DumpSignalInfo(siginfo_t* si) } else { formatter.AppendNumber(si->si_code); } - - formatter.AppendString(" "); #endif - formatter.AppendString("***\n"); + formatter.AppendChar('\n'); WriteToStderr(formatter); } @@ -396,59 +413,52 @@ void DumpSigcontext(void* uc) TFormatter formatter; - formatter.AppendString("\nERR "); + formatter.AppendString("ERR "); FormatErrorCodeName(&formatter, context->uc_mcontext.gregs[REG_ERR]); + formatter.AppendChar('\n'); - formatter.AppendString("\nTRAPNO "); - if (const char* trapName = GetTrapName(context->uc_mcontext.gregs[REG_TRAPNO])) { + formatter.AppendString("TRAPNO "); + if (const char* trapName = FindTrapName(context->uc_mcontext.gregs[REG_TRAPNO])) { formatter.AppendString(trapName); } else { formatter.AppendString("0x"); formatter.AppendNumber(context->uc_mcontext.gregs[REG_TRAPNO], 16); } + formatter.AppendChar('\n'); - formatter.AppendString("\nR8 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_R8], 16); - formatter.AppendString("\nR9 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_R9], 16); - formatter.AppendString("\nR10 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_R10], 16); - formatter.AppendString("\nR11 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_R11], 16); - formatter.AppendString("\nR12 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_R12], 16); - formatter.AppendString("\nR13 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_R13], 16); - formatter.AppendString("\nR14 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_R14], 16); - formatter.AppendString("\nR15 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_R15], 16); - formatter.AppendString("\nRDI 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RDI], 16); - formatter.AppendString("\nRSI 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RSI], 16); - formatter.AppendString("\nRBP 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RBP], 16); - formatter.AppendString("\nRBX 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RBX], 16); - formatter.AppendString("\nRDX 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RDX], 16); - formatter.AppendString("\nRAX 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RAX], 16); - formatter.AppendString("\nRCX 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RCX], 16); - formatter.AppendString("\nRSP 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RSP], 16); - formatter.AppendString("\nRIP 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_RIP], 16); - formatter.AppendString("\nEFL 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_EFL], 16); - formatter.AppendString("\nCSGSFS 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_CSGSFS], 16); - formatter.AppendString("\nOLDMASK 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_OLDMASK], 16); - formatter.AppendString("\nCR2 0x"); - formatter.AppendNumber(context->uc_mcontext.gregs[REG_CR2], 16); + auto formatRegister = [&, horizontalPos = 0] (TStringBuf name, int reg) mutable { + if (horizontalPos > 1) { + formatter.AppendChar('\n'); + horizontalPos = 0; + } else if (horizontalPos > 0) { + formatter.AppendChar(' ', 4); + } + formatter.AppendString(name); + formatter.AppendChar(' ', 7 - name.length()); + formatter.AppendString("0x"); + formatter.AppendNumber(context->uc_mcontext.gregs[reg], /*radix*/ 16, /*width*/ 16); + ++horizontalPos; + }; + formatRegister("RAX", REG_RAX); + formatRegister("RBX", REG_RBX); + formatRegister("RCX", REG_RCX); + formatRegister("RDX", REG_RDX); + formatRegister("RSI", REG_RSI); + formatRegister("RDI", REG_RDI); + formatRegister("RBP", REG_RBP); + formatRegister("RSP", REG_RSP); + formatRegister("R8", REG_R8); + formatRegister("R9", REG_R9); + formatRegister("R10", REG_R10); + formatRegister("R11", REG_R11); + formatRegister("R12", REG_R12); + formatRegister("R13", REG_R13); + formatRegister("R14", REG_R14); + formatRegister("R15", REG_R15); + formatRegister("RIP", REG_RIP); + formatRegister("EFL", REG_EFL); + formatRegister("CR2", REG_CR2); + formatRegister("CSGSFS", REG_CSGSFS); formatter.AppendChar('\n'); WriteToStderr(formatter); @@ -493,6 +503,48 @@ void DumpUndumpableBlocksInfo() #endif +Y_WEAK void MaybeThrowSafeAssertionException(TStringBuf /*message*/) +{ + // A default implementation has no means of safety. + // Actual implementation lives in yt/yt/library/safe_assert. +} + +void AssertTrapImpl( + TStringBuf trapType, + TStringBuf expr, + TStringBuf file, + int line, + TStringBuf function) +{ + TRawFormatter<1024> formatter; + formatter.AppendString("*** "); + formatter.AppendString(trapType); + formatter.AppendString("("); + formatter.AppendString(expr); + formatter.AppendString(") at "); + formatter.AppendString(file); + formatter.AppendString(":"); + formatter.AppendNumber(line); + if (function) { + formatter.AppendString(" in "); + formatter.AppendString(function); + formatter.AppendString("\n"); + } + + MaybeThrowSafeAssertionException(formatter.GetBuffer()); + + // Prevent clashes in stderr. + AcquireCrashLock(); + + WriteToStderr(formatter.GetBuffer()); + + // This (hopefully) invokes CrashSignalHandler. + YT_BUILTIN_TRAP(); + + // Not expected to get here but anyway... + ReleaseCrashLock(); +} + } // namespace NDetail //////////////////////////////////////////////////////////////////////////////// @@ -508,23 +560,13 @@ void CrashSignalHandler(int /*signal*/, siginfo_t* si, void* uc) ::signal(SIGALRM, NDetail::CrashTimeoutHandler); ::alarm(60); - // When did the crash happen? + // Prevent clashes in stderr. + NDetail::AcquireCrashLock(); + NDetail::DumpTimeInfo(); - // Dump codicils. NDetail::DumpCodicils(); - // Where did the crash happen? - { - std::array<const void*, 1> frames{NDetail::GetPC(uc)}; - NBacktrace::SymbolizeBacktrace( - TRange(frames), - [] (TStringBuf info) { - info.SkipPrefix(" 1. "); - WriteToStderr(info); - }); - } - NDetail::DumpSignalInfo(si); NDetail::DumpSigcontext(uc); @@ -534,6 +576,9 @@ void CrashSignalHandler(int /*signal*/, siginfo_t* si, void* uc) NDetail::DumpUndumpableBlocksInfo(); + // Releasing the lock gives other threads a chance to yell at us. + NDetail::ReleaseCrashLock(); + WriteToStderr("*** Waiting for logger to shut down\n"); NLogging::TLogManager::Get()->Shutdown(); diff --git a/yt/yt/core/ya.make b/yt/yt/core/ya.make index a958bb1c6a..b7850148e7 100644 --- a/yt/yt/core/ya.make +++ b/yt/yt/core/ya.make @@ -115,7 +115,6 @@ SRCS( logging/zstd_compression.cpp misc/arithmetic_formula.cpp - GLOBAL misc/assert.cpp misc/backoff_strategy.cpp misc/bitmap.cpp misc/bit_packed_unsigned_vector.cpp @@ -126,7 +125,7 @@ SRCS( misc/codicil.cpp misc/config.cpp misc/coro_pipe.cpp - misc/crash_handler.cpp + GLOBAL misc/crash_handler.cpp misc/digest.cpp misc/error.cpp misc/fs.cpp |