@@ -10,9 +10,9 @@ use std::fmt;
10
10
use std:: mem:: MaybeUninit ;
11
11
use std:: os:: windows:: ffi:: { OsStrExt , OsStringExt } ;
12
12
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 } ;
16
16
use windows:: Win32 :: System :: Diagnostics :: ToolHelp :: { CreateToolhelp32Snapshot , Thread32First , Thread32Next , TH32CS_SNAPTHREAD , THREADENTRY32 } ;
17
17
use windows:: Win32 :: System :: ErrorReporting :: { WerRegisterRuntimeExceptionModule , WER_RUNTIME_EXCEPTION_INFORMATION } ;
18
18
use windows:: Win32 :: System :: ProcessStatus :: { EnumProcessModules , GetModuleFileNameExW , GetModuleInformation , MODULEINFO } ;
@@ -23,6 +23,7 @@ use ddcommon_ffi::{wrap_with_void_ffi_result, CharSlice, Slice, VoidResult};
23
23
use serde:: { Deserialize , Serialize } ;
24
24
use log:: error;
25
25
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 } ;
26
27
use ddcommon_ffi:: slice:: AsBytes ;
27
28
use crate :: Metadata ;
28
29
@@ -44,6 +45,7 @@ pub unsafe extern "C" fn ddog_crasht_init_windows(
44
45
endpoint : Option < & mut Endpoint > ,
45
46
metadata : Metadata ,
46
47
) -> bool {
48
+ OutputDebugStringW ( w ! ( "ddog_crasht_init_windows" ) ) ;
47
49
let result: Result < ( ) , _ > = ( || {
48
50
let endpoint_option = if endpoint. is_none ( ) {
49
51
None
@@ -56,18 +58,25 @@ pub unsafe extern "C" fn ddog_crasht_init_windows(
56
58
set_error_context ( & error_context_json) ;
57
59
58
60
let path = get_module_path ( GetCurrentProcess ( ) , module) ?;
59
- let wpath: Vec < u16 > = path. encode_utf16 ( ) . collect ( ) ;
60
61
61
- WerRegisterRuntimeExceptionModule ( PCWSTR :: from_raw ( wpath . as_ptr ( ) ) , & WERCONTEXT as * const WerContext as * const c_void ) ?;
62
+ create_registry_key ( & path ) ?;
62
63
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 ( ) ) ) ;
63
68
Ok :: < ( ) , anyhow:: Error > ( ( ) )
64
69
} ) ( ) ;
65
70
66
71
if result. is_err ( )
67
72
{
73
+ OutputDebugStringW ( w ! ( "ddog_crasht_init_windows failed" ) ) ;
74
+ output_debug_string ( result. err ( ) . unwrap ( ) . to_string ( ) ) ;
75
+
68
76
return false ;
69
77
}
70
78
79
+ OutputDebugStringW ( w ! ( "ddog_crasht_init_windows succeeded" ) ) ;
71
80
true
72
81
}
73
82
@@ -78,6 +87,103 @@ pub unsafe extern "C" fn ddog_crasht_get_wercontext_for_tests() -> *const c_void
78
87
return & WERCONTEXT as * const WerContext as * const c_void ;
79
88
}
80
89
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
+
81
187
fn set_error_context ( message : & str ) {
82
188
let bytes = message. as_bytes ( ) ;
83
189
let boxed_slice = bytes. to_vec ( ) . into_boxed_slice ( ) ;
@@ -117,14 +223,47 @@ pub struct WerContext
117
223
#[ no_mangle]
118
224
#[ named]
119
225
#[ 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 (
121
259
pContext : * const c_void ,
122
260
pExceptionInformation : * const WER_RUNTIME_EXCEPTION_INFORMATION ,
123
261
pbOwnershipClaimed : * mut BOOL ,
124
262
pwszEventName : * mut u16 ,
125
263
pchSize : * mut u32 ,
126
264
pdwSignatureCount : * mut u32 ,
127
265
) -> HRESULT {
266
+ OutputDebugStringW ( w ! ( "ddog_crasht_exception_event_callback" ) ) ;
128
267
let exception_information = * pExceptionInformation;
129
268
let sym_init_result = SymInitializeW ( exception_information. hProcess , PCWSTR :: null ( ) , true ) ;
130
269
@@ -146,9 +285,18 @@ pub unsafe extern "C" fn exception_event_callback(
146
285
let mut builder = CrashInfoBuilder :: new ( ) ;
147
286
148
287
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
+ }
150
298
151
- if ( thread == crash_tid) {
299
+ if thread == crash_tid {
152
300
builder. with_stack ( stack. clone ( ) ) . expect ( "Failed to add crashed thread info" ) ;
153
301
}
154
302
@@ -227,27 +375,57 @@ pub unsafe fn read_wer_context(process_handle: HANDLE, base_address: usize) -> R
227
375
Ok ( wer_context)
228
376
}
229
377
378
+ // https://github.com/microsoft/win32metadata/issues/1044
379
+ #[ repr( align( 16 ) ) ]
380
+ #[ derive( Default ) ]
381
+ struct AlignedContext {
382
+ ctx : CONTEXT
383
+ }
384
+
230
385
pub unsafe fn walk_thread_stack ( process_handle : HANDLE , thread_id : u32 , modules : & Vec < ModuleInfo > ) -> Result < StackTrace > {
231
386
let mut stacktrace = StackTrace :: new_incomplete ( ) ;
232
-
233
387
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 ( ) ;
238
389
239
390
#[ 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
+
240
401
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
+ }
247
425
248
426
loop {
249
427
let result = StackWalkEx (
250
- IMAGE_FILE_MACHINE_AMD64 . 0 as u32 ,
428
+ machine_type ,
251
429
process_handle,
252
430
thread_handle,
253
431
& mut native_frame,
@@ -289,8 +467,6 @@ pub unsafe fn walk_thread_stack(process_handle: HANDLE, thread_id: u32, modules:
289
467
stacktrace. push_frame ( frame, true ) . expect ( "Failed to add frame" ) ;
290
468
}
291
469
292
- stacktrace. set_complete ( ) . expect ( "Failed to set complete" ) ;
293
-
294
470
stacktrace. set_complete ( ) ?;
295
471
Ok ( stacktrace)
296
472
}
0 commit comments