refactor: make proper use of cpptrace with ENABLE_STACKTRACE

This commit is contained in:
Marcus Holland-Moritz 2025-05-08 10:49:59 +02:00
parent 6b7d07a5a8
commit 1fec8314da
7 changed files with 131 additions and 59 deletions

View File

@ -33,47 +33,58 @@
#include <string_view>
#include <system_error>
#include <dwarfs/config.h>
#ifdef DWARFS_STACKTRACE_ENABLED
#include <cpptrace/cpptrace.hpp>
#endif
#include <dwarfs/source_location.h>
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,

View File

@ -35,8 +35,11 @@
#include <dwarfs/config.h>
#ifdef DWARFS_USE_EXCEPTION_TRACER
#include <folly/experimental/exception_tracer/ExceptionTracer.h>
#ifdef DWARFS_STACKTRACE_ENABLED
#if __has_include(<cpptrace/formatting.hpp>)
#include <cpptrace/formatting.hpp>
#define DWARFS_CPPTRACE_HAS_FORMATTING
#endif
#endif
#include <dwarfs/error.h>
@ -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();
}

View File

@ -43,6 +43,10 @@
#ifdef DWARFS_STACKTRACE_ENABLED
#include <cpptrace/cpptrace.hpp>
#if __has_include(<cpptrace/formatting.hpp>)
#include <cpptrace/formatting.hpp>
#define DWARFS_CPPTRACE_HAS_FORMATTING
#endif
#endif
#include <fmt/chrono.h>
@ -214,11 +218,19 @@ void stream_logger::write(level_type level, std::string_view output,
}
#ifdef DWARFS_STACKTRACE_ENABLED
std::string stacktrace;
std::vector<std::string_view> 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();

View File

@ -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());

View File

@ -43,7 +43,7 @@ class global_metadata_test : public ::testing::Test {
}
static auto throws_error(std::string_view msg) {
return testing::ThrowsMessage<dwarfs::error>(testing::StartsWith(msg));
return testing::ThrowsMessage<dwarfs::error>(testing::HasSubstr(msg));
};
test_logger lgr;

View File

@ -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;
}

View File

@ -30,6 +30,21 @@
#include <cstdlib>
#include <iostream>
#include <dwarfs/config.h>
#ifdef DWARFS_STACKTRACE_ENABLED
#include <cpptrace/from_current.hpp>
#if __has_include(<cpptrace/formatting.hpp>)
#include <cpptrace/formatting.hpp>
#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 <dwarfs/error.h>
#include <dwarfs/tool/safe_main.h>
#include <dwarfs/util.h>
@ -37,7 +52,7 @@
namespace dwarfs::tool {
int safe_main(std::function<int(void)> const& fn) {
try {
DWARFS_TRY {
install_signal_handlers();
setup_default_locale();
#ifdef _WIN32
@ -45,9 +60,19 @@ int safe_main(std::function<int(void)> 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;
}