1
|
1 /* Ddbg - Win32 Debugger for the D programming language
|
|
2 * Copyright (c) 2007 Jascha Wetzel
|
|
3 * All rights reserved. See LICENSE.TXT for details.
|
|
4 */
|
|
5
|
|
6 /**************************************************************************************************
|
|
7 The main debugger code. Handles win32 debugging events,
|
|
8 manages breakpoints, calls the disassembler, etc.
|
|
9 **************************************************************************************************/
|
|
10
|
|
11 import std.file;
|
|
12 import std.string;
|
|
13 import std.format;
|
|
14
|
|
15 import win32.winbase;
|
|
16 import win32.winuser;
|
|
17 import win32.winnt;
|
|
18 import win32.dbghelp;
|
|
19
|
|
20 import minidump;
|
|
21 import container;
|
|
22 import util;
|
|
23 import breakpoint;
|
|
24 import dbgprocess;
|
|
25 import dbgthread;
|
|
26 import disasm;
|
|
27 import callstack;
|
|
28 import expression.evaluationcontext;
|
|
29 import expression.expression_apd;
|
|
30 import expression.datahandler;
|
|
31 import codeview.coff;
|
|
32 import codeview.codeview;
|
|
33 import cli.userinterface;
|
|
34
|
|
35 const uint VERSION_MAJOR = 0,
|
3
|
36 VERSION_MINOR = 13,
|
5
|
37 VERSION_PATCH = 3;
|
1
|
38 const string VERSION_STRING = "Ddbg "~itoa(VERSION_MAJOR)~"."~itoa(VERSION_MINOR)
|
|
39 ~(VERSION_PATCH>0?"."~itoa(VERSION_PATCH):"")~" beta",
|
|
40 WELCOME_STRING = VERSION_STRING~" - D Debugger\n"~
|
|
41 "Copyright (c) 2007 Jascha Wetzel\n"~
|
|
42 "see http://ddbg.mainia.de/doc.html for documentation\n";
|
|
43
|
|
44 class DebuggerException : Exception
|
|
45 {
|
|
46 this(...)
|
|
47 {
|
|
48 string str;
|
|
49
|
|
50 void putc(dchar c)
|
|
51 {
|
|
52 str ~= c;
|
|
53 }
|
|
54
|
|
55 doFormat(&putc, _arguments, _argptr);
|
|
56 super(str);
|
|
57 }
|
|
58 }
|
|
59
|
|
60 /**************************************************************************************************
|
|
61
|
|
62 **************************************************************************************************/
|
|
63 class Debugger
|
|
64 {
|
|
65 const uint STATUS_DIGITAL_MARS_D_EXCEPTION = (3 << 30) | (1 << 29) | (0 << 28) | ('D' << 16) | 1;
|
|
66
|
|
67 //=============================================================================================
|
|
68 // variables
|
|
69 bool paused,
|
|
70 process_loaded,
|
|
71 abort,
|
4
|
72 single_step,
|
|
73 breakonmain =false
|
|
74 ;
|
1
|
75
|
|
76 size_t current_address;
|
|
77 uint last_line;
|
5
|
78 // uint repeat=0;
|
1
|
79 string[][string] source_files;
|
|
80 string[] source_search_paths;
|
|
81
|
|
82 string main_image_file;
|
|
83 COFFImage main_image;
|
|
84 ImageSet images;
|
|
85
|
|
86 Breakpoint[uint] breakpoints;
|
|
87 List!(Breakpoint) temp_breakpoints;
|
|
88 Breakpoint passing_breakpoint;
|
|
89 DbgProcess process;
|
|
90 MiniDump miniDump;
|
|
91
|
|
92 CallStack current_stack;
|
|
93
|
|
94 EXCEPTION_RECORD* exceptionRecord;
|
|
95 DEBUG_EVENT debug_event;
|
|
96 uint process_id,
|
|
97 thread_id;
|
|
98
|
|
99 UserInterface ui;
|
|
100
|
4
|
101 uint evaluationDepth = 2;
|
1
|
102 bool create_new_console;
|
|
103
|
|
104 //=============================================================================================
|
|
105 // construction
|
|
106
|
|
107 /**********************************************************************************************
|
|
108 Uses the debugger's CLI arguments for the debuggee.
|
|
109 Example: debugger debuggee arg0 arg1 ...
|
|
110 **********************************************************************************************/
|
|
111 this(string debuggee, UserInterface _ui)
|
|
112 {
|
|
113 assert(_ui !is null);
|
|
114 images = new ImageSet;
|
|
115 ui = _ui;
|
|
116 openImage(debuggee);
|
|
117 source_search_paths ~= ".\\";
|
|
118 temp_breakpoints = new List!(Breakpoint);
|
|
119 }
|
|
120
|
|
121 CallStack stack()
|
|
122 {
|
|
123 if ( current_stack is null )
|
|
124 {
|
|
125 if ( process !is null )
|
|
126 current_stack = process.loadStack(process.threads[thread_id]);
|
|
127 else if ( miniDump !is null ) {
|
|
128 CONTEXT* ctx = miniDump.getContext;
|
|
129 // TODO: where does that 4-byte offset come from
|
|
130 ubyte[] data = miniDump.getMemory(miniDump.thread.Stack.Memory)[4..$];
|
|
131 current_stack = new CallStack(cast(size_t)miniDump.thread.Stack.StartOfMemoryRange+data.length, ctx.Esp, ctx.Ebp);
|
|
132 current_stack.data = data;
|
|
133 }
|
|
134 }
|
|
135 return current_stack;
|
|
136 }
|
|
137
|
|
138 /**********************************************************************************************
|
|
139
|
|
140 **********************************************************************************************/
|
|
141 bool step(StepMode mode)
|
|
142 {
|
|
143 if ( !process_loaded )
|
|
144 return false;
|
|
145 Location loc = images.findLocation(current_address);
|
|
146
|
|
147 int bpi=-1;
|
|
148 Breakpoint bp;
|
|
149
|
|
150 switch ( mode )
|
|
151 {
|
|
152 //-------------------------------------------------------------------------------------
|
|
153 case StepMode.e_in:
|
|
154 Location next_loc = images.findNextSrcLine(loc);
|
|
155 if ( next_loc !is null )
|
|
156 {
|
|
157 bp = setBreakpoint(next_loc, bpi, thread_id);
|
|
158 if ( bp !is null )
|
|
159 addForeachBreakpoint(bp);
|
|
160 }
|
|
161
|
|
162 size_t disasm_end;
|
|
163 if ( loc.codeblock !is null )
|
|
164 disasm_end = loc.codeblock.end+loc.getCodeBase;
|
|
165 DisAsm.disasm(
|
|
166 process, current_address, disasm_end,
|
|
167 (ud_t* ud_obj) { return setBranchBreakpoints(ud_obj,StepMode.e_in); }
|
|
168 );
|
|
169 break;
|
|
170 //-------------------------------------------------------------------------------------
|
|
171 case StepMode.e_out:
|
|
172 uint ret_adr = getReturnAddress(loc);
|
|
173 if ( ret_adr > 0 ) {
|
|
174 debug DbgIO.println("setting step-out to 0x%08x", ret_adr);
|
|
175 bp = setBreakpoint(ret_adr, bpi, thread_id);
|
|
176 if ( bp !is null )
|
|
177 bp.frame_ptr = getFramePointer(loc, 1);
|
|
178 }
|
|
179 else
|
|
180 {
|
|
181 debug DbgIO.println("no scope found - falling back on disasm-step-out");
|
|
182 DisAsm.disasm(
|
|
183 process, current_address, 0,
|
|
184 (ud_t* ud_obj) { return setBranchBreakpoints(ud_obj,StepMode.e_out); }
|
|
185 );
|
|
186 }
|
|
187 break;
|
|
188 //-------------------------------------------------------------------------------------
|
|
189 case StepMode.e_over:
|
|
190 Location next_loc = images.findNextSrcLine(loc);
|
|
191 if ( next_loc !is null )
|
|
192 {
|
|
193 bp = setBreakpoint(next_loc, bpi, thread_id);
|
|
194 if ( bp !is null )
|
|
195 {
|
|
196 bp.frame_ptr = getFramePointer(loc);
|
|
197 debug DbgIO.println("FP for step over is 0x%x", bp.frame_ptr);
|
|
198 addForeachBreakpoint(bp);
|
|
199 }
|
|
200 }
|
|
201
|
|
202 size_t disasm_end;
|
|
203 if ( loc.codeblock !is null )
|
|
204 disasm_end = loc.codeblock.end+loc.getCodeBase;
|
|
205 DisAsm.disasm(
|
|
206 process, current_address, disasm_end,
|
|
207 (ud_t* ud_obj) { return setBranchBreakpoints(ud_obj,StepMode.e_over); }
|
|
208 );
|
|
209 break;
|
|
210 default:
|
|
211 assert(0);
|
|
212 }
|
|
213
|
|
214 return true;
|
|
215 }
|
|
216
|
|
217 /**********************************************************************************************
|
|
218
|
|
219 **********************************************************************************************/
|
|
220 void addForeachBreakpoint(Breakpoint foreach_end)
|
|
221 {
|
|
222 CodeView cv;
|
|
223 CodeBlock cb = images.findCodeBlockAbs(current_address, cv);
|
|
224 if ( cb !is null )
|
|
225 {
|
|
226 CodeBlock cbs[] = cb.segment.file.blocks_by_line[cb.line];
|
|
227 if ( cbs.length > 1 && cb is cbs[0] )
|
|
228 {
|
|
229 Location loc = images.findLocation(cbs[1].start+cv.getCodeBase);
|
|
230 if ( loc.scope_sym !is null && find(loc.scope_sym.mangled_name, "__foreachbody") >= 0 )
|
|
231 {
|
|
232 int bpi;
|
|
233 if ( getBreakpoint(loc.address, bpi) is null ) {
|
|
234 Breakpoint bp = new Breakpoint(loc, true, thread_id);
|
|
235 bp.foreach_end = foreach_end;
|
|
236 foreach_end.foreach_end = foreach_end;
|
|
237 addBreakpoint(bp, -1);
|
|
238 }
|
|
239 }
|
|
240 }
|
|
241 }
|
|
242 }
|
|
243
|
|
244 /**********************************************************************************************
|
|
245
|
|
246 **********************************************************************************************/
|
|
247 uint getReturnAddress(Location loc)
|
|
248 {
|
|
249 ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym;
|
|
250 if ( psym !is null )
|
|
251 {
|
|
252 uint index;
|
|
253 ubyte[] frame = stack.firstFrame(index);
|
|
254
|
|
255 if ( frame is null )
|
|
256 throw new DebuggerException("getReturnAddress: null frame");
|
|
257
|
|
258 uint adr = current_address-psym.cvdata.offset-loc.getCodeBase;
|
|
259 if ( adr >= psym.cvdata.debug_start && adr < psym.cvdata.debug_end ) {
|
|
260 frame = stack.prevFrame(index, index);
|
|
261 if ( frame.length >= 8 )
|
|
262 return (cast(uint[])frame)[1];
|
|
263 }
|
|
264 }
|
|
265 return 0;
|
|
266 }
|
|
267
|
|
268 /**********************************************************************************************
|
|
269
|
|
270 **********************************************************************************************/
|
|
271 uint getFramePointer(Location loc, uint level=0)
|
|
272 {
|
|
273 ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym;
|
|
274 if ( psym !is null )
|
|
275 {
|
|
276 uint frame_ptr;
|
|
277 if ( level > 0 )
|
|
278 {
|
|
279 uint index,
|
|
280 l;
|
|
281 ubyte[] frame = stack.firstFrame(index);
|
|
282 if ( frame is null )
|
|
283 throw new DebuggerException("getCurrenFramePointer: null frame");
|
|
284
|
|
285 for ( l = 0; l < level && frame !is null; ++l )
|
|
286 frame = stack.prevFrame(index, index);
|
|
287 if ( frame is null )
|
|
288 throw new DebuggerException("getCurrenFramePointer: null frame at level %d", l);
|
|
289
|
|
290 frame_ptr = (cast(uint[])frame)[0];
|
|
291 }
|
|
292 else
|
|
293 frame_ptr = stack.frame_ptr;
|
|
294
|
|
295 uint adr = current_address-psym.cvdata.offset-loc.getCodeBase;
|
|
296 if ( adr >= psym.cvdata.debug_start && adr < psym.cvdata.debug_end )
|
|
297 return frame_ptr;
|
|
298 }
|
|
299 return 0;
|
|
300 }
|
|
301
|
|
302 /**********************************************************************************************
|
|
303
|
|
304 **********************************************************************************************/
|
|
305 SymbolData evaluateExpression(string expr, uint frame_level=0, NamedSymbol symbol=null)
|
|
306 {
|
|
307 SyntaxTree* root;
|
|
308 bool success;
|
|
309 try success = parse("", expr, root, true);
|
|
310 catch( ParserException e )
|
|
311 throw new EvaluationException("Parser: "~e.toString);
|
|
312 if ( !success )
|
|
313 throw new EvaluationException("Parser: Invalid expression!");
|
|
314
|
|
315 string elmtype;
|
|
316 uint scope_address;
|
|
317 if ( frame_level == 0 )
|
|
318 scope_address = current_address;
|
|
319 else {
|
|
320 ubyte[] frame = stack.getFrame(frame_level);
|
|
321 scope_address = (cast(uint[])frame)[1];
|
|
322 }
|
|
323
|
|
324 SymbolData sd;
|
|
325 auto loc = images.findLocation(current_address);
|
|
326 if ( loc.codeview is null )
|
|
327 throw new EvaluationException("No debug symbols available");
|
|
328 root.Expr(new EvaluationContext(
|
|
329 loc.codeview, scope_address,
|
|
330 miniDump, process, process is null?null:process.threads[thread_id],
|
|
331 stack, frame_level), sd
|
|
332 );
|
|
333 return sd;
|
|
334 }
|
|
335
|
|
336 /**********************************************************************************************
|
|
337
|
|
338 **********************************************************************************************/
|
|
339 SymbolValue handleData(SymbolData sd, bool decreaseED)
|
|
340 {
|
|
341 auto loc = images.findLocation(current_address);
|
|
342 if ( loc.codeview is null )
|
|
343 throw new EvaluationException("No debug symbols available");
|
|
344 EvaluationContext ctx = new EvaluationContext(
|
|
345 loc.codeview, 0,
|
|
346 miniDump, process, process is null?null:process.threads[thread_id],
|
|
347 stack, 0
|
|
348 );
|
|
349
|
|
350 ubyte[] data = sd.getData(ctx);
|
|
351 if ( data.length <= 0 )
|
|
352 throw new EvaluationException("Expression evaluated to empty data");
|
|
353
|
|
354 uint ed = evaluationDepth;
|
|
355 if ( decreaseED )
|
|
356 --ed;
|
|
357 SymbolValue val = DataHandler.handle(ctx, sd.type, data, ed);
|
|
358 if ( val is null )
|
|
359 throw new EvaluationException("No DataHandler for type "~sd.type);
|
|
360 return val;
|
|
361 }
|
|
362
|
|
363
|
|
364 /**********************************************************************************************
|
|
365
|
|
366 **********************************************************************************************/
|
|
367 uint getScopeAddress(uint frame_level)
|
|
368 {
|
|
369 uint scope_address;
|
|
370 if ( frame_level == 0 )
|
|
371 scope_address = current_address;
|
|
372 else {
|
|
373 ubyte[] frame = stack.getFrame(frame_level);
|
|
374 if ( frame !is null )
|
|
375 scope_address = (cast(uint[])frame)[1];
|
|
376 }
|
|
377 return scope_address;
|
|
378 }
|
|
379
|
|
380
|
|
381 //=============================================================================================
|
|
382 // process & thread handling
|
|
383
|
|
384 /**********************************************************************************************
|
|
385
|
|
386 **********************************************************************************************/
|
|
387 void selectThread(size_t threadId)
|
|
388 {
|
|
389 thread_id = threadId;
|
|
390 current_stack = null;
|
|
391
|
|
392 if ( process !is null ) {
|
|
393 CONTEXT ctx;
|
|
394 if ( !process.threads[threadId].getContext(ctx) )
|
|
395 throw new DebuggerException("Couldn't get thread's context");
|
|
396 current_address = ctx.Eip;
|
|
397 }
|
|
398 else if ( miniDump !is null ) {
|
|
399 miniDump.selectThread(threadId);
|
|
400 CONTEXT* ctx = miniDump.getContext;
|
|
401 current_address = ctx.Eip;
|
|
402 }
|
|
403 else
|
|
404 throw new DebuggerException("Invalid debugger state");
|
|
405 }
|
|
406
|
|
407 /**********************************************************************************************
|
|
408 Loads and parses the PE image.
|
|
409 Returns: success
|
|
410 **********************************************************************************************/
|
|
411 void openImage(string filename)
|
|
412 {
|
|
413 assert(filename.length);
|
|
414 filename = strip(filename);
|
|
415 if ( !exists(filename) )
|
|
416 filename ~= ".exe";
|
|
417 if ( !exists(filename) )
|
|
418 throw new DebuggerException("Couldn't open \"%s\"", filename[0..$-4]);
|
|
419 main_image_file = filename.dup;
|
|
420
|
|
421 main_image = new COFFImage;
|
|
422 main_image.load(main_image_file);
|
|
423 // catch ( Exception e ) {
|
|
424 // throw new DebuggerException("Failed to load COFF image \""~main_image_file~"\": "~e.msg);
|
|
425 // }
|
|
426 if ( main_image.codeView is null )
|
|
427 DbgIO.println("WARNING: No debugging symbols found in main image, try compiling and linking with -g");
|
|
428 images ~= main_image;
|
|
429 // return true;
|
|
430 }
|
|
431
|
|
432 /**********************************************************************************************
|
|
433 Creates the child process for the debuggee.
|
|
434 Returns: success
|
|
435 **********************************************************************************************/
|
|
436 bool createDebugProcess(string command_line)
|
|
437 {
|
|
438 // initialiZe process startup information
|
|
439 STARTUPINFOA* startInfo = new STARTUPINFOA;
|
|
440 startInfo.cb = STARTUPINFO.sizeof;
|
|
441 startInfo.dwFlags = STARTF_FORCEONFEEDBACK | STARTF_USESHOWWINDOW;
|
|
442 startInfo.wShowWindow = SW_SHOWNORMAL;
|
|
443
|
|
444 PROCESS_INFORMATION procInfo; // process info
|
|
445
|
|
446 // create process
|
|
447 uint flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
|
|
448 if ( create_new_console )
|
|
449 flags |= CREATE_NEW_CONSOLE;
|
|
450
|
|
451 bool suc = cast(bool)CreateProcessA(
|
|
452 toStringz(main_image_file), toStringz(command_line), cast(LPSECURITY_ATTRIBUTES)null,
|
|
453 cast(LPSECURITY_ATTRIBUTES)null, false, flags, null, cast(char*)null,
|
|
454 cast(LPSTARTUPINFOA)startInfo, cast(LPPROCESS_INFORMATION)&procInfo
|
|
455 );
|
|
456 if( !suc )
|
|
457 throw new DebuggerException("CreateProcess failed on %s", main_image_file);
|
|
458 assert(procInfo.dwProcessId);
|
|
459
|
|
460 // open process for all access
|
|
461 HANDLE hChildProcess = OpenProcess(PROCESS_ALL_ACCESS, false, procInfo.dwProcessId);
|
|
462 if( hChildProcess == null ) {
|
|
463 TerminateProcess(procInfo.hProcess, 0);
|
|
464 throw new DebuggerException("OpenProcess failed");
|
|
465 }
|
|
466 process_id = procInfo.dwProcessId;
|
|
467
|
|
468 CloseHandle(procInfo.hProcess);
|
|
469 return true;
|
|
470 }
|
|
471
|
|
472 /**********************************************************************************************
|
|
473
|
|
474 **********************************************************************************************/
|
|
475 void resume()
|
|
476 {
|
|
477 single_step = false;
|
|
478 paused = false;
|
|
479 }
|
|
480
|
|
481 //=============================================================================================
|
|
482 // exception handling
|
|
483
|
|
484 /**********************************************************************************************
|
|
485 Debugger's main loop. Handles win32 debugging events.
|
|
486 **********************************************************************************************/
|
|
487 void start(string command_line)
|
|
488 {
|
|
489 createDebugProcess(command_line);
|
|
490
|
|
491 while( !abort )
|
|
492 {
|
|
493 if( WaitForDebugEvent(&debug_event, win32.winbase.INFINITE) )
|
|
494 {
|
|
495 if( debug_event.dwProcessId != process_id ) {
|
|
496 debug DbgIO.println("WARNING: Exception for unhandled process received: PID=%d", debug_event.dwProcessId);
|
|
497 ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);
|
|
498 continue;
|
|
499 }
|
|
500
|
|
501 current_stack = null;
|
|
502 thread_id = debug_event.dwThreadId;
|
|
503 exceptionRecord = null;
|
|
504
|
|
505 bool exception_handled = true;
|
|
506 switch( debug_event.dwDebugEventCode )
|
|
507 {
|
|
508 case OUTPUT_DEBUG_STRING_EVENT:
|
|
509 char[] debug_string;
|
|
510 paused = false;
|
|
511
|
|
512 debug_string.length = debug_event.DebugString.nDebugStringLength;
|
|
513 process.readProcessMemory(cast(uint)debug_event.DebugString.lpDebugStringData, debug_string.ptr, debug_string.length);
|
|
514 if ( debug_event.DebugString.fUnicode )
|
|
515 DbgIO.println("WARNING: Unicode debug strings not implemented, yet. Output might be corrupted");
|
|
516
|
|
517 if ( debug_string.length > 3 && debug_string[0..4] == "DDL:" )
|
|
518 {
|
|
519 string cmdStr = debug_string[4..$];
|
|
520 if ( strip(cmdStr).length > 0 )
|
|
521 {
|
|
522 auto r = std.regexp.RegExp("(([^\" \\t]+)|(\"[^\"]+\"))+");
|
|
523 string[] cmd;
|
|
524 foreach ( m; r.search(strip(cmdStr)) )
|
|
525 cmd ~= r.match(0);
|
|
526 switch ( cmd[0] )
|
|
527 {
|
|
528 case "load":
|
|
529 DbgIO.println("loadImage not implemented, yet: %s %s", cmd[1], cmd[2]);
|
|
530 break;
|
|
531 default:
|
|
532 DbgIO.println("WARNING: Unknown DDL command recieved: %s", cmdStr);
|
|
533 break;
|
|
534 }
|
|
535 }
|
|
536 }
|
|
537 else if ( debug_string.length > 5 && debug_string[0..5] == "Ddbg:" )
|
|
538 {
|
|
539 ui.runCommands(debug_string[5..$]);
|
|
540 paused = true;
|
|
541 }
|
|
542 else
|
|
543 ui.debugString(debug_string);
|
|
544 break;
|
|
545
|
|
546 case EXCEPTION_DEBUG_EVENT:
|
|
547 exceptionRecord = &debug_event.Exception.ExceptionRecord;
|
|
548 exception_handled = handleExceptionEvent(exceptionRecord);
|
|
549 break;
|
|
550
|
|
551 case LOAD_DLL_DEBUG_EVENT:
|
|
552 DLL dll = process.loadDLL(&debug_event.LoadDll);
|
|
553 if ( dll.image !is null )
|
|
554 {
|
|
555 images ~= dll.image;
|
|
556 foreach ( bp; breakpoints )
|
|
557 {
|
|
558 if ( bp.location.address == 0 )
|
|
559 {
|
|
560 debug DbgIO.println("binding bp %s", bp.toString);
|
|
561 if ( bp.location.bind(images, source_search_paths) ) {
|
|
562 debug DbgIO.println("activating bp %s", bp.toString);
|
|
563 bp.activate(process);
|
|
564 }
|
|
565 }
|
|
566 }
|
|
567 }
|
|
568 ui.loadedDLL(dll);
|
|
569 paused = false;
|
|
570 break;
|
|
571
|
|
572 case UNLOAD_DLL_DEBUG_EVENT:
|
|
573 // DebugDLL* dll = process.findDLL(cast(uint)debug_event.LoadDll.lpBaseOfDll);
|
|
574 // ui.unloadedDLL(dll);
|
|
575 // images.remove(dll.image);
|
|
576 paused = false;
|
|
577 break;
|
|
578
|
|
579 case CREATE_THREAD_DEBUG_EVENT:
|
|
580 process.threads[debug_event.dwThreadId] = new DbgThread(debug_event.CreateThread.hThread, debug_event.dwThreadId);
|
|
581 paused = false;
|
|
582 break;
|
|
583
|
|
584 case EXIT_THREAD_DEBUG_EVENT:
|
|
585 process.threads.remove(debug_event.dwThreadId);
|
|
586 paused = false;
|
|
587 break;
|
|
588
|
|
589 case CREATE_PROCESS_DEBUG_EVENT:
|
|
590 process = new DbgProcess;
|
|
591 process.processId = process_id;
|
|
592 process.process_handle = debug_event.CreateProcessInfo.hProcess;
|
|
593 process.mainThreadId = debug_event.dwThreadId;
|
|
594 process.threads[debug_event.dwThreadId] = new DbgThread(debug_event.CreateProcessInfo.hThread, debug_event.dwThreadId);
|
|
595 process_loaded = true;
|
|
596
|
|
597 if ( main_image !is null && main_image.codeView !is null )
|
|
598 {
|
|
599 DataSymbol mainds;
|
|
600 if ( main_image.codeView.global_pub !is null )
|
|
601 mainds = main_image.codeView.global_pub.findDataSymbol("_main");
|
|
602 if ( mainds !is null )
|
|
603 {
|
|
604 int bpi=-1;
|
|
605 Breakpoint bp = setBreakpoint(mainds.offset+main_image.getCodeBase, bpi, 0, true);
|
|
606 bp.deactivate_d_exception_handler = true;
|
4
|
607 debug DbgIO.println("set breakpoint");
|
|
608 }
|
|
609 {
|
|
610 int bpi=-1;
|
|
611 // stop on first line
|
|
612 if (breakonmain)
|
|
613 {
|
|
614 breakonmain=false;
|
|
615 mainds = main_image.codeView.global_pub.findDataSymbol("__Dmain");
|
|
616 if ( mainds !is null )
|
|
617 {
|
|
618 Breakpoint bp = setBreakpoint(mainds.offset+main_image.getCodeBase, bpi, thread_id);
|
|
619 }
|
|
620 }
|
|
621
|
1
|
622 }
|
|
623 }
|
|
624
|
|
625 foreach ( bp; breakpoints )
|
|
626 {
|
|
627 if ( bp.location.address != 0 && !bp.activate(process) )
|
|
628 throw new DebuggerException("Error: couldn't write breakpoint opcode at %s:%d", bp.file, bp.line);
|
|
629 }
|
|
630 foreach ( bp; temp_breakpoints )
|
|
631 {
|
|
632 debug DbgIO.println("tbp activated %s", bp.value);
|
|
633 if ( bp.value.location.address != 0 && !bp.value.activate(process) )
|
|
634 throw new DebuggerException("Error: couldn't write breakpoint opcode at %s:%d", bp.value.file, bp.value.line);
|
|
635 }
|
|
636 break;
|
|
637
|
|
638 case EXIT_PROCESS_DEBUG_EVENT:
|
|
639 process_loaded = false;
|
|
640 delete process;
|
|
641 paused = true;
|
|
642 single_step = false;
|
|
643 ui.exitProcess;
|
|
644 break;
|
|
645
|
|
646 case RIP_EVENT:
|
|
647 debug DbgIO.println("DebugEvent: "~getEventName(debug_event.dwDebugEventCode));
|
|
648 paused = true;
|
|
649 break;
|
|
650
|
|
651 default:
|
|
652 throw new DebuggerException("Win32Debugger ERROR - unknown debug event: %d", debug_event.dwDebugEventCode);
|
|
653 }
|
|
654
|
|
655 if ( single_step )
|
|
656 {
|
|
657 ui.singleStep();
|
|
658 paused = true;
|
|
659 activateSingleStep(true);
|
|
660 }
|
|
661
|
|
662 if ( paused )
|
|
663 {
|
|
664 bool cont=false;
|
|
665 while ( !cont )
|
|
666 {
|
|
667 debug
|
|
668 cont = ui.readCommand();
|
|
669 else
|
|
670 {
|
|
671 try cont = ui.readCommand();
|
|
672 catch ( Exception e )
|
|
673 DbgIO.println("%s", e.msg);
|
|
674 }
|
|
675 }
|
|
676 paused = false;
|
|
677 }
|
|
678
|
|
679 if ( !abort )
|
|
680 {
|
|
681 exceptionRecord = null;
|
|
682 ContinueDebugEvent(
|
|
683 debug_event.dwProcessId, debug_event.dwThreadId,
|
|
684 exception_handled?DBG_CONTINUE:DBG_EXCEPTION_NOT_HANDLED
|
|
685 );
|
|
686 }
|
|
687 }
|
|
688 else
|
|
689 throw new DebuggerException("ERROR: WaitForDebugEvent failed: %s", lastError);
|
|
690 }
|
|
691 }
|
|
692
|
|
693 /**********************************************************************************************
|
|
694 Activates single step execution by setting the trap flag in
|
|
695 the eflags register of the currently considered thread.
|
|
696 **********************************************************************************************/
|
|
697 void activateSingleStep(bool enable)
|
|
698 {
|
|
699 if ( !(thread_id in process.threads) )
|
|
700 return;
|
|
701 if ( enable )
|
|
702 process.threads[thread_id].changeContext(delegate(ref CONTEXT ctx){ ctx.EFlags |= 1<<8; });
|
|
703 else if ( !enable )
|
|
704 process.threads[thread_id].changeContext(delegate(ref CONTEXT ctx){ ctx.EFlags &= ~(1<<8); });
|
|
705 single_step = enable;
|
|
706 }
|
|
707
|
|
708 /**********************************************************************************************
|
|
709
|
|
710 **********************************************************************************************/
|
|
711 void getExceptionNameMessage(size_t objPtr, out char[] class_name, out char[] msg)
|
|
712 {
|
|
713 // get thrown object's class name
|
|
714 ClassInfo ci = process.getClassInfo(objPtr);
|
|
715 class_name.length = ci.name.length;
|
|
716 process.readProcessMemory(cast(uint)ci.name.ptr, class_name.ptr, class_name.length);
|
|
717
|
|
718 // get object's data
|
|
719 uint[] objdata;
|
|
720 objdata.length = ci.init.length;
|
|
721 process.readProcessMemory(objPtr, objdata.ptr, objdata.length*uint.sizeof);
|
|
722
|
|
723 char[] name;
|
|
724 name ~= class_name;
|
|
725 do
|
|
726 {
|
|
727 if ( name == "object.Exception" )
|
|
728 {
|
|
729 msg.length = objdata[2];
|
|
730 process.readProcessMemory(objdata[3], msg.ptr, msg.length);
|
|
731 break;
|
|
732 }
|
|
733
|
|
734 ubyte[] data;
|
|
735 data.length = ClassInfo.classinfo.init.length;
|
|
736 process.readProcessMemory(cast(uint)cast(void*)ci.base, data.ptr, data.length);
|
|
737 ci = cast(ClassInfo)data.ptr;
|
|
738 name.length = ci.name.length;
|
|
739 process.readProcessMemory(cast(uint)ci.name.ptr, name.ptr, name.length);
|
|
740 }
|
|
741 while ( ci.base !is null );
|
|
742 }
|
|
743
|
|
744 /**********************************************************************************************
|
|
745
|
|
746 **********************************************************************************************/
|
|
747 bool handleExceptionEvent(EXCEPTION_RECORD* exrec)
|
|
748 {
|
|
749 current_address = cast(uint)exrec.ExceptionAddress;
|
|
750 Lswitch: switch ( exrec.ExceptionCode )
|
|
751 {
|
|
752 case EXCEPTION_BREAKPOINT:
|
|
753 debug DbgIO.println("EXCEPTION_BREAKPOINT");
|
|
754 int bp_index;
|
|
755 Breakpoint bp = getBreakpoint(current_address, bp_index);
|
|
756 if ( bp !is null ) {
|
|
757 handleBreakpoint(bp, bp_index);
|
|
758 last_line = bp.line;
|
|
759 }
|
|
760 else
|
|
761 ui.breakpoint(-1, new Breakpoint(images.findLocation(cast(uint)exrec.ExceptionAddress), false, 0, false), process.threads[thread_id]);
|
|
762 break;
|
|
763 case EXCEPTION_SINGLE_STEP:
|
|
764 debug DbgIO.println("EXCEPTION_SINGLE_STEP");
|
|
765 // check whether this is a HWBP or real single step
|
|
766 CONTEXT ctx;
|
|
767 process.threads[thread_id].getContext(ctx, CONTEXT_DEBUG_REGISTERS);
|
|
768
|
|
769 if ( (ctx.Dr6 & 0xf) > 0 )
|
|
770 {
|
|
771 debug DbgIO.println("Hardware breakpoint");
|
|
772 size_t dr = void;
|
|
773 if ( ctx.Dr6 & 1 )
|
|
774 dr = ctx.Dr0;
|
|
775 else if ( ctx.Dr6 & 2 )
|
|
776 dr = ctx.Dr1;
|
|
777 else if ( ctx.Dr6 & 4 )
|
|
778 dr = ctx.Dr2;
|
|
779 else if ( ctx.Dr6 & 8 )
|
|
780 dr = ctx.Dr3;
|
|
781 int bp_index;
|
|
782 foreach( i, bp; breakpoints )
|
|
783 {
|
|
784 if( !bp.hardware )
|
|
785 continue;
|
|
786 if ( dr == bp.address ) {
|
|
787 handleBreakpoint(bp, i);
|
|
788 break Lswitch;
|
|
789 }
|
|
790 }
|
|
791
|
|
792 ui.breakpoint(-1, new Breakpoint(images.findLocation(dr), false, 0, false), process.threads[thread_id]);
|
|
793 }
|
|
794 else
|
|
795 {
|
|
796 debug DbgIO.println("Single Step exception");
|
|
797 // pause if interactive single step is active
|
|
798 paused = single_step;
|
|
799
|
|
800 if ( passing_breakpoint !is null )
|
|
801 {
|
|
802 debug DbgIO.println("passing breakpoint");
|
|
803 activateSingleStep(false);
|
|
804 paused = false;
|
|
805 if ( process !is null && !passing_breakpoint.activate(process) )
|
|
806 throw new DebuggerException("ERROR: Failed to write breakpoint opcode at %s:%d", passing_breakpoint.file, passing_breakpoint.line);
|
|
807 passing_breakpoint = null;
|
|
808 }
|
|
809 }
|
|
810 break;
|
|
811 // ctrl+c
|
|
812 case DBG_CONTROL_C:
|
|
813 ui.userInterrupt();
|
|
814 paused = true;
|
|
815 break;
|
|
816 // D exceptions
|
|
817 case STATUS_DIGITAL_MARS_D_EXCEPTION:
|
|
818 // normal non-post-mortem mode
|
|
819 if ( process !is null && !debug_event.Exception.dwFirstChance )
|
|
820 {
|
|
821 char[] class_name, msg;
|
|
822 getExceptionNameMessage(exrec.ExceptionInformation[0], class_name, msg);
|
|
823 ui.exception(thread_id, class_name, msg, exrec.ExceptionInformation[0]);
|
|
824 paused = true;
|
|
825 return false;
|
|
826 }
|
|
827 // minidump mode
|
|
828 else if ( miniDump !is null )
|
|
829 {
|
|
830 string className,
|
|
831 message;
|
|
832 className = (cast(char*)exrec.ExceptionInformation[1])[0..exrec.ExceptionInformation[0]];
|
|
833 message = (cast(char*)exrec.ExceptionInformation[3])[0..exrec.ExceptionInformation[2]];
|
|
834 ui.exception(thread_id, className, message, 0);
|
|
835 paused = true;
|
|
836 return false;
|
|
837 }
|
|
838 paused = false;
|
|
839 return false;
|
|
840 default:
|
|
841 ui.win32exception(thread_id, exrec);
|
|
842 paused = true;
|
|
843 single_step = false;
|
|
844 return false;
|
|
845 }
|
|
846 return true;
|
|
847 }
|
|
848
|
|
849 /**********************************************************************************************
|
|
850
|
|
851 **********************************************************************************************/
|
|
852 void handleBreakpoint(Breakpoint bp, uint bp_index)
|
|
853 {
|
|
854 CONTEXT ctx;
|
4
|
855 debug DbgIO.println("handling breakpoint");
|
1
|
856 if ( !process.threads[thread_id].getContext(ctx) )
|
|
857 throw new DebuggerException("Error: Couldn't GetThreadContext to reset instruction pointer: %s", lastError);
|
|
858
|
|
859 if ( !bp.hardware )
|
|
860 {
|
|
861 if ( process !is null && !bp.deactivate(process) )
|
|
862 throw new DebuggerException("ERROR: Failed to write opcode for breakpoint at %s:%d", bp.file, bp.line);
|
|
863
|
|
864 // adjust for "int 3" instruction byte
|
|
865 --ctx.Eip;
|
|
866 if ( !process.threads[thread_id].setContext(ctx) )
|
|
867 throw new DebuggerException("Error: Couldn't SetThreadContext to reset instruction pointer: %s", lastError);
|
|
868 }
|
|
869
|
|
870 // check if we have a constrained thread
|
|
871 if ( bp.threadId > 0 && thread_id != bp.threadId )
|
|
872 {
|
|
873 debug DbgIO.println("skipping bp in incorrect thread %d", thread_id);
|
|
874 if ( !bp.hardware ) {
|
|
875 passing_breakpoint = bp;
|
|
876 activateSingleStep(true);
|
|
877 }
|
|
878 }
|
|
879 // check if breakpoint requires a certain frame
|
|
880 else if ( bp.frame_ptr > 0 && bp.frame_ptr != ctx.Ebp )
|
|
881 {
|
|
882 debug DbgIO.println("skipping bp in incorrect frame, Ebp=0x%x required=0x%x", ctx.Ebp, bp.frame_ptr);
|
|
883 if ( !bp.hardware ) {
|
|
884 passing_breakpoint = bp;
|
|
885 activateSingleStep(true);
|
|
886 }
|
|
887 }
|
|
888 // restore non-temporary breakpoints
|
|
889 else if ( !bp.temporary )
|
|
890 {
|
|
891 ui.breakpoint(bp_index, bp, process.threads[thread_id]);
|
|
892 paused = true;
|
|
893 if ( !bp.hardware ) {
|
|
894 passing_breakpoint = bp;
|
|
895 activateSingleStep(true);
|
|
896 }
|
|
897 }
|
|
898 // propagate breakpoints to jmp/call/ret destination
|
|
899 else if ( bp.propagate )
|
|
900 {
|
|
901 debug DbgIO.println("propagate");
|
|
902 foreach ( tbp; temp_breakpoints )
|
|
903 {
|
|
904 if ( tbp.value is bp ) {
|
|
905 temp_breakpoints.remove(tbp);
|
|
906 break;
|
|
907 }
|
|
908 }
|
|
909 DisAsm.disasm(process, current_address, 0, (ud_t* ud_obj){return setPropagatedBreakpoint(ud_obj,bp);});
|
|
910 }
|
|
911 // temporary breakpoints
|
|
912 else
|
|
913 {
|
|
914 if ( bp.deactivate_d_exception_handler ) {
|
|
915 debug DbgIO.println("deactivateDExceptionHandler");
|
|
916 deactivateDExceptionHandler();
|
|
917 }
|
|
918 else {
|
|
919 clearTemporaryBreakpoints();
|
|
920 paused = ui.breakpoint(-1, bp, process.threads[thread_id]);
|
|
921 }
|
|
922 }
|
|
923 }
|
|
924
|
|
925 /**********************************************************************************************
|
|
926
|
|
927 **********************************************************************************************/
|
|
928 void deactivateDExceptionHandler()
|
|
929 {
|
|
930 if ( main_image is null || main_image.codeView is null )
|
|
931 return;
|
|
932
|
|
933 // deactivate default D exception handling
|
|
934 bool found=false;
|
|
935 foreach ( ds; main_image.codeView.global_pub.data_symbols )
|
|
936 {
|
|
937 if ( ds.name_notype == "_no_catch_exceptions" ) {
|
|
938 uint address = ds.offset+main_image.getSectionBase(ds.cvdata.segment);
|
|
939 bool nce = true;
|
|
940 process.writeProcessMemory(address, &nce, bool.sizeof);
|
|
941 found = true;
|
|
942 break;
|
|
943 }
|
|
944 else if ( ds.name_notype == "_cr_trapExceptions"
|
|
945 || ds.name_notype == "_rt_trapExceptions" )
|
|
946 {
|
|
947 uint address = ds.offset+main_image.getSectionBase(ds.cvdata.segment);
|
|
948 bool nce = false;
|
|
949 process.writeProcessMemory(address, &nce, bool.sizeof);
|
|
950 found = true;
|
|
951 break;
|
|
952 }
|
|
953 }
|
|
954 if ( !found ) {
|
|
955 debug DbgIO.println("Neither Phobos nor Tango exception handler switch not found");
|
|
956 }
|
|
957 }
|
|
958
|
|
959 //=============================================================================================
|
|
960 // source&symbol handling
|
|
961
|
|
962 /**********************************************************************************************
|
|
963 Searches the cache for the given source file and loads it if nessecary.
|
|
964 Tries to load the source file from all source_search_paths.
|
|
965 Returns: Array of lines of the source file.
|
|
966 **********************************************************************************************/
|
|
967 string[] getSourceFile(string filename)
|
|
968 {
|
|
969 if ( filename is null || filename.length <= 0 )
|
|
970 return null;
|
|
971 if ( !(filename in source_files) )
|
|
972 {
|
|
973 string full = getFullSourcePath(filename);
|
|
974 if ( full is null || !exists(full) )
|
|
975 return null;
|
|
976 auto lines = splitlines(cast(string)read(full));
|
|
977 source_files[filename] = lines;
|
|
978 }
|
|
979 return source_files[filename];
|
|
980 }
|
|
981
|
|
982 /**********************************************************************************************
|
|
983 Tries to find the source file in all source_search_paths.
|
|
984 Returns: Full path of the file
|
|
985 **********************************************************************************************/
|
|
986 string getFullSourcePath(string filename)
|
|
987 {
|
|
988 string full = getFullPath(filename);
|
|
989 if ( exists(full) )
|
|
990 return full;
|
|
991 foreach ( string sp; source_search_paths )
|
|
992 {
|
4
|
993 debug DbgIO.println("searching for source file %s",sp~filename);
|
1
|
994 full = getFullPath(sp~filename);
|
|
995 if ( exists(full) )
|
|
996 return full;
|
|
997 }
|
|
998 return filename;
|
|
999 }
|
|
1000
|
|
1001
|
|
1002 //=============================================================================================
|
|
1003 // breakpoint handling
|
|
1004
|
|
1005 /**********************************************************************************************
|
|
1006
|
|
1007 **********************************************************************************************/
|
|
1008 bool setPropagatedBreakpoint(ud* ud_obj, Breakpoint prev_bp)
|
|
1009 {
|
|
1010 if ( ud_obj is null )
|
|
1011 return true;
|
|
1012
|
|
1013 int bpi=-1;
|
|
1014 Breakpoint bp;
|
|
1015 if ( DisAsm.isConditionalJump(ud_obj.mnemonic)
|
|
1016 || ud_obj.mnemonic == ud_mnemonic_code.UD_Ijmp
|
|
1017 || ud_obj.mnemonic == ud_mnemonic_code.UD_Icall )
|
|
1018 {
|
|
1019 bool is_imm=false;
|
|
1020 uint jmp_dest = DisAsm.calcJumpDestination(process, process.threads[thread_id], ud_obj, is_imm);
|
|
1021 assert(!is_imm);
|
|
1022 debug DbgIO.println("propagating jmp/call bp to 0x%x", jmp_dest);
|
|
1023 Location loc = images.findLocation(jmp_dest);
|
|
1024 if ( loc.file !is null )
|
|
1025 bp = setBreakpoint(jmp_dest, bpi, thread_id);
|
|
1026 else {
|
|
1027 debug DbgIO.println("not source info available - checking for unconditional jmp at destination");
|
|
1028 DisAsm.disasm(process, jmp_dest, 0, (ud_t* ud_obj){return propagateUncondJmp(ud_obj, prev_bp);});
|
|
1029 }
|
|
1030 if ( bp !is null )
|
|
1031 bp.frame_ptr = prev_bp.frame_ptr;
|
|
1032 }
|
|
1033 else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Iret || ud_obj.mnemonic == ud_mnemonic_code.UD_Iretf )
|
|
1034 {
|
|
1035 uint index;
|
|
1036 size_t ret_adr = (cast(uint[])stack.data)[0];
|
|
1037 Location loc = images.findLocation(ret_adr);
|
|
1038 if ( loc.file !is null )
|
|
1039 bp = setBreakpoint(ret_adr, bpi, thread_id);
|
|
1040 else
|
|
1041 debug DbgIO.println("not setting BP on unknown ret address 0x%x", ret_adr);
|
|
1042 if ( bp !is null )
|
|
1043 bp.frame_ptr = prev_bp.frame_ptr;
|
|
1044 }
|
|
1045 else {
|
|
1046 debug DbgIO.println("unknown instruction for propagating breakpoint");
|
|
1047 assert(0);
|
|
1048 }
|
|
1049
|
|
1050 return false;
|
|
1051 }
|
|
1052
|
|
1053 /**********************************************************************************************
|
|
1054 Propagate breakpoint if unconditional jump is found.
|
|
1055 Used for virtual functions with redirectors.
|
|
1056 **********************************************************************************************/
|
|
1057 bool propagateUncondJmp(ud* ud_obj, Breakpoint prev_bp)
|
|
1058 {
|
|
1059 if ( ud_obj is null )
|
|
1060 return true;
|
|
1061
|
|
1062 int bpi=-1;
|
|
1063 Breakpoint bp;
|
|
1064 if ( DisAsm.isConditionalJump(ud_obj.mnemonic)
|
|
1065 || ud_obj.mnemonic == ud_mnemonic_code.UD_Icall )
|
|
1066 {
|
|
1067 debug DbgIO.println("aborting propagateUncondJmp: condJmp or call");
|
|
1068 return false;
|
|
1069 }
|
|
1070 else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Ijmp )
|
|
1071 {
|
|
1072 bool is_imm=false;
|
|
1073 uint jmp_dest = DisAsm.calcJumpDestination(process, process.threads[thread_id], ud_obj, is_imm);
|
|
1074 debug DbgIO.println("propagating bp to 0x%x", jmp_dest);
|
|
1075 Location loc = images.findLocation(jmp_dest);
|
|
1076 if ( loc.file !is null )
|
|
1077 bp = setBreakpoint(jmp_dest, bpi, thread_id);
|
|
1078 else {
|
|
1079 debug DbgIO.println("not source info available - checking for unconditional jmp at destination");
|
|
1080 }
|
|
1081 if ( bp !is null )
|
|
1082 bp.frame_ptr = prev_bp.frame_ptr;
|
|
1083
|
|
1084 return false;
|
|
1085 }
|
|
1086 else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Iret || ud_obj.mnemonic == ud_mnemonic_code.UD_Iretf ) {
|
|
1087 debug DbgIO.println("aborting propagateUncondJmp: ret");
|
|
1088 return false;
|
|
1089 }
|
|
1090 else
|
|
1091 return true;
|
|
1092 }
|
|
1093
|
|
1094 /**********************************************************************************************
|
|
1095 If the opcode is an immediate jump or a call, sets a temporary breakpoint
|
|
1096 at it's destination. If it's a memory call, sets a call-propagating breakpoint at
|
|
1097 the call instruction.
|
|
1098 **********************************************************************************************/
|
|
1099 bool setBranchBreakpoints(ud_t* ud_obj, StepMode mode)
|
|
1100 {
|
|
1101 // first call
|
|
1102 if ( ud_obj is null )
|
|
1103 return true;
|
|
1104
|
|
1105 int bpi=-1;
|
|
1106 Breakpoint bp;
|
|
1107
|
|
1108 if ( DisAsm.isConditionalJump(ud_obj.mnemonic) || ud_obj.mnemonic == ud_mnemonic_code.UD_Ijmp )
|
|
1109 {
|
|
1110 if ( mode!=StepMode.e_out )
|
|
1111 {
|
|
1112 bool is_imm=true;
|
|
1113 uint jmp_dest = DisAsm.calcJumpDestination(process, process.threads[thread_id], ud_obj, is_imm);
|
|
1114 if ( is_imm )
|
|
1115 bp = setBreakpoint(jmp_dest, bpi, thread_id, false);
|
|
1116 else
|
|
1117 {
|
|
1118 // set propagating breakpoint
|
|
1119 bp = setBreakpoint(cast(size_t)ud_obj.insn_offset, bpi, thread_id, true);
|
|
1120 if ( bp !is null )
|
|
1121 bp.propagate = true;
|
|
1122 }
|
|
1123 }
|
|
1124 }
|
|
1125 else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Icall )
|
|
1126 {
|
|
1127 if ( mode==StepMode.e_in )
|
|
1128 {
|
|
1129 bool is_imm=true;
|
|
1130 uint jmp_dest = DisAsm.calcJumpDestination(process, process.threads[thread_id], ud_obj, is_imm);
|
|
1131 if ( is_imm )
|
|
1132 {
|
|
1133 Location loc = images.findLocation(jmp_dest);
|
|
1134 if ( loc.file !is null )
|
|
1135 bp = setBreakpoint(jmp_dest, bpi, thread_id, false);
|
|
1136 else
|
|
1137 debug DbgIO.println("not setting BP on unknown call destination 0x%x", jmp_dest);
|
|
1138 }
|
|
1139 else
|
|
1140 {
|
|
1141 // set propagating breakpoint
|
|
1142 bp = setBreakpoint(cast(size_t)ud_obj.insn_offset, bpi, thread_id, true);
|
|
1143 if ( bp !is null )
|
|
1144 bp.propagate = true;
|
|
1145 }
|
|
1146 }
|
|
1147 }
|
|
1148 else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Iret || ud_obj.mnemonic == ud_mnemonic_code.UD_Iretf )
|
|
1149 {
|
|
1150 if ( mode!=StepMode.e_out )
|
|
1151 {
|
|
1152 Location loc = images.findLocation(current_address);
|
|
1153 if ( loc.scope_sym !is null && find(loc.scope_sym.mangled_name, "__foreachbody") >= 0 )
|
|
1154 {
|
|
1155 ProcedureSymbol ps = cast(ProcedureSymbol)loc.scope_sym;
|
|
1156 if ( ps !is null )
|
|
1157 {
|
|
1158 setBreakpoint(ps.cvdata.offset+loc.getCodeBase, bpi, thread_id);
|
|
1159 uint ret_adr = getReturnAddress(images.findLocation(getReturnAddress(loc)));
|
|
1160 if ( ret_adr > 0 )
|
|
1161 setBreakpoint(ret_adr, bpi, thread_id);
|
|
1162 }
|
|
1163 }
|
|
1164 if ( mode != StepMode.e_over || loc.scope_sym is null || find(loc.scope_sym.mangled_name, "__foreachbody") < 0 )
|
|
1165 {
|
|
1166 uint ret_adr = getReturnAddress(loc);
|
|
1167 if ( ret_adr > 0 )
|
|
1168 {
|
|
1169 debug DbgIO.println("setBranchBPs: using return address from frame");
|
|
1170 loc = images.findLocation(ret_adr);
|
|
1171 if ( loc.file !is null )
|
|
1172 bp = setBreakpoint(ret_adr, bpi, thread_id, true);
|
|
1173 else
|
|
1174 debug DbgIO.println("not setting BP on unknown ret address 0x%x", ret_adr);
|
|
1175 }
|
|
1176 else
|
|
1177 {
|
|
1178 bp = setBreakpoint(cast(size_t)ud_obj.insn_offset, bpi, thread_id, true);
|
|
1179 if ( bp !is null )
|
|
1180 bp.propagate = true;
|
|
1181 }
|
|
1182 }
|
|
1183 }
|
|
1184 else
|
|
1185 {
|
|
1186 bp = setBreakpoint(cast(size_t)ud_obj.insn_offset, bpi, thread_id, true);
|
|
1187 if ( bp !is null )
|
|
1188 bp.propagate = true;
|
|
1189 }
|
|
1190 }
|
|
1191 return true;
|
|
1192 }
|
|
1193
|
|
1194 /**********************************************************************************************
|
|
1195 Removes breakpoint from list and deactivates it.
|
|
1196 Returns: success
|
|
1197 **********************************************************************************************/
|
|
1198 bool removeBreakpoint(uint bp_index)
|
|
1199 {
|
|
1200 if ( bp_index in breakpoints )
|
|
1201 {
|
|
1202 Breakpoint bp = breakpoints[bp_index];
|
|
1203 if ( process !is null && !bp.deactivate(process) )
|
|
1204 throw new DebuggerException("ERROR: Failed to write breakpoint opcode at %s:%d", bp.file, bp.line);
|
|
1205 breakpoints.remove(bp_index);
|
|
1206 if ( passing_breakpoint is bp ) {
|
|
1207 passing_breakpoint = null;
|
|
1208 activateSingleStep(false);
|
|
1209 }
|
|
1210 delete bp;
|
|
1211 return true;
|
|
1212 }
|
|
1213 return false;
|
|
1214 }
|
|
1215
|
|
1216 /**********************************************************************************************
|
|
1217
|
|
1218 **********************************************************************************************/
|
|
1219 void clearTemporaryBreakpoints()
|
|
1220 {
|
|
1221 debug DbgIO.println("clearing temporary breakpoints");
|
|
1222 if ( process !is null )
|
|
1223 {
|
|
1224 foreach ( bp; temp_breakpoints )
|
|
1225 {
|
|
1226 if ( bp.value.foreach_end !is null && bp.value.foreach_end.address != current_address )
|
|
1227 {
|
|
1228 if ( !bp.value.hardware )
|
|
1229 passing_breakpoint = bp.value;
|
|
1230 activateSingleStep(true);
|
|
1231 continue;
|
|
1232 }
|
|
1233 if ( !bp.value.deactivate(process) )
|
|
1234 throw new DebuggerException("ERROR: Failed to restore opcode for breakpoint at %s:%d", bp.value.file, bp.value.line);
|
|
1235 temp_breakpoints.remove(bp);
|
|
1236 }
|
|
1237 }
|
|
1238 }
|
|
1239
|
|
1240 /**********************************************************************************************
|
|
1241 Creates a breakpoint at the given location and adds it with the given index.
|
|
1242 Overwrites the breakpoint at the same index if existant.
|
|
1243 Adds a temporary breakpoint instead if index < 0.
|
|
1244 Reaturns: Existing breakpoint at the location or new breakpoint.
|
|
1245 **********************************************************************************************/
|
|
1246 Breakpoint setBreakpoint(Location loc, inout int new_index, uint threadId, bool hardware=false)
|
|
1247 {
|
|
1248 if ( loc is null )
|
|
1249 return null;
|
|
1250
|
|
1251 // lookup existing breakpoint
|
|
1252 Breakpoint bp;
|
|
1253 if ( new_index >= 0 )
|
|
1254 {
|
|
1255 int bp_index;
|
|
1256 bp = getBreakpoint(loc, bp_index);
|
|
1257 if( bp !is null ) {
|
|
1258 new_index = bp_index;
|
|
1259 return bp;
|
|
1260 }
|
|
1261 }
|
|
1262
|
|
1263 // create a breakpoint
|
|
1264 bp = new Breakpoint(loc, new_index<0, threadId, hardware);
|
|
1265 addBreakpoint(bp, new_index);
|
|
1266 return bp;
|
|
1267 }
|
|
1268
|
|
1269 /**********************************************************************************************
|
|
1270 Creates a breakpoint at the given address and adds it with the given index.
|
|
1271 Overwrites the breakpoint at the same index if existant.
|
|
1272 Adds a temporary breakpoint instead if index < 0.
|
|
1273 Reaturns: Existing breakpoint at the address or new breakpoint.
|
|
1274 **********************************************************************************************/
|
|
1275 Breakpoint setBreakpoint(size_t address, inout int new_index, uint threadId, bool set_on_current_line=false, bool hardware=false)
|
|
1276 {
|
|
1277 Location loc = images.findLocation(address);
|
|
1278
|
|
1279 Breakpoint bp;
|
|
1280 int bp_index;
|
|
1281 bp = getBreakpoint(address, bp_index);
|
|
1282 if( bp !is null ) {
|
|
1283 debug DbgIO.println("instead 0x%08x using existing breakpoint at 0x%08x", address, bp.address);
|
|
1284 new_index = bp_index;
|
|
1285 return bp;
|
|
1286 }
|
|
1287
|
|
1288 if ( !set_on_current_line && loc.codeblock !is null && current_address == loc.codeblock.start+loc.getCodeBase ) {
|
|
1289 debug DbgIO.println("not setting bp at current 0x%08x", address);
|
|
1290 return null;
|
|
1291 }
|
4
|
1292 debug DbgIO.println("setting breakpoint 0x%08x", address);
|
1
|
1293 bp = new Breakpoint(loc, new_index<0, threadId, hardware);
|
|
1294 addBreakpoint(bp, new_index);
|
|
1295 return bp;
|
|
1296 }
|
|
1297
|
|
1298 /**********************************************************************************************
|
|
1299 Adds the given breakpoint with at the given index.
|
|
1300 Adds it as temporary breakpoint if new_index < 0.
|
|
1301 Activates the breakpoint.
|
|
1302 **********************************************************************************************/
|
|
1303 void addBreakpoint(Breakpoint bp, int new_index)
|
|
1304 {
|
|
1305 // add to breakpoint list
|
4
|
1306 debug DbgIO.println("adding breakpoint 0x%08x", bp.address);
|
1
|
1307 if ( bp.temporary )
|
|
1308 temp_breakpoints ~= bp;
|
|
1309 else
|
|
1310 breakpoints[new_index] = bp;
|
|
1311
|
|
1312 // set breakpoing as active in debugger
|
|
1313 if ( process !is null && !bp.activate(process) )
|
|
1314 throw new DebuggerException("ERROR: Failed to write breakpoint opcode at %s:%d", bp.file, bp.line);
|
|
1315 }
|
|
1316
|
|
1317 /**********************************************************************************************
|
|
1318 Searches the breakpoints for one that is set at the given source
|
|
1319 location.
|
|
1320 Returns: Index of the breakpoint in the breakpoints array.
|
|
1321 **********************************************************************************************/
|
|
1322 Breakpoint getBreakpoint(Location loc, out int bp_index)
|
|
1323 {
|
|
1324 foreach( uint i, Breakpoint bp; breakpoints )
|
|
1325 {
|
|
1326 if( !bp.hardware && bp.file == loc.file && bp.line == loc.line
|
|
1327 || bp.hardware && bp.address == loc.address )
|
|
1328 {
|
|
1329 bp_index = i;
|
|
1330 return bp;
|
|
1331 }
|
|
1332 }
|
|
1333 foreach( bp; temp_breakpoints )
|
|
1334 {
|
|
1335 if( !bp.value.hardware && bp.value.file == loc.file && bp.value.line == loc.line
|
|
1336 || bp.value.hardware && bp.value.address == loc.address )
|
|
1337 {
|
|
1338 bp_index = -1;
|
|
1339 return bp.value;
|
|
1340 }
|
|
1341 }
|
|
1342 return getBreakpoint(loc.address, bp_index);
|
|
1343 }
|
|
1344
|
|
1345 /**********************************************************************************************
|
|
1346 Searches for a breakpoint at the given address.
|
|
1347 Returns: Index of breakpoint in breakpoints array.
|
|
1348 **********************************************************************************************/
|
|
1349 Breakpoint getBreakpoint(uint address, out int bp_index)
|
|
1350 {
|
|
1351 foreach ( uint i, Breakpoint bp; breakpoints )
|
|
1352 {
|
|
1353 if ( bp.address == address ) {
|
|
1354 bp_index = i;
|
|
1355 return bp;
|
|
1356 }
|
|
1357 }
|
|
1358 foreach ( bp; temp_breakpoints )
|
|
1359 {
|
|
1360 if ( bp.value.address == address ) {
|
|
1361 bp_index = -1;
|
|
1362 return bp.value;
|
|
1363 }
|
|
1364 }
|
|
1365 return null;
|
|
1366 }
|
|
1367
|
|
1368 //=============================================================================================
|
|
1369 // Minidumps
|
|
1370
|
|
1371 void writeMiniDump(string filename)
|
|
1372 {
|
|
1373 char[] class_name, msg;
|
|
1374 if ( exceptionRecord !is null
|
|
1375 && exceptionRecord.ExceptionCode != EXCEPTION_BREAKPOINT
|
|
1376 && exceptionRecord.ExceptionCode != EXCEPTION_SINGLE_STEP
|
|
1377 && exceptionRecord.ExceptionCode != DBG_CONTROL_C )
|
|
1378 {
|
|
1379 getExceptionNameMessage(exceptionRecord.ExceptionInformation[0], class_name, msg);
|
|
1380 }
|
|
1381 MiniDump.writeMiniDump(filename, process, thread_id, exceptionRecord, class_name, msg);
|
|
1382 }
|
|
1383
|
|
1384 void readMiniDump(string filename)
|
|
1385 {
|
|
1386 try {
|
|
1387 miniDump = new MiniDump(filename);
|
|
1388 selectThread(miniDump.threads[miniDump.selectedThread].ThreadId);
|
|
1389 if ( miniDump.exceptionRecord !is null )
|
|
1390 handleExceptionEvent(miniDump.exceptionRecord);
|
|
1391 else
|
|
1392 thread_id = miniDump.threadInfo.currentThreadId;
|
|
1393 //miniDump.threadInfo.mainThreadId
|
|
1394 }
|
|
1395 catch ( Exception e )
|
|
1396 DbgIO.writeln("Error: "~e.msg);
|
|
1397 //return miniDump !is null;
|
|
1398 }
|
|
1399 }
|