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