Mercurial > projects > ddbg_continued
view 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 source
/* 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); } } }