1#![cfg(unix)]
4
5use super::{crash_handler::enable, receiver_manager::Receiver};
6use crate::{
7 clear_spans, clear_traces, collector::signal_handler_manager::register_crash_handlers,
8 crash_info::Metadata, reset_counters, shared::configuration::CrashtrackerReceiverConfig,
9 update_config, update_metadata, CrashtrackerConfiguration,
10};
11
12pub static DEFAULT_SYMBOLS: [libc::c_int; 4] =
13 [libc::SIGBUS, libc::SIGABRT, libc::SIGSEGV, libc::SIGILL];
14
15pub fn default_signals() -> Vec<libc::c_int> {
16 Vec::from(DEFAULT_SYMBOLS)
17}
18
19pub fn on_fork(
34 config: CrashtrackerConfiguration,
35 receiver_config: CrashtrackerReceiverConfig,
36 metadata: Metadata,
37) -> anyhow::Result<()> {
38 clear_spans()?;
39 clear_traces()?;
40 reset_counters()?;
41 update_metadata(metadata)?;
47 update_config(config)?;
48 Receiver::update_stored_config(receiver_config)?;
49 Ok(())
50}
51
52pub fn init(
63 config: CrashtrackerConfiguration,
64 receiver_config: CrashtrackerReceiverConfig,
65 metadata: Metadata,
66) -> anyhow::Result<()> {
67 update_metadata(metadata)?;
68 update_config(config.clone())?;
69 Receiver::update_stored_config(receiver_config)?;
70 register_crash_handlers(&config)?;
71 enable();
72 Ok(())
73}
74
75pub fn reconfigure(
86 config: CrashtrackerConfiguration,
87 receiver_config: CrashtrackerReceiverConfig,
88 metadata: Metadata,
89) -> anyhow::Result<()> {
90 update_metadata(metadata)?;
91 update_config(config.clone())?;
92 Receiver::update_stored_config(receiver_config)?;
93 enable();
94 Ok(())
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::{begin_op, insert_span, insert_trace, StacktraceCollection};
101 use chrono::Utc;
102 use libdd_common::tag;
103 use libdd_common::Endpoint;
104 use std::time::Duration;
105 #[ignore]
112 #[test]
113 fn test_crash() {
114 let time = Utc::now().to_rfc3339();
115 let dir = "/tmp/crashreports/";
116 let output_url = format!("file://{dir}{time}.txt");
117
118 let endpoint = Some(Endpoint::from_slice(&output_url));
119
120 let path_to_receiver_binary =
121 "/tmp/libdatadog/bin/libdatadog-crashtracking-receiver".to_string();
122 let create_alt_stack = true;
123 let use_alt_stack = true;
124 let resolve_frames = StacktraceCollection::EnabledWithInprocessSymbols;
125 let stderr_filename = Some(format!("{dir}/stderr_{time}.txt"));
126 let stdout_filename = Some(format!("{dir}/stdout_{time}.txt"));
127 let timeout = Duration::from_secs(10);
128 let receiver_config = CrashtrackerReceiverConfig::new(
129 vec![],
130 vec![],
131 path_to_receiver_binary,
132 stderr_filename,
133 stdout_filename,
134 )
135 .unwrap();
136 let config = CrashtrackerConfiguration::new(
137 vec![],
138 create_alt_stack,
139 use_alt_stack,
140 endpoint,
141 resolve_frames,
142 default_signals(),
143 Some(timeout),
144 None,
145 true,
146 )
147 .unwrap();
148 let metadata = Metadata::new(
149 "libname".to_string(),
150 "version".to_string(),
151 "family".to_string(),
152 vec![],
153 );
154 init(config, receiver_config, metadata).unwrap();
155 begin_op(crate::OpTypes::ProfilerCollectingSample).unwrap();
156 insert_span(42).unwrap();
157 insert_trace(u128::MAX).unwrap();
158 insert_span(12).unwrap();
159 insert_trace(99399939399939393993).unwrap();
160
161 let tag = tag!("apple", "banana");
162 let metadata2 = Metadata::new(
163 "libname".to_string(),
164 "version".to_string(),
165 "family".to_string(),
166 vec![tag.to_string()],
167 );
168 update_metadata(metadata2).expect("metadata");
169
170 std::thread::sleep(Duration::from_secs(2));
171
172 let p: *const u32 = std::ptr::null();
173 let q = unsafe { *p };
174 assert_eq!(q, 3);
175 }
176
177 #[test]
178 fn test_altstack_paradox() {
179 let time = Utc::now().to_rfc3339();
180 let dir = "/tmp/crashreports/";
181 let output_url = format!("file://{dir}{time}.txt");
182
183 let endpoint = Some(Endpoint::from_slice(&output_url));
184
185 let create_alt_stack = true;
186 let use_alt_stack = false;
187 let resolve_frames = StacktraceCollection::EnabledWithInprocessSymbols;
188 let timeout = Duration::from_secs(10);
189
190 let config = CrashtrackerConfiguration::new(
192 vec![],
193 create_alt_stack,
194 use_alt_stack,
195 endpoint,
196 resolve_frames,
197 default_signals(),
198 Some(timeout),
199 None,
200 true,
201 );
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: std::ptr::null_mut(),
216 ss_flags: 0,
217 ss_size: 0,
218 };
219 let res = unsafe { libc::sigaltstack(std::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 endpoint = Some(Endpoint::from_slice(&output_url));
240
241 let path_to_receiver_binary =
242 "/tmp/libdatadog/bin/libdatadog-crashtracking-receiver".to_string();
243 let create_alt_stack = true;
244 let use_alt_stack = true;
245 let resolve_frames = StacktraceCollection::EnabledWithInprocessSymbols;
246 let stderr_filename = Some(format!("{dir}/stderr_{time}.txt"));
247 let stdout_filename = Some(format!("{dir}/stdout_{time}.txt"));
248 let signals = default_signals();
249 let timeout = Duration::from_secs(10);
250 let receiver_config = CrashtrackerReceiverConfig::new(
251 vec![],
252 vec![],
253 path_to_receiver_binary,
254 stderr_filename,
255 stdout_filename,
256 )
257 .unwrap();
258 let config = CrashtrackerConfiguration::new(
259 vec![],
260 create_alt_stack,
261 use_alt_stack,
262 endpoint,
263 resolve_frames,
264 signals,
265 Some(timeout),
266 None,
267 true,
268 )
269 .unwrap();
270 let metadata = Metadata::new(
271 "libname".to_string(),
272 "version".to_string(),
273 "family".to_string(),
274 vec![],
275 );
276
277 match unsafe { libc::fork() } {
279 -1 => {
280 panic!("Failed to fork");
281 }
282 0 => {
283 let initial_sigaltstack = get_sigaltstack();
286 assert!(
287 initial_sigaltstack.is_some(),
288 "Failed to get initial sigaltstack"
289 );
290
291 init(config, receiver_config, metadata).unwrap();
295
296 let after_init_sigaltstack = get_sigaltstack();
298
299 if initial_sigaltstack == after_init_sigaltstack {
301 eprintln!("Initial sigaltstack: {initial_sigaltstack:?}");
302 std::process::exit(-5);
303 }
304
305 let mut sigaction = libc::sigaction {
307 sa_sigaction: 0,
308 sa_mask: unsafe { std::mem::zeroed::<libc::sigset_t>() },
309 sa_flags: 0,
310 sa_restorer: None,
311 };
312
313 let mut exit_code = -5;
314
315 for signal in default_signals() {
316 let signame = crate::signal_from_signum(signal).unwrap();
317 exit_code -= 1;
318 let res = unsafe { libc::sigaction(signal, std::ptr::null(), &mut sigaction) };
319 if res != 0 {
320 eprintln!("Failed to get {signame:?} handler");
321 std::process::exit(exit_code);
322 }
323
324 exit_code -= 1;
325 if sigaction.sa_flags & libc::SA_ONSTACK != libc::SA_ONSTACK {
326 eprintln!("Expected {signame:?} handler to have SA_ONSTACK");
327 std::process::exit(exit_code);
328 }
329 }
330
331 std::process::exit(42);
333 }
334 pid => {
335 let mut status = 0;
337 let _ = unsafe { libc::waitpid(pid, &mut status, 0) };
338
339 if libc::WIFEXITED(status) {
341 let exit_code = libc::WEXITSTATUS(status);
342 assert_eq!(exit_code, 42, "Child process exited with unexpected status");
343 } else {
344 panic!("Child process did not exit normally");
345 }
346 }
347 }
348 }
349
350 #[cfg_attr(miri, ignore)]
351 #[cfg(target_os = "linux")]
352 #[test]
353 fn test_altstack_use_nocreate() {
354 let time = Utc::now().to_rfc3339();
358 let dir = "/tmp/crashreports/";
359 let output_url = format!("file://{dir}{time}.txt");
360
361 let endpoint = Some(Endpoint::from_slice(&output_url));
362
363 let path_to_receiver_binary =
364 "/tmp/libdatadog/bin/libdatadog-crashtracking-receiver".to_string();
365 let create_alt_stack = false; let use_alt_stack = true;
367 let resolve_frames = StacktraceCollection::EnabledWithInprocessSymbols;
368 let stderr_filename = Some(format!("{dir}/stderr_{time}.txt"));
369 let stdout_filename = Some(format!("{dir}/stdout_{time}.txt"));
370 let signals = default_signals();
371 let timeout = Duration::from_secs(10);
372 let receiver_config = CrashtrackerReceiverConfig::new(
373 vec![],
374 vec![],
375 path_to_receiver_binary,
376 stderr_filename,
377 stdout_filename,
378 )
379 .unwrap();
380 let config = CrashtrackerConfiguration::new(
381 vec![],
382 create_alt_stack,
383 use_alt_stack,
384 endpoint,
385 resolve_frames,
386 signals,
387 Some(timeout),
388 None,
389 true,
390 )
391 .unwrap();
392 let metadata = Metadata::new(
393 "libname".to_string(),
394 "version".to_string(),
395 "family".to_string(),
396 vec![],
397 );
398
399 match unsafe { libc::fork() } {
401 -1 => {
402 panic!("Failed to fork");
403 }
404 0 => {
405 let initial_sigaltstack = get_sigaltstack();
408 assert!(
409 initial_sigaltstack.is_some(),
410 "Failed to get initial sigaltstack"
411 );
412
413 init(config, receiver_config, metadata).unwrap();
417
418 let after_init_sigaltstack = get_sigaltstack();
420
421 if initial_sigaltstack != after_init_sigaltstack {
423 eprintln!("Initial sigaltstack: {initial_sigaltstack:?}");
424 std::process::exit(-5);
425 }
426
427 let mut sigaction = libc::sigaction {
430 sa_sigaction: 0,
431 sa_mask: unsafe { std::mem::zeroed::<libc::sigset_t>() },
432 sa_flags: 0,
433 sa_restorer: None,
434 };
435
436 let res =
438 unsafe { libc::sigaction(libc::SIGBUS, std::ptr::null(), &mut sigaction) };
439 if res != 0 {
440 eprintln!("Failed to get SIGBUS handler");
441 std::process::exit(-6);
442 }
443 if sigaction.sa_flags & libc::SA_ONSTACK != libc::SA_ONSTACK {
444 eprintln!("Expected SIGBUS handler to have SA_ONSTACK");
445 std::process::exit(-7);
446 }
447
448 let res =
450 unsafe { libc::sigaction(libc::SIGSEGV, std::ptr::null(), &mut sigaction) };
451 if res != 0 {
452 eprintln!("Failed to get SIGSEGV handler");
453 std::process::exit(-8);
454 }
455 if sigaction.sa_flags & libc::SA_ONSTACK != libc::SA_ONSTACK {
456 eprintln!("Expected SIGSEGV handler to have SA_ONSTACK");
457 std::process::exit(-9);
458 }
459
460 std::process::exit(42);
462 }
463 pid => {
464 let mut status = 0;
466 let _ = unsafe { libc::waitpid(pid, &mut status, 0) };
467
468 if libc::WIFEXITED(status) {
470 let exit_code = libc::WEXITSTATUS(status);
471 assert_eq!(exit_code, 42, "Child process exited with unexpected status");
472 } else {
473 panic!("Child process did not exit normally");
474 }
475 }
476 }
477 }
478
479 #[cfg_attr(miri, ignore)]
480 #[cfg(target_os = "linux")]
481 #[test]
482 fn test_altstack_nouse() {
483 let time = Utc::now().to_rfc3339();
486 let dir = "/tmp/crashreports/";
487 let output_url = format!("file://{dir}{time}.txt");
488
489 let endpoint = Some(Endpoint::from_slice(&output_url));
490
491 let path_to_receiver_binary =
492 "/tmp/libdatadog/bin/libdatadog-crashtracking-receiver".to_string();
493 let create_alt_stack = false;
494 let use_alt_stack = false;
495 let resolve_frames = StacktraceCollection::EnabledWithInprocessSymbols;
496 let stderr_filename = Some(format!("{dir}/stderr_{time}.txt"));
497 let stdout_filename = Some(format!("{dir}/stdout_{time}.txt"));
498 let signals = default_signals();
499 let timeout = Duration::from_secs(10);
500 let receiver_config = CrashtrackerReceiverConfig::new(
501 vec![],
502 vec![],
503 path_to_receiver_binary,
504 stderr_filename,
505 stdout_filename,
506 )
507 .unwrap();
508 let config = CrashtrackerConfiguration::new(
509 vec![],
510 create_alt_stack,
511 use_alt_stack,
512 endpoint,
513 resolve_frames,
514 signals,
515 Some(timeout),
516 None,
517 true,
518 )
519 .unwrap();
520 let metadata = Metadata::new(
521 "libname".to_string(),
522 "version".to_string(),
523 "family".to_string(),
524 vec![],
525 );
526
527 match unsafe { libc::fork() } {
529 -1 => {
530 panic!("Failed to fork");
531 }
532 0 => {
533 let initial_sigaltstack = get_sigaltstack();
536 assert!(
537 initial_sigaltstack.is_some(),
538 "Failed to get initial sigaltstack"
539 );
540
541 init(config, receiver_config, metadata).unwrap();
545
546 let after_init_sigaltstack = get_sigaltstack();
548
549 if initial_sigaltstack != after_init_sigaltstack {
553 eprintln!("Initial sigaltstack: {initial_sigaltstack:?}");
554 std::process::exit(-5);
555 }
556
557 let mut sigaction = libc::sigaction {
559 sa_sigaction: 0,
560 sa_mask: unsafe { std::mem::zeroed::<libc::sigset_t>() },
561 sa_flags: 0,
562 sa_restorer: None,
563 };
564
565 let res =
567 unsafe { libc::sigaction(libc::SIGBUS, std::ptr::null(), &mut sigaction) };
568 if res != 0 {
569 eprintln!("Failed to get SIGBUS handler");
570 std::process::exit(-6);
571 }
572 if sigaction.sa_flags & libc::SA_ONSTACK == libc::SA_ONSTACK {
573 eprintln!("Expected SIGBUS handler not to have SA_ONSTACK");
574 std::process::exit(-7);
575 }
576
577 let res =
579 unsafe { libc::sigaction(libc::SIGSEGV, std::ptr::null(), &mut sigaction) };
580 if res != 0 {
581 eprintln!("Failed to get SIGSEGV handler");
582 std::process::exit(-8);
583 }
584 if sigaction.sa_flags & libc::SA_ONSTACK == libc::SA_ONSTACK {
585 eprintln!("Expected SIGSEGV handler not to have SA_ONSTACK");
586 std::process::exit(-9);
587 }
588
589 std::process::exit(42);
591 }
592 pid => {
593 let mut status = 0;
595 let _ = unsafe { libc::waitpid(pid, &mut status, 0) };
596
597 if libc::WIFEXITED(status) {
599 let exit_code = libc::WEXITSTATUS(status);
600 assert_eq!(exit_code, 42, "Child process exited with unexpected status");
601 } else {
602 panic!("Child process did not exit normally");
603 }
604 }
605 }
606 }
607
608 #[cfg_attr(miri, ignore)]
609 #[cfg(target_os = "linux")]
610 #[test]
611 fn test_waitall_nohang() {
612 let time = Utc::now().to_rfc3339();
649 let dir = "/tmp/crashreports/";
650 let output_url = format!("file://{dir}{time}.txt");
651
652 let endpoint = Some(Endpoint::from_slice(&output_url));
653
654 let path_to_receiver_binary =
655 "/tmp/libdatadog/bin/libdatadog-crashtracking-receiver".to_string();
656 let create_alt_stack = true; let use_alt_stack = true;
658 let resolve_frames = StacktraceCollection::EnabledWithInprocessSymbols;
659 let stderr_filename = Some(format!("{dir}/stderr_{time}.txt"));
660 let stdout_filename = Some(format!("{dir}/stdout_{time}.txt"));
661 let signals = default_signals();
662 let timeout = Duration::from_secs(10);
663 let receiver_config = CrashtrackerReceiverConfig::new(
664 vec![],
665 vec![],
666 path_to_receiver_binary,
667 stderr_filename,
668 stdout_filename,
669 )
670 .unwrap();
671 let config = CrashtrackerConfiguration::new(
672 vec![],
673 create_alt_stack,
674 use_alt_stack,
675 endpoint,
676 resolve_frames,
677 signals,
678 Some(timeout),
679 None,
680 true,
681 )
682 .unwrap();
683
684 let metadata = Metadata::new(
685 "libname".to_string(),
686 "version".to_string(),
687 "family".to_string(),
688 vec![],
689 );
690
691 match unsafe { libc::fork() } {
694 -1 => {
695 panic!("Failed to fork");
696 }
697 0 => {
698 init(config, receiver_config, metadata).unwrap();
701
702 let mut children = vec![];
707 let sleep_duration = Duration::from_millis(100);
708 let timeout_duration = Duration::from_millis(150);
709 for _ in 0..10 {
710 match unsafe { libc::fork() } {
711 -1 => {
712 panic!("Failed to fork");
713 }
714 0 => {
715 std::thread::sleep(sleep_duration);
717 std::process::exit(0); }
719 pid => {
720 children.push(pid); }
723 }
724 }
725
726 let start_time = std::time::Instant::now();
730 loop {
731 if start_time.elapsed() > timeout_duration {
732 eprintln!("Timed out waiting for children to exit");
733 std::process::exit(-6);
734 }
735
736 let mut status = 0;
738 let pid = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG) };
739 let errno = std::io::Error::last_os_error().raw_os_error().unwrap();
740
741 if pid == -1 && errno == libc::ECHILD {
742 std::process::exit(42);
744 }
745 }
746 }
747 pid => {
748 let mut status = 0;
750 let _ = unsafe { libc::waitpid(pid, &mut status, 0) };
751
752 if libc::WIFEXITED(status) {
754 let exit_code = libc::WEXITSTATUS(status);
755 assert_eq!(exit_code, 42, "Child process exited with unexpected status");
756 } else {
757 panic!("Child process did not exit normally");
758 }
759 }
760 }
761 }
762}