diff --git a/include/dwarfs/error.h b/include/dwarfs/error.h index d9d73b92..eb08d734 100644 --- a/include/dwarfs/error.h +++ b/include/dwarfs/error.h @@ -33,47 +33,58 @@ #include #include +#include + +#ifdef DWARFS_STACKTRACE_ENABLED +#include +#endif + #include namespace dwarfs { class error : public std::exception { public: - char const* what() const noexcept override { return what_.c_str(); } - auto location() const { return loc_; } auto file() const { return loc_.file_name(); } auto line() const { return loc_.line(); } +#ifdef DWARFS_STACKTRACE_ENABLED + cpptrace::stacktrace stacktrace() const; +#endif protected: - error(std::string_view s, source_location loc) noexcept; + error(source_location loc) noexcept; private: - std::string what_; source_location loc_; +#ifdef DWARFS_STACKTRACE_ENABLED + cpptrace::raw_trace trace_; +#endif }; class runtime_error : public error { public: - runtime_error(std::string_view s, source_location loc) noexcept - : error(s, loc) {} -}; + runtime_error(std::string_view s, source_location loc); -class system_error : public std::system_error { - public: - system_error(source_location loc) noexcept; - system_error(std::string_view s, source_location loc) noexcept; - system_error(std::string_view s, int err, source_location loc) noexcept; - system_error(int err, source_location loc) noexcept; - - auto get_errno() const { return code().value(); } - - auto location() const { return loc_; } - auto file() const { return loc_.file_name(); } - auto line() const { return loc_.line(); } + char const* what() const noexcept override { return what_.c_str(); } private: - source_location loc_; + std::string what_; +}; + +class system_error : public error { + public: + system_error(source_location loc); + system_error(std::string_view s, source_location loc); + system_error(std::string_view s, int err, source_location loc); + system_error(int err, source_location loc); + + char const* what() const noexcept override { return syserr_.what(); } + std::error_code const& code() const noexcept { return syserr_.code(); } + int get_errno() const { return code().value(); } + + private: + std::system_error syserr_; }; #define DWARFS_THROW(cls, ...) \ @@ -101,8 +112,6 @@ class system_error : public std::system_error { } \ }() -void dump_exceptions(); - [[noreturn]] void handle_nothrow(std::string_view expr, source_location loc); [[noreturn]] void assertion_failed(std::string_view expr, std::string_view msg, diff --git a/src/error.cpp b/src/error.cpp index de026af4..4bc0b4c2 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -35,8 +35,11 @@ #include -#ifdef DWARFS_USE_EXCEPTION_TRACER -#include +#ifdef DWARFS_STACKTRACE_ENABLED +#if __has_include() +#include +#define DWARFS_CPPTRACE_HAS_FORMATTING +#endif #endif #include @@ -55,40 +58,60 @@ namespace { #endif } -} // namespace - -error::error(std::string_view s, source_location loc) noexcept - : what_{fmt::format("{} [{}:{}]", s, basename(loc.file_name()), loc.line())} - , loc_{loc} {} - -system_error::system_error(source_location loc) noexcept - : system_error(errno, loc) {} - -system_error::system_error(std::string_view s, source_location loc) noexcept - : system_error(s, errno, loc) {} - -system_error::system_error(std::string_view s, int err, - source_location loc) noexcept - : std::system_error(err, std::generic_category(), std::string(s)) - , loc_{loc} {} - -system_error::system_error(int err, source_location loc) noexcept - : std::system_error(err, std::generic_category()) - , loc_{loc} {} - -void dump_exceptions() { -#ifdef DWARFS_USE_EXCEPTION_TRACER - auto exceptions = ::folly::exception_tracer::getCurrentExceptions(); - for (auto& exc : exceptions) { - std::cerr << exc << "\n"; - } +void print_stacktrace(std::ostream& os [[maybe_unused]]) { +#ifdef DWARFS_STACKTRACE_ENABLED + auto trace = cpptrace::generate_trace(); +#ifdef DWARFS_CPPTRACE_HAS_FORMATTING + auto formatter = cpptrace::formatter{}.addresses( + cpptrace::formatter::address_mode::object); + formatter.print(os, trace, true); +#else + trace.print(os, true); +#endif #endif } +} // namespace + +error::error(source_location loc) noexcept + : loc_{loc} +#ifdef DWARFS_STACKTRACE_ENABLED + , trace_{cpptrace::generate_raw_trace()} +#endif +{ +} + +#ifdef DWARFS_STACKTRACE_ENABLED +cpptrace::stacktrace error::stacktrace() const { return trace_.resolve(); } +#endif + +runtime_error::runtime_error(std::string_view s, source_location loc) + : error{loc} + , what_{fmt::format("[{}:{}] {}", basename(loc.file_name()), loc.line(), + s)} {} + +system_error::system_error(source_location loc) + : system_error(errno, loc) {} + +system_error::system_error(std::string_view s, source_location loc) + : system_error(s, errno, loc) {} + +system_error::system_error(std::string_view s, int err, source_location loc) + : error{loc} + , syserr_{err, std::generic_category(), + fmt::format("[{}:{}] {}", basename(loc.file_name()), loc.line(), + s)} {} + +system_error::system_error(int err, source_location loc) + : error{loc} + , syserr_{err, std::generic_category(), + fmt::format("[{}:{}]", basename(loc.file_name()), loc.line())} {} + void handle_nothrow(std::string_view expr, source_location loc) { std::cerr << "Expression `" << expr << "` threw `" << exception_str(std::current_exception()) << "` in " << loc.file_name() << "(" << loc.line() << ")\n"; + print_stacktrace(std::cerr); do_terminate(); } @@ -96,12 +119,14 @@ void assertion_failed(std::string_view expr, std::string_view msg, source_location loc) { std::cerr << "Assertion `" << expr << "` failed in " << loc.file_name() << "(" << loc.line() << "): " << msg << "\n"; + print_stacktrace(std::cerr); do_terminate(); } void handle_panic(std::string_view msg, source_location loc) { std::cerr << "Panic: " << msg << " in " << loc.file_name() << "(" << loc.line() << ")\n"; + print_stacktrace(std::cerr); do_terminate(); } diff --git a/src/logger.cpp b/src/logger.cpp index 81c3074f..b8d6da8a 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -43,6 +43,10 @@ #ifdef DWARFS_STACKTRACE_ENABLED #include +#if __has_include() +#include +#define DWARFS_CPPTRACE_HAS_FORMATTING +#endif #endif #include @@ -214,11 +218,19 @@ void stream_logger::write(level_type level, std::string_view output, } #ifdef DWARFS_STACKTRACE_ENABLED - std::string stacktrace; std::vector st_lines; + std::string stacktrace; if (enable_stack_trace_ || level == FATAL) { +#ifdef DWARFS_CPPTRACE_HAS_FORMATTING + auto formatter = cpptrace::formatter{} + .header({}) + .addresses(cpptrace::formatter::address_mode::object) + .paths(cpptrace::formatter::path_mode::basename); + stacktrace = formatter.format(cpptrace::generate_trace(), true); +#else stacktrace = cpptrace::generate_trace().to_string(true); +#endif split_to(stacktrace, '\n', st_lines); if (st_lines.back().empty()) { st_lines.pop_back(); diff --git a/test/error_test.cpp b/test/error_test.cpp index d9af5fb9..4b416013 100644 --- a/test/error_test.cpp +++ b/test/error_test.cpp @@ -64,7 +64,7 @@ TEST(error_test, runtime_error) { } catch (runtime_error const& e) { EXPECT_EQ("error_test.cpp", std::filesystem::path(e.file()).filename().string()); - EXPECT_EQ(fmt::format("my test error [error_test.cpp:{}]", e.line()), + EXPECT_EQ(fmt::format("[error_test.cpp:{}] my test error", e.line()), std::string(e.what())); EXPECT_EQ(expected_line, e.line()); } catch (...) { @@ -81,7 +81,8 @@ TEST(error_test, system_error) { FAIL() << "expected system_error to be thrown"; } catch (system_error const& e) { EXPECT_THAT(std::string(e.what()), - ::testing::MatchesRegex("my test system error: .*")); + ::testing::MatchesRegex( + "\\[error_test\\.cpp:.*\\] my test system error: .*")); EXPECT_EQ("error_test.cpp", std::filesystem::path(e.file()).filename().string()); EXPECT_EQ(EPERM, e.get_errno()); diff --git a/test/global_metadata_test.cpp b/test/global_metadata_test.cpp index faf2bd70..8f8888d4 100644 --- a/test/global_metadata_test.cpp +++ b/test/global_metadata_test.cpp @@ -43,7 +43,7 @@ class global_metadata_test : public ::testing::Test { } static auto throws_error(std::string_view msg) { - return testing::ThrowsMessage(testing::StartsWith(msg)); + return testing::ThrowsMessage(testing::HasSubstr(msg)); }; test_logger lgr; diff --git a/tools/src/mkdwarfs_main.cpp b/tools/src/mkdwarfs_main.cpp index e7e375ec..81ac6c79 100644 --- a/tools/src/mkdwarfs_main.cpp +++ b/tools/src/mkdwarfs_main.cpp @@ -1408,7 +1408,7 @@ int mkdwarfs_main(int argc, sys_char** argv, iolayer const& iol) { options.inode.categorizer_mgr.reset(); } - } catch (std::exception const& e) { + } catch (dwarfs::error const& e) { LOG_ERROR << exception_str(e); return 1; } diff --git a/tools/src/tool/safe_main.cpp b/tools/src/tool/safe_main.cpp index c9c6932f..8365cafb 100644 --- a/tools/src/tool/safe_main.cpp +++ b/tools/src/tool/safe_main.cpp @@ -30,6 +30,21 @@ #include #include +#include + +#ifdef DWARFS_STACKTRACE_ENABLED +#include +#if __has_include() +#include +#define DWARFS_CPPTRACE_HAS_FORMATTING +#endif +#define DWARFS_TRY CPPTRACE_TRYZ +#define DWARFS_CATCH CPPTRACE_CATCHZ +#else +#define DWARFS_TRY try +#define DWARFS_CATCH catch +#endif + #include #include #include @@ -37,7 +52,7 @@ namespace dwarfs::tool { int safe_main(std::function const& fn) { - try { + DWARFS_TRY { install_signal_handlers(); setup_default_locale(); #ifdef _WIN32 @@ -45,9 +60,19 @@ int safe_main(std::function const& fn) { #endif return fn(); - } catch (...) { + } + DWARFS_CATCH(...) { std::cerr << "ERROR: " << exception_str(std::current_exception()) << "\n"; - dump_exceptions(); +#ifdef DWARFS_STACKTRACE_ENABLED + auto stacktrace = cpptrace::from_current_exception(); +#ifdef DWARFS_CPPTRACE_HAS_FORMATTING + auto formatter = cpptrace::formatter{}.addresses( + cpptrace::formatter::address_mode::object); + formatter.print(std::cerr, stacktrace, true); +#else + stacktrace.print(std::cerr, true); +#endif +#endif } return 1; }