Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions bin_tests/src/bin/crashtracker_bin_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod unix {
use libdd_common::{tag, Endpoint};
use libdd_crashtracker::{
self as crashtracker, CrashtrackerConfiguration, CrashtrackerReceiverConfig, Metadata,
StackFrame, StackTrace,
};

const TEST_COLLECTOR_TIMEOUT: Duration = Duration::from_secs(15);
Expand Down Expand Up @@ -154,6 +155,31 @@ mod unix {
"raise_sigill" => raise(Signal::SIGILL)?,
"raise_sigbus" => raise(Signal::SIGBUS)?,
"raise_sigsegv" => raise(Signal::SIGSEGV)?,
"unhandled_exception" => {
let mut stacktrace = StackTrace::new_incomplete();
let mut stackframe1 = StackFrame::new();
stackframe1.with_ip(1234);
stackframe1.with_function("test_function1".to_string());
stackframe1.with_file("test_file1".to_string());

let mut stackframe2 = StackFrame::new();
stackframe2.with_ip(5678);
stackframe2.with_function("test_function2".to_string());
stackframe2.with_file("test_file2".to_string());

stacktrace.push_frame(stackframe1, true).unwrap();
stacktrace.push_frame(stackframe2, true).unwrap();

stacktrace.set_complete().unwrap();

crashtracker::report_unhandled_exception(
Some("RuntimeException"),
Some("an exception occured"),
stacktrace,
)?;

process::exit(0);
}
_ => anyhow::bail!("Unexpected crash_typ: {crash_typ}"),
}
crashtracker::end_op(crashtracker::OpTypes::ProfilerCollectingSample)?;
Expand Down
13 changes: 12 additions & 1 deletion bin_tests/src/test_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ pub enum CrashType {
RaiseSigBus,
/// Raise SIGSEGV
RaiseSigSegv,
/// Unhandled Exception
UnhandledException,
}

impl CrashType {
Expand All @@ -129,6 +131,7 @@ impl CrashType {
Self::RaiseSigIll => "raise_sigill",
Self::RaiseSigBus => "raise_sigbus",
Self::RaiseSigSegv => "raise_sigsegv",
Self::UnhandledException => "unhandled_exception",
}
}

Expand All @@ -138,7 +141,11 @@ impl CrashType {
pub const fn expects_success(self) -> bool {
matches!(
self,
Self::KillSigBus | Self::KillSigSegv | Self::RaiseSigBus | Self::RaiseSigSegv
Self::KillSigBus
| Self::KillSigSegv
| Self::RaiseSigBus
| Self::RaiseSigSegv
| Self::UnhandledException
)
}

Expand All @@ -150,6 +157,7 @@ impl CrashType {
Self::KillSigAbrt | Self::RaiseSigAbrt => 6, // SIGABRT
Self::KillSigIll | Self::RaiseSigIll => 4, // SIGILL
Self::KillSigBus | Self::RaiseSigBus => 7, // SIGBUS
Self::UnhandledException => 0, // no signal
}
}

Expand All @@ -160,6 +168,7 @@ impl CrashType {
Self::KillSigAbrt | Self::RaiseSigAbrt => "SIGABRT",
Self::KillSigIll | Self::RaiseSigIll => "SIGILL",
Self::KillSigBus | Self::RaiseSigBus => "SIGBUS",
Self::UnhandledException => "Unhandled Exception",
}
}
}
Expand All @@ -184,6 +193,7 @@ impl std::str::FromStr for CrashType {
"raise_sigill" => Ok(Self::RaiseSigIll),
"raise_sigbus" => Ok(Self::RaiseSigBus),
"raise_sigsegv" => Ok(Self::RaiseSigSegv),
"unhandled_exception" => Ok(Self::UnhandledException),
_ => Err(format!("Unknown crash type: {}", s)),
}
}
Expand Down Expand Up @@ -220,5 +230,6 @@ mod tests {
assert!(!CrashType::KillSigAbrt.expects_success());
assert!(CrashType::KillSigBus.expects_success());
assert!(CrashType::KillSigSegv.expects_success());
assert!(CrashType::UnhandledException.expects_success());
}
}
56 changes: 47 additions & 9 deletions bin_tests/tests/crashtracker_bin_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,42 @@ fn run_standard_crash_test_refactored(
// These tests below use the new infrastructure but require custom validation logic
// that doesn't fit the simple macro-generated pattern.

#[test]
#[cfg_attr(miri, ignore)]
fn test_crash_tracking_bin_unhandled_exception() {
let config = CrashTestConfig::new(
BuildProfile::Release,
TestMode::DoNothing,
CrashType::UnhandledException,
);
let artifacts = StandardArtifacts::new(config.profile);
let artifacts_map = build_artifacts(&artifacts.as_slice()).unwrap();

let validator: ValidatorFn = Box::new(|payload, fixtures| {
PayloadValidator::new(payload)
.validate_counters()?
.validate_error_kind("UnhandledException")?
.validate_error_message_contains("Process was terminated due to an unhandled exception of type 'RuntimeException'. Message: an exception occured")?
// The two frames emitted in the bin: test_function1 and test_function2
.validate_callstack_functions(&["test_function1", "test_function2"])?;

// Unhandled exceptions have no signal info
let sig_info = &payload["sig_info"];
assert!(
sig_info.is_null()
|| sig_info.is_object() && sig_info.as_object().is_none_or(|m| m.is_empty()),
"Expected no sig_info for unhandled exception, got: {sig_info:?}"
);

// Validate rest of telemetry
validate_telemetry(&fixtures.crash_telemetry_path, "unhandled_exception")?;

Ok(())
});

run_crash_test_with_artifacts(&config, &artifacts_map, &artifacts, validator).unwrap();
}

#[test]
#[cfg_attr(miri, ignore)]
fn test_crash_tracking_bin_runtime_callback_frame() {
Expand Down Expand Up @@ -1027,6 +1063,12 @@ fn assert_siginfo_message(sig_info: &Value, crash_typ: &str) {
assert_eq!(sig_info["si_signo"], libc::SIGILL);
assert_eq!(sig_info["si_signo_human_readable"], "SIGILL");
}
"unhandled_exception" => {
assert!(
sig_info.is_null()
|| sig_info.is_object() && sig_info.as_object().is_none_or(|m| m.is_empty())
);
}
_ => panic!("unexpected crash_typ {crash_typ}"),
}
}
Expand Down Expand Up @@ -1101,9 +1143,10 @@ fn assert_telemetry_message(crash_telemetry: &[u8], crash_typ: &str) {
"profiler_unwinding:0".to_string(),
]);

