1
|
1 /* Ddbg - Win32 Debugger for the D programming language
|
|
2 * Copyright (c) 2007 Jascha Wetzel
|
|
3 * All rights reserved. See LICENSE.TXT for details.
|
|
4 */
|
|
5
|
|
6 /**************************************************************************************************
|
|
7 The main debugger code. Handles win32 debugging events,
|
|
8 manages breakpoints, calls the disassembler, etc.
|
|
9 **************************************************************************************************/
|
|
10
|
|
11 import std.file;
|
|
12 import std.string;
|
|
13 import std.format;
|
|
14
|
|
15 import win32.winbase;
|
|
16 import win32.winuser;
|
|
17 import win32.winnt;
|
|
18 import win32.dbghelp;
|
|
19
|
|
20 import minidump;
|
|
21 import container;
|
|
22 import util;
|
|
23 import breakpoint;
|
|
24 import dbgprocess;
|
|
25 import dbgthread;
|
|
26 import disasm;
|
|
27 import callstack;
|
|
28 import expression.evaluationcontext;
|
|
29 import expression.expression_apd;
|
|
30 import expression.datahandler;
|
|
31 import codeview.coff;
|
|
32 import codeview.codeview;
|
|
33 import cli.userinterface;
|
|
34
|
|
35 const uint VERSION_MAJOR = 0,
|
3
|
36 VERSION_MINOR = 13,
|
|
37 VERSION_PATCH = 1;
|
1
|
38 const string VERSION_STRING = "Ddbg "~itoa(VERSION_MAJOR)~"."~itoa(VERSION_MINOR)
|
|
39 ~(VERSION_PATCH>0?"."~itoa(VERSION_PATCH):"")~" beta",
|
|
40 WELCOME_STRING = VERSION_STRING~" - D Debugger\n"~
|
|
41 "Copyright (c) 2007 Jascha Wetzel\n"~
|
|
42 "see http://ddbg.mainia.de/doc.html for documentation\n";
|
|
43
|
|
44 class DebuggerException : Exception
|
|
45 {
|
|
46 this(...)
|
|
47 {
|
|
48 string str;
|
|
49
|
|
50 void putc(dchar c)
|
|
51 {
|
|
52 str ~= c;
|
|
53 }
|
|
54
|
|
55 doFormat(&putc, _arguments, _argptr);
|
|
56 super(str);
|
|
57 }
|
|
58 }
|
|
59
|
|
60 /**************************************************************************************************
|
|
61
|
|
62 **************************************************************************************************/
|
|
63 class Debugger
|
|
64 {
|
|
65 const uint STATUS_DIGITAL_MARS_D_EXCEPTION = (3 << 30) | (1 << 29) | (0 << 28) | ('D' << 16) | 1;
|
|
66
|
|
67 //=============================================================================================
|
|
68 // variables
|
|
69 bool paused,
|
|
70 process_loaded,
|
|
71 abort,
|
|
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 }
|