//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeBreakpointError (CLangDebugger debugger, DebugBreakpointManager breakpointManager, DebuggeeBreakpointPending pendingBreakpoint, DebuggeeCodeContext codeContext, MiBreakpoint gdbBreakpoint, string error) : base (breakpointManager, pendingBreakpoint, codeContext, error) { m_debugger = debugger; GdbBreakpoint = gdbBreakpoint; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public CLangDebuggeeBreakpointBound (CLangDebugger debugger, DebugBreakpointManager breakpointManager, DebuggeeBreakpointPending pendingBreakpoint, DebuggeeCodeContext codeContext, MiBreakpoint gdbBreakpoint) : base (breakpointManager, pendingBreakpoint, codeContext) { m_debugger = debugger; GdbBreakpoint = gdbBreakpoint; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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; } } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void RefreshBreakpoint (object breakpoint) { // // Validate breakpoint input type. This function can be used for 'bound' and 'error' objects, so we need to handle this appropriately. // LoggingUtils.PrintFunction (); CLangDebuggeeBreakpointBound boundBreakpoint = null; CLangDebuggeeBreakpointError errorBreakpoint = null; MiBreakpoint gdbBreakpoint; DebuggeeBreakpointResolution resolution; if (breakpoint == null) { throw new ArgumentNullException ("breakpoint"); } else if (breakpoint is CLangDebuggeeBreakpointBound) { boundBreakpoint = breakpoint as CLangDebuggeeBreakpointBound; gdbBreakpoint = boundBreakpoint.GdbBreakpoint; IDebugBreakpointResolution2 boundBreakpointResolution; int handle = boundBreakpoint.GetBreakpointResolution (out boundBreakpointResolution); if (handle == Constants.E_BP_DELETED) { return; } LoggingUtils.RequireOk (handle); resolution = (DebuggeeBreakpointResolution) boundBreakpointResolution; } else if (breakpoint is CLangDebuggeeBreakpointError) { errorBreakpoint = breakpoint as CLangDebuggeeBreakpointError; gdbBreakpoint = errorBreakpoint.GdbBreakpoint; IDebugErrorBreakpointResolution2 errorBreakpointResolution; int handle = errorBreakpoint.GetBreakpointResolution (out errorBreakpointResolution); if (handle == Constants.E_BP_DELETED) { return; } resolution = (DebuggeeBreakpointResolution) errorBreakpointResolution; lock (m_errorBreakpoints) { m_errorBreakpoints.Remove (errorBreakpoint); } } else { throw new ArgumentException ("breakpoint"); } // // Query breakpoint info/status directly from GDB/MI. // try { string command = string.Format ("-break-info {0}", gdbBreakpoint.ID); m_debugger.GdbClient.SendCommand (command, delegate (MiResultRecord resultRecord) { if (resultRecord == null) { throw new InvalidOperationException (); } else if (resultRecord.IsError ()) { // // GDB/MI breakpoint info request failed. // gdbBreakpoint.Address = MiBreakpoint.Pending; (resolution as DebuggeeBreakpointResolution).CodeContext.Address = new DebuggeeAddress (gdbBreakpoint.Address); errorBreakpoint = new CLangDebuggeeBreakpointError (m_debugger, m_breakpointManager, this, (resolution as DebuggeeBreakpointResolution).CodeContext, gdbBreakpoint, resultRecord.Records [1].Stream); lock (m_errorBreakpoints) { m_errorBreakpoints.Add (errorBreakpoint); } m_debugger.Engine.Broadcast (new DebugEngineEvent.BreakpointError (errorBreakpoint), m_debugger.NativeProgram.DebugProgram, m_debugger.NativeProgram.GetThread (m_debugger.NativeProgram.CurrentThreadId)); } else { // // We've probably got sane breakpoint information back. Update current breakpoint values and re-process. // MiResultValue breakpointData = resultRecord ["BreakpointTable"] [0] ["body"] [0] ["bkpt"] [0]; MiBreakpoint currentGdbBreakpoint = new MiBreakpoint (breakpointData.Values); DebuggeeCodeContext codeContext = (resolution as DebuggeeBreakpointResolution).CodeContext; DebuggeeDocumentContext documentContext = codeContext.DocumentContext; LoggingUtils.RequireOk (CreateBoundBreakpoint (currentGdbBreakpoint, documentContext, codeContext)); } }); } catch (Exception e) { LoggingUtils.HandleException (e); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int CreateBoundBreakpoint (MiBreakpoint breakpoint, DebuggeeDocumentContext documentContext, DebuggeeCodeContext codeContext) { LoggingUtils.PrintFunction (); try { if (breakpoint == null) { throw new ArgumentNullException ("breakpoint"); } if (breakpoint.IsPending ()) { // // Address can't be satisfied. Unsatisfied likely indicates the modules or symbols associated with the context aren't loaded, yet. // DebuggeeAddress pendingAddress = new DebuggeeAddress (MiBreakpoint.Pending); DebuggeeCodeContext pendingContext = new CLangDebuggeeCodeContext (m_debugger, pendingAddress, documentContext); LoggingUtils.RequireOk (CreateErrorBreakpoint ("Additional library symbols required.", breakpoint, documentContext, pendingContext)); } else if (breakpoint.IsMultiple ()) { // // Breakpoint satisfied to multiple locations, no single memory address available. // CLangDebuggeeBreakpointBound boundBreakpoint = new CLangDebuggeeBreakpointBound (m_debugger, m_breakpointManager, this, codeContext, breakpoint); lock (m_boundBreakpoints) { m_boundBreakpoints.Clear (); m_boundBreakpoints.Add (boundBreakpoint); } m_debugger.Engine.Broadcast (new DebugEngineEvent.BreakpointBound (this, boundBreakpoint), m_debugger.NativeProgram.DebugProgram, m_debugger.NativeProgram.GetThread (m_debugger.NativeProgram.CurrentThreadId)); } else { // // Address satisfied, and the breakpoint is legitimately bound. // DebuggeeAddress boundAddress = new DebuggeeAddress (breakpoint.Address); DebuggeeCodeContext addressContext = new CLangDebuggeeCodeContext (m_debugger, boundAddress, documentContext); CLangDebuggeeBreakpointBound boundBreakpoint = new CLangDebuggeeBreakpointBound (m_debugger, m_breakpointManager, this, addressContext, breakpoint); lock (m_boundBreakpoints) { m_boundBreakpoints.Clear (); m_boundBreakpoints.Add (boundBreakpoint); } m_debugger.Engine.Broadcast (new DebugEngineEvent.BreakpointBound (this, boundBreakpoint), m_debugger.NativeProgram.DebugProgram, m_debugger.NativeProgram.GetThread (m_debugger.NativeProgram.CurrentThreadId)); } return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); return Constants.E_FAIL; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int CreateErrorBreakpoint (string errorReason, MiBreakpoint gdbBreakpoint, DebuggeeDocumentContext documentContext, DebuggeeCodeContext codeContext) { // // Create a C-language breakpoint. This is tied to a GDB/MI breakpoint object. // LoggingUtils.PrintFunction (); try { CLangDebuggeeBreakpointError errorBreakpoint = new CLangDebuggeeBreakpointError (m_debugger, m_breakpointManager, this, codeContext, gdbBreakpoint, errorReason); lock (m_errorBreakpoints) { m_errorBreakpoints.Clear (); m_errorBreakpoints.Add (errorBreakpoint); } uint numDebugPrograms = 1; IEnumDebugPrograms2 debugPrograms; IDebugProgram2 [] debugProgramsArray = new IDebugProgram2 [numDebugPrograms]; LoggingUtils.RequireOk (m_breakpointManager.Engine.EnumPrograms (out debugPrograms)); LoggingUtils.RequireOk (debugPrograms.Next (numDebugPrograms, debugProgramsArray, ref numDebugPrograms)); m_breakpointManager.Engine.Broadcast (new DebugEngineEvent.BreakpointError (errorBreakpoint), debugProgramsArray [0], null); return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); return Constants.E_FAIL; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public override int CreateBoundBreakpoint (string location, DebuggeeDocumentContext documentContext, DebuggeeCodeContext codeContext) { // // Register a new GDB breakpoint. // LoggingUtils.PrintFunction (); try { if (m_breakpointRequestInfo.bpLocation.bpLocationType == (uint)enum_BP_TYPE.BPT_DATA) { throw new NotImplementedException (); } m_debugger.RunInterruptOperation (delegate (CLangDebugger debugger) { string command = string.Format ("-break-insert -f {0} {1}", ((m_breakpointEnabled) ? "" : "-d"), PathUtils.SantiseWindowsPath (location)); debugger.GdbClient.SendCommand (command, delegate (MiResultRecord resultRecord) { if (resultRecord != null) { if (resultRecord.IsError ()) { string errorReason = "<unknown error>"; if (resultRecord.HasField ("msg")) { errorReason = resultRecord ["msg"] [0].GetString (); } LoggingUtils.RequireOk (CreateErrorBreakpoint (errorReason, documentContext, codeContext)); } else { MiResultValue breakpointData = resultRecord ["bkpt"] [0]; MiBreakpoint breakpoint = new MiBreakpoint (breakpointData.Values); LoggingUtils.RequireOk (CreateBoundBreakpoint (breakpoint, documentContext, codeContext)); } } }); }); return Constants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); return Constants.E_FAIL; } }