assert!(base_expected_tags.is_subset(&tags), "{tags:?}");

match crash_typ {
"null_deref" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_addr:0x0000000000000000"), "{tags:?}");
assert!(
tags.contains("si_code_human_readable:SEGV_ACCERR")
Expand All @@ -1118,17 +1161,14 @@ fn assert_telemetry_message(crash_telemetry: &[u8], crash_typ: &str) {
);
}
"kill_sigabrt" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_signo_human_readable:SIGABRT"), "{tags:?}");
assert!(tags.contains("si_signo:6"), "{tags:?}");
}
"kill_sigill" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_signo_human_readable:SIGILL"), "{tags:?}");
assert!(tags.contains("si_signo:4"), "{tags:?}");
}
"kill_sigbus" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_signo_human_readable:SIGBUS"), "{tags:?}");
// SIGBUS can be 7 or 10, depending on the os.
assert!(
Expand All @@ -1137,22 +1177,18 @@ fn assert_telemetry_message(crash_telemetry: &[u8], crash_typ: &str) {
);
}
"kill_sigsegv" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_signo_human_readable:SIGSEGV"), "{tags:?}");
assert!(tags.contains("si_signo:11"), "{tags:?}");
}
"raise_sigabrt" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_signo_human_readable:SIGABRT"), "{tags:?}");
assert!(tags.contains("si_signo:6"), "{tags:?}");
}
"raise_sigill" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_signo_human_readable:SIGILL"), "{tags:?}");
assert!(tags.contains("si_signo:4"), "{tags:?}");
}
"raise_sigbus" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_signo_human_readable:SIGBUS"), "{tags:?}");
// SIGBUS can be 7 or 10, depending on the os.
assert!(
Expand All @@ -1161,10 +1197,12 @@ fn assert_telemetry_message(crash_telemetry: &[u8], crash_typ: &str) {
);
}
"raise_sigsegv" => {
assert!(base_expected_tags.is_subset(&tags), "{tags:?}");
assert!(tags.contains("si_signo_human_readable:SIGSEGV"), "{tags:?}");
assert!(tags.contains("si_signo:11"), "{tags:?}");
}
"unhandled_exception" => {
// Unhandled exceptions have no signal info tags
}
_ => panic!("{crash_typ}"),
}

Expand Down
3 changes: 3 additions & 0 deletions examples/ffi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ set_vcruntime_link_type(telemetry_metrics ${VCRUNTIME_LINK_TYPE})
if(NOT WIN32)
add_executable(crashtracking crashtracking.c)
target_link_libraries(crashtracking PRIVATE Datadog::Profiling)

add_executable(crashtracking_unhandled_exception crashtracking_unhandled_exception.c)
target_link_libraries(crashtracking_unhandled_exception PRIVATE Datadog::Profiling)
endif()

add_executable(trace_exporter trace_exporter.c)
Expand Down
Loading
Loading