libdd_crashtracker/collector/
api.rs1#![cfg(unix)]
4
5use super::{crash_handler::enable, receiver_manager::Receiver};
6use crate::{
7 clear_spans, clear_traces, collector::crash_handler::register_panic_hook,
8 collector::signal_handler_manager::register_crash_handlers, crash_info::Metadata,
9 reset_counters, shared::configuration::CrashtrackerReceiverConfig, update_config,
10 update_metadata, CrashtrackerConfiguration,
11};
12
13pub static DEFAULT_SYMBOLS: [libc::c_int; 4] =
14 [libc::SIGBUS, libc::SIGABRT, libc::SIGSEGV, libc::SIGILL];
15
16pub fn default_signals() -> Vec<libc::c_int> {
17 Vec::from(DEFAULT_SYMBOLS)
18}
19
20#[cfg(target_os = "linux")]
21pub(super) fn mark_preload_logger_collector() {
22 const SYMBOL: &[u8] = b"dd_preload_logger_mark_collector\0";
25 unsafe {
26 let sym = libc::dlsym(libc::RTLD_DEFAULT, SYMBOL.as_ptr() as *const _);
27 if !sym.is_null() {
28 let func: extern "C" fn() = core::mem::transmute(sym);
29 func();
30 }
31 }
32}
33
34pub fn on_fork(
49 config: CrashtrackerConfiguration,
50 receiver_config: CrashtrackerReceiverConfig,
51 metadata: Metadata,
52) -> anyhow::Result<()> {
53 clear_spans()?;
54 clear_traces()?;
55 reset_counters()?;
56 update_metadata(metadata)?;
64 update_config(config)?;
65 Receiver::update_stored_config(receiver_config)?;
66 Ok(())
67}
68
69pub fn init(
80 config: CrashtrackerConfiguration,
81 receiver_config: CrashtrackerReceiverConfig,
82 metadata: Metadata,
83) -> anyhow::Result<()> {
84 update_metadata(metadata)?;
85 update_config(config.clone())?;
86 Receiver::update_stored_config(receiver_config)?;
87 register_crash_handlers(&config)?;
88 register_panic_hook()?;
89 #[cfg(all(target_os = "linux", target_pointer_width = "64"))]
90 super::assert_interceptor::install_assert_hook();
91 enable();
92 Ok(())
93}
94
95pub fn reconfigure(
106 config: CrashtrackerConfiguration,
107 receiver_config: CrashtrackerReceiverConfig,
108 metadata: Metadata,
109) -> anyhow::Result<()> {
110 update_metadata(metadata)?;
111 update_config(config.clone())?;
112 Receiver::update_stored_config(receiver_config)?;
113 enable();
114 Ok(())
115}
116
117#[cfg(test)]
118mod single_threaded_tests {
119 use super::*;
120 use crate::{
121 begin_op, insert_span, insert_trace, CrashtrackerConfigurationBuilder, StacktraceCollection,
122 };
123 use chrono::Utc;
124 use core::time::Duration;
125 use libdd_common::tag;
126
127 const PATH_TO_RECEIVER: &str = "/tmp/libdatadog/bin/libdatadog-crashtracking-receiver";
128 #[ignore]
135 #[test]
136 fn test_crash() {
137 let time = Utc::now().to_rfc3339();
138 let dir = "/tmp/crashreports/";
139 let output_url = format!("file://{dir}{time}.txt");
140
141 let receiver_config = CrashtrackerReceiverConfig::new(
142 vec![],
143 vec![],
144 PATH_TO_RECEIVER.to_string(),
145 Some(format!("{dir}/stderr_{time}.txt")),
146 Some(format!("{dir}/stdout_{time}.txt")),
147 )
148 .unwrap();
149 let config = CrashtrackerConfigurationBuilder::default()
150 .create_alt_stack(true)
151 .demangle_names(true)
152 .endpoint_url(output_url.as_str())
153 .resolve_frames(StacktraceCollection::EnabledWithInprocessSymbols)
154 .signals(default_signals())
155 .timeout(Duration::from_secs(10))
156 .use_alt_stack(true)
157 .build()
158 .unwrap();
159 let metadata = Metadata::new(
160 "libname".to_string(),
161 "version".to_string(),
162 "family".to_string(),
163 vec![],
164 );
165 init(config, receiver_config, metadata).unwrap();
166 begin_op(crate::OpTypes::ProfilerCollectingSample).unwrap();
167 insert_span(42).unwrap();
168 insert_trace(u128::MAX).unwrap();
169 insert_span(12).unwrap();
170 insert_trace(99399939399939393993).unwrap();
171
172 let tag = tag!("apple", "banana");
173 let metadata2 = Metadata::new(
174 "libname".to_string(),
175 "version".to_string(),
176 "family".to_string(),
177 vec![tag.to_string()],
178 );
179 update_metadata(metadata2).expect("metadata");
180
181 std::thread::sleep(Duration::from_secs(2));
182
183 let p: *const u32 = core::ptr::null();
184 let q = unsafe { *p };
185 assert_eq!(q, 3);
186 }
187
188 #[test]
189 fn test_altstack_paradox() {
190 let time = Utc::now().to_rfc3339();
191 let dir = "/tmp/crashreports/";
192 let output_url = format!("file://{dir}{time}.txt");
193
194 let config = CrashtrackerConfigurationBuilder::default()
196 .create_alt_stack(true)
197 .demangle_names(true)
198 .endpoint_url(output_url.as_str())
199 .resolve_frames(StacktraceCollection::EnabledWithInprocessSymbols)
200 .timeout(Duration::from_secs(10))
201 .build();
202
203 let err = config.unwrap_err();
206 assert_eq!(
207 err.to_string(),
208 "Cannot create an altstack without using it"
209 );
210 }
211
212 #[cfg(target_os = "linux")]
213 fn get_sigaltstack() -> Option<libc::stack_t> {
214 let mut sigaltstack = libc::stack_t {
215 ss_sp: core::ptr::null_mut(),
216 ss_flags: 0,
217 ss_size: 0,
218 };
219 let res = unsafe { libc::sigaltstack(core::ptr::null(), &mut sigaltstack) };
220 if res == 0 {
221 Some(sigaltstack)
222 } else {
223 None
224 }
225 }
226
227 #[cfg_attr(miri, ignore)]
228 #[cfg(target_os = "linux")]
229 #[test]
230 fn test_altstack_use_create() {
231 let time = Utc::now().to_rfc3339();
236 let dir = "/tmp/crashreports/";
237 let output_url = format!("file://{dir}{time}.txt");
238
239 let receiver_config = CrashtrackerReceiverConfig::new(
240 vec![],
241 vec![],
242 PATH_TO_RECEIVER.to_string(),
243 Some(format!("{dir}/stderr_{time}.txt")),
244 Some(format!("{dir}/stdout_{time}.txt")),
245 )
246 .unwrap();
247 let config = CrashtrackerConfigurationBuilder::default()
248 .create_alt_stack(true)
249 .use_alt_stack(true)
250 .endpoint_url(output_url.as_str())
251 .resolve_frames(StacktraceCollection::EnabledWithInprocessSymbols)
252 .signals(default_signals())
253 .timeout(Duration::from_secs(10))
254 .demangle_names(true)
255 .build()
256 .unwrap();
257 let metadata = Metadata::new(
258 "libname".to_string(),
259 "version".to_string(),
260 "family".to_string(),
261 vec![],
262 );
263
264 match unsafe { libc::fork() } {
266 -1 => {
267 panic!("Failed to fork");
268 }
269 0 => {
270 let initial_sigaltstack = get_sigaltstack();
273 assert!(
274 initial_sigaltstack.is_some(),
275 "Failed to get initial sigaltstack"
276 );
277
278 init(config, receiver_config, metadata).unwrap();
282
283 let after_init_sigaltstack = get_sigaltstack();
285
286 if initial_sigaltstack == after_init_sigaltstack {
288 eprintln!("Initial sigaltstack: {initial_sigaltstack:?}");
289 std::process::exit(-5);
290 }
291
292 let mut sigaction = libc::sigaction {
294 sa_sigaction: 0,
295 sa_mask: unsafe { core::mem::zeroed::<libc::sigset_t>() },
296 sa_flags: 0,
297 sa_restorer: None,
298 };
299
300 let mut exit_code = -5;
301
302 for signal in default_signals() {
303 let signame = crate::signal_from_signum(signal).unwrap();
304 exit_code -= 1;
305 let res = unsafe { libc::sigaction(signal, core::ptr::null(), &mut sigaction) };
306 if res != 0 {
307 eprintln!("Failed to get {signame:?} handler");
308 std::process::exit(exit_code);
309 }
310
311 exit_code -= 1;
312 if sigaction.sa_flags & libc::SA_ONSTACK != libc::SA_ONSTACK {
313 eprintln!("Expected {signame:?} handler to have SA_ONSTACK");
314 std::process::exit(exit_code);
315 }
316 }
317
318 std::process::exit(42);
320 }
321 pid => {
322 let mut status = 0;
324 let _ = unsafe { libc::waitpid(pid, &mut status, 0) };
325
326 if libc::WIFEXITED(status) {
328 let exit_code = libc::WEXITSTATUS(status);
329 assert_eq!(exit_code, 42, "Child process exited with unexpected status");
330 } else {
331 panic!("Child process did not exit normally");
332 }
333 }
334 }
335 }
336
337 #[cfg_attr(miri, ignore)]
338 #[cfg(target_os = "linux")]
339 #[test]
340 fn test_altstack_use_nocreate() {
341 let time = Utc::now().to_rfc3339();
345 let dir = "/tmp/crashreports/";
346 let output_url = format!("file://{dir}{time}.txt");
347
348 let receiver_config = CrashtrackerReceiverConfig::new(
349 vec![],
350 vec![],
351 PATH_TO_RECEIVER.to_string(),
352 Some(format!("{dir}/stderr_{time}.txt")),
353 Some(format!("{dir}/stdout_{time}.txt")),
354 )
355 .unwrap();
356 let config = CrashtrackerConfigurationBuilder::default()
357 .use_alt_stack(true)
358 .endpoint_url(output_url.as_str())
359 .resolve_frames(StacktraceCollection::EnabledWithInprocessSymbols)
360 .signals(default_signals())
361 .timeout(Duration::from_secs(10))
362 .demangle_names(true)
363 .build()
364 .unwrap();
365 let metadata = Metadata::new(
366 "libname".to_string(),
367 "version".to_string(),
368 "family".to_string(),
369 vec![],
370 );
371
372 match unsafe { libc::fork() } {
374 -1 => {
375 panic!("Failed to fork");
376 }
377 0 => {
378 let initial_sigaltstack = get_sigaltstack();
381 assert!(
382 initial_sigaltstack.is_some(),
383 "Failed to get initial sigaltstack"
384 );
385
386 init(config, receiver_config, metadata).unwrap();
390
391 let after_init_sigaltstack = get_sigaltstack();
393
394 if initial_sigaltstack != after_init_sigaltstack {
396 eprintln!("Initial sigaltstack: {initial_sigaltstack:?}");
397 std::process::exit(-5);
398 }
399
400 let mut sigaction = libc::sigaction {
403 sa_sigaction: 0,
404 sa_mask: unsafe { core::mem::zeroed::<libc::sigset_t>() },
405 sa_flags: 0,
406 sa_restorer: None,
407 };
408
409 let res =
411 unsafe { libc::sigaction(libc::SIGBUS, core::ptr::null(), &mut sigaction) };
412 if res != 0 {
413 eprintln!("Failed to get SIGBUS handler");
414 std::process::exit(-6);
415 }
416 if sigaction.sa_flags & libc::SA_ONSTACK != libc::SA_ONSTACK {
417 eprintln!("Expected SIGBUS handler to have SA_ONSTACK");
418 std::process::exit(-7);
419 }
420
421 let res =
423 unsafe { libc::sigaction(libc::SIGSEGV, core::ptr::null(), &mut sigaction) };
424 if res != 0 {
425 eprintln!("Failed to get SIGSEGV handler");
426 std::process::exit(-8);
427 }
428 if sigaction.sa_flags & libc::SA_ONSTACK != libc::SA_ONSTACK {
429 eprintln!("Expected SIGSEGV handler to have SA_ONSTACK");
430 std::process::exit(-9);
431 }
432
433 std::process::exit(42);
435 }
436 pid => {
437 let mut status = 0;
439 let _ = unsafe { libc::waitpid(pid, &mut status, 0) };
440
441 if libc::WIFEXITED(status) {
443 let exit_code = libc::WEXITSTATUS(status);
444 assert_eq!(exit_code, 42, "Child process exited with unexpected status");
445 } else {
446 panic!("Child process did not exit normally");
447 }
448 }
449 }
450 }
451
452 #[cfg_attr(miri, ignore)]
453 #[cfg(target_os = "linux")]
454 #[test]
455 fn test_altstack_nouse() {
456 let time = Utc::now().to_rfc3339();
459 let dir = "/tmp/crashreports/";
460 let output_url = format!("file://{dir}{time}.txt");
461
462 let receiver_config = CrashtrackerReceiverConfig::new(
463 vec![],
464 vec![],
465 PATH_TO_RECEIVER.to_string(),
466 Some(format!("{dir}/stderr_{time}.txt")),
467 Some(format!("{dir}/stdout_{time}.txt")),
468 )
469 .unwrap();
470 let config = CrashtrackerConfigurationBuilder::default()
471 .demangle_names(true)
472 .endpoint_url(output_url.as_str())
473 .resolve_frames(StacktraceCollection::EnabledWithInprocessSymbols)
474 .signals(default_signals())
475 .timeout(Duration::from_secs(10))
476 .build()
477 .unwrap();
478 let metadata = Metadata::new(
479 "libname".to_string(),
480 "version".to_string(),
481 "family".to_string(),
482 vec![],
483 );
484
485 match unsafe { libc::fork() } {
487 -1 => {
488 panic!("Failed to fork");
489 }
490 0 => {
491 let initial_sigaltstack = get_sigaltstack();
494 assert!(
495 initial_sigaltstack.is_some(),
496 "Failed to get initial sigaltstack"
497 );
498
499 init(config, receiver_config, metadata).unwrap();
503
504 let after_init_sigaltstack = get_sigaltstack();
506
507 if initial_sigaltstack != after_init_sigaltstack {
511 eprintln!("Initial sigaltstack: {initial_sigaltstack:?}");
512 std::process::exit(-5);
513 }
514
515 let mut sigaction = libc::sigaction {
517 sa_sigaction: 0,
518 sa_mask: unsafe { core::mem::zeroed::<libc::sigset_t>() },
519 sa_flags: 0,
520 sa_restorer: None,
521 };
522
523 let res =
525 unsafe { libc::sigaction(libc::SIGBUS, core::ptr::null(), &mut sigaction) };
526 if res != 0 {
527 eprintln!("Failed to get SIGBUS handler");
528 std::process::exit(-6);
529 }
530 if sigaction.sa_flags & libc::SA_ONSTACK == libc::SA_ONSTACK {
531 eprintln!("Expected SIGBUS handler not to have SA_ONSTACK");
532 std::process::exit(-7);
533 }
534
535 let res =
537 unsafe { libc::sigaction(libc::SIGSEGV, core::ptr::null(), &mut sigaction) };
538 if res != 0 {
539 eprintln!("Failed to get SIGSEGV handler");
540 std::process::exit(-8);
541 }
542 if sigaction.sa_flags & libc::SA_ONSTACK == libc::SA_ONSTACK {
543 eprintln!("Expected SIGSEGV handler not to have SA_ONSTACK");
544 std::process::exit(-9);
545 }
546
547 std::process::exit(42);
549 }
550 pid => {
551 let mut status = 0;
553 let _ = unsafe { libc::waitpid(pid, &mut status, 0) };
554
555 if libc::WIFEXITED(status) {
557 let exit_code = libc::WEXITSTATUS(status);
558 assert_eq!(exit_code, 42, "Child process exited with unexpected status");
559 } else {
560 panic!("Child process did not exit normally");
561 }
562 }
563 }
564 }
565
566 #[cfg_attr(miri, ignore)]
567 #[cfg(target_os = "linux")]
568 #[test]
569 fn test_waitall_nohang() {
570 let time = Utc::now().to_rfc3339();
607 let dir = "/tmp/crashreports/";
608 let output_url = format!("file://{dir}{time}.txt");
609
610 let receiver_config = CrashtrackerReceiverConfig::new(
611 vec![],
612 vec![],
613 PATH_TO_RECEIVER.to_string(),
614 Some(format!("{dir}/stderr_{time}.txt")),
615 Some(format!("{dir}/stdout_{time}.txt")),
616 )
617 .unwrap();
618 let config = CrashtrackerConfigurationBuilder::default()
619 .create_alt_stack(true)
620 .demangle_names(true)
621 .endpoint_url(output_url.as_str())
622 .resolve_frames(StacktraceCollection::EnabledWithInprocessSymbols)
623 .signals(default_signals())
624 .timeout(Duration::from_secs(10))
625 .use_alt_stack(true)
626 .build()
627 .unwrap();
628
629 let metadata = Metadata::new(
630 "libname".to_string(),
631 "version".to_string(),
632 "family".to_string(),
633 vec![],
634 );
635
636 match unsafe { libc::fork() } {
639 -1 => {
640 panic!("Failed to fork");
641 }
642 0 => {
643 init(config, receiver_config, metadata).unwrap();
646
647 let mut children = vec![];
652 let sleep_duration = Duration::from_millis(100);
653 let timeout_duration = Duration::from_millis(500);
654 for _ in 0..10 {
655 match unsafe { libc::fork() } {
656 -1 => {
657 panic!("Failed to fork");
658 }
659 0 => {
660 std::thread::sleep(sleep_duration);
662 std::process::exit(0); }
664 pid => {
665 children.push(pid); }
668 }
669 }
670
671 let start_time = std::time::Instant::now();
675 loop {
676 if start_time.elapsed() > timeout_duration {
677 eprintln!("Timed out waiting for children to exit");
678 std::process::exit(-6);
679 }
680
681 let mut status = 0;
683 let pid = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG) };
684 let errno = std::io::Error::last_os_error().raw_os_error().unwrap();
685
686 if pid == -1 && errno == libc::ECHILD {
687 std::process::exit(42);
689 }
690 }
691 }
692 pid => {
693 let mut status = 0;
695 let _ = unsafe { libc::waitpid(pid, &mut status, 0) };
696
697 if libc::WIFEXITED(status) {
699 let exit_code = libc::WEXITSTATUS(status);
700 assert_eq!(exit_code, 42, "Child process exited with unexpected status");
701 } else {
702 panic!("Child process did not exit normally");
703 }
704 }
705 }
706 }
707}