Mercurial > projects > ddbg_continued
view src/cli/ddbgcli.d @ 5:496dfd8f7342 default tip
added:
-repeat option for "in", "ov"
-run until a line option
-run until a function option
-break on a function start
-n is an alias for ov
author | marton@basel.hu |
---|---|
date | Sun, 17 Apr 2011 11:05:31 +0200 |
parents | a5fb1bc967e6 |
children |
line wrap: on
line source
/* Ddbg - Win32 Debugger for the D programming language * Copyright (c) 2007 Jascha Wetzel * All rights reserved. See LICENSE.TXT for details. */ module cli.ddbgcli; import std.string; static import std.regexp; import std.c.stdio : sscanf; import std.c.string; import std.demangle; import minidump; import container; import util; import codeview.codeview; import breakpoint; import debugger; import disasm; import callstack; import dbgprocess; import dbgthread; import expression.expression_apd; import expression.datahandler : SymbolValue, DataHandler; import expression.evaluationcontext; import cli.userinterface; import win32.winbase; import win32.winuser; import win32.winnt; import win32.dbghelp; /************************************************************************************************** **************************************************************************************************/ class DdbgCLI : UserInterfaceBase { const int PRINT_SOURCE_LINES = 3; string[] lastcmd, onexCommands, ontermCommands; string debuggee, command_line; uint current_frame_level; bool quit; const ubyte CPU_REGISTERS = 1, FPU_REGISTERS = 2, MMX_REGISTERS = 4, SSE_REGISTERS = 8; ubyte dump_registers = CPU_REGISTERS; uint lastEvaluationDepth = 1; bool auto_find_scope_frame, /// find frame of last active scope if current frame has no source/symbols jump_to_last_known_location_on_exception = true; uint repeat = 0; /********************************************************************************************** **********************************************************************************************/ void init(string[] args) { DbgIO.println(WELCOME_STRING); if ( args.length < 2 ) throw new DebuggerException("Usage: ddbg [-cli=<mode>] [-cmd=<commands>] <exe file> [arguments]"); debuggee = args[1]; foreach ( inout arg; args ) { if ( find(arg, ' ') >= 0 ) arg = "\""~arg~"\""; } command_line = join(args[1..$], " "); dbg = new Debugger(debuggee, this); } int start() { while ( !quit ) { debug readCommand(); else { try readCommand(); catch ( Exception e ) DbgIO.println(e.msg); } } return 0; } /********************************************************************************************** **********************************************************************************************/ void singleStep() { DbgIO.println(describeLocation(dbg.current_address)); } /********************************************************************************************** **********************************************************************************************/ void exitProcess() { DbgIO.println("Process terminated"); repeat = 0; cmdQueue ~= ontermCommands; } /********************************************************************************************** **********************************************************************************************/ void loadedDLL(DLL dll) { if( dll !is null && dll.image.name.length ) { DbgIO.write(dll.image.name); DbgIO.println(" loaded at 0x%08x", dll.base); } else DbgIO.writeln("unknown DLL loaded"); } /********************************************************************************************** **********************************************************************************************/ void win32exception(uint threadId, EXCEPTION_RECORD* exrec) { DbgIO.println( "Unhandled Exception: %s(0x%x) at %s thread(%d)", getExceptionName(exrec.ExceptionCode), exrec.ExceptionCode, describeLocation(dbg.current_address), threadId ); } /********************************************************************************************** **********************************************************************************************/ void exception(uint threadId, string class_name, string msg, size_t obj_ptr) { if ( jump_to_last_known_location_on_exception ) { uint index; current_frame_level = 0; Location loc = dbg.images.findLocation(dbg.current_address); if ( loc.codeblock is null ) { dbg.stack.firstFrame(index); ubyte[] frame = dbg.stack.prevFrame(index, index); while ( loc.codeblock is null && frame !is null ) { uint ret_adr = (cast(uint[])frame)[1]; loc = dbg.images.findLocation(ret_adr); frame = dbg.stack.prevFrame(index, index); ++current_frame_level; } } } DbgIO.println("Unhandled D Exception (%s", class_name); DbgIO.write(msg.length>0?" \""~msg~"\"":""); DbgIO.println(") at %s thread(%d)", describeLocation(dbg.current_address), threadId); cmdQueue ~= onexCommands; } /********************************************************************************************** **********************************************************************************************/ void userInterrupt() { DbgIO.println("User interrupt at %s", describeLocation(dbg.current_address)); } /********************************************************************************************** **********************************************************************************************/ bool breakpoint(int index, Breakpoint bp, DbgThread thread) { if ( bp.hardware ) { DbgIO.println("Hardware breakpoint for 0x%x hit at %s thread(%d)", bp.address, describeLocation(dbg.current_address), thread.id ); } else if ( bp.file is null || bp.line == 0 ) DbgIO.println("Unknown breakpoint hit at %s thread(%d)", describeLocation(bp.address), thread.id); else { string[] source = dbg.getSourceFile(bp.file); string source_lines; if ( source !is null && bp.line <= source.length ) { const showmore= 2; int startfrom = bp.line- 1 - showmore; int startend = bp.line- 1 + showmore; if (startfrom <0) startfrom=0; if (startend >=source.length) startend=source.length-1; int left = showmore; foreach (line;source[startfrom..startend+1]) { if (left==0) source_lines ~= ">>"~line ~"\n"; else source_lines ~= " "~line ~"\n"; left--; } } if ( !bp.temporary ) DbgIO.print("Breakpoint %d hit at ", index); debug DbgIO.println("before printing location info"); DbgIO.println("%s:%d 0x%x thread(%d)", bp.file, bp.line, bp.address, thread.id); if ( source_lines.length > 0 ) DbgIO.write(source_lines); } return true; } /********************************************************************************************** **********************************************************************************************/ void debugString(string str) { printf("OUTPUT DEBUG STRING:\n%s\n", toStringz(str)); } bool setBreakpointByFunctionname(char[][] lastcmd,uint threadId, int index) { DataSymbol[] ds; size_t codebase; // int index; foreach ( img; dbg.images.images ) { if ( img.codeView is null ) continue; DataSymbol[] tds; ds ~= img.codeView.global_pub.findDataSymbolBySubstring(lastcmd[1]); ds ~= img.codeView.global_sym.findDataSymbolBySubstring(lastcmd[1]); ds ~= img.codeView.static_sym.findDataSymbolBySubstring(lastcmd[1]); foreach ( m; img.codeView.modulesByIndex ) ds ~= m.symbols.findDataSymbolBySubstring(lastcmd[1]); if (ds.length>0) codebase=img.getCodeBase; } debug DbgIO.println("Found candidates:%d",ds.length); if (ds.length==1) { //Location loc = new Location(lastcmd[1]); //loc.bind(dbg.images, dbg.source_search_paths); if (index>=0) if ( lastcmd.length > 2 ) index = cast(int)atoi(lastcmd[2]); Breakpoint bp; if (index>=0) // not temporary {if ( index <= 0 && dbg.breakpoints.length > 0 ) index = dbg.breakpoints.keys.dup.sort[$-1]+1; } else index=-1; bp = dbg.setBreakpoint(ds[0].offset+codebase, index, threadId); DbgIO.println("Breakpoint set: %s", bp.toString); } else if (ds.length==0) { DbgIO.println("Breakpoint is not set:no matching functions are found!"); return false; } else { DbgIO.println("Breakpoint is not set:too many matching functions are found!"); int maxi=ds.length; if (maxi>20) { DbgIO.println("There are more than 20 possibilities, first twenty is snown:"); maxi=20; } else { DbgIO.println("These are the matched function names:"); } foreach (s;ds[0..maxi]) { //DbgIO.println(s.name_notype); printSymbol(s); } return false; } return true; } /********************************************************************************************** Command line parser. Gets called when debuggee is suspended. **********************************************************************************************/ bool parseCommand(string input) { if ( strip(input).length > 0 ) { auto r = std.regexp.RegExp("(([^\" \\t]+)|(\"[^\"]+\"))+"); lastcmd.length = 0; foreach ( m; r.search(input) ) { //DbgIO.writeln("match:"~r.match(0)); lastcmd ~= r.match(0); } } if ( lastcmd.length <= 0 ) return false; if (lastcmd[0][0]=='=' && lastcmd[0].length>1) { DbgIO.writeln("after ="~lastcmd[0][1..$]); lastcmd=["=",lastcmd[0][1..$]]~lastcmd[1..$]; } switch ( lastcmd[0] ) { case "help": case "h": case "?": DbgIO.write(import("ddbg_help.txt")); break; case "=": if ( !dbg.process_loaded && dbg.miniDump is null ) break; if ( lastcmd.length > 1 ) { try DbgIO.writeln(symbolValueToString(dbg.handleData(dbg.evaluateExpression(lastcmd[1], current_frame_level), false))); catch ( EvaluationException e ) { DbgIO.writeln(e.toString); } } break; case "arg": if ( dbg.process_loaded ) DbgIO.println("Warning: process already started"); command_line = debuggee~" "~join(lastcmd[1..$], " "); break; // add breakpoint case "bp": int index; if ( lastcmd.length < 2 ) { DbgIO.println("invalid syntax - see help for details"); break; } int pos = find(lastcmd[1], '#'); uint threadId; if ( pos > 0 ) { threadId = cast(uint)atoi(lastcmd[1][pos+1..$]); lastcmd[1] = lastcmd[1][0..pos]; } Location loc = new Location(lastcmd[1]); loc.bind(dbg.images, dbg.source_search_paths); if ( lastcmd.length > 2 ) index = cast(int)atoi(lastcmd[2]); Breakpoint bp; if ( index <= 0 && dbg.breakpoints.length > 0 ) index = dbg.breakpoints.keys.dup.sort[$-1]+1; bp = dbg.setBreakpoint(loc, index, threadId); DbgIO.println("Breakpoint set: %s", bp.toString); break; case "bpf": int index; if ( lastcmd.length < 2 ) { DbgIO.println("invalid syntax - see help for details"); break; } int pos = find(lastcmd[1], '#'); uint threadId; if ( pos > 0 ) { threadId = cast(uint)atoi(lastcmd[1][pos+1..$]); lastcmd[1] = lastcmd[1][0..pos]; } if (lastcmd.length>1) setBreakpointByFunctionname(lastcmd,threadId,0); break; break; // delete breakpoint case "dbp": if ( lastcmd.length > 1 ) { if ( lastcmd[1] == "*" ) { foreach ( uint i; dbg.breakpoints.keys ) dbg.removeBreakpoint(i); } else if ( isNumeric(lastcmd[1]) ) { if ( !dbg.removeBreakpoint(cast(uint)atoi(lastcmd[1])) ) DbgIO.println("invalid breakpoint index: %s",lastcmd[1]); } else { Location loc = new Location(lastcmd[1]); loc.bind(dbg.images, dbg.source_search_paths); if ( loc !is null ) { int bp_index; dbg.getBreakpoint(loc, bp_index); if ( bp_index >= 0 && !dbg.removeBreakpoint(bp_index) ) DbgIO.println("No breakpoint set at "~lastcmd[1]); } else DbgIO.println("dbp [<source file>:<line>|#index|*]"); } } else DbgIO.println("dbp [<source file>:<line>|#index|*]"); break; // set evaluation depth case "er": lastcmd.length = 1; if ( dbg.evaluationDepth > 1 ) { lastEvaluationDepth = dbg.evaluationDepth; lastcmd ~= "1"; } else lastcmd ~= .toString(lastEvaluationDepth); case "ed": if ( lastcmd.length < 2 || !isNumeric(lastcmd[1]) ) DbgIO.println("Usage: ed <depth>"); else { dbg.evaluationDepth = cast(int)atoi(lastcmd[1]); DbgIO.println("Expression evaluation depth is %d", dbg.evaluationDepth); } break; case "el": if ( lastcmd.length < 2 || !isNumeric(lastcmd[1]) ) DbgIO.println("Usage: el <length>"); else { DataHandler.max_elem_count = cast(int)atoi(lastcmd[1]); DbgIO.println("Array evaluation length is %d", DataHandler.max_elem_count); } break; // select frame case "f": if ( lastcmd.length > 1 ) current_frame_level = cast(uint)atoi(lastcmd[1]); DbgIO.println("Current frame level is %d", current_frame_level); break; // image base address case "ii": foreach ( img; dbg.images.images ) { uint ibase = img.imageBase; DbgIO.println("ImageBase\t0x%x", ibase); DbgIO.println("CodeBase\t0x%x", img.getCodeBase); DbgIO.println("Sections:\nname\t\taddress\t\tsize\t\tcharacteristics"); foreach ( s; img.sections ) with ( *s.header ) DbgIO.println("%s\t0x%08x\t0x%08x\t0x%08x", cast(char[8])Name, ibase+VirtualAddress, Misc.VirtualSize, Characteristics); } break; // jump to last known source frame case "jkf": jump_to_last_known_location_on_exception = !jump_to_last_known_location_on_exception; DbgIO.println("%s to from of last known source location on exception", jump_to_last_known_location_on_exception?"Jumping":"Not jumping" ); break; // walk memory case "mi": if ( !dbg.process_loaded ) break; uint filteredStates = MEM_COMMIT; foreach ( lc; lastcmd[1..$] ) { switch ( lc ) { case "free": filteredStates |= MEM_FREE; break; case "rsrv": filteredStates |= MEM_RESERVE; break; default: } } MEMORY_BASIC_INFORMATION[] mbis = dbg.process.walkMemory; DbgIO.println("Base AlcBase AlcProt RgnSize Stat Protect Type"); foreach ( mbi; mbis ) { if ( (mbi.State & filteredStates) == 0 ) continue; string state; switch ( mbi.State ) { case MEM_COMMIT: state = "comt"; break; case MEM_FREE: state = "free"; break; case MEM_RESERVE: state = "rsrv"; break; default: state = "unkn"; break; } string type; switch ( mbi.Type ) { case MEM_IMAGE: type = "imag"; break; case MEM_MAPPED: type = "mapd"; break; case MEM_PRIVATE: type = "priv"; break; default: type = "unkn"; break; } string protectName(uint prot) { string protStr; switch ( prot & 0xff ) { case 0: protStr = "?"; break; case PAGE_EXECUTE: protStr = "x"; break; case PAGE_EXECUTE_READ: protStr = "xr"; break; case PAGE_EXECUTE_READWRITE: protStr = "xrw"; break; case PAGE_EXECUTE_WRITECOPY: protStr = "xwc"; break; case PAGE_NOACCESS: protStr = "na"; break; case PAGE_READONLY: protStr = "ro"; break; case PAGE_READWRITE: protStr = "rw"; break; case PAGE_WRITECOPY: protStr = "wc"; break; default: protStr = format("%02x", prot & 0xff); break; } protStr ~= " "; if ( prot & PAGE_GUARD ) protStr ~= "g"; if ( prot & PAGE_NOCACHE ) protStr ~= "nc"; return protStr; } DbgIO.println("%08x %08x %- 7s %08x %s %- 7s %s", cast(size_t)mbi.BaseAddress, cast(size_t)mbi.AllocationBase, protectName(mbi.AllocationProtect), mbi.RegionSize, state, protectName(mbi.Protect), type ); } break; // list breakpoints case "lbp": if ( dbg.breakpoints.length <= 0 ) DbgIO.println("no breakpoints set"); foreach ( uint i, bp; dbg.breakpoints ) DbgIO.println("%d %s", i, bp.toString); break; // ldll case "ldll": if ( !dbg.process_loaded ) break; DbgIO.println("Base Name"); foreach ( dll; dbg.process.loaded_dlls ) DbgIO.println("%08x %s", dll.base, dll.image.name); break; // list temporary breakpoints case "ltbp": if ( dbg.temp_breakpoints.empty ) DbgIO.println("no temporary breakpoints set"); foreach ( bp; dbg.temp_breakpoints ) DbgIO.writeln(bp.value.toString); break; // list debug modules case "lm": Module[] modules_noinfo; foreach ( img; dbg.images.images ) { foreach ( m; img.codeView.modulesByIndex ) { string name = m.name; if ( m.header.iLib > 0 ) name ~= " from "~img.codeView.libraries[m.header.iLib]; if ( lastcmd.length > 1 && find(name, lastcmd[1]) < 0 ) continue; bool has_info = false; with ( m.symbols ) { if ( proc_symbols.length+stack_symbols.length+data_symbols.length > 0 ) { DbgIO.println( "%s\n\tSymbols: %d proc %d stack %d data", name, proc_symbols.length, stack_symbols.length, data_symbols.length ); has_info = true; } } if ( m.source_module !is null ) { DbgIO.println("\tSource files:"); has_info = true; foreach ( sf; m.source_module.files ) DbgIO.println("\t\t%s", sf.name); } if ( !has_info ) modules_noinfo ~= m; } } if ( modules_noinfo.length > 0 ) { DbgIO.println("Modules without debug information:"); foreach ( img; dbg.images.images ) { foreach ( m; modules_noinfo ) { string name = m.name; if ( m.header.iLib > 0 ) name ~= " from "~img.codeView.libraries[m.header.iLib]; DbgIO.println("%s #segs=%d", name, m.seginfos.length); if ( m.source_module !is null ) { DbgIO.println("\tSource files:"); foreach ( sf; m.source_module.files ) DbgIO.println("\t\t%s", sf.name); } } } } break; // list source modules case "lsm": uint[string] line_counters; uint[string] seg_counters; foreach ( img; dbg.images.images ) { if ( img.codeView is null ) continue; foreach ( m; img.codeView.modulesByIndex ) { if ( m.source_module is null ) continue; foreach ( sf; m.source_module.files ) { if ( lastcmd.length > 1 && find(sf.name, lastcmd[1]) < 0 ) continue; uint lines = sf.lines.length; if ( (sf.name in line_counters) is null ) { seg_counters[sf.name] = sf.segments.length; line_counters[sf.name] = lines; } else { seg_counters[sf.name] += sf.segments.length; line_counters[sf.name] += lines; } } } } string[] lc_keys = line_counters.keys.dup; lc_keys.sort; foreach ( key; lc_keys ) DbgIO.println("%s\t\tsegs=%d lines=%d", key, seg_counters[key], line_counters[key]); break; // list source lines per module case "lsl": foreach ( img; dbg.images.images ) { if ( img.codeView is null ) continue; foreach ( m; img.codeView.modulesByIndex ) { if ( m.source_module is null ) continue; DbgIO.println("module %s", m.name); foreach ( sf; m.source_module.files ) { DbgIO.println("file %s", sf.name); if ( lastcmd.length > 1 && find(sf.name, lastcmd[1]) < 0 ) continue; foreach ( l; sf.lines.reverse ) { DbgIO.println("line %d", l); } } } } break; // list all symbols case "ls": /* void dumpSymbolAVL(AVLNode!(NamedSymbol) node, uint indent=0) { string indentstr = new char[indent*2]; indentstr[0..indent*2] = ' '; DbgIO.println("%s%s", indentstr, node.value.name_notype); if ( node.left !is null ) dumpSymbolAVL(node.left, indent+1); if ( node.right !is null ) dumpSymbolAVL(node.right, indent+1); } DbgIO.println("AVL dump:"); dumpSymbolAVL(dbg.image.codeView.globalNamedSymbols.root); */ foreach ( img; dbg.images.images ) { if ( img.codeView is null ) continue; printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null); printSymbols(img.codeView, img.codeView.global_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null); printSymbols(img.codeView, img.codeView.static_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null); foreach ( m; img.codeView.modulesByIndex ) printSymbols(img.codeView, m.symbols.named_symbols, lastcmd.length>1?lastcmd[1]:null); } break; // list source search paths case "lsp": foreach ( s; dbg.source_search_paths ) DbgIO.writeln(s); break; // list function symbols case "lf": foreach ( img; dbg.images.images ) { if ( img.codeView is null ) continue; printSymbols(img.codeView, img.codeView.global_pub.proc_symbols, lastcmd.length>1?lastcmd[1]:null); printSymbols(img.codeView, img.codeView.global_sym.proc_symbols, lastcmd.length>1?lastcmd[1]:null); printSymbols(img.codeView, img.codeView.static_sym.proc_symbols, lastcmd.length>1?lastcmd[1]:null); foreach ( m; img.codeView.modulesByIndex ) printSymbols(img.codeView, m.symbols.proc_symbols, lastcmd.length>1?lastcmd[1]:null); } break; // list data symbols case "ld": foreach ( img; dbg.images.images ) { if ( img.codeView is null ) continue; printSymbols(img.codeView, img.codeView.global_pub.data_symbols, lastcmd.length>1?lastcmd[1]:null); printSymbols(img.codeView, img.codeView.global_sym.data_symbols, lastcmd.length>1?lastcmd[1]:null); printSymbols(img.codeView, img.codeView.static_sym.data_symbols, lastcmd.length>1?lastcmd[1]:null); foreach ( m; img.codeView.modulesByIndex ) printSymbols(img.codeView, m.symbols.data_symbols, lastcmd.length>1?lastcmd[1]:null); } break; // list global symbols case "lg": foreach ( img; dbg.images.images ) { if ( img.codeView is null ) continue; printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null); printSymbols(img.codeView, img.codeView.global_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null); } break; // list global publics case "lp": foreach ( img; dbg.images.images ) { if ( img.codeView is null ) continue; printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null); } break; // list scope variables case "lsv": if ( dbg.process_loaded || dbg.miniDump !is null ) evalScopeSymbols(); break; // list threads case "lt": if ( dbg.miniDump !is null ) { DbgIO.println(" id pri sus location"); MINIDUMP_THREAD[] threads = dbg.miniDump.threads; foreach ( thread; threads ) { CONTEXT* ctx; ctx = cast(CONTEXT*)dbg.miniDump.rvaToVa(thread.ThreadContext.Rva); Location loc = dbg.images.findLocation(ctx.Eip); DbgIO.println("%s%s%- 6d %- 3d %- 2d %s", thread.ThreadId==dbg.thread_id?">":" ", thread.ThreadId==dbg.miniDump.threadInfo.mainThreadId?"*":" ", thread.ThreadId, thread.Priority, thread.SuspendCount, describeLocation(loc) ); } } else { DbgIO.println(" id pri sus creation exit kernel user location"); foreach ( t; dbg.process.threads.values ) { // id name location pri pri-boost start-info times ulong creation, exit, kernel, user; t.times(creation, exit, kernel, user); CONTEXT ctx; t.getContext(ctx); Location loc = dbg.images.findLocation(ctx.Eip); DbgIO.println("%s%s%- 6d %- 3d%s %- 2d %s %s %s %s %s", t.id==dbg.thread_id?">":" ", t.id==dbg.process.mainThreadId?"*":" ", t.id, t.priority, t.priorityBoost?"b":" ", t.suspendCount, formatTicks(creation), formatTicks(exit), formatTicks(kernel), formatTicks(user), describeLocation(loc) ); } } break; // no console case "nc": dbg.create_new_console = !dbg.create_new_console; DbgIO.println("Starting debuggee in %s console", dbg.create_new_console?"new":"this"); break; // on exception case "onex": if ( lastcmd.length < 2 ) { foreach ( cmd; onexCommands ) DbgIO.writeln(cmd[0..$-1]); } else { onexCommands = null; string cmdList; foreach ( cmd; lastcmd[1..$] ) { if ( cmd[0] == '"' && cmd[$-1] == '"' ) cmdList ~= " "~cmd[1..$-1]; else cmdList ~= " "~cmd; } foreach ( cmd; split(cmdList, ";") ) onexCommands ~= strip(cmd)~";"; } break; // on termination case "onterm": if ( lastcmd.length < 2 ) { foreach ( cmd; ontermCommands ) DbgIO.writeln(cmd[0..$-1]); } else { ontermCommands = null; string cmdList; foreach ( cmd; lastcmd[1..$] ) { if ( cmd[0] == '"' && cmd[$-1] == '"' ) cmdList ~= " "~cmd[1..$-1]; else cmdList ~= " "~cmd; } foreach ( cmd; split(cmdList, ";") ) ontermCommands ~= strip(cmd)~";"; } break; // print source case "ps": Location loc; string[] source; do { if ( current_frame_level > 0 ) { auto frame = dbg.stack.getFrame(current_frame_level); if ( frame is null ) goto Lnotfound; uint ret_adr = (cast(uint[])frame)[1]; loc = dbg.images.findLocation(ret_adr); } else loc = dbg.images.findLocation(dbg.current_address); debug DbgIO.println("found location %s", loc.file); source = dbg.getSourceFile(loc.file); if ( source !is null ) break; if ( auto_find_scope_frame ) { ++current_frame_level; continue; } Lnotfound: DbgIO.writeln("Source file for current location unknown"); break; } while( source is null ); int start_line, end_line; if ( lastcmd.length > 1 ) { start_line = cast(uint)atoi(lastcmd[1]); end_line = loc.line + start_line; start_line = loc.line - start_line; } else if ( loc.scope_sym !is null ) { ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym; auto start_address = loc.getCodeBase + psym.cvdata.offset; auto start_loc = dbg.images.findLocation(start_address); auto end_loc = dbg.images.findLocation(start_address+psym.cvdata.proc_length-1); debug DbgIO.println("address range: 0x%x 0x%x", start_address, start_address+psym.cvdata.proc_length-1); debug DbgIO.println("lines before prev: %d - %d", start_loc.line, end_loc.line); end_loc = dbg.images.findPrevSrcLine(end_loc); start_line = start_loc.line; if ( end_loc !is null ) end_line = end_loc.line; } if ( end_line == 0 ) { start_line = loc.line - PRINT_SOURCE_LINES; end_line = loc.line + PRINT_SOURCE_LINES; } debug DbgIO.println("lines: %d - %d", start_line, end_line); for ( int l = dmax(0, start_line-1); l < dmin(cast(int)source.length, end_line); ++l ) { if ( l+1 == loc.line ) DbgIO.write(">"); DbgIO.writeln(source[l]); } break; // quit case "q": dbg.abort = true; quit = true; return true; // run/continue case "r": case "rf": if ( dbg.miniDump !is null ) { DbgIO.println("Command not valid in post-mortem mode"); break; } int index; if ( lastcmd[0]=="r" && lastcmd.length == 2 ) { int pos = find(lastcmd[1], '#'); uint threadId; if ( pos > 0 ) { threadId = cast(uint)atoi(lastcmd[1][pos+1..$]); lastcmd[1] = lastcmd[1][0..pos]; } Location loc = new Location(lastcmd[1]); if (!loc.bind(dbg.images, dbg.source_search_paths)) break; //if ( lastcmd.length > 2 ) // index = cast(int)atoi(lastcmd[2]); index=-1; Breakpoint bp; if ( index <= 0 && dbg.breakpoints.length > 0 ) index = dbg.breakpoints.keys.dup.sort[$-1]+1; bp = dbg.setBreakpoint(loc, index, threadId); DbgIO.println("Breakpoint set: %s", bp.toString); } if ( lastcmd[0]=="rf" && lastcmd.length == 2 ) { int pos = find(lastcmd[1], '#'); uint threadId; if ( pos > 0 ) { threadId = cast(uint)atoi(lastcmd[1][pos+1..$]); lastcmd[1] = lastcmd[1][0..pos]; } if (!setBreakpointByFunctionname(lastcmd,threadId,-1)) break; } if ( dbg.process_loaded ) { dbg.resume; return true; } else { dbg.breakonmain = false; dbg.start(command_line); return true; } // single-step case "s": if ( dbg.miniDump !is null ) { DbgIO.println("Command not valid in post-mortem mode"); break; } if ( dbg.process_loaded ) { dbg.single_step = true; dbg.activateSingleStep(true); } return true; // add source search path case "sp": if ( lastcmd.length > 1 ) { string sp = lastcmd[1].dup; if ( sp[$-1] != '\\' ) sp ~= '\\'; dbg.source_search_paths ~= sp; } else DbgIO.println("usage: sp <search_path>"); break; // switch thread case "st": if ( lastcmd.length < 2 ) { DbgIO.writeln("Usage: st <threadID>"); break; } size_t threadId = cast(size_t)atoi(lastcmd[1]); if ( dbg.miniDump !is null ) { foreach ( thread; dbg.miniDump.threads ) { if ( threadId == thread.ThreadId ) { dbg.selectThread(threadId); break; } } } else { foreach ( t; dbg.process.threads.values ) { if ( threadId == t.id ) { dbg.selectThread(threadId); break; } } } break; // step over case "n": case "ov": if ( dbg.miniDump !is null ) { DbgIO.println("Command not valid in post-mortem mode"); break; } if ( lastcmd.length ==2 ) { repeat=cast(uint)atoi(lastcmd[1]); repeat--; lastcmd.length = 1; } if ( dbg.process_loaded && dbg.step(StepMode.e_over) ) { debug foreach ( bp; dbg.temp_breakpoints ) DbgIO.writeln(bp.value.toString); return true; } if (!dbg.process_loaded) { dbg.breakonmain = true; dbg.start(command_line); return true; } break; // step into case "in": if ( dbg.miniDump !is null ) { DbgIO.println("Command not valid in post-mortem mode"); break; } if ( lastcmd.length ==2 ) { repeat=cast(uint)atoi(lastcmd[1]); repeat--; lastcmd.length = 1; } if ( dbg.process_loaded && dbg.step(StepMode.e_in) ) { debug foreach ( bp; dbg.temp_breakpoints ) DbgIO.writeln(bp.value.toString); return true; } break; // step out case "out": if ( dbg.miniDump !is null ) { DbgIO.println("Command not valid in post-mortem mode"); break; } if ( dbg.process_loaded && dbg.step(StepMode.e_out) ) { debug foreach ( bp; dbg.temp_breakpoints ) DbgIO.writeln(bp.value.toString); return true; } break; // disassemble case "da": if ( !dbg.process_loaded ) break; uint start_address; if ( current_frame_level > 0 ) { auto frame = dbg.stack.getFrame(current_frame_level); start_address = (cast(uint[])frame)[1]; } else start_address = dbg.current_address; if ( lastcmd.length > 1 ) sscanf(toStringz(lastcmd[1]), "%x", &start_address); else { Location loc = dbg.images.findLocation(dbg.current_address); if ( loc.scope_sym !is null ) { ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym; start_address = loc.getCodeBase + psym.cvdata.offset; DisAsm.disasm(dbg.process, start_address, start_address+psym.cvdata.proc_length, &printDisassembly); break; } } DisAsm.disasm(dbg.process, start_address, 0, &printDisassembly); break; // disassemble line case "dal": if ( !dbg.process_loaded ) break; int num_lines = 1; if ( lastcmd.length > 1 ) { sscanf(toStringz(lastcmd[1]), "%d", &num_lines); if ( num_lines < 0 ) --num_lines; } uint start_address; if ( current_frame_level > 0 ) { auto frame = dbg.stack.getFrame(current_frame_level); start_address = (cast(uint[])frame)[1]; } else start_address = dbg.current_address; for ( ; num_lines != 0; num_lines<0?++num_lines:--num_lines ) { Location loc = dbg.images.findLocation(start_address); if ( loc is null ) { DbgIO.println("No source line information available"); break; } DisAsm.disasm( dbg.process, loc.getCodeBase+loc.codeblock.start, loc.getCodeBase+loc.codeblock.end, &printDisassembly ); } break; // dump registers case "dr": if ( lastcmd.length > 1 ) { foreach ( lc; lastcmd[1..$] ) { switch ( lc ) { case "cpu": dump_registers |= CPU_REGISTERS; break; case "fpu": dump_registers |= FPU_REGISTERS; break; case "mmx": dump_registers |= MMX_REGISTERS; break; case "sse": dump_registers |= SSE_REGISTERS; break; default: DbgIO.println("Unknown register set \"%s\"", lc); } } } if ( dump_registers == 0 ) { DbgIO.println("No register set selected. See help for usage of \"dr\""); break; } if ( dbg.miniDump !is null ) printRegisterDump(dbg.miniDump.getContext); else if ( dbg.process_loaded ) printRegisterDump(dbg.process.threads[dbg.thread_id]); break; // dump stack case "ds": if ( !dbg.process_loaded ) break; int dump_length; if ( lastcmd.length > 1 ) dump_length = cast(int)atoi(lastcmd[1])*4; else { CONTEXT ctx; if ( dbg.process.threads[dbg.thread_id].getContext(ctx, CONTEXT_CONTROL) ) dump_length = ctx.Ebp-ctx.Esp+8; if ( dump_length <= 0 ) dump_length = 16*4; } int top = dbg.stack.data.length>dump_length?dump_length:dbg.stack.data.length; dumpMemory(dbg.stack.top_ptr, top, dbg.stack.data); break; case "dm": if ( !dbg.process_loaded ) break; if ( lastcmd.length < 3 ) { DbgIO.println("usage: dm <start> <length>"); break; } uint start; sscanf(toStringz(lastcmd[1]), "%x", &start); dumpMemory(start, cast(uint)atoi(lastcmd[2])); break; case "hwbp": int index; if ( lastcmd.length < 2 ) { DbgIO.println("invalid syntax - see help for details"); break; } int pos = find(lastcmd[1], '#'); uint threadId; if ( pos > 0 ) { threadId = cast(uint)atoi(lastcmd[1][pos+1..$]); lastcmd[1] = lastcmd[1][0..pos]; } Location loc; size_t address; sscanf(toStringz(lastcmd[1]), "%x", &address); if ( address > 0 ) loc = new Location(address); else loc = new Location(lastcmd[1]); loc.bind(dbg.images, dbg.source_search_paths); if ( lastcmd.length > 2 ) index = cast(int)atoi(lastcmd[2]); Breakpoint bp; if ( index <= 0 && dbg.breakpoints.length > 0 ) index = dbg.breakpoints.keys.dup.sort[$-1]+1; bp = dbg.setBreakpoint(loc, index, threadId, true); DbgIO.println("Hardware Breakpoint set: %s", bp.toString); break; // type of expression case "t": if ( dbg.process_loaded && lastcmd.length > 1 ) { SymbolData sd; try { sd = dbg.evaluateExpression(lastcmd[1], current_frame_level); string type = demangle("_D0"~sd.type); DbgIO.println("%s\n%s", sd.type, type); } catch ( EvaluationException e ) { DbgIO.writeln(e.toString); } } break; // unwind stack case "us": if ( dbg.process_loaded || dbg.miniDump !is null ) unwindStack(); break; // write minidump case "wmd": if ( dbg.miniDump !is null ) { DbgIO.println("Command not valid in post-mortem mode"); break; } if ( !dbg.process_loaded ) break; if ( !MiniDump.haveMiniDump ) { DbgIO.println("DbgHelp.dll required for MiniDump support.\nInstall the Microsoft Platform SDK or Windows XP."); break; } if ( lastcmd.length < 2 ) { DbgIO.println("Usage: wmd <filename>"); break; } dbg.writeMiniDump(lastcmd[1]); break; // read minidump case "rmd": if ( dbg.process_loaded ) { DbgIO.println("For post-mortem debugging, the process must not be started"); break; } if ( !MiniDump.haveMiniDump ) { DbgIO.println("DbgHelp.dll required for MiniDump support.\nInstall the Microsoft Platform SDK or Windows XP."); break; } if ( lastcmd.length < 2 ) { DbgIO.println("Usage: rmd <filename>"); break; } dbg.readMiniDump(lastcmd[1]); break; // unknown command default: DbgIO.println("Unknown command '%s' ignored!", lastcmd[0]); break; } return false; } /********************************************************************************************** **********************************************************************************************/ void dumpMemory(uint start, uint length, ubyte[] _data=null) { ubyte[] data; if ( _data is null ) { data.length = length; if ( !dbg.process.readProcessMemory(start, data.ptr, data.length) ) return; } else data = _data; for ( uint i = 0; i < length; ++i ) { if ( i % (4*4) == 0 ) { if ( i > 0 ) DbgIO.println(""); DbgIO.print("%08x:", start+i); } if ( i % 4 == 0 ) DbgIO.print(" "); DbgIO.print("%02x", data[i]); } DbgIO.println(""); } /********************************************************************************************** **********************************************************************************************/ void evalScopeSymbols() { uint scope_address = dbg.getScopeAddress(current_frame_level); ScopeSymbol scope_sym = dbg.images.findProcedureSymbol(scope_address); if ( scope_sym is null ) { DbgIO.println("No valid scope active"); return; } for ( ; scope_sym !is null; scope_sym = scope_sym.parent_scope ) { DbgIO.println("Scope: %s, parent: %x", scope_sym.name_type, cast(void*)scope_sym.parent_scope); StackSymbol[] locals_args = scope_sym.symbols.stack_symbols; auto psym = cast(ProcedureSymbol)scope_sym; if ( psym !is null ) locals_args ~= psym.arguments.stack_symbols; foreach ( sym; locals_args.sort ) { string name = sym.mangled_name; DbgIO.print("%s = ", name); // DbgIO.print("%s = [ebp%s%d] ", name, sym.cvdata.offset>0?"+":"", sym.cvdata.offset); try DbgIO.writeln(symbolValueToString(dbg.handleData(dbg.evaluateExpression(name, current_frame_level, sym), true))); catch ( EvaluationException e ) { DbgIO.println(e.msg); } } } } /********************************************************************************************** **********************************************************************************************/ void printAsmLine(uint address, string bytes, string asmsource, string symbol, string location, string source) { bool nl = false; if ( location !is null ) { DbgIO.print("%s ", location); nl = true; } if ( source !is null ) { DbgIO.write(source); nl = true; } if ( nl ) DbgIO.println; // align next column char[] indent; int indcount = 2*12-bytes.length; if ( indcount > 0 ) { indent.length = indcount; indent[0..indcount] = ' '; } // print aligned asm source assert(asmsource !is null); assert(bytes !is null); DbgIO.print("%08x: ", address, bytes, indent, asmsource); if ( symbol !is null ) DbgIO.write(symbol); DbgIO.println; } /********************************************************************************************** Prints the register contents of the given thread's context. **********************************************************************************************/ void printRegisterDump(DbgThread thread) { CONTEXT ctxMem; uint context_flags; if ( dump_registers & CPU_REGISTERS ) context_flags |= CONTEXT_FULL; if ( dump_registers & FPU_REGISTERS ) context_flags |= CONTEXT_FLOATING_POINT; if ( dump_registers & (MMX_REGISTERS|SSE_REGISTERS) ) context_flags |= CONTEXT_EXTENDED_REGISTERS; if ( thread.getContext(ctxMem, context_flags) ) printRegisterDump(&ctxMem); else DbgIO.println("ERROR: Couldn't get main thread's context"); } void printRegisterDump(CONTEXT* ctx) { assert ( ctx !is null ); bool first = true; if ( dump_registers & CPU_REGISTERS ) { DbgIO.println("EAX = %08x\tEBX = %08x\tECX = %08x\tEDX = %08x", ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx); DbgIO.println("EDI = %08x\tESI = %08x\tEBP = %08x\tESP = %08x", ctx.Edi, ctx.Esi, ctx.Ebp, ctx.Esp); DbgIO.println("EIP = %08x\tEFL = %08x", ctx.Eip, ctx.EFlags); DbgIO.println(" CS = %08x\t DS = %08x\t ES = %08x\t FS = %08x", ctx.SegCs, ctx.SegDs, ctx.SegEs, ctx.SegFs); DbgIO.println(" GS = %08x\t SS = %08x", ctx.SegGs, ctx.SegSs); first = false; } if ( dump_registers & FPU_REGISTERS ) { if ( !first ) DbgIO.println(); DbgIO.println("FCW = %04x\tFSW = %04x\tFTW = %04x\tFOP = %04x", cast(ushort)ctx.FloatSave.ControlWord, cast(ushort)ctx.FloatSave.StatusWord, cast(ushort)ctx.FloatSave.TagWord, (cast(ushort[])ctx.ExtendedRegisters)[3] ); DbgIO.println("IP = %08x\tCS = %04x\tDP = %08x\tDS = %04x", (cast(uint[])ctx.ExtendedRegisters)[2], (cast(ushort[])ctx.ExtendedRegisters)[6], (cast(uint[])ctx.ExtendedRegisters)[4], (cast(ushort[])ctx.ExtendedRegisters)[10] ); for ( int i = 0; i < 8; ++i ) DbgIO.println("ST%d = % .16e", i, (cast(real[])ctx.FloatSave.RegisterArea)[i]); first = false; } if ( dump_registers & MMX_REGISTERS ) { if ( !first ) DbgIO.println(); for ( int i = 0; i < 8; ++i ) { DbgIO.println("MM%d = %016x", i, (cast(long[])ctx.ExtendedRegisters)[4+i*2]); DbgIO.println(" = [%.6g, %.6g]", (cast(float[])ctx.ExtendedRegisters)[8+i*4], (cast(float[])ctx.ExtendedRegisters)[8+i*4+1] ); } first = false; } if ( dump_registers & SSE_REGISTERS ) { if ( !first ) DbgIO.println(); DbgIO.println("MXCSR = %08x", (cast(uint[])ctx.ExtendedRegisters)[6]); for ( int i = 0; i < 8; ++i ) { DbgIO.println("XMM%d = %016x%016x", i, (cast(long[])ctx.ExtendedRegisters)[20+i*2+1], (cast(long[])ctx.ExtendedRegisters)[20+i*2]); DbgIO.println(" = [%.6g, %.6g, %.6g, %.6g]", (cast(float[])ctx.ExtendedRegisters)[40+i*4], (cast(float[])ctx.ExtendedRegisters)[40+i*4+1], (cast(float[])ctx.ExtendedRegisters)[40+i*4+2], (cast(float[])ctx.ExtendedRegisters)[40+i*4+3] ); DbgIO.println(" = [%.12g, %.12g]", (cast(double[])ctx.ExtendedRegisters)[20+i*2], (cast(double[])ctx.ExtendedRegisters)[20+i*2+1] ); } } } /********************************************************************************************** **********************************************************************************************/ string symbolValueToString(SymbolValue val) { return symbolValueToString(val,""); } string symbolValueToString(SymbolValue val, string indent) { string str; if ( val.name !is null ) str = val.name~" = "; if ( val.value !is null ) str ~= val.value; else { if ( val.children.length > 0 ) { str ~= "{"; bool first = true; foreach ( c; val.children ) { if ( first ) first = false; else str ~= ","; str ~= "\n "~indent~symbolValueToString(c, indent~" "); } str ~= "\n"~indent~"}"; } else str ~= "{}"; } return str; } /********************************************************************************************** Read command and call CLI supplied parser function. Gets called when debuggee is suspended. **********************************************************************************************/ bool readCommand() { if (repeat>0) { repeat--; return parseCommand(""); } if ( cmdQueue.length <= 0 ) { DbgIO.write("->"); string input = DbgIO.readln(); cmdQueue ~= input; } if ( cmdQueue.length <= 0 ) return false; string cmd = strip(cmdQueue[0]); cmdQueue = cmdQueue[1..$]; if ( cmd.length > 0 && cmd[$-1] == ';' ) { cmd = cmd[0..$-1]; DbgIO.writeln("->"~cmd); } return parseCommand(cmd); } }