internal static DebuggerContext?GetDebuggerContext(IntPtr ptrClient) { // On our first call to the API: // 1. Store a copy of IDebugClient in DebugClient. // 2. Replace Console's output stream to be the debugger window. // 3. Create an instance of DataTarget using the IDebugClient. if (instance is null) { object client = Marshal.GetUniqueObjectForIUnknown(ptrClient); var debugClient = (IDebugClient)client; var output = new DebuggerOutput(debugClient); #pragma warning disable CA2000 // Dispose objects before losing scope var dataTarget = DataTarget.CreateFromDbgEng(ptrClient); #pragma warning restore CA2000 // Dispose objects before losing scope ClrRuntime?runtime = null; // If our ClrRuntime instance is null, it means that this is our first call, or // that the dac wasn't loaded on any previous call. Find the dac loaded in the // process (the user must use .cordll), then construct our runtime from it. // Just find a module named mscordacwks and assume it's the one the user // loaded into windbg. Process p = Process.GetCurrentProcess(); foreach (ProcessModule module in p.Modules) { if (module.FileName.ToUpperInvariant().Contains("MSCORDACWKS")) { // TODO: This does not support side-by-side CLRs. runtime = dataTarget.ClrVersions.Single().CreateRuntime(module.FileName); break; } } // Otherwise, the user didn't run .cordll. if (runtime is null) { output.WriteLine("Mscordacwks.dll not loaded into the debugger."); output.WriteLine("Run .cordll to load the dac before running this command."); } if (runtime is object) { instance = new DebuggerContext(debugClient, dataTarget, runtime, output); } } else { // If we already had a runtime, flush it for this use. This is ONLY required // for a live process or iDNA trace. If you use the IDebug* apis to detect // that we are debugging a crash dump you may skip this call for better perf. // instance.Runtime.Flush(); } return(instance); }
private static bool PrintAsyncStateMachineChain(DebuggerOutput output, AsyncStateMachine node, HashSet <AsyncStateMachine> printedMachines) { int nLevel = 0; bool multipleLineBlock = false; var loopDetection = new HashSet <AsyncStateMachine>(); for (AsyncStateMachine?p = node; p is object; p = p.Next) { printedMachines.Add(p); if (nLevel > 0) { output.WriteString(".."); multipleLineBlock = true; } else if (p.AlterPrevious is object) { output.WriteObjectAddress(p.AlterPrevious.StateMachine.Address); output.WriteString($" <{p.AlterPrevious.State}> * {p.AlterPrevious.StateMachine.Type?.Name} @ "); output.WriteMethodInfo($"{p.AlterPrevious.CodeAddress:x}", p.AlterPrevious.CodeAddress); output.WriteLine(string.Empty); output.WriteString(".."); multipleLineBlock = true; } else if (!p.SwitchToMainThreadTask.IsNull) { output.WriteObjectAddress(p.SwitchToMainThreadTask.Address); output.WriteLine(".SwitchToMainThreadAsync"); output.WriteString(".."); multipleLineBlock = true; } output.WriteObjectAddress(p.StateMachine.Address); string doubleDependentTaskMark = p.DependentCount > 1 ? " * " : " "; output.WriteString($" <{p.State}>{doubleDependentTaskMark}{p.StateMachine.Type?.Name} @ "); output.WriteMethodInfo($"{p.CodeAddress:x}", p.CodeAddress); output.WriteLine(string.Empty); if (!loopDetection.Add(p)) { output.WriteLine("!!Loop task dependencies"); break; } if (p.Next is null && p.BlockedThread.HasValue) { output.WriteString("-- "); output.WriteThreadLink(p.BlockedThread.Value); output.WriteString(" - JoinableTask: "); output.WriteObjectAddress(p.BlockedJoinableTask.Address); int state = p.BlockedJoinableTask.ReadField <int>("state"); if ((state & 0x20) == 0x20) { output.WriteLine(" SynchronouslyBlockingMainThread"); } else { output.WriteLine(string.Empty); } multipleLineBlock = true; } nLevel++; } return(multipleLineBlock); }
private static void PrintOutStateMachines(List <AsyncStateMachine> allStateMachines, DebuggerOutput output) { int loopMark = -1; foreach (var stateMachine in allStateMachines) { int depth = 0; if (stateMachine.Previous is null) { AsyncStateMachine?p = stateMachine; while (p is object) { depth++; if (p.Depth == loopMark) { break; } p.Depth = loopMark; p = p.Next; } } if (stateMachine.AlterPrevious is object) { depth++; } stateMachine.Depth = depth; loopMark--; } var printedMachines = new HashSet <AsyncStateMachine>(); foreach (var node in allStateMachines .Where(m => m.Depth > 0) .OrderByDescending(m => m.Depth) .ThenByDescending(m => m.SwitchToMainThreadTask.Address)) { bool multipleLineBlock = PrintAsyncStateMachineChain(output, node, printedMachines); if (multipleLineBlock) { output.WriteLine(string.Empty); } } // Print nodes which we didn't print because of loops. if (allStateMachines.Count > printedMachines.Count) { output.WriteLine("States form dependencies loop -- could be an error caused by the analysis tool"); foreach (var node in allStateMachines) { if (!printedMachines.Contains(node)) { PrintAsyncStateMachineChain(output, node, printedMachines); output.WriteLine(string.Empty); } } } }