diff options
author | svkrasnov <svkrasnov@yandex-team.ru> | 2022-03-23 14:49:38 +0300 |
---|---|---|
committer | svkrasnov <svkrasnov@yandex-team.ru> | 2022-03-23 14:49:38 +0300 |
commit | 0e6dafca9899ab0baf5ec5874ab205f0d8fa7f11 (patch) | |
tree | 5ef144b410bbc505a4dd07a3d1cc830d874bc606 | |
parent | ea82b3450005272f1a406cf1eac7a245c2e7c1ee (diff) | |
download | ydb-0e6dafca9899ab0baf5ec5874ab205f0d8fa7f11.tar.gz |
Introduce FormatCurrentException() based on TBackTrace::FromCurrentException()
ref:60218909c48b580eebc0b518f039afd46ca51713
-rw-r--r-- | util/generic/yexception.cpp | 53 | ||||
-rw-r--r-- | util/generic/yexception.h | 12 | ||||
-rw-r--r-- | util/generic/yexception_ut.cpp | 50 |
3 files changed, 114 insertions, 1 deletions
diff --git a/util/generic/yexception.cpp b/util/generic/yexception.cpp index 26c75b5f51..ee6d6ebe17 100644 --- a/util/generic/yexception.cpp +++ b/util/generic/yexception.cpp @@ -10,8 +10,14 @@ #include <cstdio> +static void FormatExceptionTo(IOutputStream& out, const std::exception& exception) { + out << "(" << TypeName(exception) << ") " << exception.what(); +} + TString FormatExc(const std::exception& exception) { - return TString::Join(TStringBuf("("), TypeName(exception), TStringBuf(") "), exception.what()); + TStringStream out; + FormatExceptionTo(out, exception); + return out.Str(); } TString CurrentExceptionMessage() { @@ -38,6 +44,51 @@ TString CurrentExceptionMessage() { return "(NO EXCEPTION)"; } +Y_DECLARE_UNUSED static void FormatBackTraceTo(IOutputStream& out, const TBackTrace& backtrace) { + if (backtrace.size() == 0) { + out << "backtrace is empty"; + return; + } + try { + backtrace.PrintTo(out); + } catch (const std::exception& e) { + out << "Failed to print backtrace: "; + FormatExceptionTo(out, e); + } +} + +void FormatCurrentExceptionTo(IOutputStream& out) { + auto exceptionPtr = std::current_exception(); + /* + * The lack of current exception indicates a logical bug in the client code. + * Do not make any attempts to workaround it, just panic and abort. + */ + Y_VERIFY(exceptionPtr != nullptr, "there is no current exception"); +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE + TBackTrace backtrace = TBackTrace::FromCurrentException(); +#endif + try { + std::rethrow_exception(exceptionPtr); + } catch (const std::exception& e) { + out << "Caught:\n"; + FormatExceptionTo(out, e); +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE + out << "\n\n"; + FormatBackTraceTo(out, backtrace); +#endif + return; + } catch (...) { + out << "unknown error"; + return; + } +} + +TString FormatCurrentException() { + TStringStream out; + FormatCurrentExceptionTo(out); + return out.Str(); +} + bool UncaughtException() noexcept { return std::uncaught_exceptions() > 0; } diff --git a/util/generic/yexception.h b/util/generic/yexception.h index b0c604e8c4..660c772061 100644 --- a/util/generic/yexception.h +++ b/util/generic/yexception.h @@ -156,6 +156,18 @@ void fputs(const std::exception& e, FILE* f = stderr); TString CurrentExceptionMessage(); +/** + * Formats current exception for logging purposes. Includes formatted backtrace if it is stored + * alongside the exception. + * The output format is a subject to change, do not depend or canonize it. + * The speed of this method is not guaranteed either. Do not call it in hot paths of your code. + * + * The lack of current exception prior to the invocation indicates logical bug in the client code. + * Y_VERIFY asserts the existence of exception, otherwise panic and abort. + */ +TString FormatCurrentException(); +void FormatCurrentExceptionTo(IOutputStream& out); + /* * A neat method that detects wrether stack unwinding is in progress. * As its std counterpart (that is std::uncaught_exception()) diff --git a/util/generic/yexception_ut.cpp b/util/generic/yexception_ut.cpp index cb3e29fed8..89869fcbe8 100644 --- a/util/generic/yexception_ut.cpp +++ b/util/generic/yexception_ut.cpp @@ -15,6 +15,7 @@ static inline void Throw2DontMove() { #include <util/random/mersenne.h> #include <util/stream/output.h> #include <util/string/subst.h> +#include <util/string/split.h> #include "yexception_ut.h" #include "bt_exception.h" @@ -48,6 +49,10 @@ class TExceptionTest: public TTestBase { UNIT_TEST(TestBackTrace) UNIT_TEST(TestEnsureWithBackTrace1) UNIT_TEST(TestEnsureWithBackTrace2) +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE + UNIT_TEST(TestFormatCurrentException) + UNIT_TEST(TestFormatCurrentExceptionWithInvalidBacktraceFormatter) +#endif UNIT_TEST(TestRethrowAppend) UNIT_TEST(TestMacroOverload) UNIT_TEST(TestMessageCrop) @@ -128,6 +133,51 @@ private: UNIT_ASSERT(false); } + // TODO(svkrasnov): the output should be canonized after https://st.yandex-team.ru/YMAKE-103 +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE + void TestFormatCurrentException() { + try { + throw std::logic_error("some exception"); // is instance of std::exception + UNIT_ASSERT(false); + } catch (...) { + TString exceptionMessage = FormatCurrentException(); + UNIT_ASSERT(exceptionMessage.Contains("(std::logic_error) some exception")); + TVector<TString> backtraceStrs = StringSplitter(exceptionMessage).Split('\n'); + UNIT_ASSERT(backtraceStrs.size() > 1); + } + } +#endif + +#ifdef _YNDX_LIBUNWIND_ENABLE_EXCEPTION_BACKTRACE + void TestFormatCurrentExceptionWithInvalidBacktraceFormatter() { + auto invalidFormatter = [](IOutputStream*, void* const*, size_t) { + Throw2DontMove(); + }; + SetFormatBackTraceFn(invalidFormatter); + + try { + Throw1DontMove(); + UNIT_ASSERT(false); + } catch (...) { + TString expected = "Caught:\n" + "(yexception) util/generic/yexception_ut.cpp:4: blabla\n\n" + "Failed to print backtrace: " + "(yexception) util/generic/yexception_ut.cpp:8: 1 qw 12.1"; + UNIT_ASSERT_EQUAL(FormatCurrentException(), expected); + } + try { + throw std::logic_error("std exception"); + UNIT_ASSERT(false); + } catch (...) { + TString expected = "Caught:\n" + "(std::logic_error) std exception\n\n" + "Failed to print backtrace: " + "(yexception) util/generic/yexception_ut.cpp:8: 1 qw 12.1"; + UNIT_ASSERT_EQUAL(FormatCurrentException(), expected); + } + } +#endif + inline void TestVirtualInheritance() { TStringStream ss; |