view src/cli/ddbgcli.d @ 5:496dfd8f7342 default tip

added: -repeat option for "in", "ov" -run until a line option -run until a function option -break on a function start -n is an alias for ov
author marton@basel.hu
date Sun, 17 Apr 2011 11:05:31 +0200
parents a5fb1bc967e6
children
line wrap: on
line source

/*  Ddbg - Win32 Debugger for the D programming language
 *  Copyright (c) 2007 Jascha Wetzel
 *  All rights reserved. See LICENSE.TXT for details.
 */
module cli.ddbgcli;

import std.string;
static import std.regexp;
import std.c.stdio : sscanf;
import std.c.string;
import std.demangle;

import minidump;
import container;
import util;
import codeview.codeview;
import breakpoint;
import debugger;
import disasm;
import callstack;
import dbgprocess;
import dbgthread;
import expression.expression_apd;
import expression.datahandler : SymbolValue, DataHandler;
import expression.evaluationcontext;
import cli.userinterface;

import win32.winbase;
import win32.winuser;
import win32.winnt;
import win32.dbghelp;

/**************************************************************************************************

**************************************************************************************************/
class DdbgCLI : UserInterfaceBase
{
    const int   PRINT_SOURCE_LINES = 3;

    string[]	lastcmd,
                onexCommands,
                ontermCommands;
    string		debuggee,
                command_line;

    uint        current_frame_level;

    bool		quit;

    const ubyte CPU_REGISTERS = 1,
                FPU_REGISTERS = 2,
                MMX_REGISTERS = 4,
                SSE_REGISTERS = 8;
    ubyte       dump_registers = CPU_REGISTERS;

    uint        lastEvaluationDepth = 1;

    bool        auto_find_scope_frame, /// find frame of last active scope if current frame has no source/symbols
                jump_to_last_known_location_on_exception = true;
                
    uint        repeat = 0;

    /**********************************************************************************************

    **********************************************************************************************/
    void init(string[] args)
    {
        DbgIO.println(WELCOME_STRING);
        if ( args.length < 2 )
            throw new DebuggerException("Usage: ddbg [-cli=<mode>] [-cmd=<commands>] <exe file> [arguments]");

        debuggee = args[1];
        foreach ( inout arg; args )
        {
            if ( find(arg, ' ') >= 0 )
                arg = "\""~arg~"\"";
        }
        command_line = join(args[1..$], " ");

        dbg = new Debugger(debuggee, this);
    }

