Skip to content

Commit

Permalink
Implement a minimum viable product
Browse files Browse the repository at this point in the history
  • Loading branch information
chosungmann committed Oct 20, 2024
1 parent 71328b3 commit 50b3907
Show file tree
Hide file tree
Showing 20 changed files with 566 additions and 9 deletions.
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BasedOnStyle: Chromium
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.cargo/
Cargo.lock
target/
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "third_party/breakpad"]
path = third_party/breakpad
url = https://chromium.googlesource.com/breakpad/breakpad
[submodule "third_party/lss"]
path = third_party/lss
url = https://chromium.googlesource.com/linux-syscall-support
5 changes: 5 additions & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
edition = "2021"
newline_style = "Unix"
use_small_heuristics = "Max"
version = "Two"
wrap_comments = true
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,14 @@
edition = "2021"
name = "breakpad-rs"
version = "0.1.0"

[build-dependencies]
autocxx-build = "0.27.0"
autocxx-engine = "0.27.0"
miette = { version = "7.2.0", features = ["fancy"] }

[dependencies]
autocxx = "0.27.0"
cxx = "1.0.129"
env_logger = "0.11.5"
log = "0.4.22"
164 changes: 164 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use miette::IntoDiagnostic;

const THIRD_PARTY_BREAKPAD_APPLE: &[&str] = &[
"third_party/breakpad/src/client/mac/handler/breakpad_nlist_64.cc",
"third_party/breakpad/src/client/mac/handler/dynamic_images.cc",
"third_party/breakpad/src/client/mac/handler/minidump_generator.cc",
"third_party/breakpad/src/client/minidump_file_writer.cc",
"third_party/breakpad/src/common/convert_UTF.cc",
"third_party/breakpad/src/common/mac/arch_utilities.cc",
"third_party/breakpad/src/common/mac/file_id.cc",
"third_party/breakpad/src/common/mac/macho_id.cc",
"third_party/breakpad/src/common/mac/macho_utilities.cc",
"third_party/breakpad/src/common/mac/macho_walker.cc",
"third_party/breakpad/src/common/mac/string_utilities.cc",
"third_party/breakpad/src/common/md5.cc",
"third_party/breakpad/src/common/string_conversion.cc",
];

const THIRD_PARTY_BREAKPAD_UNIX: &[&str] = &[
"third_party/breakpad/src/client/linux/crash_generation/crash_generation_client.cc",
"third_party/breakpad/src/client/linux/dump_writer_common/thread_info.cc",
"third_party/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc",
"third_party/breakpad/src/client/linux/handler/exception_handler.cc",
"third_party/breakpad/src/client/linux/handler/minidump_descriptor.cc",
"third_party/breakpad/src/client/linux/log/log.cc",
"third_party/breakpad/src/client/linux/microdump_writer/microdump_writer.cc",
"third_party/breakpad/src/client/linux/minidump_writer/linux_dumper.cc",
"third_party/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc",
"third_party/breakpad/src/client/linux/minidump_writer/minidump_writer.cc",
"third_party/breakpad/src/client/linux/minidump_writer/pe_file.cc",
"third_party/breakpad/src/client/minidump_file_writer.cc",
"third_party/breakpad/src/common/convert_UTF.cc",
"third_party/breakpad/src/common/linux/elfutils.cc",
"third_party/breakpad/src/common/linux/file_id.cc",
"third_party/breakpad/src/common/linux/guid_creator.cc",
"third_party/breakpad/src/common/linux/linux_libc_support.cc",
"third_party/breakpad/src/common/linux/memory_mapped_file.cc",
"third_party/breakpad/src/common/linux/safe_readlink.cc",
"third_party/breakpad/src/common/string_conversion.cc",
];

fn autocxx_include_paths() -> Vec<String> {
// The order of these paths is important to prevent the following error:
//
// <cstdint> tried including <stdint.h> but didn't find libc++'s <stdint.h> header. This
// usually means that your header search paths are not configured properly. The header search
// paths should contain the C++ Standard Library headers before any C Standard Library, and
// you are probably using compiler flags that make that not be the case.
//
let target = std::env::var("TARGET").unwrap_or_default();
let sysroot = std::env::var(format!("SYSROOT_{target}")).unwrap_or_default();
let sysroot_target = std::env::var(format!("SYSROOT_TARGET_{target}")).unwrap_or_default();
vec![
"src/cplusplus".to_string(),
format!("{sysroot}/usr/include/c++/v1"),
format!("{sysroot}/usr/include"),
format!("{sysroot}/usr/include/{sysroot_target}"),
]
}

fn configure_build_android(build: &mut autocxx_engine::BuilderBuild) {
build
.files(THIRD_PARTY_BREAKPAD_UNIX)
.files([
"src/cplusplus/breakpad_unix.cc",
"src/cplusplus/utility.cc",
"src/cplusplus/utility_unix.cc",
])
.flag_if_supported("-Wno-missing-field-initializers")
.flag_if_supported("-Wno-sign-compare")
.flag_if_supported("-Wno-unused-parameter")
.pic(true);
}

