| 
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