private async Task HandleBreakModeEvent()

in src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs [1039:1355]


        private async Task HandleBreakModeEvent(ResultEventArgs results, BreakRequest breakRequest)
        {
            string reason = results.Results.TryFindString("reason");
            int tid;
            if (!results.Results.Contains("thread-id"))
            {
                Results res = await MICommandFactory.ThreadInfo();
                tid = res.FindInt("id");
            }
            else
            {
                tid = results.Results.FindInt("thread-id");
            }

            if (_childProcessHandler != null && await _childProcessHandler.Stopped(results.Results, tid))
            {
                return;
            }

            // Any existing variable objects at this point are from the last time we were in break mode, and are
            //  therefore invalid.  Dispose them so they're marked for cleanup.
            lock (this.ActiveVariables)
            {
                foreach (IVariableInformation varInfo in this.ActiveVariables)
                {
                    varInfo.Dispose();
                }
                this.ActiveVariables.Clear();
                ReturnValue = null; // already disposed above
            }

            ThreadCache.MarkDirty();
            MICommandFactory.DefineCurrentThread(tid);

            DebuggedThread thread = await ThreadCache.GetThread(tid);
            if (thread == null)
            {
                if (!this.IsStopDebuggingInProgress)
                {
                    Debug.Fail("Failed to find thread on break event.");
                    throw new Exception(String.Format(CultureInfo.CurrentCulture, ResourceStrings.MissingThreadBreakEvent, tid));
                }
                else
                {
                    // It's possible that the SIGINT was sent because GDB is trying to terminate a running debuggee and stop debugging
                    // See https://devdiv.visualstudio.com/DevDiv/VS%20Diag%20IntelliTrace/_workItems?_a=edit&id=236275&triage=true
                    // for a repro
                    return;
                }
            }

            await this.EnsureModulesLoaded();
            await ThreadCache.StackFrames(thread);  // prepopulate the break thread in the thread cache
            ThreadContext cxt = await ThreadCache.GetThreadContext(thread);

            if (cxt == null)
            {
                // Something went seriously wrong. For instance, this can happen when the primary thread
                // of an app exits on linux while background threads continue to run with pthread_exit on the main thread
                // See https://devdiv.visualstudio.com/DefaultCollection/DevDiv/VS%20Diag%20IntelliTrace/_workItems?_a=edit&id=197616&triage=true
                // for a repro
                Debug.Fail("Failed to find thread on break event.");
                throw new Exception(String.Format(CultureInfo.CurrentCulture, ResourceStrings.MissingThreadBreakEvent, tid));
            }

            ThreadCache.SendThreadEvents(this, null);   // make sure that new threads have been pushed to the UI

            // If didn't hit a breakpoints then delete all pending deletions on break mode
            // For breakpoint stops deletion will be handled below.
            if (reason != "breakpoint-hit")
            {
                await _breakpointManager.DeleteBreakpointsPendingDeletion();
            }

            // Delete GDB variable objects that have been marked for cleanup
            List<string> variablesToDelete = null;
            lock (VariablesToDelete)
            {
                variablesToDelete = new List<string>(this.VariablesToDelete);
                VariablesToDelete.Clear();
            }

            foreach (var variable in variablesToDelete)
            {
                try
                {
                    await MICommandFactory.VarDelete(variable);
                }
                catch (MIException)
                {
                    //not much to do really, we're leaking MI debugger variables.
                    Debug.Fail("Failed to delete variable: " + variable + ". This is leaking memory in the MI Debugger.");
                }
            }

            if (String.IsNullOrWhiteSpace(reason) && !this.EntrypointHit)
            {
                breakRequest = BreakRequest.None;   // don't let stopping interfere with launch processing
                bool shouldContinue = true;

                if (_launchOptions.StopAtConnect)
                {
                    this.EntrypointHit = true;
                    await this.ClearEntrypointBreakpoint();

                    // Send a breakpoint event to force the client to stop (entry point may not stop depending on how the user started debugging)
                    _callback.OnBreakpoint(thread, new ReadOnlyCollection<object>(new AD7BoundBreakpoint[] { }));
                    shouldContinue = false;
                }
                // MinGW sends a stopped event on attach. gdb<->gdbserver also sends a stopped event when first attached.
                // If this is a gdb<->gdbserver connection, ignore this as the entryPoint
                else if (IsLocalLaunchUsingServer())
                {
                    // If the stopped event occurs on gdbserver, ignore it unless it contains a filename.
                    TupleValue frame = results.Results.TryFind<TupleValue>("frame");
                    if (frame.Contains("file"))
                    {
                        this.EntrypointHit = true;
                        await this.ClearEntrypointBreakpoint();
                        _callback.OnEntryPoint(thread);
                        shouldContinue = false;
                    }
                }
                else
                {
                    this.EntrypointHit = true;
                    await this.ClearEntrypointBreakpoint();
                }

                if (shouldContinue)
                {
                    CmdContinueAsync();
                }
                FireDeviceAppLauncherResume();
            }
            else if (reason == "entry-point-hit")
            {
                this.EntrypointHit = true;
                await this.OnEntrypointHit();
                _callback.OnEntryPoint(thread);
            }
            else if (reason == "breakpoint-hit")
            {
                string bkptno = results.Results.FindString("bkptno");
                ulong addr = cxt.pc ?? 0;

                bool fContinue;
                TupleValue frame = results.Results.TryFind<TupleValue>("frame");
                AD7BoundBreakpoint[] bkpt = _breakpointManager.FindHitBreakpoints(bkptno, addr, frame, out fContinue);
                await _breakpointManager.DeleteBreakpointsPendingDeletion();

                if (bkpt != null)
                {
                    if (frame != null && addr != 0)
                    {
                        string sourceFile = frame.TryFindString("fullname");
                        if (!String.IsNullOrEmpty(sourceFile))
                        {
                            await this.VerifySourceFileTimestamp(addr, sourceFile);
                        }
                    }

                    if (!this.EntrypointHit)
                    {
                        // Hitting a bp before the entrypoint overrules entrypoint processing.
                        this.EntrypointHit = true;
                        await this.OnEntrypointHit();
                    }

                    List<object> bplist = new List<object>();
                    bplist.AddRange(bkpt);
                    _callback.OnBreakpoint(thread, bplist.AsReadOnly());
                }
                else if (ExceptionManager.TryGetExceptionBreakpoint(bkptno, addr, frame, out string exceptionName, out string description, out Guid exceptionCategoryGuid)) // exception breakpoint hit
                {
                    _callback.OnException(thread, exceptionName, description, 0, exceptionCategoryGuid, ExceptionBreakpointStates.BreakThrown);
                }
                else if (!this.EntrypointHit)
                {
                    this.EntrypointHit = true;
                    await this.OnEntrypointHit();

                    _callback.OnEntryPoint(thread);
                }
                else if (bkptno == "<EMBEDDED>")
                {
                    _callback.OnBreakpoint(thread, new ReadOnlyCollection<object>(new AD7BoundBreakpoint[] { }));
                }
                else
                {
                    if (fContinue)
                    {
                        //we hit a bp pending deletion
                        //post the CmdContinueAsync operation so it does not happen until we have deleted all the pending deletes
                        CmdContinueAsyncConditional(breakRequest);
                    }
                    else
                    {
                        // not one of our breakpoints, so stop with a message
                        _callback.OnException(thread, "Unknown breakpoint", "", 0);
                    }
                }
            }
            else if (reason == "watchpoint-trigger")
            {
                var wpt = results.Results.Find("wpt");
                string bkptno = wpt.FindString("number");
                ulong addr = cxt.pc ?? 0;

                bool fContinue;
                AD7BoundBreakpoint bkpt = _breakpointManager.FindHitWatchpoint(bkptno, out fContinue);
                if (bkpt != null)
                {
                    List<object> bplist = new List<object>();
                    bplist.Add(bkpt);
                    _callback.OnBreakpoint(thread, bplist.AsReadOnly());
                }
                else
                {
                    if (fContinue)
                    {
                        //we hit a bp pending deletion
                        //post the CmdContinueAsync operation so it does not happen until we have deleted all the pending deletes
                        CmdContinueAsyncConditional(breakRequest);
                    }
                    else
                    {
                        // not one of our breakpoints, so stop with a message
                        _callback.OnException(thread, "Unknown watchpoint", "", 0);
                    }
                }
            }
            // step over/into
            // NB: unfortunately this event does not provide a return value: https://sourceware.org/bugzilla/show_bug.cgi?id=26354
            else if (reason == "end-stepping-range")
                _callback.OnStepComplete(thread);
            // step out
            else if (reason == "function-finished")
            {
                string resultVar = results.Results.TryFindString("gdb-result-var"); // a gdb value history var like "$1"
                if (!string.IsNullOrEmpty(resultVar))
                {
                    ReturnValue = new VariableInformation("$ReturnValue", resultVar, cxt, Engine, (AD7Thread)thread.Client, isParameter: false);
                    await ReturnValue.Eval(radix: 0);
                }
                _callback.OnStepComplete(thread);
            }
            else if (reason == "signal-received")
            {
                string name = results.Results.TryFindString("signal-name");
                if ((name == "SIG32") || (name == "SIG33"))
                {
                    // we are going to ignore these (Sigma) signals for now
                    CmdContinueAsyncConditional(breakRequest);
                }
                else if (MICommandFactory.IsAsyncBreakSignal(results.Results))
                {
                    _callback.OnAsyncBreakComplete(thread);
                }
                else
                {
                    uint code = 0;
                    string sigName = results.Results.TryFindString("signal-name");
                    code = results.Results.Contains("signal") ? results.Results.FindUint("signal") : 0;
                    if (String.IsNullOrEmpty(sigName) && code != 0 && EngineUtils.SignalMap.Instance.ContainsValue(code))
                    {
                        sigName = EngineUtils.SignalMap.Instance.First((p) => p.Value == code).Key;
                    }
                    else if (!String.IsNullOrEmpty(sigName) && code == 0 && EngineUtils.SignalMap.Instance.ContainsKey(sigName))
                    {
                        code = EngineUtils.SignalMap.Instance[sigName];
                    }
                    bool stoppedAtSIGSTOP = false;
                    if (sigName == "SIGSTOP" && _launchOptions.ProcessId.HasValue)
                    {
                        if (AD7Engine.RemoveChildProcess(_launchOptions.ProcessId.Value))
                        {
                            stoppedAtSIGSTOP = true;
                        }
                    }
                    string message = results.Results.TryFindString("signal-meaning");
                    if (stoppedAtSIGSTOP)
                    {
                        await MICommandFactory.Signal("SIGCONT");
                    }
                    else
                    {
                        _callback.OnException(thread, sigName, message, code);
                    }
                }
            }
            else if (reason == "exception-received")
            {
                string exceptionName = results.Results.TryFindString("exception-name");
                if (string.IsNullOrEmpty(exceptionName))
                    exceptionName = "Exception";

                string description = results.Results.FindString("exception");
                Guid? exceptionCategory;
                ExceptionBreakpointStates state;
                MICommandFactory.DecodeExceptionReceivedProperties(results.Results, out exceptionCategory, out state);

                _callback.OnException(thread, exceptionName, description, 0, exceptionCategory, state);
            }
            else
            {
                if (breakRequest == BreakRequest.None)
                {
                    Debug.Fail("Unknown stopping reason");
                    _callback.OnException(thread, "Unknown", "Unknown stopping event", 0);
                }
            }
            if (IsExternalBreakRequest(breakRequest))
            {
                _callback.OnStopComplete(thread);
            }
        }