Beispiel #1
0
 internal NodeThread(NodeDebugger process, int identity, bool isWorkerThread)
 {
     _process        = process;
     _identity       = identity;
     _isWorkerThread = isWorkerThread;
     Name            = "main thread";
 }
Beispiel #2
0
        private void StartAndAttachDebugger(string file, string nodePath, bool startBrowser)
        {
            // start the node process
            var workingDir         = _project.GetWorkingDirectory();
            var url                = GetFullUrl();
            var env                = GetEnvironmentVariablesString(url);
            var interpreterOptions = _project.GetProjectProperty(NodeProjectProperty.NodeExeArguments);
            var debugOptions       = this.GetDebugOptions();
            var script             = GetFullArguments(file, includeNodeArgs: false);

            var process = NodeDebugger.StartNodeProcessWithInspect(exe: nodePath, script: script, dir: workingDir, env: env, interpreterOptions: interpreterOptions, debugOptions: debugOptions);

            process.Start();

            // setup debug info and attach
            var debugUri = $"http://127.0.0.1:{process.DebuggerPort}";

            var dbgInfo = new VsDebugTargetInfo4();

            dbgInfo.dlo         = (uint)DEBUG_LAUNCH_OPERATION.DLO_AlreadyRunning;
            dbgInfo.LaunchFlags = (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd;

            dbgInfo.guidLaunchDebugEngine = WebkitDebuggerGuid;
            dbgInfo.dwDebugEngineCount    = 1;

            var enginesPtr = MarshalDebugEngines(new[] { WebkitDebuggerGuid });

            dbgInfo.pDebugEngines       = enginesPtr;
            dbgInfo.guidPortSupplier    = WebkitPortSupplierGuid;
            dbgInfo.bstrPortName        = debugUri;
            dbgInfo.fSendToOutputWindow = 0;

            // we connect through a URI, so no need to set the process,
            // we need to set the process id to '1' so the debugger is able to attach
            dbgInfo.bstrExe = $"\01";

            AttachDebugger(dbgInfo);

            if (startBrowser)
            {
                Uri uri = null;
                if (!String.IsNullOrWhiteSpace(url))
                {
                    uri = new Uri(url);
                }

                if (uri != null)
                {
                    OnPortOpenedHandler.CreateHandler(
                        uri.Port,
                        shortCircuitPredicate: () => process.HasExited,
                        action: () =>
                    {
                        VsShellUtilities.OpenBrowser(url, (uint)__VSOSPFLAGS.OSP_LaunchNewBrowser);
                    }
                        );
                }
            }
        }
Beispiel #3
0
 public NodeBreakpoint(NodeDebugger process, FilePosition target, bool enabled, BreakOn breakOn, string condition)
 {
     this._process   = process;
     this._target    = target;
     this._enabled   = enabled;
     this._breakOn   = breakOn;
     this._condition = condition;
 }
        private static Dictionary <int, NodeModule> GetScripts(NodeDebugger debugger, JsonArray references)
        {
            var scripts = new Dictionary <int, NodeModule>(references.Count);

            for (int i = 0; i < references.Count; i++)
            {
                JsonValue reference = references[i];
                var       scriptId  = reference.GetValue <int>("id");
                var       filename  = reference.GetValue <string>("name");

                scripts.Add(scriptId, new NodeModule(debugger, scriptId, filename));
            }
            return(scripts);
        }
        internal static NodeBreakpoint AddBreakPoint(
            NodeDebugger newproc,
            string fileName,
            int line,
            int column,
            bool enabled     = true,
            BreakOn breakOn  = new BreakOn(),
            string condition = ""
            )
        {
            NodeBreakpoint breakPoint = newproc.AddBreakpoint(fileName, line, column, enabled, breakOn, condition);

            breakPoint.BindAsync().WaitAndUnwrapExceptions();
            return(breakPoint);
        }
    void DrawLayer(NeuralLayer neuralLayer, List <NodeRenderer> debuggerLayer, ref float leftMargin, float topMargin)
    {
        float verticalShift = 0;

        if (neuralLayer.nodes.Count % 2 == 0)
        {
            topMargin += 25;
        }

        for (int i = 0; i < neuralLayer.nodes.Count; i++)
        {
            GameObject   node         = new GameObject("node" + i.ToString());
            NodeRenderer nodeRenderer = new NodeRenderer(node.AddComponent <Image>() as Image);
            nodeRenderer.parent = new GameObject("node" + i.ToString());
            NodeDebugger nodeDebugger = nodeRenderer.parent.AddComponent <NodeDebugger>();
            nodeDebugger.Init(neuralLayer.nodes[i]);

            nodeRenderer.parent.transform.SetParent(targetCanvas.transform);
            node.transform.SetParent(nodeRenderer.parent.transform);

            nodeRenderer.nodeImage.sprite = nodeTexture;
            nodeRenderer.nodeImage.rectTransform.sizeDelta = new Vector2(nodeSize, nodeSize);

            float targetCanvasHeight = targetCanvas.gameObject.GetComponent <RectTransform>().rect.height;

            float v = i % 2 == 0 ? -verticalShift : verticalShift;

            nodeRenderer.nodeImage.rectTransform.position = new Vector2(leftMargin, targetCanvasHeight - topMargin + v);
            nodeRenderer.nodeImage.color = new Color32(48, 112, 181, 255);
            debuggerLayer.Add(nodeRenderer);

            if (i % 2 == 0)
            {
                verticalShift += 60;
            }

            SetupTextLabel(node, nodeRenderer);
        }
        leftMargin += 100;
    }
        private void StartAndAttachDebugger(string file, string nodePath)
        {
            // start the node process
            var workingDir         = _project.GetWorkingDirectory();
            var url                = GetFullUrl();
            var env                = GetEnvironmentVariablesString(url);
            var interpreterOptions = _project.GetProjectProperty(NodeProjectProperty.NodeExeArguments);
            var debugOptions       = this.GetDebugOptions();
            var script             = GetFullArguments(file, includeNodeArgs: false);

            var process = NodeDebugger.StartNodeProcessWithInspect(exe: nodePath, script: script, dir: workingDir, env: env, interpreterOptions: interpreterOptions, debugOptions: debugOptions);

            process.Start();

            // setup debug info and attach
            var debugUri = $"http://127.0.0.1:{process.DebuggerPort}";

            var dbgInfo = new VsDebugTargetInfo4();

            dbgInfo.dlo         = (uint)DEBUG_LAUNCH_OPERATION.DLO_AlreadyRunning;
            dbgInfo.LaunchFlags = (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd;

            dbgInfo.guidLaunchDebugEngine = WebkitDebuggerGuid;
            dbgInfo.dwDebugEngineCount    = 1;

            var enginesPtr = MarshalDebugEngines(new[] { WebkitDebuggerGuid });

            dbgInfo.pDebugEngines       = enginesPtr;
            dbgInfo.guidPortSupplier    = WebkitPortSupplierGuid;
            dbgInfo.bstrPortName        = debugUri;
            dbgInfo.fSendToOutputWindow = 0;

            // we connect through a URI, so no need to set the process,
            // we need to set the process id to '1' so the debugger is able to attach
            dbgInfo.bstrExe = $"\01";

            AttachDebugger(dbgInfo);
        }
Beispiel #8
0
        internal NodeDebugger AttachToNodeProcess(
            Action <NodeDebugger> onProcessCreated           = null,
            Action <NodeDebugger, NodeThread> onLoadComplete = null,
            string hostName          = "localhost",
            ushort portNumber        = 5858,
            int id                   = 0,
            bool resumeOnProcessLoad = false)
        {
            // Load process
            AutoResetEvent processLoaded = new AutoResetEvent(false);
            var            process       = new NodeDebugger(new UriBuilder {
                Scheme = "tcp", Host = hostName, Port = portNumber
            }.Uri, id);

            if (onProcessCreated != null)
            {
                onProcessCreated(process);
            }
            process.ProcessLoaded += (sender, args) =>
            {
                // Invoke onLoadComplete delegate, if requested
                if (onLoadComplete != null)
                {
                    onLoadComplete(process, args.Thread);
                }
                processLoaded.Set();
            };
            process.StartListening();
            AssertWaited(processLoaded);

            // Resume, if requested
            if (resumeOnProcessLoad)
            {
                process.Resume();
            }

            return(process);
        }
        private void AttachEvents(NodeDebugger process) {
            process.ProcessLoaded += OnProcessLoaded;
            process.ModuleLoaded += OnModuleLoaded;
            process.ThreadCreated += OnThreadCreated;

            process.BreakpointBound += OnBreakpointBound;
            process.BreakpointUnbound += OnBreakpointUnbound;
            process.BreakpointBindFailure += OnBreakpointBindFailure;

            process.BreakpointHit += OnBreakpointHit;
            process.AsyncBreakComplete += OnAsyncBreakComplete;
            process.ExceptionRaised += OnExceptionRaised;
            process.ProcessExited += OnProcessExited;
            process.EntryPointHit += OnEntryPointHit;
            process.StepComplete += OnStepComplete;
            process.ThreadExited += OnThreadExited;
            process.DebuggerOutput += OnDebuggerOutput;

            // Subscribe to document changes if Edit and Continue is enabled.
            var shell = (IVsShell)Package.GetGlobalService(typeof(SVsShell));
            if (shell != null) {
                // The debug engine is loaded by VS separately from the main NTVS package, so we
                // need to make sure that the package is also loaded before querying its options.
                var packageGuid = new Guid(Guids.NodejsPackageString);
                IVsPackage package;
                shell.LoadPackage(ref packageGuid, out package);

                var nodejsPackage = package as NodejsPackage;
                if (nodejsPackage != null) {
                    _trackFileChanges = nodejsPackage.GeneralOptionsPage.EditAndContinue;

                    if (_trackFileChanges) {
                        _documentEvents = nodejsPackage.DTE.Events.DocumentEvents;
                        _documentEvents.DocumentSaved += OnDocumentSaved;
                    }
                }
            }

            process.StartListening();
        }
        // Launches a process by means of the debug engine.
        // Normally, Visual Studio launches a program using the IDebugPortEx2::LaunchSuspended method and then attaches the debugger 
        // to the suspended program. However, there are circumstances in which the debug engine may need to launch a program 
        // (for example, if the debug engine is part of an interpreter and the program being debugged is an interpreted language), 
        // in which case Visual Studio uses the IDebugEngineLaunch2::LaunchSuspended method
        // The IDebugEngineLaunch2::ResumeProcess method is called to start the process after the process has been successfully launched in a suspended state.
        int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 port, string exe, string args, string dir, string env, string options, enum_LAUNCH_FLAGS launchFlags, uint hStdInput, uint hStdOutput, uint hStdError, IDebugEventCallback2 ad7Callback, out IDebugProcess2 process) {
            LiveLogger.WriteLine("AD7Engine LaunchSuspended Called with flags '{0}' ({1})", launchFlags, GetHashCode());
            AssertMainThread();

            Debug.Assert(_events == null);
            Debug.Assert(_process == null);
            Debug.Assert(_ad7ProgramId == Guid.Empty);

            _events = ad7Callback;

            var debugOptions = NodeDebugOptions.None;
            List<string[]> dirMapping = null;
            string interpreterOptions = null;
            ushort? debugPort = null;
            if (options != null) {
                var splitOptions = SplitOptions(options);

                foreach (var optionSetting in splitOptions) {
                    var setting = optionSetting.Split(new[] { '=' }, 2);

                    if (setting.Length == 2) {
                        setting[1] = HttpUtility.UrlDecode(setting[1]);

                        switch (setting[0]) {
                            case WaitOnAbnormalExitSetting:
                                bool value;
                                if (Boolean.TryParse(setting[1], out value) && value) {
                                    debugOptions |= NodeDebugOptions.WaitOnAbnormalExit;
                                }
                                break;
                            case WaitOnNormalExitSetting:
                                if (Boolean.TryParse(setting[1], out value) && value) {
                                    debugOptions |= NodeDebugOptions.WaitOnNormalExit;
                                }
                                break;
                            case RedirectOutputSetting:
                                if (Boolean.TryParse(setting[1], out value) && value) {
                                    debugOptions |= NodeDebugOptions.RedirectOutput;
                                }
                                break;
                            case DirMappingSetting:
                                string[] dirs = setting[1].Split('|');
                                if (dirs.Length == 2) {
                                    if (dirMapping == null) {
                                        dirMapping = new List<string[]>();
                                    }
                                    LiveLogger.WriteLine(String.Format("Mapping dir {0} to {1}", dirs[0], dirs[1]));
                                    dirMapping.Add(dirs);
                                }
                                break;
                            case InterpreterOptions:
                                interpreterOptions = setting[1];
                                break;
                            case WebBrowserUrl:
                                _webBrowserUrl = setting[1];
                                break;
                            case DebuggerPort:
                                ushort dbgPortTmp;
                                if (ushort.TryParse(setting[1], out dbgPortTmp)) {
                                    debugPort = dbgPortTmp;
                                }
                                break;
                        }
                    }
                }
            }

            _process =
                new NodeDebugger(
                    exe,
                    args,
                    dir,
                    env,
                    interpreterOptions,
                    debugOptions,
                    debugPort
                );

            _process.Start(false);

            AttachEvents(_process);

            var adProcessId = new AD_PROCESS_ID();
            adProcessId.ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM;
            adProcessId.dwProcessId = (uint)_process.Id;

            EngineUtils.RequireOk(port.GetProcess(adProcessId, out process));
            LiveLogger.WriteLine("AD7Engine LaunchSuspended returning S_OK");
            Debug.Assert(process != null);
            Debug.Assert(!_process.HasExited);

            return VSConstants.S_OK;
        }
        // Called by the SDM to indicate that a synchronous debug event, previously sent by the DE to the SDM,
        // was received and processed. The only event we send in this fashion is Program Destroy.
        // It responds to that event by shutting down the engine.
        int IDebugEngine2.ContinueFromSynchronousEvent(IDebugEvent2 eventObject) {
            DebugWriteCommand("ContinueFromSynchronousEvent");
            AssertMainThread();

            if (eventObject is AD7ProgramDestroyEvent) {
                var debuggedProcess = _process;

                _events = null;
                _process = null;
                _ad7ProgramId = Guid.Empty;
                _threads.Clear();
                _modules.Clear();

                if (_trackFileChanges) {
                    _documentEvents.DocumentSaved -= OnDocumentSaved;
                    _documentEvents = null;
                }

                debuggedProcess.Close();
            } else {
                Debug.Fail("Unknown synchronous event");
            }

            return VSConstants.S_OK;
        }
        // Attach the debug engine to a program. 
        int IDebugEngine2.Attach(IDebugProgram2[] rgpPrograms, IDebugProgramNode2[] rgpProgramNodes, uint celtPrograms, IDebugEventCallback2 ad7Callback, enum_ATTACH_REASON dwReason) {
            DebugWriteCommand("Attach");

            AssertMainThread();
            Debug.Assert(_ad7ProgramId == Guid.Empty);

            if (celtPrograms != 1) {
                Debug.Fail("Node debugging only supports one program in a process");
                throw new ArgumentException();
            }

            int processId = EngineUtils.GetProcessId(rgpPrograms[0]);
            if (processId == 0) {
                // engine only supports system processes
                LiveLogger.WriteLine("AD7Engine failed to get process id during attach");
                return VSConstants.E_NOTIMPL;
            }

            EngineUtils.RequireOk(rgpPrograms[0].GetProgramId(out _ad7ProgramId));

            // Attach can either be called to attach to a new process, or to complete an attach
            // to a launched process
            if (_process == null) {
                _events = ad7Callback;

                var program = (NodeRemoteDebugProgram)rgpPrograms[0];
                var process = program.DebugProcess;
                var uri = process.DebugPort.Uri;

                _process = new NodeDebugger(uri, process.Id);

                // We only need to do fuzzy comparisons when debugging remotely
                if (!uri.IsLoopback) {
                    _process.IsRemote = true;
                    _process.FileNameMapper = new FuzzyLogicFileNameMapper(EnumerateSolutionFiles());
                }

                AttachEvents(_process);
                _attached = true;
            } else {
                if (processId != _process.Id) {
                    Debug.Fail("Asked to attach to a process while we are debugging");
                    return VSConstants.E_FAIL;
                }
            }

            lock (_syncLock) {
                _sdmAttached = true;
                HandleLoadComplete();
            }

            LiveLogger.WriteLine("AD7Engine Attach returning S_OK");
            return VSConstants.S_OK;
        }
        private void DetachEvents(NodeDebugger process) {
            process.ProcessLoaded -= OnProcessLoaded;
            process.ModuleLoaded -= OnModuleLoaded;
            process.ThreadCreated -= OnThreadCreated;

            process.BreakpointBound -= OnBreakpointBound;
            process.BreakpointUnbound -= OnBreakpointUnbound;
            process.BreakpointBindFailure -= OnBreakpointBindFailure;

            process.BreakpointHit -= OnBreakpointHit;
            process.AsyncBreakComplete -= OnAsyncBreakComplete;
            process.ExceptionRaised -= OnExceptionRaised;
            process.ProcessExited -= OnProcessExited;
            process.EntryPointHit -= OnEntryPointHit;
            process.StepComplete -= OnStepComplete;
            process.ThreadExited -= OnThreadExited;
            process.DebuggerOutput -= OnDebuggerOutput;

            if (_documentEvents != null) {
                _documentEvents.DocumentSaved -= OnDocumentSaved;
            }
        }
        private static Dictionary<int, NodeModule> GetScripts(NodeDebugger debugger, JsonArray references) {
            var scripts = new Dictionary<int, NodeModule>(references.Count);
            for (int i = 0; i < references.Count; i++) {
                JsonValue reference = references[i];
                var scriptId = reference.GetValue<int>("id");
                var filename = reference.GetValue<string>("name");

                scripts.Add(scriptId, new NodeModule(debugger, scriptId, filename));
            }
            return scripts;
        }
        internal NodeDebugger DebugProcess(
            string filename,
            Action <NodeDebugger> onProcessCreated           = null,
            Action <NodeDebugger, NodeThread> onLoadComplete = null,
            string interpreterOptions     = null,
            NodeDebugOptions debugOptions = NodeDebugOptions.None,
            string cwd               = null,
            string arguments         = "",
            bool resumeOnProcessLoad = true
            )
        {
            if (!Path.IsPathRooted(filename))
            {
                filename = DebuggerTestPath + filename;
            }
            string fullPath = Path.GetFullPath(filename);
            string dir      = cwd ?? Path.GetFullPath(Path.GetDirectoryName(filename));

            if (!String.IsNullOrEmpty(arguments))
            {
                arguments = "\"" + fullPath + "\" " + arguments;
            }
            else
            {
                arguments = "\"" + fullPath + "\"";
            }

            // Load process
            AutoResetEvent processLoaded = new AutoResetEvent(false);

            Assert.IsNotNull(Nodejs.NodeExePath, "Node isn't installed");
            NodeDebugger process =
                new NodeDebugger(
                    Nodejs.NodeExePath,
                    arguments,
                    dir,
                    null,
                    interpreterOptions,
                    debugOptions,
                    null,
                    createNodeWindow: false);

            if (onProcessCreated != null)
            {
                onProcessCreated(process);
            }
            process.ProcessLoaded += (sender, args) => {
                // Invoke onLoadComplete delegate, if requested
                if (onLoadComplete != null)
                {
                    onLoadComplete(process, args.Thread);
                }
                processLoaded.Set();
            };
            process.Start();
            AssertWaited(processLoaded);

            // Resume, if requested
            if (resumeOnProcessLoad)
            {
                process.Resume();
            }

            return(process);
        }
        internal void TestDebuggerSteps(
            NodeDebugger process,
            NodeThread thread,
            string filename,
            IEnumerable <TestStep> steps,
            ExceptionHitTreatment?defaultExceptionTreatment = null,
            ICollection <KeyValuePair <string, ExceptionHitTreatment> > exceptionTreatments = null,
            bool waitForExit = false
            )
        {
            if (!Path.IsPathRooted(filename))
            {
                filename = DebuggerTestPath + filename;
            }

            // Since Alpha does not support break on unhandled, and the commonly used Express module has handled SyntaxError exceptions,
            // for alpha we have SyntaxErrors set to BreakNever by default.  Here we set it to BreakAlways so unit tests can run
            // assuming BreakAlways is the default.
            // TODO: Remove once exception treatment is updated for just my code support when it is added after Alpha
            process.SetExceptionTreatment(null, CollectExceptionTreatments(ExceptionHitTreatment.BreakAlways, "SyntaxError"));

            if (defaultExceptionTreatment != null || exceptionTreatments != null)
            {
                process.SetExceptionTreatment(defaultExceptionTreatment, exceptionTreatments);
            }

            Dictionary <Breakpoint, NodeBreakpoint> breakpoints = new Dictionary <Breakpoint, NodeBreakpoint>();

            AutoResetEvent entryPointHit = new AutoResetEvent(false);

            process.EntryPointHit += (sender, e) => {
                Console.WriteLine("EntryPointHit");
                Assert.AreEqual(e.Thread, thread);
                entryPointHit.Set();
            };

            AutoResetEvent breakpointBound = new AutoResetEvent(false);

            process.BreakpointBound += (sender, e) => {
                Console.WriteLine("BreakpointBound {0} {1}", e.BreakpointBinding.Position.FileName, e.BreakpointBinding.Position.Line);
                breakpointBound.Set();
            };

            AutoResetEvent breakpointUnbound = new AutoResetEvent(false);

            process.BreakpointUnbound += (sender, e) => {
                Console.WriteLine("BreakpointUnbound");
                breakpointUnbound.Set();
            };

            AutoResetEvent breakpointBindFailure = new AutoResetEvent(false);

            process.BreakpointBindFailure += (sender, e) => {
                Console.WriteLine("BreakpointBindFailure");
                breakpointBindFailure.Set();
            };

            AutoResetEvent breakpointHit = new AutoResetEvent(false);

            process.BreakpointHit += (sender, e) => {
                Console.WriteLine("BreakpointHit {0}", e.BreakpointBinding.Target.Line);
                Assert.AreEqual(e.Thread, thread);
                Assert.AreEqual(e.BreakpointBinding.Target.Line, thread.Frames.First().Line);
                breakpointHit.Set();
            };

            AutoResetEvent stepComplete = new AutoResetEvent(false);

            process.StepComplete += (sender, e) => {
                Console.WriteLine("StepComplete");
                Assert.AreEqual(e.Thread, thread);
                stepComplete.Set();
            };

            AutoResetEvent exceptionRaised = new AutoResetEvent(false);
            NodeException  exception       = null;

            process.ExceptionRaised += (sender, e) => {
                Console.WriteLine("ExceptionRaised");
                Assert.AreEqual(e.Thread, thread);
                exception = e.Exception;
                exceptionRaised.Set();
            };

            AutoResetEvent processExited = new AutoResetEvent(false);
            int            exitCode      = 0;

            process.ProcessExited += (sender, e) => {
                Console.WriteLine("ProcessExited {0}", e.ExitCode);
                exitCode = e.ExitCode;
                processExited.Set();
            };

            Console.WriteLine("-----------------------------------------");
            Console.WriteLine("Begin debugger step test");
            foreach (var step in steps)
            {
                Console.WriteLine("Step: {0}", step._action);
                Assert.IsFalse(
                    ((step._expectedEntryPointHit != null ? 1 : 0) +
                     (step._expectedBreakpointHit != null ? 1 : 0) +
                     (step._expectedStepComplete != null ? 1 : 0) +
                     (step._expectedExceptionRaised != null ? 1 : 0)) > 1);
                bool           wait = false;
                NodeBreakpoint nodeBreakpoint;
                switch (step._action)
                {
                case TestAction.None:
                    break;

                case TestAction.Wait:
                    wait = true;
                    break;

                case TestAction.ResumeThread:
                    thread.Resume();
                    wait = true;
                    break;

                case TestAction.ResumeProcess:
                    process.Resume();
                    wait = true;
                    break;

                case TestAction.StepOver:
                    thread.StepOver();
                    wait = true;
                    break;

                case TestAction.StepInto:
                    thread.StepInto();
                    wait = true;
                    break;

                case TestAction.StepOut:
                    thread.StepOut();
                    wait = true;
                    break;

                case TestAction.AddBreakpoint:
                    string breakpointFileName = step._targetBreakpointFile;
                    if (breakpointFileName != null)
                    {
                        if (!step._builtin && !Path.IsPathRooted(breakpointFileName))
                        {
                            breakpointFileName = DebuggerTestPath + breakpointFileName;
                        }
                    }
                    else
                    {
                        breakpointFileName = filename;
                    }
                    int        breakpointLine   = step._targetBreakpoint.Value;
                    int        breakpointColumn = step._targetBreakpointColumn.HasValue ? step._targetBreakpointColumn.Value : 0;
                    Breakpoint breakpoint       = new Breakpoint(breakpointFileName, breakpointLine, breakpointColumn);
                    Assert.IsFalse(breakpoints.TryGetValue(breakpoint, out nodeBreakpoint));
                    breakpoints[breakpoint] =
                        AddBreakPoint(
                            process,
                            breakpointFileName,
                            breakpointLine,
                            breakpointColumn,
                            step._enabled ?? true,
                            step._breakOn ?? new BreakOn(),
                            step._condition
                            );
                    if (step._expectFailure)
                    {
                        AssertWaited(breakpointBindFailure);
                        AssertNotSet(breakpointBound);
                        breakpointBindFailure.Reset();
                    }
                    else
                    {
                        AssertWaited(breakpointBound);
                        AssertNotSet(breakpointBindFailure);
                        breakpointBound.Reset();
                    }
                    break;

                case TestAction.RemoveBreakpoint:
                    breakpointFileName = step._targetBreakpointFile ?? filename;
                    breakpointLine     = step._targetBreakpoint.Value;
                    breakpointColumn   = step._targetBreakpointColumn.HasValue ? step._targetBreakpointColumn.Value : 0;
                    breakpoint         = new Breakpoint(breakpointFileName, breakpointLine, breakpointColumn);
                    breakpoints[breakpoint].Remove().WaitAndUnwrapExceptions();
                    breakpoints.Remove(breakpoint);
                    AssertWaited(breakpointUnbound);
                    breakpointUnbound.Reset();
                    break;

                case TestAction.UpdateBreakpoint:
                    breakpointFileName = step._targetBreakpointFile ?? filename;
                    breakpointLine     = step._targetBreakpoint.Value;
                    breakpointColumn   = step._targetBreakpointColumn.HasValue ? step._targetBreakpointColumn.Value : 0;
                    breakpoint         = new Breakpoint(breakpointFileName, breakpointLine, breakpointColumn);
                    nodeBreakpoint     = breakpoints[breakpoint];
                    foreach (var breakpointBinding in nodeBreakpoint.GetBindings())
                    {
                        if (step._hitCount != null)
                        {
                            Assert.IsTrue(breakpointBinding.SetHitCountAsync(step._hitCount.Value).WaitAndUnwrapExceptions());
                        }
                        if (step._enabled != null)
                        {
                            Assert.IsTrue(breakpointBinding.SetEnabledAsync(step._enabled.Value).WaitAndUnwrapExceptions());
                        }
                        if (step._breakOn != null)
                        {
                            Assert.IsTrue(breakpointBinding.SetBreakOnAsync(step._breakOn.Value).WaitAndUnwrapExceptions());
                        }
                        if (step._condition != null)
                        {
                            Assert.IsTrue(breakpointBinding.SetConditionAsync(step._condition).WaitAndUnwrapExceptions());
                        }
                    }
                    break;

                case TestAction.KillProcess:
                    process.Terminate();
                    break;

                case TestAction.Detach:
                    process.Detach();
                    break;
                }

                if (wait)
                {
                    if (step._expectedEntryPointHit != null)
                    {
                        AssertWaited(entryPointHit);
                        AssertNotSet(breakpointHit);
                        AssertNotSet(stepComplete);
                        AssertNotSet(exceptionRaised);
                        Assert.IsNull(exception);
                        entryPointHit.Reset();
                    }
                    else if (step._expectedBreakpointHit != null)
                    {
                        if (step._expectReBind)
                        {
                            AssertWaited(breakpointUnbound);
                            AssertWaited(breakpointBound);
                            breakpointUnbound.Reset();
                            breakpointBound.Reset();
                        }
                        AssertWaited(breakpointHit);
                        AssertNotSet(entryPointHit);
                        AssertNotSet(stepComplete);
                        AssertNotSet(exceptionRaised);
                        Assert.IsNull(exception);
                        breakpointHit.Reset();
                    }
                    else if (step._expectedStepComplete != null)
                    {
                        AssertWaited(stepComplete);
                        AssertNotSet(entryPointHit);
                        AssertNotSet(breakpointHit);
                        AssertNotSet(exceptionRaised);
                        Assert.IsNull(exception);
                        stepComplete.Reset();
                    }
                    else if (step._expectedExceptionRaised != null)
                    {
                        AssertWaited(exceptionRaised);
                        AssertNotSet(entryPointHit);
                        AssertNotSet(breakpointHit);
                        AssertNotSet(stepComplete);
                        exceptionRaised.Reset();
                    }
                    else
                    {
                        AssertNotSet(entryPointHit);
                        AssertNotSet(breakpointHit);
                        AssertNotSet(stepComplete);
                        AssertNotSet(exceptionRaised);
                        Assert.IsNull(exception);
                    }
                }

                if (step._expectedEntryPointHit != null)
                {
                    Assert.AreEqual(step._expectedEntryPointHit.Value, thread.Frames.First().Line);
                }
                else if (step._expectedBreakpointHit != null)
                {
                    Assert.AreEqual(step._expectedBreakpointHit.Value, thread.Frames.First().Line);
                }
                else if (step._expectedStepComplete != null)
                {
                    Assert.AreEqual(step._expectedStepComplete.Value, thread.Frames.First().Line);
                }
                else if (step._expectedExceptionRaised != null)
                {
                    Assert.AreEqual(step._expectedExceptionRaised.TypeName, exception.TypeName);
                    Assert.AreEqual(step._expectedExceptionRaised.Description, exception.Description);
                    if (step._expectedExceptionRaised.LineNo != null)
                    {
                        Assert.AreEqual(step._expectedExceptionRaised.LineNo.Value, thread.Frames[0].Line);
                    }
                    exception = null;
                }
                var expectedBreakFile = step._expectedBreakFile;
                if (expectedBreakFile != null)
                {
                    if (!step._builtin && !Path.IsPathRooted(expectedBreakFile))
                    {
                        expectedBreakFile = DebuggerTestPath + expectedBreakFile;
                    }
                    Assert.AreEqual(expectedBreakFile, thread.Frames.First().FileName);
                }
                var expectedBreakFunction = step._expectedBreakFunction;
                if (expectedBreakFunction != null)
                {
                    Assert.AreEqual(expectedBreakFunction, thread.Frames.First().FunctionName);
                }

                if (step._expectedHitCount != null)
                {
                    string breakpointFileName = step._targetBreakpointFile ?? filename;
                    if (!step._builtin && !Path.IsPathRooted(breakpointFileName))
                    {
                        breakpointFileName = DebuggerTestPath + breakpointFileName;
                    }
                    int breakpointLine   = step._targetBreakpoint.Value;
                    int breakpointColumn = step._targetBreakpointColumn.HasValue ? step._targetBreakpointColumn.Value : 0;
                    var breakpoint       = new Breakpoint(breakpointFileName, breakpointLine, breakpointColumn);
                    nodeBreakpoint = breakpoints[breakpoint];
                    foreach (var breakpointBinding in nodeBreakpoint.GetBindings())
                    {
                        Assert.AreEqual(step._expectedHitCount.Value, breakpointBinding.GetHitCount());
                    }
                }

                if (step._validation != null)
                {
                    step._validation(process, thread);
                }

                if (step._expectedExitCode != null)
                {
                    AssertWaited(processExited);
                    Assert.AreEqual(step._expectedExitCode.Value, exitCode);
                }
            }

            if (waitForExit)
            {
                process.WaitForExit(10000);
            }

            AssertNotSet(entryPointHit);
            AssertNotSet(breakpointHit);
            AssertNotSet(stepComplete);
            AssertNotSet(exceptionRaised);
            Assert.IsNull(exception);
        }