1- use anyhow:: Context ;
1+ use anyhow:: { bail , Context } ;
22use serde:: Serialize ;
33use std:: path:: PathBuf ;
44
@@ -37,6 +37,27 @@ pub struct DumpOpenOptions {
3737 pub path : PathBuf ,
3838}
3939
40+ #[ derive( Debug , Clone ) ]
41+ pub struct DumpWriteOptions {
42+ pub path : PathBuf ,
43+ pub kind : DumpKind ,
44+ pub overwrite : bool ,
45+ }
46+
47+ #[ derive( Debug , Clone ) ]
48+ pub struct ProcessDumpOptions {
49+ pub process_id : u32 ,
50+ pub initial_break_timeout_ms : u32 ,
51+ pub write : DumpWriteOptions ,
52+ }
53+
54+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Serialize ) ]
55+ #[ serde( rename_all = "snake_case" ) ]
56+ pub enum DumpKind {
57+ Mini ,
58+ Full ,
59+ }
60+
4061#[ derive( Debug , Clone , Copy , PartialEq , Eq , Serialize ) ]
4162#[ serde( rename_all = "snake_case" ) ]
4263pub enum LiveLaunchEnd {
@@ -61,6 +82,18 @@ pub struct LiveLaunchResult {
6182 pub end : LiveLaunchEnd ,
6283}
6384
85+ #[ derive( Debug , Clone , Serialize ) ]
86+ pub struct DumpWriteResult {
87+ pub path : PathBuf ,
88+ pub kind : DumpKind ,
89+ pub qualifier : u32 ,
90+ pub format_flags : u32 ,
91+ pub overwrite : bool ,
92+ pub target : String ,
93+ pub process_id : Option < u32 > ,
94+ pub detached : bool ,
95+ }
96+
6497#[ derive( Debug , Clone , Serialize ) ]
6598pub struct DebuggerExecutionStatus {
6699 pub raw : Option < u32 > ,
@@ -196,6 +229,10 @@ pub fn open_dump_session(options: DumpOpenOptions) -> anyhow::Result<DebuggerSes
196229 open_dump_session_impl ( options)
197230}
198231
232+ pub fn write_process_dump ( options : ProcessDumpOptions ) -> anyhow:: Result < DumpWriteResult > {
233+ write_process_dump_impl ( options)
234+ }
235+
199236#[ cfg( windows) ]
200237pub struct DebuggerSession {
201238 kind : DebuggerSessionKind ,
@@ -281,6 +318,23 @@ impl DebuggerSession {
281318 Ok ( ( ) )
282319 }
283320
321+ pub fn write_dump ( & self , options : DumpWriteOptions ) -> anyhow:: Result < DumpWriteResult > {
322+ if self . kind != DebuggerSessionKind :: Live {
323+ bail ! ( "DbgEng dump writing requires a live target session" ) ;
324+ }
325+ write_dump_file ( & self . client , & options) ?;
326+ Ok ( DumpWriteResult {
327+ path : options. path ,
328+ kind : options. kind ,
329+ qualifier : dump_kind_qualifier ( options. kind ) ,
330+ format_flags : dump_format_flags ( options. overwrite ) ,
331+ overwrite : options. overwrite ,
332+ target : self . target . clone ( ) ,
333+ process_id : self . current_process_system_id ( ) . ok ( ) . or ( self . process_id ) ,
334+ detached : false ,
335+ } )
336+ }
337+
284338 pub fn core_registers ( & self ) -> anyhow:: Result < CoreRegisterState > {
285339 let instruction_offset = unsafe { self . registers . GetInstructionOffset ( ) . ok ( ) } ;
286340 let stack_offset = unsafe { self . registers . GetStackOffset ( ) . ok ( ) } ;
@@ -699,6 +753,10 @@ impl DebuggerSession {
699753 anyhow:: bail!( "DbgEng sessions are only supported on Windows" )
700754 }
701755
756+ pub fn write_dump ( & self , _options : DumpWriteOptions ) -> anyhow:: Result < DumpWriteResult > {
757+ anyhow:: bail!( "DbgEng dump writing is only supported on Windows" )
758+ }
759+
702760 pub fn core_registers ( & self ) -> anyhow:: Result < CoreRegisterState > {
703761 anyhow:: bail!( "DbgEng sessions are only supported on Windows" )
704762 }
@@ -952,6 +1010,27 @@ fn open_dump_session_impl(options: DumpOpenOptions) -> anyhow::Result<DebuggerSe
9521010 } )
9531011}
9541012
1013+ #[ cfg( windows) ]
1014+ fn write_process_dump_impl ( options : ProcessDumpOptions ) -> anyhow:: Result < DumpWriteResult > {
1015+ let session = attach_live_session_impl ( LiveAttachOptions {
1016+ process_id : options. process_id ,
1017+ initial_break_timeout_ms : options. initial_break_timeout_ms ,
1018+ } ) ?;
1019+ let write_result = session. write_dump ( options. write ) ;
1020+ let detach_result = session. detach ( ) ;
1021+ match ( write_result, detach_result) {
1022+ ( Ok ( mut result) , Ok ( ( ) ) ) => {
1023+ result. detached = true ;
1024+ Ok ( result)
1025+ }
1026+ ( Ok ( _) , Err ( error) ) => Err ( error) . context ( "dump was written, but DbgEng detach failed" ) ,
1027+ ( Err ( error) , Ok ( ( ) ) ) => Err ( error) ,
1028+ ( Err ( write_error) , Err ( detach_error) ) => Err ( write_error) . with_context ( || {
1029+ format ! ( "DbgEng detach also failed after dump write failed: {detach_error}" )
1030+ } ) ,
1031+ }
1032+ }
1033+
9551034#[ cfg( windows) ]
9561035fn read_wide_string < F > ( mut reader : F ) -> anyhow:: Result < String >
9571036where
@@ -999,6 +1078,26 @@ fn debug_value_type_name(value_type: u32) -> &'static str {
9991078 }
10001079}
10011080
1081+ const DEBUG_DUMP_SMALL_VALUE : u32 = 1024 ;
1082+ const DEBUG_DUMP_FULL_VALUE : u32 = 1026 ;
1083+ const DEBUG_FORMAT_DEFAULT_VALUE : u32 = 0x0000_0000 ;
1084+ const DEBUG_FORMAT_NO_OVERWRITE_VALUE : u32 = 0x8000_0000 ;
1085+
1086+ fn dump_kind_qualifier ( kind : DumpKind ) -> u32 {
1087+ match kind {
1088+ DumpKind :: Mini => DEBUG_DUMP_SMALL_VALUE ,
1089+ DumpKind :: Full => DEBUG_DUMP_FULL_VALUE ,
1090+ }
1091+ }
1092+
1093+ fn dump_format_flags ( overwrite : bool ) -> u32 {
1094+ if overwrite {
1095+ DEBUG_FORMAT_DEFAULT_VALUE
1096+ } else {
1097+ DEBUG_FORMAT_NO_OVERWRITE_VALUE
1098+ }
1099+ }
1100+
10021101fn encode_hex ( bytes : & [ u8 ] ) -> String {
10031102 let mut result = String :: with_capacity ( bytes. len ( ) * 2 ) ;
10041103 for byte in bytes {
@@ -1008,6 +1107,28 @@ fn encode_hex(bytes: &[u8]) -> String {
10081107 result
10091108}
10101109
1110+ #[ cfg( windows) ]
1111+ fn write_dump_file (
1112+ client : & windows:: Win32 :: System :: Diagnostics :: Debug :: Extensions :: IDebugClient5 ,
1113+ options : & DumpWriteOptions ,
1114+ ) -> anyhow:: Result < ( ) > {
1115+ use windows:: core:: PCWSTR ;
1116+
1117+ let path_string = options. path . to_string_lossy ( ) . to_string ( ) ;
1118+ let mut path = path_string. encode_utf16 ( ) . collect :: < Vec < _ > > ( ) ;
1119+ path. push ( 0 ) ;
1120+ unsafe {
1121+ client. WriteDumpFileWide (
1122+ PCWSTR ( path. as_ptr ( ) ) ,
1123+ 0 ,
1124+ dump_kind_qualifier ( options. kind ) ,
1125+ dump_format_flags ( options. overwrite ) ,
1126+ PCWSTR ( std:: ptr:: null ( ) ) ,
1127+ ) ?;
1128+ }
1129+ Ok ( ( ) )
1130+ }
1131+
10111132#[ cfg( not( windows) ) ]
10121133fn start_process_server_impl ( options : ProcessServerOptions ) -> anyhow:: Result < ProcessServerResult > {
10131134 let _ = options;
@@ -1037,3 +1158,26 @@ fn open_dump_session_impl(options: DumpOpenOptions) -> anyhow::Result<DebuggerSe
10371158 let _ = options;
10381159 anyhow:: bail!( "DbgEng dump sessions are only supported on Windows" )
10391160}
1161+
1162+ #[ cfg( not( windows) ) ]
1163+ fn write_process_dump_impl ( options : ProcessDumpOptions ) -> anyhow:: Result < DumpWriteResult > {
1164+ let _ = options;
1165+ anyhow:: bail!( "DbgEng dump writing is only supported on Windows" )
1166+ }
1167+
1168+ #[ cfg( test) ]
1169+ mod tests {
1170+ use super :: * ;
1171+
1172+ #[ test]
1173+ fn maps_dump_kinds_to_dbgeng_qualifiers ( ) {
1174+ assert_eq ! ( dump_kind_qualifier( DumpKind :: Mini ) , 1024 ) ;
1175+ assert_eq ! ( dump_kind_qualifier( DumpKind :: Full ) , 1026 ) ;
1176+ }
1177+
1178+ #[ test]
1179+ fn uses_no_overwrite_by_default ( ) {
1180+ assert_eq ! ( dump_format_flags( false ) , 0x8000_0000 ) ;
1181+ assert_eq ! ( dump_format_flags( true ) , 0 ) ;
1182+ }
1183+ }
0 commit comments