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