comparison src/debugger.d @ 1:4a9dcbd9e54f

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