use super::builder::CrashInfoBuilder;
use super::stacktrace::{StackFrame, StackTrace};
use super::CrashInfo;
use crate::{BuildIdType, FileType, Metadata};
#[cxx::bridge(namespace = "datadog::crashtracker")]
pub mod ffi {
#[repr(u32)]
enum CxxErrorKind {
Panic = 0,
UnhandledException = 1,
UnixSignal = 2,
}
enum CxxBuildIdType {
GNU,
GO,
PDB,
SHA1,
}
enum CxxFileType {
APK,
ELF,
PE,
}
struct Metadata {
library_name: String,
library_version: String,
family: String,
tags: Vec<String>,
}
struct ProcInfo {
pid: u32,
tid: u32,
}
struct OsInfo {
architecture: String,
bitness: String,
os_type: String,
version: String,
}
extern "Rust" {
type CrashInfoBuilder;
type StackFrame;
type StackTrace;
type CrashInfo;
#[Self = "CrashInfoBuilder"]
fn create() -> Box<CrashInfoBuilder>;
#[Self = "StackFrame"]
fn create() -> Box<StackFrame>;
#[Self = "StackTrace"]
fn create() -> Box<StackTrace>;
fn set_kind(self: &mut CrashInfoBuilder, kind: CxxErrorKind) -> Result<()>;
fn set_metadata(self: &mut CrashInfoBuilder, metadata: Metadata) -> Result<()>;
fn set_proc_info(self: &mut CrashInfoBuilder, proc_info: ProcInfo) -> Result<()>;
fn set_os_info(self: &mut CrashInfoBuilder, os_info: OsInfo) -> Result<()>;
fn add_stack(self: &mut CrashInfoBuilder, stack: Box<StackTrace>) -> Result<()>;
fn add_stack_frame(
self: &mut CrashInfoBuilder,
frame: Box<StackFrame>,
incomplete: bool,
) -> Result<()>;
fn with_message(self: &mut CrashInfoBuilder, message: String) -> Result<()>;
fn with_fingerprint(self: &mut CrashInfoBuilder, fingerprint: String) -> Result<()>;
fn with_incomplete(self: &mut CrashInfoBuilder, incomplete: bool) -> Result<()>;
fn with_counter(self: &mut CrashInfoBuilder, name: String, value: i64) -> Result<()>;
fn with_log_message(
self: &mut CrashInfoBuilder,
message: String,
also_print: bool,
) -> Result<()>;
fn with_file(self: &mut CrashInfoBuilder, filename: String) -> Result<()>;
fn with_timestamp_now(self: &mut CrashInfoBuilder) -> Result<()>;
fn with_os_info_this_machine(self: &mut CrashInfoBuilder) -> Result<()>;
fn with_stack_set_complete(self: &mut CrashInfoBuilder) -> Result<()>;
fn crashinfo_build(builder: Box<CrashInfoBuilder>) -> Result<Box<CrashInfo>>;
fn build_id_type(self: &mut StackFrame, build_id_type: CxxBuildIdType);
fn file_type(self: &mut StackFrame, file_type: CxxFileType);
fn with_ip(self: &mut StackFrame, ip: usize);
fn with_module_base_address(self: &mut StackFrame, addr: usize);
fn with_sp(self: &mut StackFrame, sp: usize);
fn with_symbol_address(self: &mut StackFrame, addr: usize);
fn with_build_id(self: &mut StackFrame, build_id: String);
fn with_path(self: &mut StackFrame, path: String);
fn with_relative_address(self: &mut StackFrame, addr: usize);
fn with_function(self: &mut StackFrame, function: String);
fn with_file(self: &mut StackFrame, file: String);
fn with_line(self: &mut StackFrame, line: u32);
fn with_column(self: &mut StackFrame, column: u32);
fn add_frame(self: &mut StackTrace, frame: Box<StackFrame>, incomplete: bool)
-> Result<()>;
#[cxx_name = "mark_complete"]
fn set_complete(self: &mut StackTrace) -> Result<()>;
fn to_json(self: &CrashInfo) -> Result<String>;
}
}
impl CrashInfoBuilder {
pub fn create() -> Box<CrashInfoBuilder> {
Box::new(CrashInfoBuilder::default())
}
}
impl StackFrame {
pub fn create() -> Box<StackFrame> {
Box::new(StackFrame::new())
}
}
impl StackTrace {
pub fn create() -> Box<StackTrace> {
Box::new(StackTrace::new_incomplete())
}
}
impl CrashInfoBuilder {
pub fn set_kind(&mut self, kind: ffi::CxxErrorKind) -> anyhow::Result<()> {
let internal_kind = match kind {
ffi::CxxErrorKind::Panic => crate::ErrorKind::Panic,
ffi::CxxErrorKind::UnhandledException => crate::ErrorKind::UnhandledException,
ffi::CxxErrorKind::UnixSignal => crate::ErrorKind::UnixSignal,
_ => anyhow::bail!("Unknown error kind"),
};
self.with_kind(internal_kind)
}
pub fn set_metadata(&mut self, metadata: ffi::Metadata) -> anyhow::Result<()> {
let internal_metadata = Metadata::new(
metadata.library_name,
metadata.library_version,
metadata.family,
metadata.tags,
);
self.with_metadata(internal_metadata)
}
pub fn set_proc_info(&mut self, proc_info: ffi::ProcInfo) -> anyhow::Result<()> {
let tid = if proc_info.tid == 0 {
None
} else {
Some(proc_info.tid)
};
let internal_proc_info = crate::ProcInfo {
pid: proc_info.pid,
tid,
};
self.with_proc_info(internal_proc_info)
}
pub fn set_os_info(&mut self, os_info: ffi::OsInfo) -> anyhow::Result<()> {
let internal_os_info = crate::OsInfo {
architecture: os_info.architecture,
bitness: os_info.bitness,
os_type: os_info.os_type,
version: os_info.version,
};
self.with_os_info(internal_os_info)
}
#[allow(clippy::boxed_local)]
pub fn add_stack(&mut self, stack: Box<StackTrace>) -> anyhow::Result<()> {
self.with_stack(*stack)
}
#[allow(clippy::boxed_local)]
pub fn add_stack_frame(
&mut self,
frame: Box<StackFrame>,
incomplete: bool,
) -> anyhow::Result<()> {
self.with_stack_frame(*frame, incomplete)
}
}
pub fn crashinfo_build(builder: Box<CrashInfoBuilder>) -> anyhow::Result<Box<CrashInfo>> {
Ok(Box::new(builder.build()?))
}
impl StackFrame {
pub fn build_id_type(&mut self, build_id_type: ffi::CxxBuildIdType) {
let internal_type = match build_id_type {
ffi::CxxBuildIdType::GNU => BuildIdType::GNU,
ffi::CxxBuildIdType::GO => BuildIdType::GO,
ffi::CxxBuildIdType::PDB => BuildIdType::PDB,
ffi::CxxBuildIdType::SHA1 => BuildIdType::SHA1,
_ => return,
};
self.set_build_id_type(internal_type);
}
pub fn file_type(&mut self, file_type: ffi::CxxFileType) {
let internal_type = match file_type {
ffi::CxxFileType::APK => FileType::APK,
ffi::CxxFileType::ELF => FileType::ELF,
ffi::CxxFileType::PE => FileType::PE,
_ => return,
};
self.set_file_type(internal_type);
}
}
impl StackTrace {
#[allow(clippy::boxed_local)]
pub fn add_frame(&mut self, frame: Box<StackFrame>, incomplete: bool) -> anyhow::Result<()> {
StackTrace::push_frame(self, *frame, incomplete)
}
}
impl CrashInfo {
pub fn to_json(&self) -> anyhow::Result<String> {
Ok(serde_json::to_string_pretty(self)?)
}
}