Skip to content

Commit

Permalink
feat: better stacktrace using cpptrace
Browse files Browse the repository at this point in the history
  • Loading branch information
ABeltramo committed Jan 21, 2024
1 parent f2e7d21 commit d2ff748
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 7 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/linux-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ jobs:
libevdev-dev \
libudev-dev \
libdrm-dev \
libpci-dev
libpci-dev \
libunwind-dev
- name: Setup Rust
uses: ATiltedTree/setup-rust@v1
Expand Down Expand Up @@ -117,6 +118,7 @@ jobs:
libudev-dev \
libdrm-dev \
libpci-dev \
libunwind-dev \
${{ join(matrix.other_pkgs, ' ') }}
- name: Setup Rust
Expand Down
1 change: 1 addition & 0 deletions docker/wolf.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ RUN apt-get update -y && \
libcurl4 \
libdrm2 \
libpci3 \
libunwind8 \
&& rm -rf /var/lib/apt/lists/*

# gst-plugin-wayland runtime dependencies
Expand Down
9 changes: 8 additions & 1 deletion src/moonlight-server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,15 @@ unset(CMAKE_PROJECT_INCLUDE_BEFORE)
FetchContent_Declare(
cpptrace
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
GIT_TAG v0.3.1
GIT_TAG 448c325
)
find_package(PkgConfig)
pkg_check_modules(LIBUNWIND QUIET libunwind)
if(LIBUNWIND_FOUND)
set(CPPTRACE_UNWIND_WITH_LIBUNWIND ON)
else ()
message(WARNING "Missing libunwind, stacktraces will not be available.")
endif ()
FetchContent_MakeAvailable(cpptrace)

##############################
Expand Down
44 changes: 44 additions & 0 deletions src/moonlight-server/exceptions/exceptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include <cpptrace/cpptrace.hpp>
#include <helpers/logger.hpp>
#include <istream>
#include <ostream>

static void safe_dump_stacktrace_to(std::ostream &out) {
constexpr std::size_t N = 100;
cpptrace::frame_ptr buffer[N];
std::size_t count = cpptrace::safe_generate_raw_trace(buffer, N);
if (count > 0) {
out << count << '\n';
for (std::size_t i = 0; i < count; i++) {
cpptrace::safe_object_frame frame{};
cpptrace::get_safe_object_frame(buffer[i], &frame);
out << frame.address_relative_to_object_start << ' ' << frame.raw_address << ' ';
out.write(frame.object_path, sizeof(frame.object_path));
out << '\n';
}
}
}

static std::unique_ptr<cpptrace::object_trace> load_stacktrace_from(std::istream &in) {
cpptrace::object_trace trace{};
std::size_t count;
in >> count;
in.ignore(); // ignore newline
for (std::size_t i = 0; i < count; i++) {
cpptrace::safe_object_frame frame{};
in >> frame.address_relative_to_object_start;
in.ignore(); // ignore space
in >> frame.raw_address;
in.ignore(); // ignore space
in.read(frame.object_path, sizeof(frame.object_path));
in.ignore(); // ignore newline
try {
trace.frames.push_back(frame.resolve());
} catch (std::exception &ex) {
logs::log(logs::debug, "Unable to parse stacktrace frame, skipping");
}
}
return std::make_unique<cpptrace::object_trace>(trace);
}
45 changes: 40 additions & 5 deletions src/moonlight-server/wolf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
#include <core/audio.hpp>
#include <core/docker.hpp>
#include <core/virtual-display.hpp>
#include <cpptrace/cpptrace.hpp>
#include <csignal>
#include <exceptions/exceptions.h>
#include <filesystem>
#include <fstream>
#include <gst-plugin/video.hpp>
Expand Down Expand Up @@ -402,9 +402,38 @@ auto setup_sessions_handlers(const immer::box<state::AppState> &app_state,
return handlers.persistent();
}

static void terminate_handler() {
cpptrace::generate_trace().print();
std::abort();
static std::string backtrace_file_src() {
return fmt::format("{}/backtrace.dump", utils::get_env("WOLF_CFG_FOLDER", "."));
}

static void shutdown_handler(int signum) {
logs::log(logs::info, "Received interrupt signal {}, clean exit", signum);
if (signum == SIGABRT || signum == SIGSEGV) {
auto stack_file = backtrace_file_src();
logs::log(logs::error, "Runtime error, dumping stacktrace to {}", stack_file);
std::ofstream fs(stack_file);
safe_dump_stacktrace_to(fs);
fs.close();
}

logs::log(logs::info, "See ya!");
exit(signum);
}

/**
* @brief: if an exception was raised we should have created a dump file, here we can pretty print it
*/
static void check_exceptions() {
auto stack_file = backtrace_file_src();
if (boost::filesystem::exists(stack_file)) {
std::ifstream ifs(stack_file);
load_stacktrace_from(ifs)->resolve().print();
ifs.close();
auto now = std::chrono::system_clock::now();
boost::filesystem::rename(
stack_file,
fmt::format("{}/backtrace.{:%Y-%m-%d-%H-%M-%S}.dump", utils::get_env("WOLF_CFG_FOLDER", "."), now));
}
}

/**
Expand All @@ -413,7 +442,13 @@ static void terminate_handler() {
int main(int argc, char *argv[]) {
logs::init(logs::parse_level(utils::get_env("WOLF_LOG_LEVEL", "INFO")));
// Exception and termination handling
std::set_terminate(terminate_handler);
std::signal(SIGINT, shutdown_handler);
std::signal(SIGTERM, shutdown_handler);
std::signal(SIGQUIT, shutdown_handler);
std::signal(SIGSEGV, shutdown_handler);
std::signal(SIGABRT, shutdown_handler);
std::set_terminate([]() { shutdown_handler(SIGABRT); });
check_exceptions();

streaming::init(); // Need to initialise gstreamer once
control::init(); // Need to initialise enet once
Expand Down
21 changes: 21 additions & 0 deletions tests/testExceptions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "catch2/catch_all.hpp"
using Catch::Matchers::EndsWith;
using Catch::Matchers::Equals;

#include <exceptions/exceptions.h>
#include <fstream>

TEST_CASE("Exceptions", "[Exceptions]") {
std::ofstream ofs("stacktrace.txt");
safe_dump_stacktrace_to(ofs);
ofs.close();

std::ifstream ifs("stacktrace.txt");
auto trace = load_stacktrace_from(ifs);
REQUIRE(trace->frames.size() > 0);

auto stacktrace = trace->resolve();
REQUIRE(stacktrace.frames.size() > 0);
REQUIRE_THAT(stacktrace.frames[0].filename, EndsWith("src/moonlight-server/exceptions/exceptions.h"));
REQUIRE_THAT(stacktrace.frames[0].symbol, Equals("safe_dump_stacktrace_to"));
}

0 comments on commit d2ff748

Please sign in to comment.