Mercurial > projects > ddbg_continued
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 } |