fn configure_build_ios(build: &mut autocxx_engine::BuilderBuild) {
build
.files(THIRD_PARTY_BREAKPAD_APPLE)
.files([
"src/cplusplus/breakpad_apple.cc",
"src/cplusplus/utility.cc",
"third_party/breakpad/src/client/ios/exception_handler_no_mach.cc",
])
.flag_if_supported("-Wno-mismatched-tags")
.flag_if_supported("-Wno-unused-parameter");
println!("cargo:rustc-link-lib=framework=CoreFoundation");
}

fn configure_build_linux(build: &mut autocxx_engine::BuilderBuild) {
build
.files(THIRD_PARTY_BREAKPAD_UNIX)
.files([
"src/cplusplus/breakpad_unix.cc",
"src/cplusplus/utility.cc",
"src/cplusplus/utility_unix.cc",
])
.flag_if_supported("-Wno-implicit-fallthrough")
.flag_if_supported("-Wno-maybe-uninitialized")
.flag_if_supported("-Wno-missing-field-initializers")
.pic(true);
}

fn configure_build_macos(build: &mut autocxx_engine::BuilderBuild) {
build
.files(THIRD_PARTY_BREAKPAD_APPLE)
.files([
"src/cplusplus/breakpad_apple.cc",
"src/cplusplus/utility.cc",
"third_party/breakpad/src/client/mac/crash_generation/crash_generation_client.cc",
"third_party/breakpad/src/client/mac/handler/exception_handler.cc",
"third_party/breakpad/src/common/mac/MachIPC.mm",
])
.flag_if_supported("-Wno-deprecated-copy-with-user-provided-copy")
.flag_if_supported("-Wno-unused-parameter");
println!("cargo:rustc-link-lib=framework=CoreFoundation");
}

fn configure_build_windows(build: &mut autocxx_engine::BuilderBuild) {
build
.define("NOMINMAX", None)
.define("UNICODE", None)
.define("WIN32_LEAN_AND_MEAN", None)
.define("_UNICODE", None)
.files([
"src/cplusplus/breakpad_windows.cc",
"src/cplusplus/utility.cc",
"src/cplusplus/utility_windows.cc",
"third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.cc",
"third_party/breakpad/src/client/windows/handler/exception_handler.cc",
"third_party/breakpad/src/common/windows/guid_string.cc",
]);
}

fn configure_build(build: &mut autocxx_engine::BuilderBuild) {
match std::env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() {
"android" => configure_build_android(build),
"ios" => configure_build_ios(build),
"linux" => configure_build_linux(build),
"macos" => configure_build_macos(build),
"windows" => configure_build_windows(build),
target_os => panic!("Unsupported Platform: {target_os}"),
}
}

fn main() -> miette::Result<()> {
let mut build = autocxx_build::Builder::new("src/lib.rs", autocxx_include_paths())
.auto_allowlist(true)
.build()
.into_diagnostic()?;
configure_build(&mut build);

#[cfg(not(debug_assertions))]
build.define("NDEBUG", None);

build
.flag_if_supported("-std=c++17")
.flag_if_supported("/std:c++17")
.includes(["./", "third_party/breakpad/src/"])
.compile("breakpad");

println!("cargo:rerun-if-changed=src/");
println!("cargo:rerun-if-changed=third_party/");

Ok(())
}
26 changes: 26 additions & 0 deletions examples/crash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
struct Delegate;

impl breakpad_rs::ExceptionHandlerDelegate for Delegate {
fn did_write_minidump(&self, working_path: String, minidump_id: String) {
log::debug!("[did_write_minidump] working_path={working_path} minidump_id={minidump_id}");
}

fn get_working_path(&self) -> String {
log::debug!("[get_working_path] return=.");
String::from(".")
}

fn should_write_minidump(&self) -> bool {
log::debug!("[should_write_minidump] return=true");
true
}
}

fn main() {
env_logger::init();
let _breakpad = breakpad_rs::Breakpad::new(Some(Box::new(Delegate)));
unsafe {
let null: *mut u8 = std::ptr::null_mut();
*null = 42;
}
}
29 changes: 29 additions & 0 deletions src/cplusplus/breakpad.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <memory>

#include "breakpad_exception_handler_delegate.h"
#include "platform.h"

#if defined(PLATFORM_ANDROID)
#include "third_party/breakpad/src/client/linux/handler/exception_handler.h"
#elif defined(PLATFORM_IOS)
#include "third_party/breakpad/src/client/ios/exception_handler_no_mach.h"
#elif defined(PLATFORM_LINUX)
#include "third_party/breakpad/src/client/linux/handler/exception_handler.h"
#elif defined(PLATFORM_MACOS)
#include "third_party/breakpad/src/client/mac/handler/exception_handler.h"
#elif defined(PLATFORM_WINDOWS)
#include "third_party/breakpad/src/client/windows/handler/exception_handler.h"
#else
#error "Unsupported Platform!"
#endif

