@@ -147,6 +147,17 @@ fn test_crash_tracking_errors_intake_crash_ping() {
147147    test_crash_tracking_errors_intake_dual_upload ( BuildProfile :: Release ,  "donothing" ,  "null_deref" ) ; 
148148} 
149149
150+ #[ test]  
151+ #[ cfg_attr( miri,  ignore) ]  
152+ #[ cfg( unix) ]  
153+ fn  test_crash_tracking_errors_intake_uds_socket ( )  { 
154+     test_crash_tracking_bin_with_errors_intake_uds ( 
155+         BuildProfile :: Release , 
156+         "donothing" , 
157+         "null_deref" , 
158+     ) ; 
159+ } 
160+ 
150161// This test is disabled for now on x86_64 musl and macos 
151162// It seems that on aarch64 musl, libc has CFI which allows 
152163// unwinding passed the signal frame. 
@@ -864,6 +875,165 @@ fn test_crash_tracking_errors_intake_dual_upload(
864875    assert_telemetry_message ( & crash_telemetry,  crash_typ) ; 
865876} 
866877
878+ #[ cfg( unix) ]  
879+ fn  test_crash_tracking_bin_with_errors_intake_uds ( 
880+     crash_tracking_receiver_profile :  BuildProfile , 
881+     mode :  & str , 
882+     crash_typ :  & str , 
883+ )  { 
884+     let  ( crashtracker_bin,  crashtracker_receiver)  =
885+         setup_crashtracking_crates ( crash_tracking_receiver_profile) ; 
886+     let  fixtures = setup_test_fixtures ( & [ & crashtracker_receiver,  & crashtracker_bin] ) ; 
887+ 
888+     // Try to create the standard agent UDS socket for testing 
889+     let  socket_path = std:: path:: Path :: new ( "/var/run/datadog/apm.socket" ) ; 
890+ 
891+     // Create directory if it doesn't exist 
892+     if  let  Some ( parent)  = socket_path. parent ( )  { 
893+         if  std:: fs:: create_dir_all ( parent) . is_err ( )  { 
894+             // Skip test if we can't create the directory (permission issues) 
895+             eprintln ! ( "Skipping UDS test - cannot create /var/run/datadog directory" ) ; 
896+             return ; 
897+         } 
898+     } 
899+ 
900+     // Remove socket if it exists from a previous run 
901+     let  _ = std:: fs:: remove_file ( socket_path) ; 
902+ 
903+     // Create the Unix socket at the standard agent location 
904+     let  listener = match  std:: os:: unix:: net:: UnixListener :: bind ( socket_path)  { 
905+         Ok ( l)  => l, 
906+         Err ( _)  => { 
907+             eprintln ! ( "Skipping UDS test - cannot create socket at /var/run/datadog/apm.socket" ) ; 
908+             return ; 
909+         } 
910+     } ; 
911+ 
912+     let  mut  p = process:: Command :: new ( & fixtures. artifacts [ & crashtracker_bin] ) 
913+         . arg ( "" )  // Empty endpoint so both use agent detection 
914+         . arg ( fixtures. artifacts [ & crashtracker_receiver] . as_os_str ( ) ) 
915+         . arg ( & fixtures. output_dir ) 
916+         . arg ( mode) 
917+         . arg ( crash_typ) 
918+         // Don't set DD_TRACE_AGENT_URL - let it auto-detect the UDS socket 
919+         . spawn ( ) 
920+         . unwrap ( ) ; 
921+ 
922+     let  exit_status = bin_tests:: timeit!( "exit after signal" ,  { 
923+         eprintln!( "Waiting for exit" ) ; 
924+         p. wait( ) . unwrap( ) 
925+     } ) ; 
926+ 
927+     match  crash_typ { 
928+         "kill_sigabrt"  | "kill_sigill"  | "null_deref"  | "raise_sigabrt"  | "raise_sigill"  => { 
929+             assert ! ( !exit_status. success( ) ) 
930+         } 
931+         "kill_sigbus"  | "kill_sigsegv"  | "raise_sigbus"  | "raise_sigsegv"  => { 
932+             assert ! ( exit_status. success( ) ) 
933+         } 
934+         _ => unreachable ! ( "{crash_typ} shouldn't happen" ) , 
935+     } 
936+ 
937+     // Handle HTTP requests on the Unix socket - expect 4 requests total 
938+     // 2 from telemetry (crash ping + crash report) and 2 from errors intake (crash ping + crash 
939+     // report) 
940+     let  ( mut  stream1,  _)  = listener. accept ( ) . unwrap ( ) ; 
941+     let  body1 = read_http_request_body ( & mut  stream1) ; 
942+     stream1
943+         . write_all ( b"HTTP/1.1 200 OK\r \n Content-Length: 0\r \n \r \n " ) 
944+         . unwrap ( ) ; 
945+ 
946+     let  ( mut  stream2,  _)  = listener. accept ( ) . unwrap ( ) ; 
947+     let  body2 = read_http_request_body ( & mut  stream2) ; 
948+     stream2
949+         . write_all ( b"HTTP/1.1 200 OK\r \n Content-Length: 0\r \n \r \n " ) 
950+         . unwrap ( ) ; 
951+ 
952+     let  ( mut  stream3,  _)  = listener. accept ( ) . unwrap ( ) ; 
953+     let  body3 = read_http_request_body ( & mut  stream3) ; 
954+     stream3
955+         . write_all ( b"HTTP/1.1 200 OK\r \n Content-Length: 0\r \n \r \n " ) 
956+         . unwrap ( ) ; 
957+ 
958+     let  ( mut  stream4,  _)  = listener. accept ( ) . unwrap ( ) ; 
959+     let  body4 = read_http_request_body ( & mut  stream4) ; 
960+     stream4
961+         . write_all ( b"HTTP/1.1 200 OK\r \n Content-Length: 0\r \n \r \n " ) 
962+         . unwrap ( ) ; 
963+ 
964+     let  all_bodies = [ body1,  body2,  body3,  body4] ; 
965+ 
966+     // Separate crash pings from crash reports 
967+     let  mut  crash_pings = Vec :: new ( ) ; 
968+     let  mut  crash_reports = Vec :: new ( ) ; 
969+ 
970+     for  ( i,  body)  in  all_bodies. iter ( ) . enumerate ( )  { 
971+         if  body. contains ( "is_crash_ping:true" )  { 
972+             crash_pings. push ( ( i + 1 ,  body) ) ; 
973+         }  else  if  body. contains ( "is_crash:true" )  { 
974+             crash_reports. push ( ( i + 1 ,  body) ) ; 
975+         } 
976+     } 
977+ 
978+     assert_eq ! ( 
979+         crash_pings. len( ) , 
980+         2 , 
981+         "Expected 2 crash pings (telemetry + errors intake), got {}" , 
982+         crash_pings. len( ) 
983+     ) ; 
984+     assert_eq ! ( 
985+         crash_reports. len( ) , 
986+         2 , 
987+         "Expected 2 crash reports (telemetry + errors intake), got {}" , 
988+         crash_reports. len( ) 
989+     ) ; 
990+ 
991+     // Find telemetry requests (contain api_version and request_type) 
992+     let  telemetry_crash_ping = crash_pings
993+         . iter ( ) 
994+         . find ( |( _,  body) | body. contains ( "api_version" )  && body. contains ( "request_type" ) ) 
995+         . expect ( "Should have telemetry crash ping" ) ; 
996+     validate_crash_ping_telemetry ( telemetry_crash_ping. 1 ) ; 
997+ 
998+     let  telemetry_crash_report = crash_reports
999+         . iter ( ) 
1000+         . find ( |( _,  body) | body. contains ( "api_version" )  && body. contains ( "request_type" ) ) 
1001+         . expect ( "Should have telemetry crash report" ) ; 
1002+     assert_telemetry_message ( telemetry_crash_report. 1 . as_bytes ( ) ,  crash_typ) ; 
1003+ 
1004+     // Find errors intake requests (contain ddsource: crashtracker but no api_version) 
1005+     let  errors_crash_ping = crash_pings
1006+         . iter ( ) 
1007+         . find ( |( _,  body) | { 
1008+             body. contains ( "\" ddsource\" :\" crashtracker\" " )  && !body. contains ( "api_version" ) 
1009+         } ) 
1010+         . expect ( "Should have errors intake crash ping" ) ; 
1011+ 
1012+     let  errors_crash_report = crash_reports
1013+         . iter ( ) 
1014+         . find ( |( _,  body) | { 
1015+             body. contains ( "\" ddsource\" :\" crashtracker\" " )  && !body. contains ( "api_version" ) 
1016+         } ) 
1017+         . expect ( "Should have errors intake crash report" ) ; 
1018+ 
1019+     // Parse and validate errors intake payloads 
1020+     let  errors_ping_payload:  serde_json:: Value  = serde_json:: from_str ( errors_crash_ping. 1 ) . unwrap ( ) ; 
1021+     let  errors_report_payload:  serde_json:: Value  =
1022+         serde_json:: from_str ( errors_crash_report. 1 ) . unwrap ( ) ; 
1023+ 
1024+     // Validate errors intake crash ping (is_crash: false) 
1025+     assert_eq ! ( errors_ping_payload[ "ddsource" ] ,  "crashtracker" ) ; 
1026+     assert_eq ! ( errors_ping_payload[ "error" ] [ "is_crash" ] ,  false ) ; 
1027+ 
1028+     // Validate errors intake crash report (is_crash: true) 
1029+     assert_eq ! ( errors_report_payload[ "ddsource" ] ,  "crashtracker" ) ; 
1030+     assert_eq ! ( errors_report_payload[ "error" ] [ "is_crash" ] ,  true ) ; 
1031+ 
1032+     // Clean up 
1033+     drop ( listener) ; 
1034+     let  _ = std:: fs:: remove_file ( socket_path) ; 
1035+ } 
1036+ 
8671037fn  assert_errors_intake_payload ( payload :  & Value ,  crash_typ :  & str )  { 
8681038    // Validate basic structure 
8691039    assert_eq ! ( payload[ "ddsource" ] ,  "crashtracker" ) ; 
0 commit comments