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);
+    }
+}