Mercurial > projects > ddbg_continued
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cli/ddbgcli.d Tue Apr 05 20:44:01 2011 +0200 @@ -0,0 +1,1380 @@ +/* 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; + + /********************************************************************************************** + + **********************************************************************************************/ + 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"); + 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_line; + if ( source !is null && bp.line <= source.length ) + source_line = source[bp.line-1]; + + if ( !bp.temporary ) + DbgIO.print("Breakpoint %d hit at ", index); + DbgIO.println("%s:%d 0x%x thread(%d)", bp.file, bp.line, bp.address, thread.id); + if ( source_line.length > 0 ) + DbgIO.writeln(source_line); + } + return true; + } + + /********************************************************************************************** + + **********************************************************************************************/ + void debugString(string str) + { + printf("OUTPUT DEBUG STRING:\n%s\n", toStringz(str)); + } + + /********************************************************************************************** + 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) ) + lastcmd ~= r.match(0); + } + if ( lastcmd.length <= 0 ) + return false; + + 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; + // 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": + if ( dbg.miniDump !is null ) { + DbgIO.println("Command not valid in post-mortem mode"); + break; + } + if ( dbg.process_loaded ) { + dbg.resume; + return true; + } + else { + 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 "ov": + if ( dbg.miniDump !is null ) { + DbgIO.println("Command not valid in post-mortem mode"); + break; + } + if ( dbg.process_loaded && dbg.step(StepMode.e_over) ) + { + debug foreach ( bp; dbg.temp_breakpoints ) + DbgIO.writeln(bp.value.toString); + return true; + } + break; + // step into + case "in": + if ( dbg.miniDump !is null ) { + DbgIO.println("Command not valid in post-mortem mode"); + break; + } + 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 ( 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); + } +}