    int start()
    {
        while ( !quit )
        {
            debug
                readCommand();
            else
            {
                try readCommand();
                catch ( Exception e )
                    DbgIO.println(e.msg);
            }
        }
        return 0;
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void singleStep()
    {
        DbgIO.println(describeLocation(dbg.current_address));
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void exitProcess()
    {
        DbgIO.println("Process terminated");
        repeat = 0;
        cmdQueue ~= ontermCommands;
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void loadedDLL(DLL dll)
    {
        if( dll !is null && dll.image.name.length ) {
            DbgIO.write(dll.image.name);
            DbgIO.println(" loaded at 0x%08x", dll.base);
        }
        else
            DbgIO.writeln("unknown DLL loaded");
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void win32exception(uint threadId, EXCEPTION_RECORD* exrec)
    {
        DbgIO.println(
            "Unhandled Exception: %s(0x%x) at %s thread(%d)",
            getExceptionName(exrec.ExceptionCode), exrec.ExceptionCode, describeLocation(dbg.current_address), threadId
        );
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void exception(uint threadId, string class_name, string msg, size_t obj_ptr)
    {
        if ( jump_to_last_known_location_on_exception )
        {
            uint	index;
            current_frame_level = 0;
            Location loc = dbg.images.findLocation(dbg.current_address);

            if ( loc.codeblock is null )
            {
                dbg.stack.firstFrame(index);
                ubyte[] frame = dbg.stack.prevFrame(index, index);
                while ( loc.codeblock is null && frame !is null )
                {
                    uint ret_adr = (cast(uint[])frame)[1];
                    loc = dbg.images.findLocation(ret_adr);
                    frame = dbg.stack.prevFrame(index, index);
                    ++current_frame_level;
                }
            }
        }

        DbgIO.println("Unhandled D Exception (%s", class_name);
        DbgIO.write(msg.length>0?" \""~msg~"\"":"");
        DbgIO.println(") at %s thread(%d)", describeLocation(dbg.current_address), threadId);
        cmdQueue ~= onexCommands;
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void userInterrupt()
    {
        DbgIO.println("User interrupt at %s", describeLocation(dbg.current_address));
    }

    /**********************************************************************************************

    **********************************************************************************************/
    bool breakpoint(int index, Breakpoint bp, DbgThread thread)
    {
        if ( bp.hardware )
        {
            DbgIO.println("Hardware breakpoint for 0x%x hit at %s thread(%d)",
                bp.address, describeLocation(dbg.current_address), thread.id
            );
        }
        else if ( bp.file is null || bp.line == 0 )
            DbgIO.println("Unknown breakpoint hit at %s thread(%d)", describeLocation(bp.address), thread.id);
        else
        {
            string[]	source = dbg.getSourceFile(bp.file);
            string		source_lines;
            if ( source !is null && bp.line <= source.length )
            {
                const showmore= 2;
                int startfrom = bp.line- 1 - showmore;
                int startend = bp.line- 1 + showmore;
                if (startfrom <0)
                   startfrom=0;
                if (startend >=source.length)
                   startend=source.length-1;
                int left = showmore;
                foreach (line;source[startfrom..startend+1])
                {
                  if (left==0)
                    source_lines ~= ">>"~line ~"\n";
                  else
                    source_lines ~= "  "~line ~"\n";
                  left--;
                }
            }

            if ( !bp.temporary )
                DbgIO.print("Breakpoint %d hit at ", index);
            debug  DbgIO.println("before printing location info");
            DbgIO.println("%s:%d 0x%x thread(%d)", bp.file, bp.line, bp.address, thread.id);
            if ( source_lines.length > 0 )
                DbgIO.write(source_lines);
        }
        return true;
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void debugString(string str)
    {
        printf("OUTPUT DEBUG STRING:\n%s\n", toStringz(str));
    }
    bool setBreakpointByFunctionname(char[][] lastcmd,uint threadId, int index)
    {
        DataSymbol[] ds;
        size_t codebase;
       // int index;
       
                 foreach ( img; dbg.images.images )
                {
                    if ( img.codeView is null )
                        continue;
                    DataSymbol[] tds;
                    ds ~= img.codeView.global_pub.findDataSymbolBySubstring(lastcmd[1]);
                    ds ~=  img.codeView.global_sym.findDataSymbolBySubstring(lastcmd[1]);
                    ds ~= img.codeView.static_sym.findDataSymbolBySubstring(lastcmd[1]);
               
                    foreach ( m; img.codeView.modulesByIndex )
                        ds ~= m.symbols.findDataSymbolBySubstring(lastcmd[1]);
                    if (ds.length>0)
                      codebase=img.getCodeBase;
                  
                }
                debug DbgIO.println("Found candidates:%d",ds.length);
                if (ds.length==1)
                {
                //Location loc = new Location(lastcmd[1]);
                //loc.bind(dbg.images, dbg.source_search_paths);
                if (index>=0)
                if ( lastcmd.length > 2 )
                    index = cast(int)atoi(lastcmd[2]);

                Breakpoint bp;
                if (index>=0) // not temporary
                {if ( index <= 0 && dbg.breakpoints.length > 0 )
                    index = dbg.breakpoints.keys.dup.sort[$-1]+1;
                }
                else
                  index=-1;
                bp = dbg.setBreakpoint(ds[0].offset+codebase, index, threadId);
        
                DbgIO.println("Breakpoint set: %s", bp.toString);
                }
                else
                if (ds.length==0)
                {
                  DbgIO.println("Breakpoint is not set:no matching functions are found!");
                  return false;
                }
                else
                {
                  DbgIO.println("Breakpoint is not set:too many matching functions are found!");
                  int maxi=ds.length;
                  if (maxi>20)
                  {
                    DbgIO.println("There are more than 20 possibilities, first twenty is snown:");
                    maxi=20;
                  }
                  else
                  {
                    DbgIO.println("These are the matched function names:");
                  }
                  foreach (s;ds[0..maxi])
                  {
                   //DbgIO.println(s.name_notype);
                   printSymbol(s);
                  }
                   return false;
                }
       return true;
    }

    /**********************************************************************************************
        Command line parser. Gets called when debuggee is suspended.
    **********************************************************************************************/
    bool parseCommand(string input)
    {
        if ( strip(input).length > 0 )
        {
            auto r = std.regexp.RegExp("(([^\" \\t]+)|(\"[^\"]+\"))+");
            lastcmd.length = 0;
            foreach ( m; r.search(input) )
            {
                //DbgIO.writeln("match:"~r.match(0));
                lastcmd ~= r.match(0);
            }
            
        }
        if ( lastcmd.length <= 0 )
            return false;
        if (lastcmd[0][0]=='=' && lastcmd[0].length>1)
        {
          DbgIO.writeln("after ="~lastcmd[0][1..$]);
          lastcmd=["=",lastcmd[0][1..$]]~lastcmd[1..$];
        }
        switch ( lastcmd[0] )
        {
            case "help":
            case "h":
            case "?":
                DbgIO.write(import("ddbg_help.txt"));
                break;
            case "=":
                if ( !dbg.process_loaded && dbg.miniDump is null )
                    break;
                if ( lastcmd.length > 1 )
                {
                    try DbgIO.writeln(symbolValueToString(dbg.handleData(dbg.evaluateExpression(lastcmd[1], current_frame_level), false)));
                    catch ( EvaluationException e ) {
                        DbgIO.writeln(e.toString);
                    }
                }
                break;
            case "arg":
                if ( dbg.process_loaded )
                    DbgIO.println("Warning: process already started");
                command_line = debuggee~" "~join(lastcmd[1..$], " ");
                break;
            // add breakpoint
            case "bp":
                int index;
                if ( lastcmd.length < 2 ) {
                    DbgIO.println("invalid syntax - see help for details");
                    break;
                }
           
                int pos = find(lastcmd[1], '#');
                uint threadId;
                if ( pos > 0 )
                {
                    threadId = cast(uint)atoi(lastcmd[1][pos+1..$]);
                    lastcmd[1] = lastcmd[1][0..pos];
                }
                Location loc = new Location(lastcmd[1]);
                loc.bind(dbg.images, dbg.source_search_paths);
                if ( lastcmd.length > 2 )
                    index = cast(int)atoi(lastcmd[2]);

                Breakpoint bp;
                if ( index <= 0 && dbg.breakpoints.length > 0 )
                    index = dbg.breakpoints.keys.dup.sort[$-1]+1;
                bp = dbg.setBreakpoint(loc, index, threadId);
                DbgIO.println("Breakpoint set: %s", bp.toString);
                break;
            case "bpf":
                int index;
                if ( lastcmd.length < 2 ) {
                    DbgIO.println("invalid syntax - see help for details");
                    break;
                }
           
                int pos = find(lastcmd[1], '#');
                uint threadId;
                if ( pos > 0 )
                {
                    threadId = cast(uint)atoi(lastcmd[1][pos+1..$]);
                    lastcmd[1] = lastcmd[1][0..pos];
                }
               if (lastcmd.length>1)
                 setBreakpointByFunctionname(lastcmd,threadId,0);
                  break;
                
                
  
                break;
            // delete breakpoint
            case "dbp":
                if ( lastcmd.length > 1 )
                {
                    if ( lastcmd[1] == "*" )
                    {
                        foreach ( uint i; dbg.breakpoints.keys )
                            dbg.removeBreakpoint(i);
                    }
                    else if ( isNumeric(lastcmd[1]) )
                    {
                        if ( !dbg.removeBreakpoint(cast(uint)atoi(lastcmd[1])) )
                            DbgIO.println("invalid breakpoint index: %s",lastcmd[1]);
                    }
                    else
                    {
                        Location loc = new Location(lastcmd[1]);
                        loc.bind(dbg.images, dbg.source_search_paths);
                        if ( loc !is null )
                        {
                            int bp_index;
                            dbg.getBreakpoint(loc, bp_index);
                            if ( bp_index >= 0 && !dbg.removeBreakpoint(bp_index) )
                                DbgIO.println("No breakpoint set at "~lastcmd[1]);
                        }
                        else
                            DbgIO.println("dbp [<source file>:<line>|#index|*]");
                    }
                }
                else
                    DbgIO.println("dbp [<source file>:<line>|#index|*]");
                break;
            // set evaluation depth
            case "er":
                lastcmd.length = 1;
                if ( dbg.evaluationDepth > 1 ) {
                    lastEvaluationDepth = dbg.evaluationDepth;
                    lastcmd ~= "1";
                }
                else
                    lastcmd ~= .toString(lastEvaluationDepth);
            case "ed":
                if ( lastcmd.length < 2 || !isNumeric(lastcmd[1]) )
                    DbgIO.println("Usage: ed <depth>");
                else {
                    dbg.evaluationDepth = cast(int)atoi(lastcmd[1]);
                    DbgIO.println("Expression evaluation depth is %d", dbg.evaluationDepth);
                }
                break;
            case "el":
                if ( lastcmd.length < 2 || !isNumeric(lastcmd[1]) )
                    DbgIO.println("Usage: el <length>");
                else {
                    DataHandler.max_elem_count = cast(int)atoi(lastcmd[1]);
                    DbgIO.println("Array evaluation length is %d", DataHandler.max_elem_count);
                }
                break;
            // select frame
            case "f":
                if ( lastcmd.length > 1 )
                    current_frame_level = cast(uint)atoi(lastcmd[1]);
                DbgIO.println("Current frame level is %d", current_frame_level);
                break;
            // image base address
            case "ii":
                foreach ( img; dbg.images.images )
                {
                    uint ibase = img.imageBase;
                    DbgIO.println("ImageBase\t0x%x", ibase);
                    DbgIO.println("CodeBase\t0x%x", img.getCodeBase);
                    DbgIO.println("Sections:\nname\t\taddress\t\tsize\t\tcharacteristics");
                    foreach ( s; img.sections )
                        with ( *s.header ) DbgIO.println("%s\t0x%08x\t0x%08x\t0x%08x", cast(char[8])Name, ibase+VirtualAddress, Misc.VirtualSize, Characteristics);
                }
                break;
            // jump to last known source frame
            case "jkf":
                jump_to_last_known_location_on_exception = !jump_to_last_known_location_on_exception;
                DbgIO.println("%s to from of last known source location on exception",
                    jump_to_last_known_location_on_exception?"Jumping":"Not jumping"
                );
            break;
            // walk memory
            case "mi":
                if ( !dbg.process_loaded )
                    break;
                uint filteredStates = MEM_COMMIT;
                foreach ( lc; lastcmd[1..$] )
                {
                    switch ( lc )
                    {
                        case "free":    filteredStates |= MEM_FREE; break;
                        case "rsrv":    filteredStates |= MEM_RESERVE; break;
                        default:
                    }
                }

                MEMORY_BASIC_INFORMATION[] mbis = dbg.process.walkMemory;

                DbgIO.println("Base     AlcBase  AlcProt RgnSize  Stat Protect Type");
                foreach ( mbi; mbis )
                {
                    if ( (mbi.State & filteredStates) == 0 )
                        continue;

                    string state;
                    switch ( mbi.State )
                    {
                        case MEM_COMMIT:    state = "comt"; break;
                        case MEM_FREE:      state = "free"; break;
                        case MEM_RESERVE:   state = "rsrv"; break;
                        default:            state = "unkn"; break;
                    }

                    string type;
                    switch ( mbi.Type )
                    {
                        case MEM_IMAGE:     type = "imag"; break;
                        case MEM_MAPPED:    type = "mapd"; break;
                        case MEM_PRIVATE:   type = "priv"; break;
                        default:            type = "unkn"; break;
                    }

                    string protectName(uint prot)
                    {
                        string protStr;
                        switch ( prot & 0xff )
                        {
                            case 0:                         protStr = "?"; break;
                            case PAGE_EXECUTE:              protStr = "x"; break;
                            case PAGE_EXECUTE_READ:         protStr = "xr"; break;
                            case PAGE_EXECUTE_READWRITE:    protStr = "xrw"; break;
                            case PAGE_EXECUTE_WRITECOPY:    protStr = "xwc"; break;
                            case PAGE_NOACCESS:             protStr = "na"; break;
                            case PAGE_READONLY:             protStr = "ro"; break;
                            case PAGE_READWRITE:            protStr = "rw"; break;
                            case PAGE_WRITECOPY:            protStr = "wc"; break;
                            default:                        protStr = format("%02x", prot & 0xff); break;
                        }
                        protStr ~= " ";
                        if ( prot & PAGE_GUARD )
                            protStr ~= "g";
                        if ( prot & PAGE_NOCACHE )
                            protStr ~= "nc";
                        return protStr;
                    }

                    DbgIO.println("%08x %08x %- 7s %08x %s %- 7s %s",
                        cast(size_t)mbi.BaseAddress, cast(size_t)mbi.AllocationBase, protectName(mbi.AllocationProtect),
                        mbi.RegionSize, state, protectName(mbi.Protect), type
                    );
                }
                break;
            // list breakpoints
            case "lbp":
                if ( dbg.breakpoints.length <= 0 )
                    DbgIO.println("no breakpoints set");
                foreach ( uint i, bp; dbg.breakpoints )
                    DbgIO.println("%d %s", i, bp.toString);
                break;
            // ldll
            case "ldll":
                if ( !dbg.process_loaded )
                    break;
                DbgIO.println("Base     Name");
                foreach ( dll; dbg.process.loaded_dlls )
                    DbgIO.println("%08x %s", dll.base, dll.image.name);
                break;
            // list temporary breakpoints
            case "ltbp":
                if ( dbg.temp_breakpoints.empty )
                    DbgIO.println("no temporary breakpoints set");
                foreach ( bp; dbg.temp_breakpoints )
                    DbgIO.writeln(bp.value.toString);
                break;
            // list debug modules
            case "lm":
                Module[]	modules_noinfo;
                foreach ( img; dbg.images.images )
                {
                    foreach ( m; img.codeView.modulesByIndex )
                    {
                        string name = m.name;

                        if ( m.header.iLib > 0 )
                            name ~= " from "~img.codeView.libraries[m.header.iLib];

                        if ( lastcmd.length > 1 && find(name, lastcmd[1]) < 0 )
                            continue;

                        bool has_info = false;
                        with ( m.symbols )
                        {
                            if ( proc_symbols.length+stack_symbols.length+data_symbols.length > 0 )
                            {
                                DbgIO.println(
                                    "%s\n\tSymbols: %d proc %d stack %d data",
                                    name, proc_symbols.length, stack_symbols.length, data_symbols.length
                                );
                                has_info = true;
                            }
                        }
                        if ( m.source_module !is null )
                        {
                            DbgIO.println("\tSource files:");
                            has_info = true;
                            foreach ( sf; m.source_module.files )
                                DbgIO.println("\t\t%s", sf.name);
                        }
                        if ( !has_info )
                            modules_noinfo ~= m;
                    }
                }
                if ( modules_noinfo.length > 0 )
                {
                    DbgIO.println("Modules without debug information:");
                    foreach ( img; dbg.images.images )
                    {
                        foreach ( m; modules_noinfo )
                        {
                            string name = m.name;

                            if ( m.header.iLib > 0 )
                                name ~= " from "~img.codeView.libraries[m.header.iLib];

                            DbgIO.println("%s #segs=%d", name, m.seginfos.length);
                            if ( m.source_module !is null )
                            {
                                DbgIO.println("\tSource files:");
                                foreach ( sf; m.source_module.files )
                                    DbgIO.println("\t\t%s", sf.name);
                            }
                        }
                    }
                }
                break;
            // list source modules
            case "lsm":
                uint[string]    line_counters;
                uint[string]    seg_counters;
                foreach ( img; dbg.images.images )
                {
                    if ( img.codeView is null )
                        continue;
                    foreach ( m; img.codeView.modulesByIndex )
                    {
                        if ( m.source_module is null )
                            continue;
                        foreach ( sf; m.source_module.files )
                        {
                            if ( lastcmd.length > 1 && find(sf.name, lastcmd[1]) < 0 )
                                continue;
                            uint lines = sf.lines.length;
                            if ( (sf.name in line_counters) is null ) {
                                seg_counters[sf.name] = sf.segments.length;
                                line_counters[sf.name] = lines;
                            }
                            else {
                                seg_counters[sf.name] += sf.segments.length;
                                line_counters[sf.name] += lines;
                            }
                        }
                    }
                }
                string[] lc_keys = line_counters.keys.dup;
                lc_keys.sort;
                foreach ( key; lc_keys )
                    DbgIO.println("%s\t\tsegs=%d lines=%d", key, seg_counters[key], line_counters[key]);
                break;
            // list source lines per module
            case "lsl":
                foreach ( img; dbg.images.images )
                {
                    if ( img.codeView is null )
                        continue;
                    foreach ( m; img.codeView.modulesByIndex )
                    {
                        if ( m.source_module is null )
                            continue;
                        DbgIO.println("module %s", m.name);
                        foreach ( sf; m.source_module.files )
                        {
                            DbgIO.println("file %s", sf.name);
                            if ( lastcmd.length > 1 && find(sf.name, lastcmd[1]) < 0 )
                                continue;
                            foreach ( l; sf.lines.reverse )
                            {
                                DbgIO.println("line %d", l);
                            }
                        }
                    }
                }
                break;
            // list all symbols
            case "ls":
/*
                void dumpSymbolAVL(AVLNode!(NamedSymbol) node, uint indent=0)
                {
                    string indentstr = new char[indent*2];
                    indentstr[0..indent*2] = ' ';
                    DbgIO.println("%s%s", indentstr, node.value.name_notype);
                    if ( node.left !is null )
                        dumpSymbolAVL(node.left, indent+1);
                    if ( node.right !is null )
                        dumpSymbolAVL(node.right, indent+1);
                }
                DbgIO.println("AVL dump:");
                dumpSymbolAVL(dbg.image.codeView.globalNamedSymbols.root);
*/
                foreach ( img; dbg.images.images )
                {
                    if ( img.codeView is null )
                        continue;
                    printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null);
                    printSymbols(img.codeView, img.codeView.global_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null);
                    printSymbols(img.codeView, img.codeView.static_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null);
                    foreach ( m; img.codeView.modulesByIndex )
                        printSymbols(img.codeView, m.symbols.named_symbols, lastcmd.length>1?lastcmd[1]:null);
                }
                break;
            // list source search paths
            case "lsp":
                foreach ( s; dbg.source_search_paths )
                    DbgIO.writeln(s);
                break;
            // list function symbols
            case "lf":
                foreach ( img; dbg.images.images )
                {
                    if ( img.codeView is null )
                        continue;
                    printSymbols(img.codeView, img.codeView.global_pub.proc_symbols, lastcmd.length>1?lastcmd[1]:null);
                    printSymbols(img.codeView, img.codeView.global_sym.proc_symbols, lastcmd.length>1?lastcmd[1]:null);
                    printSymbols(img.codeView, img.codeView.static_sym.proc_symbols, lastcmd.length>1?lastcmd[1]:null);
                    foreach ( m; img.codeView.modulesByIndex )
                        printSymbols(img.codeView, m.symbols.proc_symbols, lastcmd.length>1?lastcmd[1]:null);
                }
                break;
            // list data symbols
            case "ld":
                foreach ( img; dbg.images.images )
                {
                    if ( img.codeView is null )
                        continue;
                    printSymbols(img.codeView, img.codeView.global_pub.data_symbols, lastcmd.length>1?lastcmd[1]:null);
                    printSymbols(img.codeView, img.codeView.global_sym.data_symbols, lastcmd.length>1?lastcmd[1]:null);
                    printSymbols(img.codeView, img.codeView.static_sym.data_symbols, lastcmd.length>1?lastcmd[1]:null);
                    foreach ( m; img.codeView.modulesByIndex )
                        printSymbols(img.codeView, m.symbols.data_symbols, lastcmd.length>1?lastcmd[1]:null);
                }
                break;
            // list global symbols
            case "lg":
                foreach ( img; dbg.images.images )
                {
                    if ( img.codeView is null )
                        continue;
                    printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null);
                    printSymbols(img.codeView, img.codeView.global_sym.named_symbols, lastcmd.length>1?lastcmd[1]:null);
                }
                break;
            // list global publics
            case "lp":
                foreach ( img; dbg.images.images )
                {
                    if ( img.codeView is null )
                        continue;
                    printSymbols(img.codeView, img.codeView.global_pub.named_symbols, lastcmd.length>1?lastcmd[1]:null);
                }
                break;
            // list scope variables
            case "lsv":
                if ( dbg.process_loaded || dbg.miniDump !is null )
                    evalScopeSymbols();
                break;
            // list threads
            case "lt":
                if ( dbg.miniDump !is null )
                {
                    DbgIO.println("  id     pri  sus location");
                    MINIDUMP_THREAD[] threads = dbg.miniDump.threads;
                    foreach ( thread; threads )
                    {
                        CONTEXT* ctx;
                        ctx = cast(CONTEXT*)dbg.miniDump.rvaToVa(thread.ThreadContext.Rva);
                        Location    loc = dbg.images.findLocation(ctx.Eip);

                        DbgIO.println("%s%s%- 6d %- 3d  %- 2d  %s",
                            thread.ThreadId==dbg.thread_id?">":" ", thread.ThreadId==dbg.miniDump.threadInfo.mainThreadId?"*":" ",
                            thread.ThreadId, thread.Priority, thread.SuspendCount, describeLocation(loc)
                        );
                    }
                }
                else
                {
                    DbgIO.println("  id     pri  sus creation     exit         kernel       user         location");
                    foreach ( t; dbg.process.threads.values )
                    {
                        // id name location pri pri-boost start-info times
                        ulong   creation,
                                exit,
                                kernel,
                                user;

                        t.times(creation, exit, kernel, user);

                        CONTEXT ctx;
                        t.getContext(ctx);
                        Location    loc = dbg.images.findLocation(ctx.Eip);

                        DbgIO.println("%s%s%- 6d %- 3d%s %- 2d  %s %s %s %s %s",
                            t.id==dbg.thread_id?">":" ", t.id==dbg.process.mainThreadId?"*":" ", t.id,
                            t.priority, t.priorityBoost?"b":" ", t.suspendCount,
                            formatTicks(creation), formatTicks(exit), formatTicks(kernel), formatTicks(user),
                            describeLocation(loc)
                        );
                    }
                }
                break;
            // no console
            case "nc":
                dbg.create_new_console = !dbg.create_new_console;
                DbgIO.println("Starting debuggee in %s console", dbg.create_new_console?"new":"this");
                break;
            // on exception
            case "onex":
                if ( lastcmd.length < 2 )
                {
                    foreach ( cmd; onexCommands )
                        DbgIO.writeln(cmd[0..$-1]);
                }
                else
                {
                    onexCommands = null;
                    string cmdList;
                    foreach ( cmd; lastcmd[1..$] )
                    {
                        if ( cmd[0] == '"' && cmd[$-1] == '"' )
                            cmdList ~= " "~cmd[1..$-1];
                        else
                            cmdList ~= " "~cmd;
                    }
                    foreach ( cmd; split(cmdList, ";") )
                        onexCommands ~= strip(cmd)~";";
                }
                break;
            // on termination
            case "onterm":
                if ( lastcmd.length < 2 )
                {
                    foreach ( cmd; ontermCommands )
                        DbgIO.writeln(cmd[0..$-1]);
                }
                else
                {
                    ontermCommands = null;
                    string cmdList;
                    foreach ( cmd; lastcmd[1..$] )
                    {
                        if ( cmd[0] == '"' && cmd[$-1] == '"' )
                            cmdList ~= " "~cmd[1..$-1];
                        else
                            cmdList ~= " "~cmd;
                    }
                    foreach ( cmd; split(cmdList, ";") )
                        ontermCommands ~= strip(cmd)~";";
                }
                break;
            // print source
            case "ps":
                Location    loc;
                string[]    source;

                do
                {
                    if ( current_frame_level > 0 )
                    {
                        auto frame = dbg.stack.getFrame(current_frame_level);
                        if ( frame is null )
                            goto Lnotfound;
                        uint ret_adr = (cast(uint[])frame)[1];
                        loc = dbg.images.findLocation(ret_adr);
                    }
                    else
                        loc = dbg.images.findLocation(dbg.current_address);
                    debug DbgIO.println("found location %s", loc.file);

                    source = dbg.getSourceFile(loc.file);
                    if ( source !is null )
                        break;
                    if ( auto_find_scope_frame ) {
                        ++current_frame_level;
                        continue;
                    }
                Lnotfound:
                    DbgIO.writeln("Source file for current location unknown");
                    break;
                }
                while( source is null );

                int start_line, end_line;
                if ( lastcmd.length > 1 ) {
                    start_line = cast(uint)atoi(lastcmd[1]);
                    end_line = loc.line + start_line;
                    start_line = loc.line - start_line;
                }
                else if ( loc.scope_sym !is null )
                {
                    ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym;
                    auto start_address = loc.getCodeBase + psym.cvdata.offset;
                    auto start_loc  = dbg.images.findLocation(start_address);
                    auto end_loc    = dbg.images.findLocation(start_address+psym.cvdata.proc_length-1);
                    debug DbgIO.println("address range: 0x%x 0x%x", start_address, start_address+psym.cvdata.proc_length-1);
                    debug DbgIO.println("lines before prev: %d - %d", start_loc.line, end_loc.line);
                    end_loc = dbg.images.findPrevSrcLine(end_loc);
                    start_line = start_loc.line;
                    if ( end_loc !is null )
                        end_line = end_loc.line;
                }
                if ( end_line == 0 ) {
                    start_line = loc.line - PRINT_SOURCE_LINES;
                    end_line = loc.line + PRINT_SOURCE_LINES;
                }

                debug DbgIO.println("lines: %d - %d", start_line, end_line);
                for ( int l = dmax(0, start_line-1); l < dmin(cast(int)source.length, end_line); ++l )
                {
                    if ( l+1 == loc.line )
                        DbgIO.write(">");
                    DbgIO.writeln(source[l]);
                }
                break;
            // quit
            case "q":
                dbg.abort = true;
                quit = true;
                return true;
            // run/continue
            case "r": case "rf":
                if ( dbg.miniDump !is null ) {
                    DbgIO.println("Command not valid in post-mortem mode");
                    break;
                }
                int index;
                if ( lastcmd[0]=="r" && lastcmd.length == 2 ) {
                   
           
                int pos = find(lastcmd[1], '#');
                uint threadId;
                if ( pos > 0 )
                {
                    threadId = cast(uint)atoi(lastcmd[1][pos+1..$]);
                    lastcmd[1] = lastcmd[1][0..pos];
                }
                Location loc = new Location(lastcmd[1]);
                if (!loc.bind(dbg.images, dbg.source_search_paths))
                   break;
                //if ( lastcmd.length > 2 )
                //    index = cast(int)atoi(lastcmd[2]);
                index=-1;
                Breakpoint bp;
                if ( index <= 0 && dbg.breakpoints.length > 0 )
                    index = dbg.breakpoints.keys.dup.sort[$-1]+1;
                bp = dbg.setBreakpoint(loc, index, threadId);
                DbgIO.println("Breakpoint set: %s", bp.toString);
                }
                if ( lastcmd[0]=="rf" && lastcmd.length == 2 ) {
                 int pos = find(lastcmd[1], '#');
                uint threadId;
                if ( pos > 0 )
                {
                    threadId = cast(uint)atoi(lastcmd[1][pos+1..$]);
                    lastcmd[1] = lastcmd[1][0..pos];
                }
                 if (!setBreakpointByFunctionname(lastcmd,threadId,-1))
                   break;
                
                
                }
                if ( dbg.process_loaded ) {
                    dbg.resume;
                    return true;
                }
                else {
                    dbg.breakonmain = false;
                    dbg.start(command_line);                 
                    return true;
                }
            // single-step
            case "s":
                if ( dbg.miniDump !is null ) {
                    DbgIO.println("Command not valid in post-mortem mode");
                    break;
                }
                if ( dbg.process_loaded ) {
                    dbg.single_step = true;
                    dbg.activateSingleStep(true);
                }
                return true;
            // add source search path
            case "sp":
                if ( lastcmd.length > 1 )
                {
                    string sp = lastcmd[1].dup;
                    if ( sp[$-1] != '\\' )
                        sp ~= '\\';
                    dbg.source_search_paths ~= sp;
                }
                else
                    DbgIO.println("usage: sp <search_path>");
                break;
            // switch thread
            case "st":
                if ( lastcmd.length < 2 ) {
                    DbgIO.writeln("Usage: st <threadID>");
                    break;
                }
                size_t threadId = cast(size_t)atoi(lastcmd[1]);
                if ( dbg.miniDump !is null )
                {
                    foreach ( thread; dbg.miniDump.threads )
                    {
                        if ( threadId == thread.ThreadId ) {
                            dbg.selectThread(threadId);
                            break;
                        }
                    }
                }
                else
                {
                    foreach ( t; dbg.process.threads.values )
                    {
                        if ( threadId == t.id ) {
                            dbg.selectThread(threadId);
                            break;
                        }
                    }
                }
                break;
            // step over
            case "n": case "ov":
                if ( dbg.miniDump !is null ) {
                    DbgIO.println("Command not valid in post-mortem mode");
                    break;
                }
                if ( lastcmd.length ==2 ) {
                   repeat=cast(uint)atoi(lastcmd[1]);
                   repeat--;
                   lastcmd.length = 1;
                }
                if ( dbg.process_loaded && dbg.step(StepMode.e_over) )
                {
                    debug foreach ( bp; dbg.temp_breakpoints )
                        DbgIO.writeln(bp.value.toString);
                    return true;
                }
                if (!dbg.process_loaded)
                {
                   dbg.breakonmain = true;
                   dbg.start(command_line);
                   return true;
                }
                break;
            // step into
            case "in":
                if ( dbg.miniDump !is null ) {
                    DbgIO.println("Command not valid in post-mortem mode");
                    break;
                }
                if ( lastcmd.length ==2 ) {
                   repeat=cast(uint)atoi(lastcmd[1]);
                   repeat--;
                   lastcmd.length = 1;
                }
                if ( dbg.process_loaded && dbg.step(StepMode.e_in) )
                {
                    debug foreach ( bp; dbg.temp_breakpoints )
                        DbgIO.writeln(bp.value.toString);
                    return true;
                }
                break;
            // step out
            case "out":
                if ( dbg.miniDump !is null ) {
                    DbgIO.println("Command not valid in post-mortem mode");
                    break;
                }
                if ( dbg.process_loaded && dbg.step(StepMode.e_out) )
                {
                    debug foreach ( bp; dbg.temp_breakpoints )
                        DbgIO.writeln(bp.value.toString);
                    return true;
                }
                break;
            // disassemble
            case "da":
                if ( !dbg.process_loaded )
                    break;

                uint start_address;
                if ( current_frame_level > 0 ) {
                    auto frame = dbg.stack.getFrame(current_frame_level);
                    start_address = (cast(uint[])frame)[1];
                }
                else
                    start_address = dbg.current_address;

                if ( lastcmd.length > 1 )
                    sscanf(toStringz(lastcmd[1]), "%x", &start_address);
                else
                {
                    Location loc = dbg.images.findLocation(dbg.current_address);
                    if ( loc.scope_sym !is null ) {
                        ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym;
                        start_address = loc.getCodeBase + psym.cvdata.offset;
                        DisAsm.disasm(dbg.process, start_address, start_address+psym.cvdata.proc_length, &printDisassembly);
                        break;
                    }
                }
                DisAsm.disasm(dbg.process, start_address, 0, &printDisassembly);
                break;
            // disassemble line
            case "dal":
                if ( !dbg.process_loaded )
                    break;

                int num_lines = 1;
                if ( lastcmd.length > 1 )
                {
                    sscanf(toStringz(lastcmd[1]), "%d", &num_lines);
                    if ( num_lines < 0 )
                        --num_lines;
                }

                uint start_address;
                if ( current_frame_level > 0 ) {
                    auto frame = dbg.stack.getFrame(current_frame_level);
                    start_address = (cast(uint[])frame)[1];
                }
                else
                    start_address = dbg.current_address;

                for ( ; num_lines != 0; num_lines<0?++num_lines:--num_lines )
                {
                    Location loc = dbg.images.findLocation(start_address);
                    if ( loc is null ) {
                        DbgIO.println("No source line information available");
                        break;
                    }
                    DisAsm.disasm(
                        dbg.process,
                        loc.getCodeBase+loc.codeblock.start,
                        loc.getCodeBase+loc.codeblock.end,
                        &printDisassembly
                    );
                }
                break;
            // dump registers
            case "dr":
                if ( lastcmd.length > 1 )
                {
                    foreach ( lc; lastcmd[1..$] )
                    {
                        switch ( lc )
                        {
                            case "cpu": dump_registers |= CPU_REGISTERS;    break;
                            case "fpu": dump_registers |= FPU_REGISTERS;    break;
                            case "mmx": dump_registers |= MMX_REGISTERS;    break;
                            case "sse": dump_registers |= SSE_REGISTERS;    break;
                            default:
                                DbgIO.println("Unknown register set \"%s\"", lc);
                        }
                    }
                }
                if ( dump_registers == 0 ) {
                    DbgIO.println("No register set selected. See help for usage of \"dr\"");
                    break;
                }
                if ( dbg.miniDump !is null )
                    printRegisterDump(dbg.miniDump.getContext);
                else if ( dbg.process_loaded )
                    printRegisterDump(dbg.process.threads[dbg.thread_id]);
                break;
            // dump stack
            case "ds":
                if ( !dbg.process_loaded )
                    break;

                int dump_length;
                if ( lastcmd.length > 1 )
                    dump_length = cast(int)atoi(lastcmd[1])*4;
                else
                {
                    CONTEXT ctx;
                    if ( dbg.process.threads[dbg.thread_id].getContext(ctx, CONTEXT_CONTROL) )
                        dump_length = ctx.Ebp-ctx.Esp+8;
                    if ( dump_length <= 0 )
                        dump_length = 16*4;
                }
                int top = dbg.stack.data.length>dump_length?dump_length:dbg.stack.data.length;
                dumpMemory(dbg.stack.top_ptr, top, dbg.stack.data);
                break;
            case "dm":
                if ( !dbg.process_loaded )
                    break;

                if ( lastcmd.length < 3 ) {
                    DbgIO.println("usage: dm <start> <length>");
                    break;
                }
                uint start;
                sscanf(toStringz(lastcmd[1]), "%x", &start);
                dumpMemory(start, cast(uint)atoi(lastcmd[2]));
                break;
            case "hwbp":
                int index;
                if ( lastcmd.length < 2 ) {
                    DbgIO.println("invalid syntax - see help for details");
                    break;
                }

                int pos = find(lastcmd[1], '#');
                uint threadId;
                if ( pos > 0 )
                {
                    threadId = cast(uint)atoi(lastcmd[1][pos+1..$]);
                    lastcmd[1] = lastcmd[1][0..pos];
                }

                Location loc;

                size_t address;
                sscanf(toStringz(lastcmd[1]), "%x", &address);
                if ( address > 0 )
                    loc = new Location(address);
                else
                    loc = new Location(lastcmd[1]);
                loc.bind(dbg.images, dbg.source_search_paths);
                if ( lastcmd.length > 2 )
                    index = cast(int)atoi(lastcmd[2]);

                Breakpoint bp;
                if ( index <= 0 && dbg.breakpoints.length > 0 )
                    index = dbg.breakpoints.keys.dup.sort[$-1]+1;
                bp = dbg.setBreakpoint(loc, index, threadId, true);
                DbgIO.println("Hardware Breakpoint set: %s", bp.toString);
                break;
            // type of expression
            case "t":
                if ( dbg.process_loaded && lastcmd.length > 1 )
                {
                    SymbolData sd;
                    try {
                        sd = dbg.evaluateExpression(lastcmd[1], current_frame_level);
                        string type = demangle("_D0"~sd.type);
                        DbgIO.println("%s\n%s", sd.type, type);
                    }
                    catch ( EvaluationException e ) {
                        DbgIO.writeln(e.toString);
                    }
                }
                break;
            // unwind stack
            case "us":
                if ( dbg.process_loaded || dbg.miniDump !is null )
                    unwindStack();
                break;
            // write minidump
            case "wmd":
                if ( dbg.miniDump !is null ) {
                    DbgIO.println("Command not valid in post-mortem mode");
                    break;
                }
                if ( !dbg.process_loaded )
                    break;
                if ( !MiniDump.haveMiniDump ) {
                    DbgIO.println("DbgHelp.dll required for MiniDump support.\nInstall the Microsoft Platform SDK or Windows XP.");
                    break;
                }
                if ( lastcmd.length < 2 ) {
                    DbgIO.println("Usage: wmd <filename>");
                    break;
                }
                dbg.writeMiniDump(lastcmd[1]);
                break;
            // read minidump
            case "rmd":
                if ( dbg.process_loaded ) {
                    DbgIO.println("For post-mortem debugging, the process must not be started");
                    break;
                }
                if ( !MiniDump.haveMiniDump ) {
                    DbgIO.println("DbgHelp.dll required for MiniDump support.\nInstall the Microsoft Platform SDK or Windows XP.");
                    break;
                }
                if ( lastcmd.length < 2 ) {
                    DbgIO.println("Usage: rmd <filename>");
                    break;
                }
                dbg.readMiniDump(lastcmd[1]);
                break;
            // unknown command
            default:
                DbgIO.println("Unknown command '%s' ignored!", lastcmd[0]);
                break;
        }

        return false;
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void dumpMemory(uint start, uint length, ubyte[] _data=null)
    {
        ubyte[]	data;
        if ( _data is null )
        {
            data.length = length;
            if ( !dbg.process.readProcessMemory(start, data.ptr, data.length) )
                return;
        }
        else
            data = _data;
        for ( uint i = 0; i < length; ++i )
        {
            if ( i % (4*4) == 0 )
            {
                if ( i > 0 )
                    DbgIO.println("");
                DbgIO.print("%08x:", start+i);
            }
            if ( i % 4 == 0 )
                DbgIO.print(" ");
            DbgIO.print("%02x", data[i]);
        }
        DbgIO.println("");
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void evalScopeSymbols()
    {
        uint scope_address = dbg.getScopeAddress(current_frame_level);

        ScopeSymbol scope_sym = dbg.images.findProcedureSymbol(scope_address);
        if ( scope_sym is null ) {
            DbgIO.println("No valid scope active");
            return;
        }

        for ( ; scope_sym !is null; scope_sym = scope_sym.parent_scope )
        {
            DbgIO.println("Scope: %s, parent: %x", scope_sym.name_type, cast(void*)scope_sym.parent_scope);

            StackSymbol[] locals_args = scope_sym.symbols.stack_symbols;
            auto psym = cast(ProcedureSymbol)scope_sym;
            if  ( psym !is null )
                locals_args ~= psym.arguments.stack_symbols;
            foreach ( sym; locals_args.sort )
            {
                string name = sym.mangled_name;
                DbgIO.print("%s = ", name);
//                DbgIO.print("%s = [ebp%s%d] ", name, sym.cvdata.offset>0?"+":"", sym.cvdata.offset);
                try DbgIO.writeln(symbolValueToString(dbg.handleData(dbg.evaluateExpression(name, current_frame_level, sym), true)));
                catch ( EvaluationException e ) {
                    DbgIO.println(e.msg);
                }
            }
        }
    }

    /**********************************************************************************************

    **********************************************************************************************/
    void printAsmLine(uint address, string bytes, string asmsource, string symbol, string location, string source)
    {
        bool nl = false;
        if ( location !is null ) {
            DbgIO.print("%s ", location);
            nl = true;
        }
        if ( source !is null ) {
            DbgIO.write(source);
            nl = true;
        }
        if ( nl )
            DbgIO.println;

        // align next column
        char[] indent;
        int indcount = 2*12-bytes.length;
        if ( indcount > 0 ) {
            indent.length = indcount;
            indent[0..indcount] = ' ';
        }
        // print aligned asm source
        assert(asmsource !is null);
        assert(bytes !is null);
        DbgIO.print("%08x: ", address, bytes, indent, asmsource);

        if ( symbol !is null )
            DbgIO.write(symbol);
        DbgIO.println;
    }

    /**********************************************************************************************
        Prints the register contents of the given thread's context.
    **********************************************************************************************/
    void printRegisterDump(DbgThread thread)
    {
        CONTEXT     ctxMem;
        uint context_flags;
        if ( dump_registers & CPU_REGISTERS )
            context_flags |= CONTEXT_FULL;
        if ( dump_registers & FPU_REGISTERS )
            context_flags |= CONTEXT_FLOATING_POINT;
        if ( dump_registers & (MMX_REGISTERS|SSE_REGISTERS) )
            context_flags |= CONTEXT_EXTENDED_REGISTERS;
        if ( thread.getContext(ctxMem, context_flags) )
            printRegisterDump(&ctxMem);
        else
            DbgIO.println("ERROR: Couldn't get main thread's context");
    }

    void printRegisterDump(CONTEXT* ctx)
    {
        assert ( ctx !is null );

        bool first = true;

        if ( dump_registers & CPU_REGISTERS )
        {
            DbgIO.println("EAX = %08x\tEBX = %08x\tECX = %08x\tEDX = %08x", ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx);
            DbgIO.println("EDI = %08x\tESI = %08x\tEBP = %08x\tESP = %08x", ctx.Edi, ctx.Esi, ctx.Ebp, ctx.Esp);
            DbgIO.println("EIP = %08x\tEFL = %08x", ctx.Eip, ctx.EFlags);
            DbgIO.println(" CS = %08x\t DS = %08x\t ES = %08x\t FS = %08x", ctx.SegCs, ctx.SegDs, ctx.SegEs, ctx.SegFs);
            DbgIO.println(" GS = %08x\t SS = %08x", ctx.SegGs, ctx.SegSs);
            first = false;
        }

        if ( dump_registers & FPU_REGISTERS )
        {
            if ( !first )
                DbgIO.println();
            DbgIO.println("FCW = %04x\tFSW = %04x\tFTW = %04x\tFOP = %04x",
                cast(ushort)ctx.FloatSave.ControlWord, cast(ushort)ctx.FloatSave.StatusWord,
                cast(ushort)ctx.FloatSave.TagWord, (cast(ushort[])ctx.ExtendedRegisters)[3]
            );
            DbgIO.println("IP = %08x\tCS = %04x\tDP = %08x\tDS = %04x",
                (cast(uint[])ctx.ExtendedRegisters)[2], (cast(ushort[])ctx.ExtendedRegisters)[6],
                (cast(uint[])ctx.ExtendedRegisters)[4], (cast(ushort[])ctx.ExtendedRegisters)[10]
            );
            for ( int i = 0; i < 8; ++i )
                DbgIO.println("ST%d = % .16e", i, (cast(real[])ctx.FloatSave.RegisterArea)[i]);
            first = false;
        }

        if ( dump_registers & MMX_REGISTERS )
        {
            if ( !first )
                DbgIO.println();
            for ( int i = 0; i < 8; ++i )
            {
                DbgIO.println("MM%d = %016x", i, (cast(long[])ctx.ExtendedRegisters)[4+i*2]);
                DbgIO.println("    = [%.6g, %.6g]",
                    (cast(float[])ctx.ExtendedRegisters)[8+i*4],
                    (cast(float[])ctx.ExtendedRegisters)[8+i*4+1]
                );
            }
            first = false;
        }

        if ( dump_registers & SSE_REGISTERS )
        {
            if ( !first )
                DbgIO.println();
            DbgIO.println("MXCSR = %08x", (cast(uint[])ctx.ExtendedRegisters)[6]);
            for ( int i = 0; i < 8; ++i )
            {
                DbgIO.println("XMM%d = %016x%016x", i, (cast(long[])ctx.ExtendedRegisters)[20+i*2+1], (cast(long[])ctx.ExtendedRegisters)[20+i*2]);
                DbgIO.println("     = [%.6g, %.6g, %.6g, %.6g]",
                    (cast(float[])ctx.ExtendedRegisters)[40+i*4],
                    (cast(float[])ctx.ExtendedRegisters)[40+i*4+1],
                    (cast(float[])ctx.ExtendedRegisters)[40+i*4+2],
                    (cast(float[])ctx.ExtendedRegisters)[40+i*4+3]
                );
                DbgIO.println("     = [%.12g, %.12g]",
                    (cast(double[])ctx.ExtendedRegisters)[20+i*2],
                    (cast(double[])ctx.ExtendedRegisters)[20+i*2+1]
                );
            }
        }
    }

    /**********************************************************************************************

    **********************************************************************************************/
    string symbolValueToString(SymbolValue val)
    {
        return symbolValueToString(val,"");
    }

    string symbolValueToString(SymbolValue val, string indent)
    {
        string str;
        if ( val.name !is null )
            str = val.name~" = ";
        if ( val.value !is null )
            str ~= val.value;
        else
        {
            if ( val.children.length > 0 )
            {
                str ~= "{";
                bool first = true;
                foreach ( c; val.children )
                {
                    if ( first )
                        first = false;
                    else
                        str ~= ",";
                    str ~= "\n  "~indent~symbolValueToString(c, indent~"  ");
                }
                str ~= "\n"~indent~"}";
            }
            else
                str ~= "{}";
        }
        return str;
    }

    /**********************************************************************************************
        Read command and call CLI supplied parser function.
        Gets called when debuggee is suspended.
    **********************************************************************************************/
    bool readCommand()
    {
  
        if (repeat>0)
        {
          repeat--;
          return parseCommand("");
        }
        if ( cmdQueue.length <= 0 ) {
            DbgIO.write("->");
            string input = DbgIO.readln();
            cmdQueue ~= input;
        }
        if ( cmdQueue.length <= 0 )
            return false;

        string cmd = strip(cmdQueue[0]);
        cmdQueue = cmdQueue[1..$];
        if ( cmd.length > 0 && cmd[$-1] == ';' ) {
            cmd = cmd[0..$-1];
            DbgIO.writeln("->"~cmd);
        }
        return parseCommand(cmd);
    }
}