namespace breakpad {

using ExceptionHandler = google_breakpad::ExceptionHandler;

std::unique_ptr<ExceptionHandler> CreateExceptionHandler(
const ExceptionHandlerDelegate& delegate);

} // namespace breakpad
37 changes: 37 additions & 0 deletions src/cplusplus/breakpad_apple.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "breakpad.h"

#include "utility.h"

namespace breakpad {

namespace {

bool FilterCallback(void* context) {
if (const auto* delegate = static_cast<ExceptionHandlerDelegate*>(context)) {
return delegate->ShouldWriteMinidump();
}
return true;
}

bool MinidumpCallback(const char* working_path,
const char* minidump_id,
void* context,
bool succeeded) {
if (const auto* delegate = static_cast<ExceptionHandlerDelegate*>(context)) {
delegate->DidWriteMinidump(working_path, minidump_id);
}
return succeeded;
}

} // namespace

std::unique_ptr<ExceptionHandler> CreateExceptionHandler(
const ExceptionHandlerDelegate& delegate) {
return std::make_unique<ExceptionHandler>(
GetValidWorkingPath(delegate.GetWorkingPath()), FilterCallback,
MinidumpCallback,
static_cast<void*>(const_cast<ExceptionHandlerDelegate*>(&delegate)),
true, nullptr);
}

} // namespace breakpad
17 changes: 17 additions & 0 deletions src/cplusplus/breakpad_exception_handler_delegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <string>

namespace breakpad {

class ExceptionHandlerDelegate {
public:
virtual void DidWriteMinidump(const std::string& working_path,
const std::string& minidump_id) const = 0;
virtual std::string GetWorkingPath() const = 0;
virtual bool ShouldWriteMinidump() const = 0;

virtual ~ExceptionHandlerDelegate() = default;
};

} // namespace breakpad
39 changes: 39 additions & 0 deletions src/cplusplus/breakpad_unix.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "breakpad.h"

#include "utility.h"

namespace breakpad {

namespace {

bool FilterCallback(void* context) {
if (const auto* delegate = static_cast<ExceptionHandlerDelegate*>(context)) {
return delegate->ShouldWriteMinidump();
}
return true;
}

bool MinidumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
void* context,
bool succeeded) {
if (const auto* delegate = static_cast<ExceptionHandlerDelegate*>(context)) {
const auto& [working_path, minidump_id] =
GetMinidumpPathComponents(descriptor.path());
delegate->DidWriteMinidump(working_path, minidump_id);
}
return succeeded;
}

} // namespace

std::unique_ptr<ExceptionHandler> CreateExceptionHandler(
const ExceptionHandlerDelegate& delegate) {
return std::make_unique<ExceptionHandler>(
google_breakpad::MinidumpDescriptor(
GetValidWorkingPath(delegate.GetWorkingPath())),
FilterCallback, MinidumpCallback,
static_cast<void*>(const_cast<ExceptionHandlerDelegate*>(&delegate)),
true, -1);
}

} // namespace breakpad
42 changes: 42 additions & 0 deletions src/cplusplus/breakpad_windows.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "breakpad.h"

#include "utility.h"

namespace breakpad {

namespace {

bool FilterCallback(void* context,
[[maybe_unused]] EXCEPTION_POINTERS* exinfo,
[[maybe_unused]] MDRawAssertionInfo* assertion) {
if (const auto* delegate = static_cast<ExceptionHandlerDelegate*>(context)) {
return delegate->ShouldWriteMinidump();
}
return true;
}

bool MinidumpCallback(const wchar_t* working_path,
const wchar_t* minidump_id,
void* context,
[[maybe_unused]] EXCEPTION_POINTERS* exinfo,
[[maybe_unused]] MDRawAssertionInfo* assertion,
bool succeeded) {
if (const auto* delegate = static_cast<ExceptionHandlerDelegate*>(context)) {
delegate->DidWriteMinidump(ToStdString(working_path),
ToStdString(minidump_id));
}
return succeeded;
}

} // namespace

std::unique_ptr<ExceptionHandler> CreateExceptionHandler(
const ExceptionHandlerDelegate& delegate) {
return std::make_unique<ExceptionHandler>(
ToStdWstring(GetValidWorkingPath(delegate.GetWorkingPath())),
FilterCallback, MinidumpCallback,
static_cast<void*>(const_cast<ExceptionHandlerDelegate*>(&delegate)),
ExceptionHandler::HANDLER_ALL);
}

} // namespace breakpad
Loading

0 comments on commit 50b3907

Please sign in to comment.