|
1 | 1 | // Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/ |
2 | 2 | // SPDX-License-Identifier: Apache-2.0 |
3 | 3 |
|
4 | | -#[cfg(not(unix))] |
5 | | -fn main() {} |
6 | | - |
7 | 4 | #[cfg(unix)] |
8 | 5 | fn main() -> anyhow::Result<()> { |
9 | 6 | unix::main() |
10 | 7 | } |
11 | 8 |
|
| 9 | +#[cfg(windows)] |
| 10 | +fn main() -> anyhow::Result<()> { |
| 11 | + windows::main() |
| 12 | +} |
| 13 | + |
| 14 | +#[cfg(not(any(unix, windows)))] |
| 15 | +fn main() -> anyhow::Result<()> { |
| 16 | + eprintln!("Crashing test app is only supported on Unix and Windows"); |
| 17 | + std::process::exit(1); |
| 18 | +} |
| 19 | + |
12 | 20 | #[cfg(unix)] |
13 | 21 | mod unix { |
14 | 22 | use anyhow::ensure; |
@@ -104,3 +112,137 @@ mod unix { |
104 | 112 | Ok(()) |
105 | 113 | } |
106 | 114 | } |
| 115 | + |
| 116 | +#[cfg(windows)] |
| 117 | +mod windows { |
| 118 | + use anyhow::Context; |
| 119 | + use std::env; |
| 120 | + |
| 121 | + use datadog_crashtracker::{init_crashtracking_windows, Metadata}; |
| 122 | + use ddcommon::{tag, Endpoint}; |
| 123 | + |
| 124 | + #[inline(never)] |
| 125 | + unsafe fn fn3() { |
| 126 | + // Force a true access violation that WER will catch |
| 127 | + // Use inline assembly to avoid Rust's null pointer checks |
| 128 | + #[cfg(target_arch = "x86_64")] |
| 129 | + { |
| 130 | + std::arch::asm!( |
| 131 | + "mov rax, 0", |
| 132 | + "mov dword ptr [rax], 42", |
| 133 | + out("rax") _, |
| 134 | + ); |
| 135 | + } |
| 136 | + |
| 137 | + #[cfg(target_arch = "x86")] |
| 138 | + { |
| 139 | + std::arch::asm!( |
| 140 | + "mov eax, 0", |
| 141 | + "mov dword ptr [eax], 42", |
| 142 | + out("eax") _, |
| 143 | + ); |
| 144 | + } |
| 145 | + |
| 146 | + #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))] |
| 147 | + { |
| 148 | + // Fallback for other architectures - this will be a panic, not WER |
| 149 | + let null_ptr = std::ptr::null_mut::<i32>(); |
| 150 | + *null_ptr = 42; |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + #[inline(never)] |
| 155 | + fn fn2() { |
| 156 | + unsafe { fn3() } |
| 157 | + } |
| 158 | + |
| 159 | + #[inline(never)] |
| 160 | + fn fn1() { |
| 161 | + fn2() |
| 162 | + } |
| 163 | + |
| 164 | + #[inline(never)] |
| 165 | + pub fn main() -> anyhow::Result<()> { |
| 166 | + println!("Windows crashing test app starting..."); |
| 167 | + |
| 168 | + // Parse arguments: output_url |
| 169 | + let mut args = env::args().skip(1); |
| 170 | + let output_url = args.next().context("Expected output_url argument")?; |
| 171 | + |
| 172 | + // Check environment variables for named pipe configuration |
| 173 | + if let Ok(pipe_name) = env::var("DD_TRACE_PIPE_NAME") { |
| 174 | + println!("Using named pipe: {}", pipe_name); |
| 175 | + } |
| 176 | + |
| 177 | + if let Ok(errors_intake) = env::var("_DD_ERRORS_INTAKE_ENABLED") { |
| 178 | + println!("Errors intake enabled: {}", errors_intake); |
| 179 | + } |
| 180 | + |
| 181 | + if let Ok(api_key) = env::var("DD_API_KEY") { |
| 182 | + println!("API key configured (length: {})", api_key.len()); |
| 183 | + } |
| 184 | + |
| 185 | + if let Ok(direct_submission) = env::var("_DD_DIRECT_SUBMISSION_ENABLED") { |
| 186 | + println!("Direct submission enabled: {}", direct_submission); |
| 187 | + } |
| 188 | + |
| 189 | + // Create endpoint for crash output |
| 190 | + let endpoint = if output_url.is_empty() { |
| 191 | + None |
| 192 | + } else { |
| 193 | + Some(Endpoint::from_slice(&output_url)) |
| 194 | + }; |
| 195 | + |
| 196 | + println!("Output endpoint: {:?}", endpoint.as_ref().map(|e| &e.url)); |
| 197 | + |
| 198 | + // Initialize Windows crashtracker |
| 199 | + let metadata = Metadata { |
| 200 | + library_name: "libdatadog".to_owned(), |
| 201 | + library_version: "1.0.0".to_owned(), |
| 202 | + family: "native".to_owned(), |
| 203 | + tags: vec![ |
| 204 | + tag!("service", "foo"), |
| 205 | + tag!("service_version", "bar"), |
| 206 | + tag!("runtime-id", "xyz"), |
| 207 | + tag!("language", "native"), |
| 208 | + ] |
| 209 | + .into_iter() |
| 210 | + .map(|x| x.to_string()) |
| 211 | + .collect(), |
| 212 | + }; |
| 213 | + |
| 214 | + // Get current module path (this executable) |
| 215 | + let module_path = env::current_exe()?; |
| 216 | + let module_name = module_path.to_string_lossy().to_string(); |
| 217 | + |
| 218 | + println!("Initializing Windows crashtracker..."); |
| 219 | + println!("Module: {}", module_name); |
| 220 | + println!("Endpoint: {:?}", endpoint); |
| 221 | + |
| 222 | + // Initialize crashtracker |
| 223 | + init_crashtracking_windows(module_name, endpoint.as_ref(), metadata)?; |
| 224 | + |
| 225 | + println!("Crashtracker initialized successfully!"); |
| 226 | + |
| 227 | + // Enable Windows Error Reporting like the FFI test app does |
| 228 | + println!("Enabling Windows Error Reporting..."); |
| 229 | + unsafe { |
| 230 | + use windows::Win32::System::Diagnostics::Debug::{SetErrorMode, THREAD_ERROR_MODE}; |
| 231 | + SetErrorMode(THREAD_ERROR_MODE(0x0001)); |
| 232 | + } |
| 233 | + |
| 234 | + // Add a longer delay to ensure WER registration is complete |
| 235 | + println!("Waiting for WER registration to complete..."); |
| 236 | + std::thread::sleep(std::time::Duration::from_millis(500)); |
| 237 | + |
| 238 | + println!("About to trigger crash in call stack: main -> fn1 -> fn2 -> fn3"); |
| 239 | + println!("This should generate an access violation that WER will catch..."); |
| 240 | + |
| 241 | + // Create the call stack for better crash traces |
| 242 | + fn1(); |
| 243 | + |
| 244 | + // This should never be reached |
| 245 | + println!("ERROR: Failed to crash!"); |
| 246 | + Ok(()) |
| 247 | + } |
| 248 | +} |
0 commit comments