comparison src/cli/ddbgcli.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 a5fb1bc967e6
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 module cli.ddbgcli;
6
7 import std.string;
8 static import std.regexp;
9 import std.c.stdio : sscanf;
10 import std.c.string;
11 import std.demangle;
12
13 import minidump;
14 import container;
15 import util;
16 import codeview.codeview;
17 import breakpoint;
18 import debugger;
19 import disasm;
20 import callstack;
21 import dbgprocess;
22 import dbgthread;
23 import expression.expression_apd;
24 import expression.datahandler : SymbolValue, DataHandler;
25 import expression.evaluationcontext;
26 import cli.userinterface;
27
28 import win32.winbase;
29 import win32.winuser;
30 import win32.winnt;
31 import win32.dbghelp;
32
33 /**************************************************************************************************
34
35 **************************************************************************************************/
36 class DdbgCLI : UserInterfaceBase
37 {
38 const int PRINT_SOURCE_LINES = 3;
39
40 string[] lastcmd,
41 onexCommands,
42 ontermCommands;
43 string debuggee,
44 command_line;
45
46 uint current_frame_level;
47
48 bool quit;
49
50 const ubyte CPU_REGISTERS = 1,
51 FPU_REGISTERS = 2,
52 MMX_REGISTERS = 4,
53 SSE_REGISTERS = 8;
54 ubyte dump_registers = CPU_REGISTERS;
55
56 uint lastEvaluationDepth = 1;
57
58 bool auto_find_scope_frame, /// find frame of last active scope if current frame has no source/symbols
59 jump_to_last_known_location_on_exception = true;
60
61 /**********************************************************************************************
62
63 **********************************************************************************************/
64 void init(string[] args)
65 {
66 DbgIO.println(WELCOME_STRING);
67 if ( args.length < 2 )
68 throw new DebuggerException("Usage: ddbg [-cli=<mode>] [-cmd=<commands>] <exe file> [arguments]");
69
70 debuggee = args[1];
71 foreach ( inout arg; args )
72 {
73 if ( find(arg, ' ') >= 0 )
74 arg = "\""~arg~"\"";
75 }
76 command_line = join(args[1..$], " ");
77
78 dbg = new Debugger(debuggee, this);
79 }
80
81 int start()
82 {
83 while ( !quit )
84 {
85 debug
86 readCommand();
87 else
88 {
89 try readCommand();
90 catch ( Exception e )
91 DbgIO.println(e.msg);
92 }
93 }
94 return 0;
95 }
96
97 /**********************************************************************************************
98
99 **********************************************************************************************/
100 void singleStep()
101 {
102 DbgIO.println(describeLocation(dbg.current_address));
103 }
104
105 /**********************************************************************************************
106
107 **********************************************************************************************/
108 void exitProcess()
109 {
110 DbgIO.println("Process terminated");
111 cmdQueue ~= ontermCommands;
112 }
113
114 /**********************************************************************************************
115
116 **********************************************************************************************/
117 void loadedDLL(DLL dll)
118 {
119 if( dll !is null && dll.image.name.length ) {
120 DbgIO.write(dll.image.name);
121 DbgIO.println(" loaded at 0x%08x", dll.base);
122 }
123 else
124 DbgIO.writeln("unknown DLL loaded");
125 }
126
127 /**********************************************************************************************
128
129 **********************************************************************************************/
130 void win32exception(uint threadId, EXCEPTION_RECORD* exrec)
131 {
132 DbgIO.println(
133 "Unhandled Exception: %s(0x%x) at %s thread(%d)",
134 getExceptionName(exrec.ExceptionCode), exrec.ExceptionCode, describeLocation(dbg.current_address), threadId
135 );
136 }
137
138 /**********************************************************************************************
139
140 **********************************************************************************************/
141 void exception(uint threadId, string class_name, string msg, size_t obj_ptr)
142 {
143 if ( jump_to_last_known_location_on_exception )
144 {
145 uint index;
146 current_frame_level = 0;
147 Location loc = dbg.images.findLocation(dbg.current_address);
148
149 if ( loc.codeblock is null )
150 {
151 dbg.stack.firstFrame(index);
152 ubyte[] frame = dbg.stack.prevFrame(index, index);
153 while ( loc.codeblock is null && frame !is null )
154 {
155 uint ret_adr = (cast(uint[])frame)[1];
156 loc = dbg.images.findLocation(ret_adr);
157 frame = dbg.stack.prevFrame(index, index);
158 ++current_frame_level;
159 }
160 }
161 }
162
163 DbgIO.println("Unhandled D Exception (%s", class_name);
164 DbgIO.write(msg.length>0?" \""~msg~"\"":"");
165 DbgIO.println(") at %s thread(%d)", describeLocation(dbg.current_address), threadId);
166 cmdQueue ~= onexCommands;
167 }
168
169 /**********************************************************************************************
170
171 **********************************************************************************************/
172 void userInterrupt()
173 {
174 DbgIO.println("User interrupt at %s", describeLocation(dbg.current_address));
175 }
176
177 /**********************************************************************************************
178
179 **********************************************************************************************/
180 bool breakpoint(int index, Breakpoint bp, DbgThread thread)
181 {
182 if ( bp.hardware )
183 {
184 DbgIO.println("Hardware breakpoint for 0x%x hit at %s thread(%d)",
185 bp.address, describeLocation(dbg.current_address), thread.id
186 );
187 }
188 else if ( bp.file is null || bp.line == 0 )
189 DbgIO.println("Unknown breakpoint hit at %s thread(%d)", describeLocation(bp.address), thread.id);
190 else
191 {
192 string[] source = dbg.getSourceFile(bp.file);
193 string source_line;
194 if ( source !is null && bp.line <= source.length )
195 source_line = source[bp.line-1];
196
197 if ( !bp.temporary )
198 DbgIO.print("Breakpoint %d hit at ", index);
199 DbgIO.println("%s:%d 0x%x thread(%d)", bp.file, bp.line, bp.address, thread.id);
200 if ( source_line.length > 0 )
201 DbgIO.writeln(source_line);
202 }
203 return true;
204 }
205
206 /**********************************************************************************************
207
208 **********************************************************************************************/
209 void debugString(string str)
210 {
211 printf("OUTPUT DEBUG STRING:\n%s\n", toStringz(str));
212 }
213
214 /**********************************************************************************************
215 Command line parser. Gets called when debuggee is suspended.
216 **********************************************************************************************/
217 bool parseCommand(string input)
218 {
219 if ( strip(input).length > 0 )
220 {
221 auto r = std.regexp.RegExp("(([^\" \\t]+)|(\"[^\"]+\"))+");
222 lastcmd.length = 0;
223 foreach ( m; r.search(input) )
224 lastcmd ~= r.match(0);
225 }
226 if ( lastcmd.length <= 0 )
227 return false;
228
229 switch ( lastcmd[0] )
230 {
231 case "help":
232 case "h":
233 case "?":
234 DbgIO.write(import("ddbg_help.txt"));
235 break;
236 case "=":
237 if ( !dbg.process_loaded && dbg.miniDump is null )
238 break;
239 if ( lastcmd.length > 1 )
240 {
241 try DbgIO.writeln(symbolValueToString(dbg.handleData(dbg.evaluateExpression(lastcmd[1], current_frame_level), false)));
242 catch ( EvaluationException e ) {
243 DbgIO.writeln(e.toString);
244 }
245 }
246 break;
247 case "arg":
248 if ( dbg.process_loaded )
249 DbgIO.println("Warning: process already started");
250 command_line = debuggee~" "~join(lastcmd[1..$], " ");
251 break;
252 // add breakpoint
253 case "bp":
254 int index;
255 if ( lastcmd.length < 2 ) {
256 DbgIO.println("invalid syntax - see help for details");
257 break;
258 }
259
260 int pos = find(lastcmd[1], '#');
261 uint threadId;
262 if ( pos > 0 )
263 {
264 threadId = cast(uint)atoi(lastcmd[1][pos+1..$]);
265 lastcmd[1] = lastcmd[1][0..pos];
266 }
267 Location loc = new Location(lastcmd[1]);
268 loc.bind(dbg.images, dbg.source_search_paths);
269 if ( lastcmd.length > 2 )
270 index = cast(int)atoi(lastcmd[2]);
271
272 Breakpoint bp;
273 if ( index <= 0 && dbg.breakpoints.length > 0 )
274 index = dbg.breakpoints.keys.dup.sort[$-1]+1;
275 bp = dbg.setBreakpoint(loc, index, threadId);
276 DbgIO.println("Breakpoint set: %s", bp.toString);
277 break;
278 // delete breakpoint
279 case "dbp":
280 if ( lastcmd.length > 1 )
281 {
282 if ( lastcmd[1] == "*" )
283 {
284 foreach ( uint i; dbg.breakpoints.keys )
285 dbg.removeBreakpoint(i);
286 }
287 else if ( isNumeric(lastcmd[1]) )
288 {
289 if ( !dbg.removeBreakpoint(cast(uint)atoi(lastcmd[1])) )
290 DbgIO.println("invalid breakpoint index: %s",lastcmd[1]);
291 }
292 else
293 {
294 Location loc = new Location(lastcmd[1]);
295 loc.bind(dbg.images, dbg.source_search_paths);
296 if ( loc !is null )
297 {
298 int bp_index;
299 dbg.getBreakpoint(loc, bp_index);
300 if ( bp_index >= 0 && !dbg.removeBreakpoint(bp_index) )
301 DbgIO.println("No breakpoint set at "~lastcmd[1]);
302 }
303 else
304 DbgIO.println("dbp [<source file>:<line>|#index|*]");
305 }
306 }
307 else
308 DbgIO.println("dbp [<source file>:<line>|#index|*]");
309 break;
310 // set evaluation depth
311 case "er":
312 lastcmd.length = 1;
313 if ( dbg.evaluationDepth > 1 ) {
314 lastEvaluationDepth = dbg.evaluationDepth;
315 lastcmd ~= "1";
316 }
317 else
318 lastcmd ~= .toString(lastEvaluationDepth);
319 case "ed":
320 if ( lastcmd.length < 2 || !isNumeric(lastcmd[1]) )
321 DbgIO.println("Usage: ed <depth>");
322 else {
323 dbg.evaluationDepth = cast(int)atoi(lastcmd[1]);
324 DbgIO.println("Expression evaluation depth is %d", dbg.evaluationDepth);
325 }
326 break;
327 case "el":
328 if ( lastcmd.length < 2 || !isNumeric(lastcmd[1]) )
329 DbgIO.println("Usage: el <length>");
330 else {
331 DataHandler.max_elem_count = cast(int)atoi(lastcmd[1]);
332 DbgIO.println("Array evaluation length is %d", DataHandler.max_elem_count);
333 }
334 break;
335 // select frame
336 case "f":
337 if ( lastcmd.length > 1 )
338 current_frame_level = cast(uint)atoi(lastcmd[1]);
339 DbgIO.println("Current frame level is %d", current_frame_level);
340 break;
341 // image base address
342 case "ii":
343 foreach ( img; dbg.images.images )
344 {
345 uint ibase = img.imageBase;
346 DbgIO.println("ImageBase\t0x%x", ibase);
347 DbgIO.println("CodeBase\t0x%x", img.getCodeBase);
348 DbgIO.println("Sections:\nname\t\taddress\t\tsize\t\tcharacteristics");
349 foreach ( s; img.sections )
350 with ( *s.header ) DbgIO.println("%s\t0x%08x\t0x%08x\t0x%08x", cast(char[8])Name, ibase+VirtualAddress, Misc.VirtualSize, Characteristics);
351 }
352 break;
353 // jump to last known source frame
354 case "jkf":
355 jump_to_last_known_location_on_exception = !jump_to_last_known_location_on_exception;
356 DbgIO.println("%s to from of last known source location on exception",
357 jump_to_last_known_location_on_exception?"Jumping":"Not jumping"
358 );
359 break;
360 // walk memory
361 case "mi":
362 if ( !dbg.process_loaded )
363 break;
364 uint filteredStates = MEM_COMMIT;
365 foreach ( lc; lastcmd[1..$] )
366 {
367 switch ( lc )
368 {
369 case "free": filteredStates |= MEM_FREE; break;
370 case "rsrv": filteredStates |= MEM_RESERVE; break;
371 default:
372 }
373 }
374
375 MEMORY_BASIC_INFORMATION[] mbis = dbg.process.walkMemory;
376
377 DbgIO.println("Base AlcBase AlcProt RgnSize Stat Protect Type");
378 foreach ( mbi; mbis )
379 {
380 if ( (mbi.State & filteredStates) == 0 )
381 continue;
382
383 string state;
384 switch ( mbi.State )
385 {
386 case MEM_COMMIT: state = "comt"; break;
387 case MEM_FREE: state = "free"; break;
388 case MEM_RESERVE: state = "rsrv"; break;
389 default: state = "unkn"; break;
390 }
391
392 string type;
393 switch ( mbi.Type )
394 {
395 case MEM_IMAGE: type = "imag"; break;
396 case MEM_MAPPED: type = "mapd"; break;
397 case MEM_PRIVATE: type = "priv"; break;
398 default: type = "unkn"; break;
399 }
400
401 string protectName(uint prot)
402 {
403 string protStr;
404 switch ( prot & 0xff )
405 {
406 case 0: protStr = "?"; break;
407 case PAGE_EXECUTE: protStr = "x"; break;
408 case PAGE_EXECUTE_READ: protStr = "xr"; break;
409 case PAGE_EXECUTE_READWRITE: protStr = "xrw"; break;
410 case PAGE_EXECUTE_WRITECOPY: protStr = "xwc"; break;
411 case PAGE_NOACCESS: protStr = "na"; break;
412 case PAGE_READONLY: protStr = "ro"; break;
413 case PAGE_READWRITE: protStr = "rw"; break;
414 case PAGE_WRITECOPY: protStr = "wc"; break;
415 default: protStr = format("%02x", prot & 0xff); break;
416 }
417 protStr ~= " ";
418 if ( prot & PAGE_GUARD )
419 protStr ~= "g";
420 if ( prot & PAGE_NOCACHE )
421 protStr ~= "nc";
422 return protStr;
423 }
424
425 DbgIO.println("%08x %08x %- 7s %08x %s %- 7s %s",
426 cast(size_t)mbi.BaseAddress, cast(size_t)mbi.AllocationBase, protectName(mbi.AllocationProtect),
427 mbi.RegionSize, state, protectName(mbi.Protect), type
428 );
429 }
430 break;
431 // list breakpoints
432 case "lbp":
433 if ( dbg.breakpoints.length <= 0 )
434 DbgIO.println("no breakpoints set");
435 foreach ( uint i, bp; dbg.breakpoints )
436 DbgIO.println("%d %s", i, bp.toString);
437 break;
438 // ldll
439 case "ldll":
440 if ( !dbg.process_loaded )
441 break;
442 DbgIO.println("Base Name");
443 foreach ( dll; dbg.process.loaded_dlls )
444 DbgIO.println("%08x %s", dll.base, dll.image.name);
445 break;
446 // list temporary breakpoints
447 case "ltbp":
448 if ( dbg.temp_breakpoints.empty )
449 DbgIO.println("no temporary breakpoints set");
450 foreach ( bp; dbg.temp_breakpoints )
451 DbgIO.writeln(bp.value.toString);
452 break;
453 // list debug modules
454 case "lm":
455 Module[] modules_noinfo;
456 foreach ( img; dbg.images.images )
457 {
458 foreach ( m; img.codeView.modulesByIndex )
459 {
460 string name = m.name;
461
462 if ( m.header.iLib > 0 )
463 name ~= " from "~img.codeView.libraries[m.header.iLib];
464
465 if ( lastcmd.length > 1 && find(name, lastcmd[1]) < 0 )
466 continue;
467
468 bool has_info = false;
469 with ( m.symbols )
470 {
471 if ( proc_symbols.length+stack_symbols.length+data_symbols.length > 0 )
472 {
473 DbgIO.println(
474 "%s\n\tSymbols: %d proc %d stack %d data",
475 name, proc_symbols.length, stack_symbols.length, data_symbols.length
476 );
477 has_info = true;
478 }
479 }
480 if ( m.source_module !is null )
481 {
482 DbgIO.println("\tSource files:");
483 has_info = true;
484 foreach ( sf; m.source_module.files )
485 DbgIO.println("\t\t%s", sf.name);
486 }
487 if ( !has_info )
488 modules_noinfo ~= m;
489 }
490 }
491 if ( modules_noinfo.length > 0 )
492 {
493 DbgIO.println("Modules without debug information:");
494 foreach ( img; dbg.images.images )
495 {
496 foreach ( m; modules_noinfo )
497 {
498 string name = m.name;
499
500 if ( m.header.iLib > 0 )
501 name ~= " from "~img.codeView.libraries[m.header.iLib];
502
503 DbgIO.println("%s #segs=%d", name, m.seginfos.length);
504 if ( m.source_module !is null )
505 {
506 DbgIO.println("\tSource files:");
507 foreach ( sf; m.source_module.files )
508 DbgIO.println("\t\t%s", sf.name);
509 }
510 }
511 }
512 }
513 break;
514 // list source modules
515 case "lsm":
516 uint[string] line_counters;
517 uint[string] seg_counters;
518 foreach ( img; dbg.images.images )
519 {
520 if ( img.codeView is null )
521 continue;
522 foreach ( m; img.codeView.modulesByIndex )
523 {
524 if ( m.source_module is null )
525 continue;
526 foreach ( sf; m.source_module.files )
527 {
528 if ( lastcmd.length > 1 && find(sf.name, lastcmd[1]) < 0 )
529 continue;
530 uint lines = sf.lines.length;
531 if ( (sf.name in line_counters) is null ) {
532 seg_counters[sf.name] = sf.segments.length;
533 line_counters[sf.name] = lines;
534 }
535 else {
536 seg_counters[sf.name] += sf.segments.length;
537 line_counters[sf.name] += lines;
538 }
539 }
540 }
541 }
542 string[] lc_keys = line_counters.keys.dup;
543 lc_keys.sort;
544 foreach ( key; lc_keys )
545 DbgIO.println("%s\t\tsegs=%d lines=%d", key, seg_counters[key], line_counters[key]);
546 break;
547 // list source lines per module
548 case "lsl":
549 foreach ( img; dbg.images.images )
550 {
551 if ( img.codeView is null )
552 continue;
553 foreach ( m; img.codeView.modulesByIndex )
554 {
555 if ( m.source_module is null )
556 continue;
557 DbgIO.println("module %s", m.name);
558 foreach ( sf; m.source_module.files )
559 {
560 DbgIO.println("file %s", sf.name);
561 if ( lastcmd.length > 1 && find(sf.name, lastcmd[1]) < 0 )
562 continue;
563 foreach ( l; sf.lines.reverse )
564 {
565 DbgIO.println("line %d", l);
566 }
567 }
568 }
569 }
570 break;
571 // list all symbols
572 case "ls":
573 /*
574 void dumpSymbolAVL(AVLNode!(NamedSymbol) node, uint indent=0)
575 {
576 string indentstr = new char[indent*2];
577 indentstr[0..indent*2] = ' ';
578 DbgIO.println("%s%s", indentstr, node.value.name_notype);
579 if ( node.left !is null )
580 dumpSymbolAVL(node.left, indent+1);
581 if ( node.right !is null )
582 dumpSymbolAVL(node.right, indent+1);
583 }
584 DbgIO.println("AVL dump:");
585 dumpSymbolAVL(dbg.image.codeView.globalNamedSymbols.root);
586 */
587 foreach ( img; dbg.images.images )
588 {
589 if ( img.codeView is null )
590 continue;
591 printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null);
592 printSymbols(img.codeView, img.codeView.global_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null);
593 printSymbols(img.codeView, img.codeView.static_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null);
594 foreach ( m; img.codeView.modulesByIndex )
595 printSymbols(img.codeView, m.symbols.named_symbols, lastcmd.length>1?lastcmd[1]:null);
596 }
597 break;
598 // list source search paths
599 case "lsp":
600 foreach ( s; dbg.source_search_paths )
601 DbgIO.writeln(s);
602 break;
603 // list function symbols
604 case "lf":
605 foreach ( img; dbg.images.images )
606 {
607 if ( img.codeView is null )
608 continue;
609 printSymbols(img.codeView, img.codeView.global_pub.proc_symbols, lastcmd.length>1?lastcmd[1]:null);
610 printSymbols(img.codeView, img.codeView.global_sym.proc_symbols, lastcmd.length>1?lastcmd[1]:null);
611 printSymbols(img.codeView, img.codeView.static_sym.proc_symbols, lastcmd.length>1?lastcmd[1]:null);
612 foreach ( m; img.codeView.modulesByIndex )
613 printSymbols(img.codeView, m.symbols.proc_symbols, lastcmd.length>1?lastcmd[1]:null);
614 }
615 break;
616 // list data symbols
617 case "ld":
618 foreach ( img; dbg.images.images )
619 {
620 if ( img.codeView is null )
621 continue;
622 printSymbols(img.codeView, img.codeView.global_pub.data_symbols, lastcmd.length>1?lastcmd[1]:null);
623 printSymbols(img.codeView, img.codeView.global_sym.data_symbols, lastcmd.length>1?lastcmd[1]:null);
624 printSymbols(img.codeView, img.codeView.static_sym.data_symbols, lastcmd.length>1?lastcmd[1]:null);
625 foreach ( m; img.codeView.modulesByIndex )
626 printSymbols(img.codeView, m.symbols.data_symbols, lastcmd.length>1?lastcmd[1]:null);
627 }
628 break;
629 // list global symbols
630 case "lg":
631 foreach ( img; dbg.images.images )
632 {
633 if ( img.codeView is null )
634 continue;
635 printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null);
636 printSymbols(img.codeView, img.codeView.global_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null);
637 }
638 break;
639 // list global publics
640 case "lp":
641 foreach ( img; dbg.images.images )
642 {
643 if ( img.codeView is null )
644 continue;
645 printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null);
646 }
647 break;
648 // list scope variables
649 case "lsv":
650 if ( dbg.process_loaded || dbg.miniDump !is null )
651 evalScopeSymbols();
652 break;
653 // list threads
654 case "lt":
655 if ( dbg.miniDump !is null )
656 {
657 DbgIO.println(" id pri sus location");
658 MINIDUMP_THREAD[] threads = dbg.miniDump.threads;
659 foreach ( thread; threads )
660 {
661 CONTEXT* ctx;
662 ctx = cast(CONTEXT*)dbg.miniDump.rvaToVa(thread.ThreadContext.Rva);
663 Location loc = dbg.images.findLocation(ctx.Eip);
664
665 DbgIO.println("%s%s%- 6d %- 3d %- 2d %s",
666 thread.ThreadId==dbg.thread_id?">":" ", thread.ThreadId==dbg.miniDump.threadInfo.mainThreadId?"*":" ",
667 thread.ThreadId, thread.Priority, thread.SuspendCount, describeLocation(loc)
668 );
669 }
670 }
671 else
672 {
673 DbgIO.println(" id pri sus creation exit kernel user location");
674 foreach ( t; dbg.process.threads.values )
675 {
676 // id name location pri pri-boost start-info times
677 ulong creation,
678 exit,
679 kernel,
680 user;
681
682 t.times(creation, exit, kernel, user);
683
684 CONTEXT ctx;
685 t.getContext(ctx);
686 Location loc = dbg.images.findLocation(ctx.Eip);
687
688 DbgIO.println("%s%s%- 6d %- 3d%s %- 2d %s %s %s %s %s",
689 t.id==dbg.thread_id?">":" ", t.id==dbg.process.mainThreadId?"*":" ", t.id,
690 t.priority, t.priorityBoost?"b":" ", t.suspendCount,
691 formatTicks(creation), formatTicks(exit), formatTicks(kernel), formatTicks(user),
692 describeLocation(loc)
693 );
694 }
695 }
696 break;
697 // no console
698 case "nc":
699 dbg.create_new_console = !dbg.create_new_console;
700 DbgIO.println("Starting debuggee in %s console", dbg.create_new_console?"new":"this");
701 break;
702 // on exception
703 case "onex":
704 if ( lastcmd.length < 2 )
705 {
706 foreach ( cmd; onexCommands )
707 DbgIO.writeln(cmd[0..$-1]);
708 }
709 else
710 {
711 onexCommands = null;
712 string cmdList;
713 foreach ( cmd; lastcmd[1..$] )
714 {
715 if ( cmd[0] == '"' && cmd[$-1] == '"' )
716 cmdList ~= " "~cmd[1..$-1];
717 else
718 cmdList ~= " "~cmd;
719 }
720 foreach ( cmd; split(cmdList, ";") )
721 onexCommands ~= strip(cmd)~";";
722 }
723 break;
724 // on termination
725 case "onterm":
726 if ( lastcmd.length < 2 )
727 {
728 foreach ( cmd; ontermCommands )
729 DbgIO.writeln(cmd[0..$-1]);
730 }
731 else
732 {
733 ontermCommands = null;
734 string cmdList;
735 foreach ( cmd; lastcmd[1..$] )
736 {
737 if ( cmd[0] == '"' && cmd[$-1] == '"' )
738 cmdList ~= " "~cmd[1..$-1];
739 else
740 cmdList ~= " "~cmd;
741 }
742 foreach ( cmd; split(cmdList, ";") )
743 ontermCommands ~= strip(cmd)~";";
744 }
745 break;
746 // print source
747 case "ps":
748 Location loc;
749 string[] source;
750
751 do
752 {
753 if ( current_frame_level > 0 )
754 {
755 auto frame = dbg.stack.getFrame(current_frame_level);
756 if ( frame is null )
757 goto Lnotfound;
758 uint ret_adr = (cast(uint[])frame)[1];
759 loc = dbg.images.findLocation(ret_adr);
760 }
761 else
762 loc = dbg.images.findLocation(dbg.current_address);
763 debug DbgIO.println("found location %s", loc.file);
764
765 source = dbg.getSourceFile(loc.file);
766 if ( source !is null )
767 break;
768 if ( auto_find_scope_frame ) {
769 ++current_frame_level;
770 continue;
771 }
772 Lnotfound:
773 DbgIO.writeln("Source file for current location unknown");
774 break;
775 }
776 while( source is null );
777
778 int start_line, end_line;
779 if ( lastcmd.length > 1 ) {
780 start_line = cast(uint)atoi(lastcmd[1]);
781 end_line = loc.line + start_line;
782 start_line = loc.line - start_line;
783 }
784 else if ( loc.scope_sym !is null )
785 {
786 ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym;
787 auto start_address = loc.getCodeBase + psym.cvdata.offset;
788 auto start_loc = dbg.images.findLocation(start_address);
789 auto end_loc = dbg.images.findLocation(start_address+psym.cvdata.proc_length-1);
790 debug DbgIO.println("address range: 0x%x 0x%x", start_address, start_address+psym.cvdata.proc_length-1);
791 debug DbgIO.println("lines before prev: %d - %d", start_loc.line, end_loc.line);
792 end_loc = dbg.images.findPrevSrcLine(end_loc);
793 start_line = start_loc.line;
794 if ( end_loc !is null )
795 end_line = end_loc.line;
796 }
797 if ( end_line == 0 ) {
798 start_line = loc.line - PRINT_SOURCE_LINES;
799 end_line = loc.line + PRINT_SOURCE_LINES;
800 }
801
802 debug DbgIO.println("lines: %d - %d", start_line, end_line);
803 for ( int l = dmax(0, start_line-1); l < dmin(cast(int)source.length, end_line); ++l )
804 {
805 if ( l+1 == loc.line )
806 DbgIO.write(">");
807 DbgIO.writeln(source[l]);
808 }
809 break;
810 // quit
811 case "q":
812 dbg.abort = true;
813 quit = true;
814 return true;
815 // run/continue
816 case "r":
817 if ( dbg.miniDump !is null ) {
818 DbgIO.println("Command not valid in post-mortem mode");
819 break;
820 }
821 if ( dbg.process_loaded ) {
822 dbg.resume;
823 return true;
824 }
825 else {
826 dbg.start(command_line);
827 return true;
828 }
829 // single-step
830 case "s":
831 if ( dbg.miniDump !is null ) {
832 DbgIO.println("Command not valid in post-mortem mode");
833 break;
834 }
835 if ( dbg.process_loaded ) {
836 dbg.single_step = true;
837 dbg.activateSingleStep(true);
838 }
839 return true;
840 // add source search path
841 case "sp":
842 if ( lastcmd.length > 1 )
843 {
844 string sp = lastcmd[1].dup;
845 if ( sp[$-1] != '\\' )
846 sp ~= '\\';
847 dbg.source_search_paths ~= sp;
848 }
849 else
850 DbgIO.println("usage: sp <search_path>");
851 break;
852 // switch thread
853 case "st":
854 if ( lastcmd.length < 2 ) {
855 DbgIO.writeln("Usage: st <threadID>");
856 break;
857 }
858 size_t threadId = cast(size_t)atoi(lastcmd[1]);
859 if ( dbg.miniDump !is null )
860 {
861 foreach ( thread; dbg.miniDump.threads )
862 {
863 if ( threadId == thread.ThreadId ) {
864 dbg.selectThread(threadId);
865 break;
866 }
867 }
868 }
869 else
870 {
871 foreach ( t; dbg.process.threads.values )
872 {
873 if ( threadId == t.id ) {
874 dbg.selectThread(threadId);
875 break;
876 }
877 }
878 }
879 break;
880 // step over
881 case "ov":
882 if ( dbg.miniDump !is null ) {
883 DbgIO.println("Command not valid in post-mortem mode");
884 break;
885 }
886 if ( dbg.process_loaded && dbg.step(StepMode.e_over) )
887 {
888 debug foreach ( bp; dbg.temp_breakpoints )
889 DbgIO.writeln(bp.value.toString);
890 return true;
891 }
892 break;
893 // step into
894 case "in":
895 if ( dbg.miniDump !is null ) {
896 DbgIO.println("Command not valid in post-mortem mode");
897 break;
898 }
899 if ( dbg.process_loaded && dbg.step(StepMode.e_in) )
900 {
901 debug foreach ( bp; dbg.temp_breakpoints )
902 DbgIO.writeln(bp.value.toString);
903 return true;
904 }
905 break;
906 // step out
907 case "out":
908 if ( dbg.miniDump !is null ) {
909 DbgIO.println("Command not valid in post-mortem mode");
910 break;
911 }
912 if ( dbg.process_loaded && dbg.step(StepMode.e_out) )
913 {
914 debug foreach ( bp; dbg.temp_breakpoints )
915 DbgIO.writeln(bp.value.toString);
916 return true;
917 }
918 break;
919 // disassemble
920 case "da":
921 if ( !dbg.process_loaded )
922 break;
923
924 uint start_address;
925 if ( current_frame_level > 0 ) {
926 auto frame = dbg.stack.getFrame(current_frame_level);
927 start_address = (cast(uint[])frame)[1];
928 }
929 else
930 start_address = dbg.current_address;
931
932 if ( lastcmd.length > 1 )
933 sscanf(toStringz(lastcmd[1]), "%x", &start_address);
934 else
935 {
936 Location loc = dbg.images.findLocation(dbg.current_address);
937 if ( loc.scope_sym !is null ) {
938 ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym;
939 start_address = loc.getCodeBase + psym.cvdata.offset;
940 DisAsm.disasm(dbg.process, start_address, start_address+psym.cvdata.proc_length, &printDisassembly);
941 break;
942 }
943 }
944 DisAsm.disasm(dbg.process, start_address, 0, &printDisassembly);
945 break;
946 // disassemble line
947 case "dal":
948 if ( !dbg.process_loaded )
949 break;
950
951 int num_lines = 1;
952 if ( lastcmd.length > 1 )
953 {
954 sscanf(toStringz(lastcmd[1]), "%d", &num_lines);
955 if ( num_lines < 0 )
956 --num_lines;
957 }
958
959 uint start_address;
960 if ( current_frame_level > 0 ) {
961 auto frame = dbg.stack.getFrame(current_frame_level);
962 start_address = (cast(uint[])frame)[1];
963 }
964 else
965 start_address = dbg.current_address;
966
967 for ( ; num_lines != 0; num_lines<0?++num_lines:--num_lines )
968 {
969 Location loc = dbg.images.findLocation(start_address);
970 if ( loc is null ) {
971 DbgIO.println("No source line information available");
972 break;
973 }
974 DisAsm.disasm(
975 dbg.process,
976 loc.getCodeBase+loc.codeblock.start,
977 loc.getCodeBase+loc.codeblock.end,
978 &printDisassembly
979 );
980 }
981 break;
982 // dump registers
983 case "dr":
984 if ( lastcmd.length > 1 )
985 {
986 foreach ( lc; lastcmd[1..$] )
987 {
988 switch ( lc )
989 {
990 case "cpu": dump_registers |= CPU_REGISTERS; break;
991 case "fpu": dump_registers |= FPU_REGISTERS; break;
992 case "mmx": dump_registers |= MMX_REGISTERS; break;
993 case "sse": dump_registers |= SSE_REGISTERS; break;
994 default:
995 DbgIO.println("Unknown register set \"%s\"", lc);
996 }
997 }
998 }
999 if ( dump_registers == 0 ) {
1000 DbgIO.println("No register set selected. See help for usage of \"dr\"");
1001 break;
1002 }
1003 if ( dbg.miniDump !is null )
1004 printRegisterDump(dbg.miniDump.getContext);
1005 else if ( dbg.process_loaded )
1006 printRegisterDump(dbg.process.threads[dbg.thread_id]);
1007 break;
1008 // dump stack
1009 case "ds":
1010 if ( !dbg.process_loaded )
1011 break;
1012
1013 int dump_length;
1014 if ( lastcmd.length > 1 )
1015 dump_length = cast(int)atoi(lastcmd[1])*4;
1016 else
1017 {
1018 CONTEXT ctx;
1019 if ( dbg.process.threads[dbg.thread_id].getContext(ctx, CONTEXT_CONTROL) )
1020 dump_length = ctx.Ebp-ctx.Esp+8;
1021 if ( dump_length <= 0 )
1022 dump_length = 16*4;
1023 }
1024 int top = dbg.stack.data.length>dump_length?dump_length:dbg.stack.data.length;
1025 dumpMemory(dbg.stack.top_ptr, top, dbg.stack.data);
1026 break;
1027 case "dm":
1028 if ( !dbg.process_loaded )
1029 break;
1030
1031 if ( lastcmd.length < 3 ) {
1032 DbgIO.println("usage: dm <start> <length>");
1033 break;
1034 }
1035 uint start;
1036 sscanf(toStringz(lastcmd[1]), "%x", &start);
1037 dumpMemory(start, cast(uint)atoi(lastcmd[2]));
1038 break;
1039 case "hwbp":
1040 int index;
1041 if ( lastcmd.length < 2 ) {
1042 DbgIO.println("invalid syntax - see help for details");
1043 break;
1044 }
1045
1046 int pos = find(lastcmd[1], '#');
1047 uint threadId;
1048 if ( pos > 0 )
1049 {
1050 threadId = cast(uint)atoi(lastcmd[1][pos+1..$]);
1051 lastcmd[1] = lastcmd[1][0..pos];
1052 }
1053
1054 Location loc;
1055
1056 size_t address;
1057 sscanf(toStringz(lastcmd[1]), "%x", &address);
1058 if ( address > 0 )
1059 loc = new Location(address);
1060 else
1061 loc = new Location(lastcmd[1]);
1062 loc.bind(dbg.images, dbg.source_search_paths);
1063 if ( lastcmd.length > 2 )
1064 index = cast(int)atoi(lastcmd[2]);
1065
1066 Breakpoint bp;
1067 if ( index <= 0 && dbg.breakpoints.length > 0 )
1068 index = dbg.breakpoints.keys.dup.sort[$-1]+1;
1069 bp = dbg.setBreakpoint(loc, index, threadId, true);
1070 DbgIO.println("Hardware Breakpoint set: %s", bp.toString);
1071 break;
1072 // type of expression
1073 case "t":
1074 if ( dbg.process_loaded && lastcmd.length > 1 )
1075 {
1076 SymbolData sd;
1077 try {
1078 sd = dbg.evaluateExpression(lastcmd[1], current_frame_level);
1079 string type = demangle("_D0"~sd.type);
1080 DbgIO.println("%s\n%s", sd.type, type);
1081 }
1082 catch ( EvaluationException e ) {
1083 DbgIO.writeln(e.toString);
1084 }
1085 }
1086 break;
1087 // unwind stack
1088 case "us":
1089 if ( dbg.process_loaded || dbg.miniDump !is null )
1090 unwindStack();
1091 break;
1092 // write minidump
1093 case "wmd":
1094 if ( dbg.miniDump !is null ) {
1095 DbgIO.println("Command not valid in post-mortem mode");
1096 break;
1097 }
1098 if ( !dbg.process_loaded )
1099 break;
1100 if ( !MiniDump.haveMiniDump ) {
1101 DbgIO.println("DbgHelp.dll required for MiniDump support.\nInstall the Microsoft Platform SDK or Windows XP.");
1102 break;
1103 }
1104 if ( lastcmd.length < 2 ) {
1105 DbgIO.println("Usage: wmd <filename>");
1106 break;
1107 }
1108 dbg.writeMiniDump(lastcmd[1]);
1109 break;
1110 // read minidump
1111 case "rmd":
1112 if ( dbg.process_loaded ) {
1113 DbgIO.println("For post-mortem debugging, the process must not be started");
1114 break;
1115 }
1116 if ( !MiniDump.haveMiniDump ) {
1117 DbgIO.println("DbgHelp.dll required for MiniDump support.\nInstall the Microsoft Platform SDK or Windows XP.");
1118 break;
1119 }
1120 if ( lastcmd.length < 2 ) {
1121 DbgIO.println("Usage: rmd <filename>");
1122 break;
1123 }
1124 dbg.readMiniDump(lastcmd[1]);
1125 break;
1126 // unknown command
1127 default:
1128 DbgIO.println("Unknown command '%s' ignored!", lastcmd[0]);
1129 break;
1130 }
1131
1132 return false;
1133 }
1134
1135 /**********************************************************************************************
1136
1137 **********************************************************************************************/
1138 void dumpMemory(uint start, uint length, ubyte[] _data=null)
1139 {
1140 ubyte[] data;
1141 if ( _data is null )
1142 {
1143 data.length = length;
1144 if ( !dbg.process.readProcessMemory(start, data.ptr, data.length) )
1145 return;
1146 }
1147 else
1148 data = _data;
1149 for ( uint i = 0; i < length; ++i )
1150 {
1151 if ( i % (4*4) == 0 )
1152 {
1153 if ( i > 0 )
1154 DbgIO.println("");
1155 DbgIO.print("%08x:", start+i);
1156 }
1157 if ( i % 4 == 0 )
1158 DbgIO.print(" ");
1159 DbgIO.print("%02x", data[i]);
1160 }
1161 DbgIO.println("");
1162 }
1163
1164 /**********************************************************************************************
1165
1166 **********************************************************************************************/
1167 void evalScopeSymbols()
1168 {
1169 uint scope_address = dbg.getScopeAddress(current_frame_level);
1170
1171 ScopeSymbol scope_sym = dbg.images.findProcedureSymbol(scope_address);
1172 if ( scope_sym is null ) {
1173 DbgIO.println("No valid scope active");
1174 return;
1175 }
1176
1177 for ( ; scope_sym !is null; scope_sym = scope_sym.parent_scope )
1178 {
1179 DbgIO.println("Scope: %s, parent: %x", scope_sym.name_type, cast(void*)scope_sym.parent_scope);
1180
1181 StackSymbol[] locals_args = scope_sym.symbols.stack_symbols;
1182 auto psym = cast(ProcedureSymbol)scope_sym;
1183 if ( psym !is null )
1184 locals_args ~= psym.arguments.stack_symbols;
1185 foreach ( sym; locals_args.sort )
1186 {
1187 string name = sym.mangled_name;
1188 DbgIO.print("%s = ", name);
1189 // DbgIO.print("%s = [ebp%s%d] ", name, sym.cvdata.offset>0?"+":"", sym.cvdata.offset);
1190 try DbgIO.writeln(symbolValueToString(dbg.handleData(dbg.evaluateExpression(name, current_frame_level, sym), true)));
1191 catch ( EvaluationException e ) {
1192 DbgIO.println(e.msg);
1193 }
1194 }
1195 }
1196 }
1197
1198 /**********************************************************************************************
1199
1200 **********************************************************************************************/
1201 void printAsmLine(uint address, string bytes, string asmsource, string symbol, string location, string source)
1202 {
1203 bool nl = false;
1204 if ( location !is null ) {
1205 DbgIO.print("%s ", location);
1206 nl = true;
1207 }
1208 if ( source !is null ) {
1209 DbgIO.write(source);
1210 nl = true;
1211 }
1212 if ( nl )
1213 DbgIO.println;
1214
1215 // align next column
1216 char[] indent;
1217 int indcount = 2*12-bytes.length;
1218 if ( indcount > 0 ) {
1219 indent.length = indcount;
1220 indent[0..indcount] = ' ';
1221 }
1222 // print aligned asm source
1223 assert(asmsource !is null);
1224 assert(bytes !is null);
1225 DbgIO.print("%08x: ", address, bytes, indent, asmsource);
1226
1227 if ( symbol !is null )
1228 DbgIO.write(symbol);
1229 DbgIO.println;
1230 }
1231
1232 /**********************************************************************************************
1233 Prints the register contents of the given thread's context.
1234 **********************************************************************************************/
1235 void printRegisterDump(DbgThread thread)
1236 {
1237 CONTEXT ctxMem;
1238 uint context_flags;
1239 if ( dump_registers & CPU_REGISTERS )
1240 context_flags |= CONTEXT_FULL;
1241 if ( dump_registers & FPU_REGISTERS )
1242 context_flags |= CONTEXT_FLOATING_POINT;
1243 if ( dump_registers & (MMX_REGISTERS|SSE_REGISTERS) )
1244 context_flags |= CONTEXT_EXTENDED_REGISTERS;
1245 if ( thread.getContext(ctxMem, context_flags) )
1246 printRegisterDump(&ctxMem);
1247 else
1248 DbgIO.println("ERROR: Couldn't get main thread's context");
1249 }
1250
1251 void printRegisterDump(CONTEXT* ctx)
1252 {
1253 assert ( ctx !is null );
1254
1255 bool first = true;
1256
1257 if ( dump_registers & CPU_REGISTERS )
1258 {
1259 DbgIO.println("EAX = %08x\tEBX = %08x\tECX = %08x\tEDX = %08x", ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx);
1260 DbgIO.println("EDI = %08x\tESI = %08x\tEBP = %08x\tESP = %08x", ctx.Edi, ctx.Esi, ctx.Ebp, ctx.Esp);
1261 DbgIO.println("EIP = %08x\tEFL = %08x", ctx.Eip, ctx.EFlags);
1262 DbgIO.println(" CS = %08x\t DS = %08x\t ES = %08x\t FS = %08x", ctx.SegCs, ctx.SegDs, ctx.SegEs, ctx.SegFs);
1263 DbgIO.println(" GS = %08x\t SS = %08x", ctx.SegGs, ctx.SegSs);
1264 first = false;
1265 }
1266
1267 if ( dump_registers & FPU_REGISTERS )
1268 {
1269 if ( !first )
1270 DbgIO.println();
1271 DbgIO.println("FCW = %04x\tFSW = %04x\tFTW = %04x\tFOP = %04x",
1272 cast(ushort)ctx.FloatSave.ControlWord, cast(ushort)ctx.FloatSave.StatusWord,
1273 cast(ushort)ctx.FloatSave.TagWord, (cast(ushort[])ctx.ExtendedRegisters)[3]
1274 );
1275 DbgIO.println("IP = %08x\tCS = %04x\tDP = %08x\tDS = %04x",
1276 (cast(uint[])ctx.ExtendedRegisters)[2], (cast(ushort[])ctx.ExtendedRegisters)[6],
1277 (cast(uint[])ctx.ExtendedRegisters)[4], (cast(ushort[])ctx.ExtendedRegisters)[10]
1278 );
1279 for ( int i = 0; i < 8; ++i )
1280 DbgIO.println("ST%d = % .16e", i, (cast(real[])ctx.FloatSave.RegisterArea)[i]);
1281 first = false;
1282 }
1283
1284 if ( dump_registers & MMX_REGISTERS )
1285 {
1286 if ( !first )
1287 DbgIO.println();
1288 for ( int i = 0; i < 8; ++i )
1289 {
1290 DbgIO.println("MM%d = %016x", i, (cast(long[])ctx.ExtendedRegisters)[4+i*2]);
1291 DbgIO.println(" = [%.6g, %.6g]",
1292 (cast(float[])ctx.ExtendedRegisters)[8+i*4],
1293 (cast(float[])ctx.ExtendedRegisters)[8+i*4+1]
1294 );
1295 }
1296 first = false;
1297 }
1298
1299 if ( dump_registers & SSE_REGISTERS )
1300 {
1301 if ( !first )
1302 DbgIO.println();
1303 DbgIO.println("MXCSR = %08x", (cast(uint[])ctx.ExtendedRegisters)[6]);
1304 for ( int i = 0; i < 8; ++i )
1305 {
1306 DbgIO.println("XMM%d = %016x%016x", i, (cast(long[])ctx.ExtendedRegisters)[20+i*2+1], (cast(long[])ctx.ExtendedRegisters)[20+i*2]);
1307 DbgIO.println(" = [%.6g, %.6g, %.6g, %.6g]",
1308 (cast(float[])ctx.ExtendedRegisters)[40+i*4],
1309 (cast(float[])ctx.ExtendedRegisters)[40+i*4+1],
1310 (cast(float[])ctx.ExtendedRegisters)[40+i*4+2],
1311 (cast(float[])ctx.ExtendedRegisters)[40+i*4+3]
1312 );
1313 DbgIO.println(" = [%.12g, %.12g]",
1314 (cast(double[])ctx.ExtendedRegisters)[20+i*2],
1315 (cast(double[])ctx.ExtendedRegisters)[20+i*2+1]
1316 );
1317 }
1318 }
1319 }
1320
1321 /**********************************************************************************************
1322
1323 **********************************************************************************************/
1324 string symbolValueToString(SymbolValue val)
1325 {
1326 return symbolValueToString(val,"");
1327 }
1328
1329 string symbolValueToString(SymbolValue val, string indent)
1330 {
1331 string str;
1332 if ( val.name !is null )
1333 str = val.name~" = ";
1334 if ( val.value !is null )
1335 str ~= val.value;
1336 else
1337 {
1338 if ( val.children.length > 0 )
1339 {
1340 str ~= "{";
1341 bool first = true;
1342 foreach ( c; val.children )
1343 {
1344 if ( first )
1345 first = false;
1346 else
1347 str ~= ",";
1348 str ~= "\n "~indent~symbolValueToString(c, indent~" ");
1349 }
1350 str ~= "\n"~indent~"}";
1351 }
1352 else
1353 str ~= "{}";
1354 }
1355 return str;
1356 }
1357
1358 /**********************************************************************************************
1359 Read command and call CLI supplied parser function.
1360 Gets called when debuggee is suspended.
1361 **********************************************************************************************/
1362 bool readCommand()
1363 {
1364 if ( cmdQueue.length <= 0 ) {
1365 DbgIO.write("->");
1366 string input = DbgIO.readln();
1367 cmdQueue ~= input;
1368 }
1369 if ( cmdQueue.length <= 0 )
1370 return false;
1371
1372 string cmd = strip(cmdQueue[0]);
1373 cmdQueue = cmdQueue[1..$];
1374 if ( cmd.length > 0 && cmd[$-1] == ';' ) {
1375 cmd = cmd[0..$-1];
1376 DbgIO.writeln("->"~cmd);
1377 }
1378 return parseCommand(cmd);
1379 }
1380 }