Mercurial > projects > ddbg_continued
diff src/cli/gdbcli.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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cli/gdbcli.d Tue Apr 05 20:44:01 2011 +0200 @@ -0,0 +1,832 @@ +/* Ddbg - Win32 Debugger for the D programming language + * Copyright (c) 2007 Jascha Wetzel + * All rights reserved. See LICENSE.TXT for details. + */ +module cli.gdbcli; + +import std.string; +static import std.regexp; +import std.c.stdio : sscanf; +import std.c.string; +import std.demangle; + +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; +import expression.evaluationcontext; +import cli.userinterface; + +import win32.winbase; +import win32.winuser; +import win32.winnt; + +/************************************************************************************************** + +**************************************************************************************************/ +class GDBCLI : UserInterfaceBase +{ + string[] lastcmd; + + uint current_frame_level; + + string prompt, + command_line, + debuggee; + bool fullname = false, + quit; + + /********************************************************************************************** + + **********************************************************************************************/ + this() + { + prompt = "(gdb) "; + } + + /********************************************************************************************** + + **********************************************************************************************/ + void init(string[] args) + { + DbgIO.println(WELCOME_STRING); + if ( args.length < 2 ) + throw new DebuggerException("Usage: ddbg -cli=gdb [-cmd=<commands>] [-fullname] -args <exe file> [arguments]"); + + bool read_args = false; + Lfe: foreach ( int i, a; args ) + { + switch ( a ) + { + case "-fullname": + case "--fullname": + fullname = true; + break; + case "-args": + case "--args": + read_args=true; + break; + default: + debuggee = a; + if ( read_args ) + { + if ( find(a, ' ') >= 0 ) + command_line = "\""~a~"\""; + else + command_line = a; + if ( args.length > i+1 ) + command_line ~= " "~join(args[i+1..$], " "); + break Lfe; + } + } + } + + dbg = new Debugger(debuggee, this); + dbg.create_new_console = true; + } + + int start() + { + while ( !quit ) + { + try readCommand(); + catch ( Exception e ) + DbgIO.println(e.msg); + } + return 0; + } + + /********************************************************************************************** + + **********************************************************************************************/ + void singleStep() + { + DbgIO.println(describeLocation(dbg.current_address)); + } + + /********************************************************************************************** + + **********************************************************************************************/ + void exitProcess() + { + DbgIO.println("Program exited"); + } + + /********************************************************************************************** + + **********************************************************************************************/ + void loadedDLL(DLL dll) + { + if( dll !is null && dll.image.name.length ) + DbgIO.writeln(dll.image.name~" loaded"); + else + DbgIO.writeln("unknown DLL loaded"); + } + + /********************************************************************************************** + + **********************************************************************************************/ + void win32exception(uint threadId, EXCEPTION_RECORD* exrec) + { + DbgIO.println( + "Program received signal SIG Unhandled Exception: %s(0x%x) at 0x%x", + getExceptionName(exrec.ExceptionCode), exrec.ExceptionCode, exrec.ExceptionAddress //describeLocation(dbg.current_address) + ); + } + + /********************************************************************************************** + + **********************************************************************************************/ + void exception(uint threadId, string class_name, string msg, size_t obj_ptr) + { + DbgIO.println( + "Program received signal SIG Unhandled D Exception (%s%s) at %s", + class_name, msg.length>0?" \""~msg~"\"":"", describeLocation(dbg.current_address) + ); + } + + /********************************************************************************************** + + **********************************************************************************************/ + void userInterrupt() + { + DbgIO.println("User interrupt at %s", describeLocation(dbg.current_address)); + } + + /********************************************************************************************** + + **********************************************************************************************/ + bool breakpoint(int index, Breakpoint bp, DbgThread thread) + { + if ( bp.file is null || bp.line == 0 ) + DbgIO.println("Unknown breakpoint hit at %s", describeLocation(bp.address)); + else + DbgIO.println("\032\032%s:%d:0:begmidl:0x%08x", fullname?dbg.getFullSourcePath(bp.file):bp.file, bp.line, bp.address); + return true; + } + + /********************************************************************************************** + + **********************************************************************************************/ + void debugString(string str) + { + printf("Error 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(strip(input)) ) + lastcmd ~= r.match(0); + } + if ( lastcmd.length <= 0 ) + return false; + + int slash_pos = find(lastcmd[0], '/'); + if ( slash_pos >= 0 ) { + lastcmd ~= lastcmd[0]; + lastcmd[0] = lastcmd[0][0..slash_pos]; + } + + switch ( lastcmd[0] ) + { + case "help": + DbgIO.println("GDB emulation mode - no help available, yet"); + break; + case "delete": + if ( lastcmd.length < 2 ) + DbgIO.println("Warning: too few arguments for set"); + else switch ( lastcmd[1] ) + { + case "breakpoints": + if ( lastcmd.length < 3 ) + { + foreach ( uint i; dbg.breakpoints.keys ) + dbg.removeBreakpoint(i); + } + else if ( !isNumeric(lastcmd[2]) ) + DbgIO.println("Warning: invalid breakpoint index '%s'", lastcmd[2]); + else + dbg.removeBreakpoint(cast(uint)atoi(lastcmd[2])); + break; + default: + DbgIO.println("Warning: unknown argument %s", lastcmd[1]); + } + break; + case "set": + if ( lastcmd.length < 2 ) + DbgIO.println("Warning: too few arguments for set"); + else switch ( lastcmd[1] ) + { + case "prompt": + if ( lastcmd.length < 3 ) + DbgIO.println("Warning: too few arguments for set prompt"); + else + prompt = lastcmd[2]; + break; + case "args": + if ( dbg.process_loaded ) + DbgIO.println("Warning: process already started"); + command_line = debuggee~" "~join(lastcmd[2..$], " "); + break; + default: + DbgIO.println("Warning: unknown variable %s", lastcmd[1]); + } + break; + case "info": + if ( lastcmd.length < 2 ) + DbgIO.println("Warning: too few arguments for info"); + else + { + switch ( lastcmd[1] ) + { + case "locals": + if ( dbg.process_loaded ) + { + uint scope_address = dbg.getScopeAddress(current_frame_level); + ScopeSymbol scope_sym = dbg.images.findProcedureSymbol(scope_address); + for ( ; scope_sym !is null; scope_sym = scope_sym.parent_scope ) + { + if ( scope_sym is null ) + DbgIO.println("No valid scope active"); + else + evalStackSymbols(scope_sym.symbols.stack_symbols); + } + } + break; + case "args": + if ( dbg.process_loaded ) + { + uint scope_address = dbg.getScopeAddress(current_frame_level); + ScopeSymbol scope_sym = dbg.images.findProcedureSymbol(scope_address); + for ( ; scope_sym !is null; scope_sym = scope_sym.parent_scope ) + { + if ( scope_sym is null ) + DbgIO.println("No valid scope active"); + else + { + auto psym = cast(ProcedureSymbol)scope_sym; + if ( psym !is null ) + evalStackSymbols(psym.arguments.stack_symbols); + } + } + } + break; + case "registers": + printRegisterDump(dbg.process.threads[dbg.thread_id]); + break; + case "frame": + uint scope_address = dbg.getScopeAddress(current_frame_level); + ubyte[] frame = dbg.stack.getFrame(current_frame_level); + Location loc = dbg.images.findLocation(scope_address); + DbgIO.print("Stack level %d, frame at ", current_frame_level); + if ( loc.scope_sym !is null ) { + ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym; + DbgIO.println("0x%x:", loc.getCodeBase + psym.cvdata.offset + psym.cvdata.debug_start); + } + else + DbgIO.println("0x%x:", dbg.current_address); + + DbgIO.print(" eip = 0x%x in %s (", + loc.address, loc.scope_sym is null?"??":loc.scope_sym.name_notype + ); + + if ( loc.codeblock !is null ) + DbgIO.print("%s:%d", loc.file, loc.line); + else if ( loc.mod !is null ) + DbgIO.write(loc.mod.name); + else + { + DLL dll = dbg.process.findDLL(loc.address); + if ( dll !is null ) + DbgIO.write(dll.image.name); + } + + DbgIO.println("); saved eip 0x%x", (cast(uint[])frame)[1]); + break; + default: + DbgIO.println("Warning: unknown argument %s", lastcmd[1]); + } + } + break; + // select frame + case "select-frame": + if ( lastcmd.length > 1 ) + current_frame_level = cast(uint)atoi(lastcmd[1]); + DbgIO.println("Current frame level is %d", current_frame_level); + break; + // add breakpoint + case "tbreak": + case "clear": + case "break": + int index; + if ( lastcmd.length < 2 ) { + DbgIO.println("invalid syntax - see help for details"); + break; + } + Location loc = new Location(lastcmd[1]); + loc.bind(dbg.images, dbg.source_search_paths); + if ( loc is null ) + break; + if ( lastcmd.length > 2 ) + index = cast(int)atoi(lastcmd[2]); + + if ( lastcmd[0] == "clear" ) + { + Breakpoint bp = dbg.getBreakpoint(loc, index); + dbg.removeBreakpoint(index); + } + else + { + Breakpoint bp; + if ( lastcmd[0] == "tbreak" ) + index = -1; + else if ( index <= 0 && dbg.breakpoints.length > 0 ) + index = dbg.breakpoints.keys.dup.sort[$-1]+1; + bp = dbg.setBreakpoint(loc, index, 0); + DbgIO.println("Breakpoint %d at 0x%08x", index, bp.address); + } + break; + // add source search path + case "directory": + if ( lastcmd.length > 1 ) + { + string sp = lastcmd[1].dup; + if ( find(sp, '/') >= 0 ) + sp = replace(sp, "/", "\\"); + if ( sp[$-1] != '\\' ) + sp ~= '\\'; + dbg.source_search_paths ~= sp; + } + else + DbgIO.println("usage: ssp <search_path>"); + break; + // quit + case "quit": + case "q": + dbg.abort = true; + quit = true; + return true; + // run/continue + case "cont": + case "run": + case "start": + if ( dbg.process_loaded ) { + dbg.single_step = false; + dbg.paused = false; + return true; + } + else { + dbg.start(command_line); + return true; + } + // single-step + case "nexti": + dbg.single_step = true; + dbg.activateSingleStep(true); + return true; + // step over + case "next": + if ( dbg.step(StepMode.e_over) ) + return true; + break; + // step into + case "step": + if ( dbg.step(StepMode.e_in) ) + return true; + break; + // step out + case "finish": + if ( dbg.step(StepMode.e_out) ) + return true; + break; + // unwind stack + case "bt": + unwindStack(); + break; + case "print": + case "output": + if ( dbg.process_loaded && lastcmd.length > 1 ) + { + string expr; + if ( lastcmd[1][0] == '/' ) + expr = lastcmd[2]; + else + expr = lastcmd[1]; + try DbgIO.println(symbolValueToString(dbg.handleData(dbg.evaluateExpression(expr), false))); + catch ( EvaluationException e ) { + DbgIO.writeln(e.toString); + } + } + break; + case "disassemble": + if ( !dbg.process_loaded ) + break; + + uint 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; + case "x": + uint start_address; + if ( lastcmd.length > 2 ) + { + if ( lastcmd[1].length > 2 && lastcmd[1][0..2] == "0x" ) + lastcmd[1] = lastcmd[1][2..$]; + sscanf(toStringz(lastcmd[1]), "%x", &start_address); + string count_str = lastcmd[$-1][2..$-2]; + uint count = cast(uint)atoi(count_str); + + dumpMemory(start_address, count); + } + else + DbgIO.println("Warning: too few arguments for x"); + break; + case "whatis": + if ( dbg.process_loaded && lastcmd.length > 1 ) + { + SymbolData sd = dbg.evaluateExpression(lastcmd[1], current_frame_level); + string type = demangle("_D0"~sd.type); + try DbgIO.println("type = %s", type); + catch ( EvaluationException e ) { + DbgIO.writeln(e.toString); + } + } + break; + + + // list source search paths + case "lsp": + foreach ( s; dbg.source_search_paths ) + DbgIO.writeln(s); + 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; + // 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": + string[] modules_noinfo; + foreach ( img; dbg.images.images ) + { + if ( img.codeView is null ) + continue; + 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 ~= name; + } + } + if ( modules_noinfo.length > 0 ) + { + DbgIO.println("Modules without debug information:"); + foreach ( m; modules_noinfo ) + DbgIO.println("%s ", m); + } + break; + // list source modules + case "lsm": + foreach ( img; dbg.images.images ) + { + if ( img.codeView is null ) + continue; + foreach ( m; img.codeView.modulesByIndex ) + { + if ( m.source_module !is null ) + { + foreach ( sf; m.source_module.files ) + { + if ( lastcmd.length > 1 && find(sf.name, lastcmd[1]) < 0 ) + continue; + DbgIO.writeln(sf.name); + } + } + } + } + break; + // list all symbols + case "ls": + 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 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; + // delete breakpoint + case "dbp": + if ( lastcmd.length > 1 ) + { + if ( lastcmd[1] == "*" ) + { + foreach ( uint i, bp; dbg.breakpoints ) + { + bp.deactivate(dbg.process); + dbg.breakpoints.remove(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("Usage: dbp [<bp index>|<file:line>]"); + } + } + else + DbgIO.println("Usage: bp <sourc file>:<line>"); + break; + // dump stack + case "ds": + 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 ( 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; + + // unknown command + default: + DbgIO.println("Warning: 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 % (8*4) == 0 ) + { + if ( i > 0 ) + DbgIO.println(""); + DbgIO.print("0x%08x:", start+i); + } + DbgIO.print(" 0x%02x", data[i]); + } + DbgIO.println(""); + } + + /********************************************************************************************** + + **********************************************************************************************/ + void evalStackSymbols(StackSymbol[] symbols) + { + foreach ( sym; symbols ) + { + string name = sym.mangled_name; + DbgIO.print("%s = ", name); + 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("0x%08x <??>: // ", address); + DbgIO.write(location); + nl = true; + } + if ( source !is null ) + { + if ( !nl ) + DbgIO.print("0x%08x <??>: // ", address); + DbgIO.write(source); + nl = true; + } + if ( nl ) + DbgIO.println; + + assert(asmsource !is null); + DbgIO.print("0x%08x <??>: ", address, 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 ctx; + if ( thread.getContext(ctx) ) + { + DbgIO.println("eax\t0x%x -\nebx\t0x%x -\necx\t0x%x -\nedx\t0x%x -", ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx); + DbgIO.println("cs\t0x%x -\nds\t0x%x -\nes\t0x%x -\nfs\t0x%x -", ctx.SegCs, ctx.SegDs, ctx.SegEs, ctx.SegFs); + DbgIO.println("gs\t0x%x -\nss\t0x%x -\nedi\t0x%x -\nesi\t0x%x -", ctx.SegGs, ctx.SegSs, ctx.Edi, ctx.Esi); + DbgIO.println("ebp\t0x%x -\nesp\t0x%x -\neip\t0x%x -\neflags\t0x%x -", ctx.Ebp, ctx.Esp, ctx.Eip, ctx.EFlags); + } + else + DbgIO.println("Warning: Couldn't get main thread's context"); + } + + /********************************************************************************************** + + **********************************************************************************************/ + string symbolValueToString(SymbolValue val) + { + 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 ~= symbolValueToString(c); + } + str ~= "}"; + } + else + str ~= "{}"; + } + return str; + } + + /********************************************************************************************** + Read command and call CLI supplied parser function. + Gets called when debuggee is suspended. + **********************************************************************************************/ + bool readCommand() + { + if ( cmdQueue.length > 0 ) + { + string cmd = strip(cmdQueue[0]); + cmdQueue = cmdQueue[1..$]; + DbgIO.writeln(prompt~cmd); + return parseCommand(cmd); + } + else { + DbgIO.write(prompt); + string input = DbgIO.readln(); + return parseCommand(input); + } + } +}