diff src/debugger.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 47dd986a534e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/debugger.d	Tue Apr 05 20:44:01 2011 +0200
@@ -0,0 +1,1378 @@
+/*  Ddbg - Win32 Debugger for the D programming language
+ *  Copyright (c) 2007 Jascha Wetzel
+ *  All rights reserved. See LICENSE.TXT for details.
+ */
+
+/**************************************************************************************************
+    The main debugger code. Handles win32 debugging events,
+    manages breakpoints, calls the disassembler, etc.
+**************************************************************************************************/
+
+import std.file;
+import std.string;
+import std.format;
+
+import win32.winbase;
+import win32.winuser;
+import win32.winnt;
+import win32.dbghelp;
+
+import minidump;
+import container;
+import util;
+import breakpoint;
+import dbgprocess;
+import dbgthread;
+import disasm;
+import callstack;
+import expression.evaluationcontext;
+import expression.expression_apd;
+import expression.datahandler;
+import codeview.coff;
+import codeview.codeview;
+import cli.userinterface;
+
+const uint      VERSION_MAJOR = 0,
+                VERSION_MINOR = 12,
+                VERSION_PATCH = 0;
+const string	VERSION_STRING =    "Ddbg "~itoa(VERSION_MAJOR)~"."~itoa(VERSION_MINOR)
+                                    ~(VERSION_PATCH>0?"."~itoa(VERSION_PATCH):"")~" beta",
+                WELCOME_STRING =	VERSION_STRING~" - D Debugger\n"~
+                                    "Copyright (c) 2007 Jascha Wetzel\n"~
+                                    "see http://ddbg.mainia.de/doc.html for documentation\n";
+
+class DebuggerException : Exception
+{
+    this(...)
+    {
+        string str;
+
+        void putc(dchar c)
+        {
+            str ~= c;
+        }
+
+        doFormat(&putc, _arguments, _argptr);
+        super(str);
+    }
+}
+
+/**************************************************************************************************
+
+**************************************************************************************************/
+class Debugger
+{
+    const uint STATUS_DIGITAL_MARS_D_EXCEPTION = (3 << 30) | (1 << 29) | (0 << 28) | ('D' << 16) | 1;
+
+    //=============================================================================================
+    // variables
+    bool	            paused,
+                        process_loaded,
+                        abort,
+                        single_step;
+
+    size_t			    current_address;
+    uint                last_line;
+
+    string[][string]	source_files;
+    string[]			source_search_paths;
+
+    string			    main_image_file;
+    COFFImage           main_image;
+    ImageSet            images;
+
+    Breakpoint[uint]	breakpoints;
+    List!(Breakpoint)   temp_breakpoints;
+    Breakpoint		    passing_breakpoint;
+    DbgProcess		    process;
+    MiniDump            miniDump;
+
+    CallStack           current_stack;
+
+    EXCEPTION_RECORD*   exceptionRecord;
+    DEBUG_EVENT 	    debug_event;
+    uint			    process_id,
+                        thread_id;
+
+    UserInterface	    ui;
+
+    uint                evaluationDepth = 1;
+    bool                create_new_console;
+
+    //=============================================================================================
+    // construction
+
+    /**********************************************************************************************
+        Uses the debugger's CLI arguments for the debuggee.
+        Example: debugger debuggee arg0 arg1 ...
+    **********************************************************************************************/
+    this(string debuggee, UserInterface _ui)
+    {
+        assert(_ui !is null);
+        images = new ImageSet;
+        ui = _ui;
+        openImage(debuggee);
+        source_search_paths ~= ".\\";
+        temp_breakpoints = new List!(Breakpoint);
+    }
+
+    CallStack stack()
+    {
+        if ( current_stack is null )
+        {
+            if ( process !is null )
+                current_stack = process.loadStack(process.threads[thread_id]);
+            else if ( miniDump !is null ) {
+                CONTEXT* ctx = miniDump.getContext;
+                // TODO: where does that 4-byte offset come from
+                ubyte[] data = miniDump.getMemory(miniDump.thread.Stack.Memory)[4..$];
+                current_stack = new CallStack(cast(size_t)miniDump.thread.Stack.StartOfMemoryRange+data.length, ctx.Esp, ctx.Ebp);
+                current_stack.data = data;
+            }
+        }
+        return current_stack;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    bool step(StepMode mode)
+    {
+        if ( !process_loaded )
+            return false;
+        Location loc = images.findLocation(current_address);
+
+        int bpi=-1;
+        Breakpoint bp;
+
+        switch ( mode )
+        {
+            //-------------------------------------------------------------------------------------
+            case StepMode.e_in:
+                Location next_loc = images.findNextSrcLine(loc);
+                if ( next_loc !is null )
+                {
+                    bp = setBreakpoint(next_loc, bpi, thread_id);
+                    if ( bp !is null )
+                        addForeachBreakpoint(bp);
+                }
+
+                size_t disasm_end;
+                if ( loc.codeblock !is null )
+                    disasm_end = loc.codeblock.end+loc.getCodeBase;
+                DisAsm.disasm(
+                    process, current_address, disasm_end,
+                    (ud_t* ud_obj) { return setBranchBreakpoints(ud_obj,StepMode.e_in); }
+                );
+                break;
+            //-------------------------------------------------------------------------------------
+            case StepMode.e_out:
+                uint ret_adr = getReturnAddress(loc);
+                if ( ret_adr > 0 ) {
+                    debug DbgIO.println("setting step-out to 0x%08x", ret_adr);
+                    bp = setBreakpoint(ret_adr, bpi, thread_id);
+                    if ( bp !is null )
+                        bp.frame_ptr = getFramePointer(loc, 1);
+                }
+                else
+                {
+                    debug DbgIO.println("no scope found - falling back on disasm-step-out");
+                    DisAsm.disasm(
+                        process, current_address, 0,
+                        (ud_t* ud_obj) { return setBranchBreakpoints(ud_obj,StepMode.e_out); }
+                    );
+                }
+                break;
+            //-------------------------------------------------------------------------------------
+            case StepMode.e_over:
+                Location next_loc = images.findNextSrcLine(loc);
+                if ( next_loc !is null )
+                {
+                    bp = setBreakpoint(next_loc, bpi, thread_id);
+                    if ( bp !is null )
+                    {
+                        bp.frame_ptr = getFramePointer(loc);
+                        debug DbgIO.println("FP for step over is 0x%x", bp.frame_ptr);
+                        addForeachBreakpoint(bp);
+                    }
+                }
+
+                size_t disasm_end;
+                if ( loc.codeblock !is null )
+                    disasm_end = loc.codeblock.end+loc.getCodeBase;
+                DisAsm.disasm(
+                    process, current_address, disasm_end,
+                    (ud_t* ud_obj) { return setBranchBreakpoints(ud_obj,StepMode.e_over); }
+                );
+                break;
+            default:
+                assert(0);
+        }
+
+        return true;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    void addForeachBreakpoint(Breakpoint foreach_end)
+    {
+        CodeView cv;
+        CodeBlock cb = images.findCodeBlockAbs(current_address, cv);
+        if ( cb !is null )
+        {
+            CodeBlock cbs[] = cb.segment.file.blocks_by_line[cb.line];
+            if ( cbs.length > 1 && cb is cbs[0] )
+            {
+                Location loc = images.findLocation(cbs[1].start+cv.getCodeBase);
+                if ( loc.scope_sym !is null && find(loc.scope_sym.mangled_name, "__foreachbody") >= 0 )
+                {
+                    int bpi;
+                    if ( getBreakpoint(loc.address, bpi) is null ) {
+                        Breakpoint bp = new Breakpoint(loc, true, thread_id);
+                        bp.foreach_end = foreach_end;
+                        foreach_end.foreach_end = foreach_end;
+                        addBreakpoint(bp, -1);
+                    }
+                }
+            }
+        }
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    uint getReturnAddress(Location loc)
+    {
+        ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym;
+        if ( psym !is null )
+        {
+            uint	index;
+            ubyte[] frame = stack.firstFrame(index);
+
+            if ( frame is null )
+                throw new DebuggerException("getReturnAddress: null frame");
+
+            uint adr = current_address-psym.cvdata.offset-loc.getCodeBase;
+            if ( adr >= psym.cvdata.debug_start && adr < psym.cvdata.debug_end ) {
+                frame = stack.prevFrame(index, index);
+                if ( frame.length >= 8 )
+                    return (cast(uint[])frame)[1];
+            }
+        }
+        return 0;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    uint getFramePointer(Location loc, uint level=0)
+    {
+        ProcedureSymbol psym = cast(ProcedureSymbol)loc.scope_sym;
+        if ( psym !is null )
+        {
+            uint frame_ptr;
+            if ( level > 0 )
+            {
+                uint	index,
+                        l;
+                ubyte[] frame = stack.firstFrame(index);
+                if ( frame is null )
+                    throw new DebuggerException("getCurrenFramePointer: null frame");
+
+                for ( l = 0; l < level && frame !is null; ++l )
+                    frame = stack.prevFrame(index, index);
+                if ( frame is null )
+                    throw new DebuggerException("getCurrenFramePointer: null frame at level %d", l);
+
+                frame_ptr = (cast(uint[])frame)[0];
+            }
+            else
+                frame_ptr = stack.frame_ptr;
+
+            uint adr = current_address-psym.cvdata.offset-loc.getCodeBase;
+            if ( adr >= psym.cvdata.debug_start && adr < psym.cvdata.debug_end )
+                return frame_ptr;
+        }
+        return 0;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    SymbolData evaluateExpression(string expr, uint frame_level=0, NamedSymbol symbol=null)
+    {
+        SyntaxTree* root;
+        bool success;
+        try success = parse("", expr, root, true);
+        catch( ParserException e )
+            throw new EvaluationException("Parser: "~e.toString);
+        if ( !success )
+            throw new EvaluationException("Parser: Invalid expression!");
+
+        string elmtype;
+        uint scope_address;
+        if ( frame_level == 0 )
+            scope_address = current_address;
+        else {
+            ubyte[]	frame = stack.getFrame(frame_level);
+            scope_address = (cast(uint[])frame)[1];
+        }
+
+        SymbolData sd;
+        auto loc = images.findLocation(current_address);
+        if ( loc.codeview is null )
+            throw new EvaluationException("No debug symbols available");
+        root.Expr(new EvaluationContext(
+            loc.codeview, scope_address,
+            miniDump, process, process is null?null:process.threads[thread_id],
+            stack, frame_level), sd
+        );
+        return sd;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    SymbolValue handleData(SymbolData sd, bool decreaseED)
+    {
+        auto loc = images.findLocation(current_address);
+        if ( loc.codeview is null )
+            throw new EvaluationException("No debug symbols available");
+        EvaluationContext ctx = new EvaluationContext(
+            loc.codeview, 0,
+            miniDump, process, process is null?null:process.threads[thread_id],
+            stack, 0
+        );
+
+        ubyte[] data = sd.getData(ctx);
+        if ( data.length <= 0 )
+            throw new EvaluationException("Expression evaluated to empty data");
+
+        uint ed = evaluationDepth;
+        if ( decreaseED )
+            --ed;
+        SymbolValue val = DataHandler.handle(ctx, sd.type, data, ed);
+        if ( val is null )
+            throw new EvaluationException("No DataHandler for type "~sd.type);
+        return val;
+    }
+
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    uint getScopeAddress(uint frame_level)
+    {
+        uint scope_address;
+        if ( frame_level == 0 )
+            scope_address = current_address;
+        else {
+            ubyte[]	frame = stack.getFrame(frame_level);
+            if ( frame !is null )
+                scope_address = (cast(uint[])frame)[1];
+        }
+        return scope_address;
+    }
+
+
+    //=============================================================================================
+    // process & thread handling
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    void selectThread(size_t threadId)
+    {
+        thread_id = threadId;
+        current_stack = null;
+
+        if ( process !is null ) {
+            CONTEXT ctx;
+            if ( !process.threads[threadId].getContext(ctx) )
+                throw new DebuggerException("Couldn't get thread's context");
+            current_address = ctx.Eip;
+        }
+        else if ( miniDump !is null ) {
+            miniDump.selectThread(threadId);
+            CONTEXT* ctx = miniDump.getContext;
+            current_address = ctx.Eip;
+        }
+        else
+            throw new DebuggerException("Invalid debugger state");
+    }
+
+    /**********************************************************************************************
+        Loads and parses the PE image.
+        Returns: success
+    **********************************************************************************************/
+    void openImage(string filename)
+    {
+        assert(filename.length);
+        filename = strip(filename);
+        if ( !exists(filename) )
+            filename ~= ".exe";
+        if ( !exists(filename) )
+            throw new DebuggerException("Couldn't open \"%s\"", filename[0..$-4]);
+        main_image_file = filename.dup;
+
+        main_image = new COFFImage;
+        main_image.load(main_image_file);
+//		catch ( Exception e ) {
+//			throw new DebuggerException("Failed to load COFF image \""~main_image_file~"\": "~e.msg);
+//		}
+        if ( main_image.codeView is null )
+            DbgIO.println("WARNING: No debugging symbols found in main image, try compiling and linking with -g");
+        images ~= main_image;
+       // return true;
+    }
+
+    /**********************************************************************************************
+        Creates the child process for the debuggee.
+        Returns: success
+    **********************************************************************************************/
+    bool createDebugProcess(string command_line)
+    {
+        // initialiZe process startup information
+        STARTUPINFOA* startInfo	= new STARTUPINFOA;
+        startInfo.cb			= STARTUPINFO.sizeof;
+        startInfo.dwFlags		= STARTF_FORCEONFEEDBACK | STARTF_USESHOWWINDOW;
+        startInfo.wShowWindow   = SW_SHOWNORMAL;
+
+        PROCESS_INFORMATION	procInfo;	// process info
+
+        // create process
+        uint flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
+        if ( create_new_console )
+            flags |= CREATE_NEW_CONSOLE;
+
+        bool suc = cast(bool)CreateProcessA(
+            toStringz(main_image_file), toStringz(command_line), cast(LPSECURITY_ATTRIBUTES)null,
+            cast(LPSECURITY_ATTRIBUTES)null, false, flags, null, cast(char*)null,
+            cast(LPSTARTUPINFOA)startInfo, cast(LPPROCESS_INFORMATION)&procInfo
+        );
+        if( !suc )
+            throw new DebuggerException("CreateProcess failed on %s", main_image_file);
+        assert(procInfo.dwProcessId);
+
+        // open process for all access
+        HANDLE hChildProcess = OpenProcess(PROCESS_ALL_ACCESS, false, procInfo.dwProcessId);
+        if( hChildProcess == null ) {
+            TerminateProcess(procInfo.hProcess, 0);
+            throw new DebuggerException("OpenProcess failed");
+        }
+        process_id = procInfo.dwProcessId;
+
+        CloseHandle(procInfo.hProcess);
+        return true;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    void resume()
+    {
+        single_step = false;
+        paused = false;
+    }
+
+    //=============================================================================================
+    // exception handling
+
+    /**********************************************************************************************
+        Debugger's main loop. Handles win32 debugging events.
+    **********************************************************************************************/
+    void start(string command_line)
+    {
+        createDebugProcess(command_line);
+
+        while( !abort )
+        {
+            if( WaitForDebugEvent(&debug_event, win32.winbase.INFINITE) )
+            {
+                if( debug_event.dwProcessId != process_id ) {
+                    debug DbgIO.println("WARNING: Exception for unhandled process received: PID=%d", debug_event.dwProcessId);
+                    ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);
+                    continue;
+                }
+
+                current_stack = null;
+                thread_id = debug_event.dwThreadId;
+                exceptionRecord = null;
+
+                bool exception_handled = true;
+                switch( debug_event.dwDebugEventCode )
+                {
+                    case OUTPUT_DEBUG_STRING_EVENT:
+                        char[] debug_string;
+                        paused = false;
+
+                        debug_string.length = debug_event.DebugString.nDebugStringLength;
+                        process.readProcessMemory(cast(uint)debug_event.DebugString.lpDebugStringData, debug_string.ptr, debug_string.length);
+                        if ( debug_event.DebugString.fUnicode )
+                            DbgIO.println("WARNING: Unicode debug strings not implemented, yet. Output might be corrupted");
+
+                        if ( debug_string.length > 3 && debug_string[0..4] == "DDL:" )
+                        {
+                            string cmdStr = debug_string[4..$];
+                            if ( strip(cmdStr).length > 0 )
+                            {
+                                auto r = std.regexp.RegExp("(([^\" \\t]+)|(\"[^\"]+\"))+");
+                                string[] cmd;
+                                foreach ( m; r.search(strip(cmdStr)) )
+                                    cmd ~= r.match(0);
+                                switch ( cmd[0] )
+                                {
+                                    case "load":
+                                        DbgIO.println("loadImage not implemented, yet: %s %s", cmd[1], cmd[2]);
+                                        break;
+                                    default:
+                                        DbgIO.println("WARNING: Unknown DDL command recieved: %s", cmdStr);
+                                        break;
+                                }
+                            }
+                        }
+                        else if ( debug_string.length > 5 && debug_string[0..5] == "Ddbg:" )
+                        {
+                            ui.runCommands(debug_string[5..$]);
+                            paused = true;
+                        }
+                        else
+                            ui.debugString(debug_string);
+                        break;
+
+                    case EXCEPTION_DEBUG_EVENT:
+                        exceptionRecord = &debug_event.Exception.ExceptionRecord;
+                        exception_handled = handleExceptionEvent(exceptionRecord);
+                        break;
+
+                    case LOAD_DLL_DEBUG_EVENT:
+                        DLL dll = process.loadDLL(&debug_event.LoadDll);
+                        if ( dll.image !is null )
+                        {
+                            images ~= dll.image;
+                            foreach ( bp; breakpoints )
+                            {
+                                if ( bp.location.address == 0 )
+                                {
+                                    debug DbgIO.println("binding bp %s", bp.toString);
+                                    if ( bp.location.bind(images, source_search_paths) ) {
+                                        debug DbgIO.println("activating bp %s", bp.toString);
+                                        bp.activate(process);
+                                    }
+                                }
+                            }
+                        }
+                        ui.loadedDLL(dll);
+                        paused = false;
+                        break;
+
+                    case UNLOAD_DLL_DEBUG_EVENT:
+//						DebugDLL* dll = process.findDLL(cast(uint)debug_event.LoadDll.lpBaseOfDll);
+//						ui.unloadedDLL(dll);
+//                        images.remove(dll.image);
+                        paused = false;
+                        break;
+
+                    case CREATE_THREAD_DEBUG_EVENT:
+                        process.threads[debug_event.dwThreadId] = new DbgThread(debug_event.CreateThread.hThread, debug_event.dwThreadId);
+                        paused = false;
+                        break;
+
+                    case EXIT_THREAD_DEBUG_EVENT:
+                        process.threads.remove(debug_event.dwThreadId);
+                        paused = false;
+                        break;
+
+                    case CREATE_PROCESS_DEBUG_EVENT:
+                        process 					    = new DbgProcess;
+                        process.processId               = process_id;
+                        process.process_handle	        = debug_event.CreateProcessInfo.hProcess;
+                        process.mainThreadId	        = debug_event.dwThreadId;
+                        process.threads[debug_event.dwThreadId] = new DbgThread(debug_event.CreateProcessInfo.hThread, debug_event.dwThreadId);
+                        process_loaded = true;
+
+                        if ( main_image !is null && main_image.codeView !is null )
+                        {
+                            DataSymbol mainds;
+                            if ( main_image.codeView.global_pub !is null )
+                                mainds = main_image.codeView.global_pub.findDataSymbol("_main");
+                            if ( mainds !is null )
+                            {
+                                int bpi=-1;
+                                Breakpoint bp = setBreakpoint(mainds.offset+main_image.getCodeBase, bpi, 0, true);
+                                bp.deactivate_d_exception_handler = true;
+                            }
+                        }
+
+                        foreach ( bp; breakpoints )
+                        {
+                            if ( bp.location.address != 0 && !bp.activate(process) )
+                                throw new DebuggerException("Error: couldn't write breakpoint opcode at %s:%d", bp.file, bp.line);
+                        }
+                        foreach ( bp; temp_breakpoints )
+                        {
+                            debug DbgIO.println("tbp activated %s", bp.value);
+                            if ( bp.value.location.address != 0 && !bp.value.activate(process) )
+                                throw new DebuggerException("Error: couldn't write breakpoint opcode at %s:%d", bp.value.file, bp.value.line);
+                        }
+                        break;
+
+                    case EXIT_PROCESS_DEBUG_EVENT:
+                        process_loaded = false;
+                        delete process;
+                        paused = true;
+                        single_step = false;
+                        ui.exitProcess;
+                        break;
+
+                    case RIP_EVENT:
+                        debug DbgIO.println("DebugEvent: "~getEventName(debug_event.dwDebugEventCode));
+                        paused = true;
+                        break;
+
+                    default:
+                        throw new DebuggerException("Win32Debugger ERROR - unknown debug event: %d", debug_event.dwDebugEventCode);
+                }
+
+                if ( single_step )
+                {
+                    ui.singleStep();
+                    paused = true;
+                    activateSingleStep(true);
+                }
+
+                if ( paused )
+                {
+                    bool cont=false;
+                    while ( !cont )
+                    {
+                        debug
+                            cont = ui.readCommand();
+                        else
+                        {
+                            try cont = ui.readCommand();
+                            catch ( Exception e )
+                                DbgIO.println("%s", e.msg);
+                        }
+                    }
+                    paused = false;
+                }
+
+                if ( !abort )
+                {
+                    exceptionRecord = null;
+                    ContinueDebugEvent(
+                        debug_event.dwProcessId, debug_event.dwThreadId,
+                        exception_handled?DBG_CONTINUE:DBG_EXCEPTION_NOT_HANDLED
+                    );
+                }
+            }
+            else
+                throw new DebuggerException("ERROR: WaitForDebugEvent failed: %s", lastError);
+        }
+    }
+
+    /**********************************************************************************************
+        Activates single step execution by setting the trap flag in
+        the eflags register of the currently considered thread.
+    **********************************************************************************************/
+    void activateSingleStep(bool enable)
+    {
+        if ( !(thread_id in process.threads) )
+            return;
+        if ( enable )
+            process.threads[thread_id].changeContext(delegate(ref CONTEXT ctx){ ctx.EFlags |= 1<<8; });
+        else if ( !enable )
+            process.threads[thread_id].changeContext(delegate(ref CONTEXT ctx){ ctx.EFlags &= ~(1<<8); });
+        single_step = enable;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    void getExceptionNameMessage(size_t objPtr, out char[] class_name, out char[] msg)
+    {
+        // get thrown object's class name
+        ClassInfo ci = process.getClassInfo(objPtr);
+        class_name.length = ci.name.length;
+        process.readProcessMemory(cast(uint)ci.name.ptr, class_name.ptr, class_name.length);
+
+        // get object's data
+        uint[] objdata;
+        objdata.length = ci.init.length;
+        process.readProcessMemory(objPtr, objdata.ptr, objdata.length*uint.sizeof);
+
+        char[]	name;
+        name ~= class_name;
+        do
+        {
+            if ( name == "object.Exception" )
+            {
+                msg.length = objdata[2];
+                process.readProcessMemory(objdata[3], msg.ptr, msg.length);
+                break;
+            }
+
+            ubyte[] data;
+            data.length = ClassInfo.classinfo.init.length;
+            process.readProcessMemory(cast(uint)cast(void*)ci.base, data.ptr, data.length);
+            ci = cast(ClassInfo)data.ptr;
+            name.length = ci.name.length;
+            process.readProcessMemory(cast(uint)ci.name.ptr, name.ptr, name.length);
+        }
+        while ( ci.base !is null );
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    bool handleExceptionEvent(EXCEPTION_RECORD* exrec)
+    {
+        current_address = cast(uint)exrec.ExceptionAddress;
+        Lswitch: switch ( exrec.ExceptionCode )
+        {
+            case EXCEPTION_BREAKPOINT:
+                debug DbgIO.println("EXCEPTION_BREAKPOINT");
+                int bp_index;
+                Breakpoint bp = getBreakpoint(current_address, bp_index);
+                if ( bp !is null ) {
+                    handleBreakpoint(bp, bp_index);
+                    last_line = bp.line;
+                }
+                else
+                    ui.breakpoint(-1, new Breakpoint(images.findLocation(cast(uint)exrec.ExceptionAddress), false, 0, false), process.threads[thread_id]);
+                break;
+            case EXCEPTION_SINGLE_STEP:
+                debug DbgIO.println("EXCEPTION_SINGLE_STEP");
+                // check whether this is a HWBP or real single step
+                CONTEXT ctx;
+                process.threads[thread_id].getContext(ctx, CONTEXT_DEBUG_REGISTERS);
+
+                if ( (ctx.Dr6 & 0xf) > 0 )
+                {
+                    debug DbgIO.println("Hardware breakpoint");
+                    size_t dr = void;
+                    if ( ctx.Dr6 & 1 )
+                        dr = ctx.Dr0;
+                    else if ( ctx.Dr6 & 2 )
+                        dr = ctx.Dr1;
+                    else if ( ctx.Dr6 & 4 )
+                        dr = ctx.Dr2;
+                    else if ( ctx.Dr6 & 8 )
+                        dr = ctx.Dr3;
+                    int bp_index;
+                    foreach( i, bp; breakpoints )
+                    {
+                        if( !bp.hardware )
+                            continue;
+                        if ( dr == bp.address ) {
+                            handleBreakpoint(bp, i);
+                            break Lswitch;
+                        }
+                    }
+
+                    ui.breakpoint(-1, new Breakpoint(images.findLocation(dr), false, 0, false), process.threads[thread_id]);
+                }
+                else
+                {
+                    debug DbgIO.println("Single Step exception");
+                    // pause if interactive single step is active
+                    paused = single_step;
+
+                    if ( passing_breakpoint !is null )
+                    {
+                        debug DbgIO.println("passing breakpoint");
+                        activateSingleStep(false);
+                        paused = false;
+                        if ( process !is null && !passing_breakpoint.activate(process) )
+                            throw new DebuggerException("ERROR: Failed to write breakpoint opcode at %s:%d", passing_breakpoint.file, passing_breakpoint.line);
+                        passing_breakpoint = null;
+                    }
+                }
+                break;
+            // ctrl+c
+            case DBG_CONTROL_C:
+                ui.userInterrupt();
+                paused = true;
+                break;
+            // D exceptions
+            case STATUS_DIGITAL_MARS_D_EXCEPTION:
+                // normal non-post-mortem mode
+                if ( process !is null && !debug_event.Exception.dwFirstChance )
+                {
+                    char[] class_name, msg;
+                    getExceptionNameMessage(exrec.ExceptionInformation[0], class_name, msg);
+                    ui.exception(thread_id, class_name, msg, exrec.ExceptionInformation[0]);
+                    paused = true;
+                    return false;
+                }
+                // minidump mode
+                else if ( miniDump !is null )
+                {
+                    string  className,
+                            message;
+                    className = (cast(char*)exrec.ExceptionInformation[1])[0..exrec.ExceptionInformation[0]];
+                    message = (cast(char*)exrec.ExceptionInformation[3])[0..exrec.ExceptionInformation[2]];
+                    ui.exception(thread_id, className, message, 0);
+                    paused = true;
+                    return false;
+                }
+                paused = false;
+                return false;
+            default:
+                ui.win32exception(thread_id, exrec);
+                paused = true;
+                single_step = false;
+                return false;
+        }
+        return true;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    void handleBreakpoint(Breakpoint bp, uint bp_index)
+    {
+        CONTEXT ctx;
+        if ( !process.threads[thread_id].getContext(ctx) )
+            throw new DebuggerException("Error: Couldn't GetThreadContext to reset instruction pointer: %s", lastError);
+
+        if ( !bp.hardware )
+        {
+            if ( process !is null && !bp.deactivate(process) )
+                throw new DebuggerException("ERROR: Failed to write opcode for breakpoint at %s:%d", bp.file, bp.line);
+
+            // adjust for "int 3" instruction byte
+            --ctx.Eip;
+            if ( !process.threads[thread_id].setContext(ctx) )
+                throw new DebuggerException("Error: Couldn't SetThreadContext to reset instruction pointer: %s", lastError);
+        }
+
+        // check if we have a constrained thread
+        if ( bp.threadId > 0 && thread_id != bp.threadId )
+        {
+            debug DbgIO.println("skipping bp in incorrect thread %d", thread_id);
+            if ( !bp.hardware ) {
+                passing_breakpoint = bp;
+                activateSingleStep(true);
+            }
+        }
+        // check if breakpoint requires a certain frame
+        else if ( bp.frame_ptr > 0 && bp.frame_ptr != ctx.Ebp )
+        {
+            debug DbgIO.println("skipping bp in incorrect frame, Ebp=0x%x required=0x%x", ctx.Ebp, bp.frame_ptr);
+            if ( !bp.hardware ) {
+                passing_breakpoint = bp;
+                activateSingleStep(true);
+            }
+        }
+        // restore non-temporary breakpoints
+        else if ( !bp.temporary )
+        {
+            ui.breakpoint(bp_index, bp, process.threads[thread_id]);
+            paused = true;
+            if ( !bp.hardware ) {
+                passing_breakpoint = bp;
+                activateSingleStep(true);
+            }
+        }
+        // propagate breakpoints to jmp/call/ret destination
+        else if ( bp.propagate )
+        {
+            debug DbgIO.println("propagate");
+            foreach ( tbp; temp_breakpoints )
+            {
+                if ( tbp.value is bp ) {
+                    temp_breakpoints.remove(tbp);
+                    break;
+                }
+            }
+            DisAsm.disasm(process, current_address, 0, (ud_t* ud_obj){return setPropagatedBreakpoint(ud_obj,bp);});
+        }
+        // temporary breakpoints
+        else
+        {
+            if ( bp.deactivate_d_exception_handler ) {
+                debug DbgIO.println("deactivateDExceptionHandler");
+                deactivateDExceptionHandler();
+            }
+            else {
+                clearTemporaryBreakpoints();
+                paused = ui.breakpoint(-1, bp, process.threads[thread_id]);
+            }
+        }
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    void deactivateDExceptionHandler()
+    {
+        if ( main_image is null || main_image.codeView is null )
+            return;
+
+        // deactivate default D exception handling
+        bool found=false;
+        foreach ( ds; main_image.codeView.global_pub.data_symbols )
+        {
+            if ( ds.name_notype == "_no_catch_exceptions" ) {
+                uint address = ds.offset+main_image.getSectionBase(ds.cvdata.segment);
+                bool nce = true;
+                process.writeProcessMemory(address, &nce, bool.sizeof);
+                found = true;
+                break;
+            }
+            else if ( ds.name_notype == "_cr_trapExceptions"
+                || ds.name_notype == "_rt_trapExceptions" )
+            {
+                uint address = ds.offset+main_image.getSectionBase(ds.cvdata.segment);
+                bool nce = false;
+                process.writeProcessMemory(address, &nce, bool.sizeof);
+                found = true;
+                break;
+            }
+        }
+        if ( !found ) {
+            debug DbgIO.println("Neither Phobos nor Tango exception handler switch not found");
+        }
+    }
+
+    //=============================================================================================
+    // source&symbol handling
+
+    /**********************************************************************************************
+        Searches the cache for the given source file and loads it if nessecary.
+        Tries to load the source file from all source_search_paths.
+        Returns: Array of lines of the source file.
+    **********************************************************************************************/
+    string[] getSourceFile(string filename)
+    {
+        if ( filename is null || filename.length <= 0 )
+            return null;
+        if ( !(filename in source_files) )
+        {
+            string full = getFullSourcePath(filename);
+            if ( full is null || !exists(full) )
+                return null;
+            auto lines = splitlines(cast(string)read(full));
+            source_files[filename] = lines;
+        }
+        return source_files[filename];
+    }
+
+    /**********************************************************************************************
+        Tries to find the source file in all source_search_paths.
+        Returns: Full path of the file
+    **********************************************************************************************/
+    string getFullSourcePath(string filename)
+    {
+        string full = getFullPath(filename);
+        if ( exists(full) )
+            return full;
+        foreach ( string sp; source_search_paths )
+        {
+            full = getFullPath(sp~filename);
+            if ( exists(full) )
+                return full;
+        }
+        return filename;
+    }
+
+
+    //=============================================================================================
+    // breakpoint handling
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    bool setPropagatedBreakpoint(ud* ud_obj, Breakpoint prev_bp)
+    {
+        if ( ud_obj is null )
+            return true;
+
+        int bpi=-1;
+        Breakpoint bp;
+        if ( DisAsm.isConditionalJump(ud_obj.mnemonic)
+            || ud_obj.mnemonic == ud_mnemonic_code.UD_Ijmp
+            || ud_obj.mnemonic == ud_mnemonic_code.UD_Icall )
+        {
+            bool is_imm=false;
+            uint jmp_dest = DisAsm.calcJumpDestination(process, process.threads[thread_id], ud_obj, is_imm);
+            assert(!is_imm);
+            debug DbgIO.println("propagating jmp/call bp to 0x%x", jmp_dest);
+            Location loc = images.findLocation(jmp_dest);
+            if ( loc.file !is null )
+                bp = setBreakpoint(jmp_dest, bpi, thread_id);
+            else {
+                debug DbgIO.println("not source info available - checking for unconditional jmp at destination");
+                DisAsm.disasm(process, jmp_dest, 0, (ud_t* ud_obj){return propagateUncondJmp(ud_obj, prev_bp);});
+            }
+            if ( bp !is null )
+                bp.frame_ptr = prev_bp.frame_ptr;
+        }
+        else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Iret || ud_obj.mnemonic == ud_mnemonic_code.UD_Iretf )
+        {
+            uint index;
+            size_t ret_adr = (cast(uint[])stack.data)[0];
+            Location loc = images.findLocation(ret_adr);
+            if ( loc.file !is null )
+                bp = setBreakpoint(ret_adr, bpi, thread_id);
+            else
+                debug DbgIO.println("not setting BP on unknown ret address 0x%x", ret_adr);
+            if ( bp !is null )
+                bp.frame_ptr = prev_bp.frame_ptr;
+        }
+        else {
+            debug DbgIO.println("unknown instruction for propagating breakpoint");
+            assert(0);
+        }
+
+        return false;
+    }
+
+    /**********************************************************************************************
+        Propagate breakpoint if unconditional jump is found.
+        Used for virtual functions with redirectors.
+    **********************************************************************************************/
+    bool propagateUncondJmp(ud* ud_obj, Breakpoint prev_bp)
+    {
+        if ( ud_obj is null )
+            return true;
+
+        int bpi=-1;
+        Breakpoint bp;
+        if ( DisAsm.isConditionalJump(ud_obj.mnemonic)
+            || ud_obj.mnemonic == ud_mnemonic_code.UD_Icall )
+        {
+            debug DbgIO.println("aborting propagateUncondJmp: condJmp or call");
+            return false;
+        }
+        else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Ijmp )
+        {
+            bool is_imm=false;
+            uint jmp_dest = DisAsm.calcJumpDestination(process, process.threads[thread_id], ud_obj, is_imm);
+            debug DbgIO.println("propagating bp to 0x%x", jmp_dest);
+            Location loc = images.findLocation(jmp_dest);
+            if ( loc.file !is null )
+                bp = setBreakpoint(jmp_dest, bpi, thread_id);
+            else {
+                debug DbgIO.println("not source info available - checking for unconditional jmp at destination");
+            }
+            if ( bp !is null )
+                bp.frame_ptr = prev_bp.frame_ptr;
+
+            return false;
+        }
+        else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Iret || ud_obj.mnemonic == ud_mnemonic_code.UD_Iretf ) {
+            debug DbgIO.println("aborting propagateUncondJmp: ret");
+            return false;
+        }
+        else
+            return true;
+    }
+
+    /**********************************************************************************************
+        If the opcode is an immediate jump or a call, sets a temporary breakpoint
+        at it's destination. If it's a memory call, sets a call-propagating breakpoint at
+        the call instruction.
+    **********************************************************************************************/
+    bool setBranchBreakpoints(ud_t* ud_obj, StepMode mode)
+    {
+        // first call
+        if ( ud_obj is null )
+            return true;
+
+        int bpi=-1;
+        Breakpoint bp;
+
+        if ( DisAsm.isConditionalJump(ud_obj.mnemonic) || ud_obj.mnemonic == ud_mnemonic_code.UD_Ijmp )
+        {
+            if ( mode!=StepMode.e_out )
+            {
+                bool is_imm=true;
+                uint jmp_dest = DisAsm.calcJumpDestination(process, process.threads[thread_id], ud_obj, is_imm);
+                if ( is_imm )
+                    bp = setBreakpoint(jmp_dest, bpi, thread_id, false);
+                else
+                {
+                    // set propagating breakpoint
+                    bp = setBreakpoint(cast(size_t)ud_obj.insn_offset, bpi, thread_id, true);
+                    if ( bp !is null )
+                        bp.propagate = true;
+                }
+            }
+        }
+        else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Icall )
+        {
+            if ( mode==StepMode.e_in )
+            {
+                bool is_imm=true;
+                uint jmp_dest = DisAsm.calcJumpDestination(process, process.threads[thread_id], ud_obj, is_imm);
+                if ( is_imm )
+                {
+                    Location loc = images.findLocation(jmp_dest);
+                    if ( loc.file !is null )
+                        bp = setBreakpoint(jmp_dest, bpi, thread_id, false);
+                    else
+                        debug DbgIO.println("not setting BP on unknown call destination 0x%x", jmp_dest);
+                }
+                else
+                {
+                    // set propagating breakpoint
+                    bp = setBreakpoint(cast(size_t)ud_obj.insn_offset, bpi, thread_id, true);
+                    if ( bp !is null )
+                        bp.propagate = true;
+                }
+            }
+        }
+        else if ( ud_obj.mnemonic == ud_mnemonic_code.UD_Iret || ud_obj.mnemonic == ud_mnemonic_code.UD_Iretf )
+        {
+            if ( mode!=StepMode.e_out )
+            {
+                Location loc = images.findLocation(current_address);
+                if ( loc.scope_sym !is null && find(loc.scope_sym.mangled_name, "__foreachbody") >= 0 )
+                {
+                    ProcedureSymbol ps = cast(ProcedureSymbol)loc.scope_sym;
+                    if ( ps !is null )
+                    {
+                        setBreakpoint(ps.cvdata.offset+loc.getCodeBase, bpi, thread_id);
+                        uint ret_adr = getReturnAddress(images.findLocation(getReturnAddress(loc)));
+                        if ( ret_adr > 0 )
+                            setBreakpoint(ret_adr, bpi, thread_id);
+                    }
+                }
+                if ( mode != StepMode.e_over || loc.scope_sym is null || find(loc.scope_sym.mangled_name, "__foreachbody") < 0 )
+                {
+                    uint ret_adr = getReturnAddress(loc);
+                    if ( ret_adr > 0 )
+                    {
+                        debug DbgIO.println("setBranchBPs: using return address from frame");
+                        loc = images.findLocation(ret_adr);
+                        if ( loc.file !is null )
+                            bp = setBreakpoint(ret_adr, bpi, thread_id, true);
+                        else
+                            debug DbgIO.println("not setting BP on unknown ret address 0x%x", ret_adr);
+                    }
+                    else
+                    {
+                        bp = setBreakpoint(cast(size_t)ud_obj.insn_offset, bpi, thread_id, true);
+                        if ( bp !is null )
+                            bp.propagate = true;
+                    }
+                }
+            }
+            else
+            {
+                bp = setBreakpoint(cast(size_t)ud_obj.insn_offset, bpi, thread_id, true);
+                if ( bp !is null )
+                    bp.propagate = true;
+            }
+        }
+        return true;
+    }
+
+    /**********************************************************************************************
+        Removes breakpoint from list and deactivates it.
+        Returns: success
+    **********************************************************************************************/
+    bool removeBreakpoint(uint bp_index)
+    {
+        if ( bp_index in breakpoints )
+        {
+            Breakpoint bp = breakpoints[bp_index];
+            if ( process !is null && !bp.deactivate(process) )
+                throw new DebuggerException("ERROR: Failed to write breakpoint opcode at %s:%d", bp.file, bp.line);
+            breakpoints.remove(bp_index);
+            if ( passing_breakpoint is bp ) {
+                passing_breakpoint = null;
+                activateSingleStep(false);
+            }
+            delete bp;
+            return true;
+        }
+        return false;
+    }
+
+    /**********************************************************************************************
+
+    **********************************************************************************************/
+    void clearTemporaryBreakpoints()
+    {
+        debug DbgIO.println("clearing temporary breakpoints");
+        if ( process !is null )
+        {
+            foreach ( bp; temp_breakpoints )
+            {
+                if ( bp.value.foreach_end !is null && bp.value.foreach_end.address != current_address )
+                {
+                    if ( !bp.value.hardware )
+                        passing_breakpoint = bp.value;
+                    activateSingleStep(true);
+                    continue;
+                }
+                if ( !bp.value.deactivate(process) )
+                    throw new DebuggerException("ERROR: Failed to restore opcode for breakpoint at %s:%d", bp.value.file, bp.value.line);
+                temp_breakpoints.remove(bp);
+            }
+        }
+    }
+
+    /**********************************************************************************************
+        Creates a breakpoint at the given location and adds it with the given index.
+        Overwrites the breakpoint at the same index if existant.
+        Adds a temporary breakpoint instead if index < 0.
+        Reaturns: Existing breakpoint at the location or new breakpoint.
+    **********************************************************************************************/
+    Breakpoint setBreakpoint(Location loc, inout int new_index, uint threadId, bool hardware=false)
+    {
+        if ( loc is null )
+            return null;
+
+        // lookup existing breakpoint
+        Breakpoint bp;
+        if ( new_index >= 0 )
+        {
+            int bp_index;
+            bp = getBreakpoint(loc, bp_index);
+            if( bp !is null ) {
+                new_index = bp_index;
+                return bp;
+            }
+        }
+
+        // create a breakpoint
+        bp = new Breakpoint(loc, new_index<0, threadId, hardware);
+        addBreakpoint(bp, new_index);
+        return bp;
+    }
+
+    /**********************************************************************************************
+        Creates a breakpoint at the given address and adds it with the given index.
+        Overwrites the breakpoint at the same index if existant.
+        Adds a temporary breakpoint instead if index < 0.
+        Reaturns: Existing breakpoint at the address or new breakpoint.
+    **********************************************************************************************/
+    Breakpoint setBreakpoint(size_t address, inout int new_index, uint threadId, bool set_on_current_line=false, bool hardware=false)
+    {
+        Location loc = images.findLocation(address);
+
+        Breakpoint bp;
+        int bp_index;
+        bp = getBreakpoint(address, bp_index);
+        if( bp !is null ) {
+            debug DbgIO.println("instead 0x%08x using existing breakpoint at 0x%08x", address, bp.address);
+            new_index = bp_index;
+            return bp;
+        }
+
+        if ( !set_on_current_line && loc.codeblock !is null && current_address == loc.codeblock.start+loc.getCodeBase ) {
+            debug DbgIO.println("not setting bp at current 0x%08x", address);
+            return null;
+        }
+        bp = new Breakpoint(loc, new_index<0, threadId, hardware);
+        addBreakpoint(bp, new_index);
+        return bp;
+    }
+
+    /**********************************************************************************************
+        Adds the given breakpoint with at the given index.
+        Adds it as temporary breakpoint if new_index < 0.
+        Activates the breakpoint.
+    **********************************************************************************************/
+    void addBreakpoint(Breakpoint bp, int new_index)
+    {
+        // add to breakpoint list
+        if ( bp.temporary )
+            temp_breakpoints ~= bp;
+        else
+            breakpoints[new_index] = bp;
+
+        // set breakpoing as active in debugger
+        if ( process !is null && !bp.activate(process) )
+            throw new DebuggerException("ERROR: Failed to write breakpoint opcode at %s:%d", bp.file, bp.line);
+    }
+
+    /**********************************************************************************************
+        Searches the breakpoints for one that is set at the given source
+        location.
+        Returns: Index of the breakpoint in the breakpoints array.
+    **********************************************************************************************/
+    Breakpoint getBreakpoint(Location loc, out int bp_index)
+    {
+        foreach( uint i, Breakpoint bp; breakpoints )
+        {
+            if( !bp.hardware && bp.file == loc.file && bp.line == loc.line
+                || bp.hardware && bp.address == loc.address )
+            {
+                bp_index = i;
+                return bp;
+            }
+        }
+        foreach( bp; temp_breakpoints )
+        {
+            if( !bp.value.hardware && bp.value.file == loc.file && bp.value.line == loc.line
+                || bp.value.hardware && bp.value.address == loc.address )
+            {
+                bp_index = -1;
+                return bp.value;
+            }
+        }
+        return getBreakpoint(loc.address, bp_index);
+    }
+
+    /**********************************************************************************************
+        Searches for a breakpoint at the given address.
+        Returns: Index of breakpoint in breakpoints array.
+    **********************************************************************************************/
+    Breakpoint getBreakpoint(uint address, out int bp_index)
+    {
+        foreach ( uint i, Breakpoint bp; breakpoints )
+        {
+            if ( bp.address == address ) {
+                bp_index = i;
+                return bp;
+            }
+        }
+        foreach ( bp; temp_breakpoints )
+        {
+            if ( bp.value.address == address ) {
+                bp_index = -1;
+                return bp.value;
+            }
+        }
+        return null;
+    }
+
+    //=============================================================================================
+    // Minidumps
+
+    void writeMiniDump(string filename)
+    {
+        char[] class_name, msg;
+        if ( exceptionRecord !is null
+            && exceptionRecord.ExceptionCode != EXCEPTION_BREAKPOINT
+            && exceptionRecord.ExceptionCode != EXCEPTION_SINGLE_STEP
+            && exceptionRecord.ExceptionCode != DBG_CONTROL_C )
+        {
+            getExceptionNameMessage(exceptionRecord.ExceptionInformation[0], class_name, msg);
+        }
+        MiniDump.writeMiniDump(filename, process, thread_id, exceptionRecord, class_name, msg);
+    }
+
+    void readMiniDump(string filename)
+    {
+        try {
+            miniDump = new MiniDump(filename);
+            selectThread(miniDump.threads[miniDump.selectedThread].ThreadId);
+            if ( miniDump.exceptionRecord !is null )
+                handleExceptionEvent(miniDump.exceptionRecord);
+            else
+                thread_id = miniDump.threadInfo.currentThreadId;
+            //miniDump.threadInfo.mainThreadId
+        }
+        catch ( Exception e )
+            DbgIO.writeln("Error: "~e.msg);
+        //return miniDump !is null;
+    }
+}