Skip to content

Commit 61a0ba7

Browse files
author
Kevin Gosse
committed
Checkpoint
1 parent 722690e commit 61a0ba7

File tree

8 files changed

+413
-35
lines changed

8 files changed

+413
-35
lines changed

crashtracker-ffi/Cargo.toml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,7 @@ symbolic-demangle = { version = "12.8.0", default-features = false, features = [
3434
symbolic-common = { version = "12.8.0", default-features = false, optional = true }
3535
function_name = "0.3.0"
3636
libc = "0.2.167"
37-
windows = { version = "0.59.0", features = [
38-
"Win32_System_Diagnostics_Debug",
39-
"Win32_System_Diagnostics_ToolHelp",
40-
"Win32_System_ErrorReporting",
41-
"Win32_System_Kernel",
42-
"Win32_System_ProcessStatus",
43-
"Win32_System_SystemInformation",
44-
"Win32_System_SystemServices",
45-
"Win32_System_Threading",
46-
] }
37+
windows = { version = "0.59.0", features = ["Win32_System_Diagnostics_Debug", "Win32_System_Diagnostics_ToolHelp", "Win32_System_ErrorReporting", "Win32_System_Kernel", "Win32_System_ProcessStatus", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_Security"] }
4738
serde_json = "1.0.132"
4839
serde = { version = "1.0.214", features = ["derive"] }
4940
log = "0.4.22"

crashtracker-ffi/src/collector_windows/api.rs

Lines changed: 198 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use std::fmt;
1010
use std::mem::MaybeUninit;
1111
use std::os::windows::ffi::{OsStrExt, OsStringExt};
1212
use std::ptr::{addr_of, read_unaligned};
13-
use windows::core::{s, HRESULT, PCWSTR};
14-
use windows::Win32::Foundation::{GetLastError, BOOL, HANDLE, HMODULE, MAX_PATH, S_OK, TRUE};
15-
use windows::Win32::System::Diagnostics::Debug::{AddrModeFlat, GetThreadContext, OutputDebugStringA, ReadProcessMemory, StackWalkEx, SymInitializeW, CONTEXT, CONTEXT_FULL_AMD64, IMAGE_DATA_DIRECTORY, IMAGE_DEBUG_DIRECTORY, IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_DEBUG, IMAGE_FILE_HEADER, IMAGE_NT_HEADERS32, IMAGE_NT_HEADERS64, IMAGE_OPTIONAL_HEADER_MAGIC, STACKFRAME_EX, SYM_STKWALK_DEFAULT};
13+
use windows::core::{s, w, HRESULT, PCWSTR};
14+
use windows::Win32::Foundation::{GetLastError, BOOL, ERROR_SUCCESS, HANDLE, HMODULE, MAX_PATH, S_OK, TRUE};
15+
use windows::Win32::System::Diagnostics::Debug::{AddrModeFlat, GetThreadContext, OutputDebugStringA, OutputDebugStringW, ReadProcessMemory, StackWalkEx, SymInitializeW, CONTEXT, CONTEXT_FULL_AMD64, CONTEXT_FULL_X86, IMAGE_DATA_DIRECTORY, IMAGE_DEBUG_DIRECTORY, IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_DEBUG, IMAGE_FILE_HEADER, IMAGE_NT_HEADERS32, IMAGE_NT_HEADERS64, IMAGE_OPTIONAL_HEADER_MAGIC, STACKFRAME_EX, SYM_STKWALK_DEFAULT};
1616
use windows::Win32::System::Diagnostics::ToolHelp::{CreateToolhelp32Snapshot, Thread32First, Thread32Next, TH32CS_SNAPTHREAD, THREADENTRY32};
1717
use windows::Win32::System::ErrorReporting::{WerRegisterRuntimeExceptionModule, WER_RUNTIME_EXCEPTION_INFORMATION};
1818
use windows::Win32::System::ProcessStatus::{EnumProcessModules, GetModuleFileNameExW, GetModuleInformation, MODULEINFO};
@@ -23,6 +23,7 @@ use ddcommon_ffi::{wrap_with_void_ffi_result, CharSlice, Slice, VoidResult};
2323
use serde::{Deserialize, Serialize};
2424
use log::error;
2525
use windows::core::imp::HSTRING;
26+
use windows::Win32::System::Registry::{RegCloseKey, RegOpenKeyExW, RegQueryValueExW, RegCreateKeyExW, RegSetValueExW, HKEY, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ, KEY_WRITE, REG_DWORD, REG_OPTION_NON_VOLATILE};
2627
use ddcommon_ffi::slice::AsBytes;
2728
use crate::Metadata;
2829

@@ -44,6 +45,7 @@ pub unsafe extern "C" fn ddog_crasht_init_windows(
4445
endpoint: Option<&mut Endpoint>,
4546
metadata: Metadata,
4647
) -> bool {
48+
OutputDebugStringW(w!("ddog_crasht_init_windows"));
4749
let result: Result<(), _> = (|| {
4850
let endpoint_option = if endpoint.is_none() {
4951
None
@@ -56,18 +58,25 @@ pub unsafe extern "C" fn ddog_crasht_init_windows(
5658
set_error_context(&error_context_json);
5759

5860
let path = get_module_path(GetCurrentProcess(), module)?;
59-
let wpath: Vec<u16> = path.encode_utf16().collect();
6061

61-
WerRegisterRuntimeExceptionModule(PCWSTR::from_raw(wpath.as_ptr()), &WERCONTEXT as *const WerContext as *const c_void)?;
62+
create_registry_key(&path)?;
6263

64+
let mut wpath: Vec<u16> = path.encode_utf16().collect();
65+
wpath.push(0); // Ensure null termination
66+
WerRegisterRuntimeExceptionModule(PCWSTR::from_raw(wpath.as_ptr()), &WERCONTEXT as *const WerContext as *const c_void)?;
67+
OutputDebugStringW(PCWSTR::from_raw(wpath.as_ptr()));
6368
Ok::<(), anyhow::Error>(())
6469
})();
6570

6671
if result.is_err()
6772
{
73+
OutputDebugStringW(w!("ddog_crasht_init_windows failed"));
74+
output_debug_string(result.err().unwrap().to_string());
75+
6876
return false;
6977
}
7078

79+
OutputDebugStringW(w!("ddog_crasht_init_windows succeeded"));
7180
true
7281
}
7382

@@ -78,6 +87,103 @@ pub unsafe extern "C" fn ddog_crasht_get_wercontext_for_tests() -> *const c_void
7887
return &WERCONTEXT as *const WerContext as *const c_void;
7988
}
8089

90+
unsafe fn create_registry_key(path: &String) -> Result<()> {
91+
// First, check if there is already a key named "path" in SOFTWARE\Microsoft\Windows\Windows Error Reporting\RuntimeExceptionHelperModules,
92+
// in either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER.
93+
// If not, create it in HKEY_CURRENT_USER.
94+
95+
// Convert value name to null-terminated wide string
96+
let mut name_wide: Vec<u16> = path.encode_utf16().collect();
97+
name_wide.push(0); // Add null terminator
98+
let name_pcwstr = PCWSTR::from_raw(name_wide.as_ptr());
99+
100+
// Subkey path as wide string constant
101+
let subkey = w!(
102+
"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\RuntimeExceptionHelperModules"
103+
);
104+
105+
// Check both HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER
106+
for root in [HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER] {
107+
let mut hkey = HKEY::default();
108+
109+
// Try to open the registry key
110+
let open_result = unsafe {
111+
RegOpenKeyExW(
112+
root,
113+
subkey,
114+
None,
115+
KEY_READ,
116+
&mut hkey,
117+
)
118+
};
119+
120+
if open_result == ERROR_SUCCESS {
121+
// Check if the value exists
122+
let query_result =
123+
RegQueryValueExW(
124+
hkey,
125+
name_pcwstr,
126+
None,
127+
None,
128+
None,
129+
None,
130+
);
131+
132+
let _ = RegCloseKey(hkey);
133+
134+
if query_result == ERROR_SUCCESS {
135+
// Value exists in either hive, exit successfully
136+
return Ok(());
137+
}
138+
}
139+
}
140+
141+
// Value doesn't exist in either hive, create in HKEY_CURRENT_USER
142+
let mut hkey = HKEY::default();
143+
144+
// Create or open the key with write access
145+
let create_result = RegCreateKeyExW(
146+
HKEY_CURRENT_USER,
147+
subkey,
148+
None,
149+
None,
150+
REG_OPTION_NON_VOLATILE,
151+
KEY_WRITE,
152+
None,
153+
&mut hkey,
154+
None,
155+
);
156+
157+
if create_result != ERROR_SUCCESS {
158+
return Err(anyhow!("Failed to create registry key"));
159+
}
160+
161+
// Create the DWORD value (0)
162+
let dword_value: u32 = 0;
163+
let dword_bytes = dword_value.to_ne_bytes();
164+
let set_value_result = RegSetValueExW(
165+
hkey,
166+
name_pcwstr,
167+
None,
168+
REG_DWORD,
169+
Some(&dword_bytes)
170+
);
171+
172+
if set_value_result != ERROR_SUCCESS {
173+
return Err(anyhow!("Failed to set registry value"));
174+
}
175+
176+
let _ = RegCloseKey(hkey);
177+
178+
Ok(())
179+
}
180+
181+
unsafe fn output_debug_string(message: String) {
182+
let mut wstr: Vec<u16> = message.encode_utf16().collect();
183+
wstr.push(0); // Ensure null termination
184+
OutputDebugStringW(PCWSTR::from_raw(wstr.as_ptr()));
185+
}
186+
81187
fn set_error_context(message: &str) {
82188
let bytes = message.as_bytes();
83189
let boxed_slice = bytes.to_vec().into_boxed_slice();
@@ -117,14 +223,47 @@ pub struct WerContext
117223
#[no_mangle]
118224
#[named]
119225
#[cfg(windows)]
120-
pub unsafe extern "C" fn exception_event_callback(
226+
pub unsafe extern "C" fn ddog_crasht_event_signature_callback(
227+
pContext: *const c_void,
228+
pExceptionInformation: *const WER_RUNTIME_EXCEPTION_INFORMATION,
229+
dwIndex: i32,
230+
pwszName: *mut u16,
231+
pchSize: *mut u32,
232+
pwszValue: *mut u16,
233+
pchValue: *mut u32,
234+
) -> HRESULT
235+
{
236+
OutputDebugStringW(w!("ddog_crasht_event_signature_callback"));
237+
S_OK
238+
}
239+
240+
#[no_mangle]
241+
#[named]
242+
#[cfg(windows)]
243+
pub unsafe extern "C" fn ddog_crasht_debugger_launch_callback(
244+
pContext: *const c_void,
245+
pExceptionInformation: *const WER_RUNTIME_EXCEPTION_INFORMATION,
246+
pbIsCustomDebugger: *mut BOOL,
247+
pwszDebuggerLaunch: *mut u16,
248+
pchDebuggerLaunch: *mut u32,
249+
pbIsDebuggerAutolaunch: *mut BOOL,
250+
) -> HRESULT {
251+
OutputDebugStringW(w!("ddog_crasht_debugger_launch_callback"));
252+
S_OK
253+
}
254+
255+
#[no_mangle]
256+
#[named]
257+
#[cfg(windows)]
258+
pub unsafe extern "C" fn ddog_crasht_exception_event_callback(
121259
pContext: *const c_void,
122260
pExceptionInformation: *const WER_RUNTIME_EXCEPTION_INFORMATION,
123261
pbOwnershipClaimed: *mut BOOL,
124262
pwszEventName: *mut u16,
125263
pchSize: *mut u32,
126264
pdwSignatureCount: *mut u32,
127265
) -> HRESULT {
266+
OutputDebugStringW(w!("ddog_crasht_exception_event_callback"));
128267
let exception_information = *pExceptionInformation;
129268
let sym_init_result = SymInitializeW(exception_information.hProcess, PCWSTR::null(), true);
130269

@@ -146,9 +285,18 @@ pub unsafe extern "C" fn exception_event_callback(
146285
let mut builder = CrashInfoBuilder::new();
147286

148287
for thread in threads.unwrap() {
149-
let stack = walk_thread_stack(exception_information.hProcess, thread, &modules).unwrap_or_else(|_| StackTrace::new_incomplete());
288+
let stack: StackTrace;
289+
let stack_result = walk_thread_stack(exception_information.hProcess, thread, &modules);
290+
291+
if stack_result.is_err() {
292+
OutputDebugStringW(w!("Failed to walk thread stack"));
293+
output_debug_string(stack_result.err().unwrap().to_string());
294+
stack = StackTrace::new_incomplete();
295+
} else {
296+
stack = stack_result.unwrap();
297+
}
150298

151-
if (thread == crash_tid) {
299+
if thread == crash_tid {
152300
builder.with_stack(stack.clone()).expect("Failed to add crashed thread info");
153301
}
154302

@@ -227,27 +375,57 @@ pub unsafe fn read_wer_context(process_handle: HANDLE, base_address: usize) -> R
227375
Ok(wer_context)
228376
}
229377

378+
// https://github.com/microsoft/win32metadata/issues/1044
379+
#[repr(align(16))]
380+
#[derive(Default)]
381+
struct AlignedContext {
382+
ctx: CONTEXT
383+
}
384+
230385
pub unsafe fn walk_thread_stack(process_handle: HANDLE, thread_id: u32, modules: &Vec<ModuleInfo>) -> Result<StackTrace> {
231386
let mut stacktrace = StackTrace::new_incomplete();
232-
233387
let thread_handle = OpenThread(THREAD_ALL_ACCESS, false, thread_id)?;
234-
235-
let mut context = CONTEXT::default();
236-
context.ContextFlags = CONTEXT_FULL_AMD64;
237-
GetThreadContext(thread_handle, &mut context)?;
388+
let mut context = AlignedContext::default();
238389

239390
#[cfg(target_arch = "x86_64")]
391+
{
392+
context.ctx.ContextFlags = CONTEXT_FULL_AMD64;
393+
}
394+
#[cfg(target_arch = "x86")]
395+
{
396+
context.ctx.ContextFlags = CONTEXT_FULL_X86;
397+
}
398+
399+
GetThreadContext(thread_handle, &mut context.ctx)?;
400+
240401
let mut native_frame = STACKFRAME_EX::default();
241-
native_frame.AddrPC.Offset = context.Rip as u64;
242-
native_frame.AddrPC.Mode = AddrModeFlat;
243-
native_frame.AddrStack.Offset = context.Rsp as u64;
244-
native_frame.AddrStack.Mode = AddrModeFlat;
245-
native_frame.AddrFrame.Offset = context.Rbp as u64;
246-
native_frame.AddrFrame.Mode = AddrModeFlat;
402+
let machine_type:u32;
403+
404+
#[cfg(target_arch = "x86_64")]
405+
{
406+
machine_type = IMAGE_FILE_MACHINE_AMD64.0 as u32;
407+
native_frame.AddrPC.Offset = context.ctx.Rip as u64;
408+
native_frame.AddrPC.Mode = AddrModeFlat;
409+
native_frame.AddrStack.Offset = context.ctx.Rsp as u64;
410+
native_frame.AddrStack.Mode = AddrModeFlat;
411+
native_frame.AddrFrame.Offset = context.ctx.Rbp as u64;
412+
native_frame.AddrFrame.Mode = AddrModeFlat;
413+
}
414+
415+
#[cfg(target_arch = "x86")]
416+
{
417+
machine_type = IMAGE_FILE_MACHINE_I386.0 as u32;
418+
native_frame.AddrPC.Offset = context.ctx.Eip as u64;
419+
native_frame.AddrPC.Mode = AddrModeFlat;
420+
native_frame.AddrStack.Offset = context.ctx.Esp as u64;
421+
native_frame.AddrStack.Mode = AddrModeFlat;
422+
native_frame.AddrFrame.Offset = context.ctx.Ebp as u64;
423+
native_frame.AddrFrame.Mode = AddrModeFlat;
424+
}
247425

248426
loop {
249427
let result = StackWalkEx(
250-
IMAGE_FILE_MACHINE_AMD64.0 as u32,
428+
machine_type,
251429
process_handle,
252430
thread_handle,
253431
&mut native_frame,
@@ -289,8 +467,6 @@ pub unsafe fn walk_thread_stack(process_handle: HANDLE, thread_id: u32, modules:
289467
stacktrace.push_frame(frame, true).expect("Failed to add frame");
290468
}
291469

292-
stacktrace.set_complete().expect("Failed to set complete");
293-
294470
stacktrace.set_complete()?;
295471
Ok(stacktrace)
296472
}

sidecar/src/entry.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::config::{self, Config};
2929
use crate::self_telemetry::self_telemetry;
3030
use crate::tracer::SHM_LIMITER;
3131
use crate::watchdog::Watchdog;
32-
use crate::{ddog_daemon_entry_point, setup_daemon_process};
32+
use crate::{ddog_daemon_entry_point, ddog_setup_crashtracking, setup_daemon_process};
3333

3434
async fn main_loop<L, C, Fut>(listener: L, cancel: Arc<C>) -> io::Result<()>
3535
where
@@ -200,6 +200,11 @@ pub fn daemonize(listener: IpcServer, mut cfg: Config) -> anyhow::Result<()> {
200200
Ok(())
201201
}
202202

203+
pub fn start_crashtracking() -> anyhow::Result<()> {
204+
ddog_setup_crashtracking();
205+
Ok(())
206+
}
207+
203208
pub fn start_or_connect_to_sidecar(cfg: Config) -> anyhow::Result<SidecarTransport> {
204209
let liaison = match cfg.ipc_mode {
205210
config::IpcMode::Shared => setup::DefaultLiason::ipc_shared(),

sidecar/src/windows.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use datadog_ipc::platform::{
99
use futures::FutureExt;
1010
use lazy_static::lazy_static;
1111
use manual_future::ManualFuture;
12-
use spawn_worker::{SpawnWorker, Stdio};
13-
use std::ffi::CStr;
12+
use spawn_worker::{write_crashtracking_trampoline, SpawnWorker, Stdio};
13+
use std::ffi::{CStr, CString};
1414
use std::io::{self, Error};
1515
use std::os::windows::io::{AsRawHandle, IntoRawHandle, OwnedHandle};
1616
use std::ptr::null_mut;
@@ -128,6 +128,24 @@ pub fn setup_daemon_process(listener: OwnedHandle, spawn_cfg: &mut SpawnWorker)
128128
Ok(())
129129
}
130130

131+
#[no_mangle]
132+
pub extern "C" fn ddog_setup_crashtracking() -> *mut libc::c_char {
133+
// Ensure unique process names - we spawn one sidecar per console session id (see
134+
// setup/windows.rs for the reasoning)
135+
let result = write_crashtracking_trampoline(&format!(
136+
"datadog-crashtracking-{}",
137+
primary_sidecar_identifier()
138+
));
139+
140+
if result.is_ok() {
141+
// TODO: initialize crashtracking
142+
let path = result.unwrap().0.into_os_string().into_string().unwrap();
143+
return CString::new(path).unwrap().into_raw();
144+
}
145+
146+
return null_mut();
147+
}
148+
131149
lazy_static! {
132150
static ref SIDECAR_IDENTIFIER: String = fetch_sidecar_identifier();
133151
}

0 commit comments

Comments
 (0)