int IDebugBreakpointBoundEvent2.EnumBoundBreakpoints (out IEnumDebugBoundBreakpoints2 ppEnum) { LoggingUtils.PrintFunction (); List<IDebugBoundBreakpoint2> boundBreakpoints = new List<IDebugBoundBreakpoint2> (1); boundBreakpoints.Add (m_boundBreakpoint); ppEnum = new DebuggeeBreakpointBound.Enumerator (boundBreakpoints); return Constants.S_OK; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void OnClientAsyncRecord (MiAsyncRecord asyncRecord) { LoggingUtils.PrintFunction (); switch (asyncRecord.Type) { case MiAsyncRecord.AsyncType.Exec: { // // Records prefixed '*'. // switch (asyncRecord.Class) { case "running": { // // The target is now running. The thread field tells which specific thread is now running, can be 'all' if every thread is running. // lock (NativeProgram) { NativeProgram.SetRunning (true); string threadId = asyncRecord ["thread-id"] [0].GetString (); if (threadId.Equals ("all")) { Dictionary<uint, DebuggeeThread> programThreads = NativeProgram.GetThreads (); lock (programThreads) { foreach (DebuggeeThread thread in programThreads.Values) { thread.SetRunning (true); } } } else { uint numericThreadId = uint.Parse (threadId); NativeProgram.CurrentThreadId = numericThreadId; CLangDebuggeeThread thread = NativeProgram.GetThread (numericThreadId); if (thread != null) { thread.SetRunning (true); } } } break; } case "stopped": { // // The target has stopped. // CLangDebuggeeThread stoppedThread = null; lock (NativeProgram) { NativeProgram.SetRunning (false); if (asyncRecord.HasField ("thread-id")) { uint threadId = asyncRecord ["thread-id"] [0].GetUnsignedInt (); NativeProgram.CurrentThreadId = threadId; } if (stoppedThread == null) { stoppedThread = NativeProgram.GetThread (NativeProgram.CurrentThreadId); } if (stoppedThread != null) { stoppedThread.SetRunning (false); } else { throw new InvalidOperationException ("Could not evaluate a thread on which we stopped"); } // // Flag some or all of the program's threads as stopped, directed by 'stopped-threads' field. // bool hasStoppedThreads = asyncRecord.HasField ("stopped-threads"); if (hasStoppedThreads) { // // If all threads are stopped, the stopped field will have the value of "all". // Otherwise, the value of the stopped field will be a list of thread identifiers. // MiResultValue stoppedThreadsRecord = asyncRecord ["stopped-threads"] [0]; if (stoppedThreadsRecord is MiResultValueList) { MiResultValueList stoppedThreads = stoppedThreadsRecord as MiResultValueList; foreach (MiResultValue stoppedThreadValue in stoppedThreads.List) { uint stoppedThreadId = stoppedThreadValue.GetUnsignedInt (); CLangDebuggeeThread thread = NativeProgram.GetThread (stoppedThreadId); if (thread != null) { thread.SetRunning (false); } } } else { Dictionary<uint, DebuggeeThread> programThreads = NativeProgram.GetThreads (); lock (programThreads) { foreach (DebuggeeThread thread in programThreads.Values) { thread.SetRunning (false); } } } } } // // Unblocks waiting for 'stopped' to be processed. Skipping event handling during interrupt requests as it confuses VS debugger flow. // bool ignoreInterruptSignal = false; if (m_interruptOperationCompleted != null) { m_interruptOperationCompleted.Set (); ignoreInterruptSignal = true; } // // Process any pending requests to refresh registered breakpoints. // #if false RefreshSharedLibraries (); #endif #if false NativeProgram.RefreshAllThreads (); #endif if (!GdbClient.GetClientFeatureSupported ("breakpoint-notifications")) { Engine.BreakpointManager.RefreshBreakpoints (); } // // This behaviour seems at odds with the GDB/MI spec, but a *stopped event can contain // multiple 'reason' fields. This seems to occur mainly when signals have been ignored prior // to a non-ignored triggering, i.e: // // Signal Stop\tPrint\tPass to program\tDescription\n // SIGSEGV No\tYes\tYes\t\tSegmentation fault\n // // *stopped,reason="signal-received",signal-name="SIGSEGV",signal-meaning="Segmentation fault",reason="signal-received",signal-name="SIGSEGV",signal-meaning="Segmentation fault",reason="exited-signalled",signal-name="SIGSEGV",signal-meaning="Segmentation fault" // if (asyncRecord.HasField ("reason")) { // // Here we pick the most recent (unhandled) signal. // int stoppedIndex = asyncRecord ["reason"].Count - 1; MiResultValue stoppedReason = asyncRecord ["reason"] [stoppedIndex]; // // The reason field can have one of the following values: // switch (stoppedReason.GetString ()) { case "breakpoint-hit": case "watchpoint-trigger": { bool canContinue = true; uint breakpointId = asyncRecord ["bkptno"] [0].GetUnsignedInt (); string breakpointMode = asyncRecord ["disp"] [0].GetString (); if (breakpointMode.Equals ("del")) { // // For temporary breakpoints, we won't have a valid managed object - so will just enforce a break event. // //Engine.Broadcast (new DebugEngineEvent.Break (), NativeProgram.DebugProgram, stoppedThread); Engine.Broadcast (new DebugEngineEvent.BreakpointHit (null), NativeProgram.DebugProgram, stoppedThread); } else { DebuggeeBreakpointBound boundBreakpoint = Engine.BreakpointManager.FindBoundBreakpoint (breakpointId); if (boundBreakpoint == null) { // // Could not find the breakpoint we're looking for. Refresh everything and try again. // Engine.BreakpointManager.SetDirty (true); Engine.BreakpointManager.RefreshBreakpoints (); boundBreakpoint = Engine.BreakpointManager.FindBoundBreakpoint (breakpointId); } if (boundBreakpoint == null) { // // Could not locate a registered breakpoint with matching id. // DebugEngineEvent.Exception exception = new DebugEngineEvent.Exception (NativeProgram.DebugProgram, stoppedReason.GetString (), "Breakpoint #" + breakpointId + "hit", 0x00000000, canContinue); Engine.Broadcast (exception, NativeProgram.DebugProgram, stoppedThread); } else { enum_BP_STATE [] breakpointState = new enum_BP_STATE [1]; LoggingUtils.RequireOk (boundBreakpoint.GetState (breakpointState)); if (breakpointState [0] == enum_BP_STATE.BPS_DELETED) { // // Hit a breakpoint which internally is flagged as deleted. Oh noes! // DebugEngineEvent.Exception exception = new DebugEngineEvent.Exception (NativeProgram.DebugProgram, stoppedReason.GetString (), "Breakpoint #" + breakpointId + " hit [deleted]", 0x00000000, canContinue); Engine.Broadcast (exception, NativeProgram.DebugProgram, stoppedThread); } else { // // Hit a breakpoint which is known about. Issue break event. // IDebugBoundBreakpoint2 [] boundBreakpoints = new IDebugBoundBreakpoint2 [] { boundBreakpoint }; IEnumDebugBoundBreakpoints2 enumeratedBoundBreakpoint = new DebuggeeBreakpointBound.Enumerator (boundBreakpoints); Engine.Broadcast (new DebugEngineEvent.BreakpointHit (enumeratedBoundBreakpoint), NativeProgram.DebugProgram, stoppedThread); } } } break; } case "end-stepping-range": case "function-finished": { Engine.Broadcast (new DebugEngineEvent.StepComplete (), NativeProgram.DebugProgram, stoppedThread); break; } case "signal-received": { string signalName = asyncRecord ["signal-name"] [stoppedIndex].GetString (); string signalMeaning = asyncRecord ["signal-meaning"] [stoppedIndex].GetString (); switch (signalName) { case null: case "SIGINT": { if (!ignoreInterruptSignal) { Engine.Broadcast (new DebugEngineEvent.Break (), NativeProgram.DebugProgram, stoppedThread); } break; } default: { StringBuilder signalDescription = new StringBuilder (); signalDescription.AppendFormat ("{0} ({1})", signalName, signalMeaning); if (asyncRecord.HasField ("frame")) { MiResultValueTuple frameTuple = asyncRecord ["frame"] [0] as MiResultValueTuple; if (frameTuple.HasField ("addr")) { string address = frameTuple ["addr"] [0].GetString (); signalDescription.AppendFormat (" at {0}", address); } if (frameTuple.HasField ("func")) { string function = frameTuple ["func"] [0].GetString (); signalDescription.AppendFormat (" ({0})", function); } } bool canContinue = true; DebugEngineEvent.Exception exception = new DebugEngineEvent.Exception (NativeProgram.DebugProgram, signalName, signalDescription.ToString (), 0x80000000, canContinue); Engine.Broadcast (exception, NativeProgram.DebugProgram, stoppedThread); break; } } break; } case "read-watchpoint-trigger": case "access-watchpoint-trigger": case "location-reached": case "watchpoint-scope": case "solib-event": case "fork": case "vfork": case "syscall-entry": case "exec": { Engine.Broadcast (new DebugEngineEvent.Break (), NativeProgram.DebugProgram, stoppedThread); break; } case "exited": case "exited-normally": case "exited-signalled": { // // React to program termination, but defer this so it doesn't consume the async output thread. // ThreadPool.QueueUserWorkItem (delegate (object state) { try { LoggingUtils.RequireOk (Engine.Detach (NativeProgram.DebugProgram)); } catch (Exception e) { LoggingUtils.HandleException (e); } }); break; } } } break; } } break; } case MiAsyncRecord.AsyncType.Status: { // // Records prefixed '+'. // break; } case MiAsyncRecord.AsyncType.Notify: { // // Records prefixed '='. // switch (asyncRecord.Class) { case "thread-group-added": case "thread-group-started": { // // A thread group became associated with a running program, either because the program was just started or the thread group was attached to a program. // try { string threadGroupId = asyncRecord ["id"] [0].GetString (); m_threadGroupStatus [threadGroupId] = 0; } catch (Exception e) { LoggingUtils.HandleException (e); } break; } case "thread-group-removed": case "thread-group-exited": { // // A thread group is no longer associated with a running program, either because the program has exited, or because it was detached from. // try { string threadGroupId = asyncRecord ["id"] [0].GetString (); if (asyncRecord.HasField ("exit-code")) { m_threadGroupStatus [threadGroupId] = asyncRecord ["exit-code"] [0].GetUnsignedInt (); } } catch (Exception e) { LoggingUtils.HandleException (e); } break; } case "thread-created": { // // A thread either was created. The id field contains the gdb identifier of the thread. The gid field identifies the thread group this thread belongs to. // try { uint threadId = asyncRecord ["id"] [0].GetUnsignedInt (); string threadGroupId = asyncRecord ["group-id"] [0].GetString (); CLangDebuggeeThread thread = NativeProgram.GetThread (threadId); if (thread == null) { NativeProgram.AddThread (threadId); } } catch (Exception e) { LoggingUtils.HandleException (e); } break; } case "thread-exited": { // // A thread has exited. The 'id' field contains the GDB identifier of the thread. The 'group-id' field identifies the thread group this thread belongs to. // try { uint threadId = asyncRecord ["id"] [0].GetUnsignedInt (); string threadGroupId = asyncRecord ["group-id"] [0].GetString (); uint exitCode = m_threadGroupStatus [threadGroupId]; NativeProgram.RemoveThread (threadId, exitCode); } catch (Exception e) { LoggingUtils.HandleException (e); } break; } case "thread-selected": { // // Informs that the selected thread was changed as result of the last command. // try { uint threadId = asyncRecord ["id"] [0].GetUnsignedInt (); NativeProgram.CurrentThreadId = threadId; } catch (Exception e) { LoggingUtils.HandleException (e); } break; } case "library-loaded": { // // Reports that a new library file was loaded by the program. // try { string moduleName = asyncRecord ["id"] [0].GetString (); CLangDebuggeeModule module = NativeProgram.GetModule (moduleName); if (module == null) { module = NativeProgram.AddModule (moduleName, asyncRecord); } if (!GdbClient.GetClientFeatureSupported ("breakpoint-notifications")) { Engine.BreakpointManager.SetDirty (true); } } catch (Exception e) { LoggingUtils.HandleException (e); } break; } case "library-unloaded": { // // Reports that a library was unloaded by the program. // try { string moduleName = asyncRecord ["id"] [0].GetString (); NativeProgram.RemoveModule (moduleName); if (!GdbClient.GetClientFeatureSupported ("breakpoint-notifications")) { Engine.BreakpointManager.SetDirty (true); } } catch (Exception e) { LoggingUtils.HandleException (e); } break; } case "breakpoint-created": case "breakpoint-modified": case "breakpoint-deleted": { try { IDebugPendingBreakpoint2 pendingBreakpoint = null; if (asyncRecord.HasField ("bkpt")) { MiResultValue breakpointData = asyncRecord ["bkpt"] [0]; MiBreakpoint currentGdbBreakpoint = new MiBreakpoint (breakpointData.Values); pendingBreakpoint = Engine.BreakpointManager.FindPendingBreakpoint (currentGdbBreakpoint.ID); // If the breakpoint is unknown, this usually means it was bound externally to the IDE. /*if (pendingBreakpoint == null) { // // CreatePendingBreakpoint always sets the dirty flag, so we need to reset this if it's handled immediately. // DebugBreakpointRequest breakpointRequest = new DebugBreakpointRequest (currentGdbBreakpoint.Address); LoggingUtils.RequireOk (Engine.BreakpointManager.CreatePendingBreakpoint (breakpointRequest, out pendingBreakpoint)); }*/ } else if (asyncRecord.HasField ("id")) { pendingBreakpoint = Engine.BreakpointManager.FindPendingBreakpoint (asyncRecord ["id"] [0].GetUnsignedInt ()); } bool wasDirty = Engine.BreakpointManager.IsDirty (); if (pendingBreakpoint != null) { DebuggeeBreakpointPending thisBreakpoint = pendingBreakpoint as DebuggeeBreakpointPending; thisBreakpoint.RefreshBoundBreakpoints (); thisBreakpoint.RefreshErrorBreakpoints (); } if (wasDirty) { Engine.BreakpointManager.SetDirty (true); } } catch (Exception e) { LoggingUtils.HandleException (e); } break; } } break; } } }