public void OnBeginStepIn(DkmThread thread) { var frameInfo = new RemoteComponent.GetCurrentFrameInfoRequest { ThreadId = thread.UniqueId }.SendLower(thread.Process); var workList = DkmWorkList.Create(null); var topFrame = thread.GetTopStackFrame(); var curAddr = (topFrame != null) ? topFrame.InstructionAddress as DkmNativeInstructionAddress : null; foreach (var gate in _stepInGates) { gate.Breakpoint.Enable(); // A step-in may happen when we are stopped inside a step-in gate function. For example, when the gate function // calls out to user code more than once, and the user then steps out from the first call; we're now inside the // gate, but the runtime exit breakpoints for that gate have been cleared after the previous step-in completed. // To correctly handle this scenario, we need to check whether we're inside a gate with multiple exit points, and // if so, call the associated gate handler (as it the entry breakpoint for the gate is hit) so that it re-enables // the runtime exit breakpoints for that gate. if (gate.HasMultipleExitPoints && curAddr != null) { var addr = (DkmNativeInstructionAddress)gate.Breakpoint.InstructionAddress; if (addr.IsInSameFunction(curAddr)) { gate.Handler(thread, frameInfo.FrameBase, frameInfo.VFrame, useRegisters: false); } } } }
internal string ExecuteExpression(string expression, DkmStackContext stackContext, DkmStackWalkFrame input, bool allowZero) { var compilerId = new DkmCompilerId(DkmVendorId.Microsoft, DkmLanguageId.Cpp); var language = DkmLanguage.Create("C++", compilerId); var languageExpression = DkmLanguageExpression.Create(language, DkmEvaluationFlags.None, expression, null); var inspectionContext = DkmInspectionContext.Create(stackContext.InspectionSession, input.RuntimeInstance, stackContext.Thread, 200, DkmEvaluationFlags.None, DkmFuncEvalFlags.None, 10, language, null); var workList = DkmWorkList.Create(null); string resultText = null; inspectionContext.EvaluateExpression(workList, languageExpression, input, res => { if (res.ErrorCode == 0) { var result = res.ResultObject as DkmSuccessEvaluationResult; if (result != null && result.TagValue == DkmEvaluationResult.Tag.SuccessResult && (allowZero || result.Address.Value != 0)) { resultText = result.Value; } res.ResultObject.Close(); } }); workList.Execute(); return(resultText); }
internal static DkmEvaluationResult ExecuteRawExpression(string expression, DkmInspectionSession inspectionSession, DkmThread thread, DkmStackWalkFrame input, DkmRuntimeInstance runtimeInstance, DkmEvaluationFlags flags) { var compilerId = new DkmCompilerId(DkmVendorId.Microsoft, DkmLanguageId.Cpp); var language = DkmLanguage.Create("C++", compilerId); var languageExpression = DkmLanguageExpression.Create(language, DkmEvaluationFlags.None, expression, null); var inspectionContext = DkmInspectionContext.Create(inspectionSession, runtimeInstance, thread, 200, flags, DkmFuncEvalFlags.None, 10, language, null, null, DkmCompiledVisualizationDataPriority.None, null, workerConnection); var workList = DkmWorkList.Create(null); try { DkmEvaluationResult result = null; inspectionContext.EvaluateExpression(workList, languageExpression, input, res => { if (res.ErrorCode == 0) { result = res.ResultObject; } }); workList.Execute(); return(result); } catch (OperationCanceledException) { return(null); } }
private void QueryColumnDetails(int columnCount) { var exprs = new List <DkmLanguageExpression>(); try { DkmProcess process = this.inspectionContext.Thread.Process; DkmWorkList workList = DkmWorkList.Create(null); SqliteVisualizerException ex = null; var columnNamesLocal = new string[columnCount]; for (int i = 0; i < columnCount; ++i) { var i_local = i; var colName = this.AddFuncEval( workList, $"sqlite3_column_name(*(sqlite3_stmt **){this.procMemForQuery}, {i})", (r) => { DkmSuccessEvaluationResult suc; if (!this.VerifySuccess(r, out suc)) { ex = ex ?? new SqliteVisualizerException(Resources.ErrMsg_FuncEvalFailed, r.FullName); return; } ulong address = suc.Address.Value; byte[] stringMaybe = process.ReadMemoryString(address, DkmReadMemoryFlags.None, 1, 1024); int len = stringMaybe.Length; if (len > 0) { // The debugger null terminates all strings, but encoding doesn't strip null when creating a string len--; columnNamesLocal[i_local] = Encoding.UTF8.GetString(stringMaybe, 0, len); } }); exprs.Add(colName); } workList.Execute(); if (ex != null) { throw ex; } this.ColumnNames = columnNamesLocal; } finally { foreach (var e in exprs) { e.Close(); } } }
public DkmEvaluationResult TryEvaluate(string expr) { using (var cppExpr = DkmLanguageExpression.Create(CppLanguage, DkmEvaluationFlags.NoSideEffects, expr, null)) { DkmEvaluationResult cppEvalResult = null; var cppWorkList = DkmWorkList.Create(null); _cppInspectionContext.EvaluateExpression(cppWorkList, cppExpr, _nativeFrame, (result) => { cppEvalResult = result.ResultObject; }); cppWorkList.Execute(); return(cppEvalResult); } }
internal static string ExecuteExpression(string expression, DkmInspectionSession inspectionSession, DkmThread thread, DkmStackWalkFrame input, DkmEvaluationFlags flags, bool allowZero, out ulong address) { if (Log.instance != null) { Log.instance.Verbose($"ExecuteExpression begin evaluation of '{expression}'"); } var compilerId = new DkmCompilerId(DkmVendorId.Microsoft, DkmLanguageId.Cpp); var language = DkmLanguage.Create("C++", compilerId); var languageExpression = DkmLanguageExpression.Create(language, DkmEvaluationFlags.None, expression, null); var inspectionContext = DkmInspectionContext.Create(inspectionSession, input.RuntimeInstance, thread, 200, flags, DkmFuncEvalFlags.None, 10, language, null, null, DkmCompiledVisualizationDataPriority.None, null, workerConnection); var workList = DkmWorkList.Create(null); try { string resultText = null; ulong resultAddress = 0; inspectionContext.EvaluateExpression(workList, languageExpression, input, res => { if (res.ErrorCode == 0) { var result = res.ResultObject as DkmSuccessEvaluationResult; if (result != null && result.TagValue == DkmEvaluationResult.Tag.SuccessResult && (allowZero || result.Address.Value != 0)) { resultText = result.Value; resultAddress = result.Address.Value; } res.ResultObject.Close(); } }); workList.Execute(); if (Log.instance != null) { Log.instance.Verbose($"ExecuteExpression completed"); } address = resultAddress; return(resultText); } catch (OperationCanceledException) { address = 0; return(null); } }
private int QueryColumnCount() { int columnCount = 0; DkmLanguageExpression expr = null; try { DkmWorkList workList = DkmWorkList.Create(null); SqliteVisualizerException ex = null; expr = this.AddFuncEval( workList, $"sqlite3_column_count(*(sqlite3_stmt **){this.procMemForQuery})", (r) => { DkmSuccessEvaluationResult suc; if (!this.VerifySuccess(r, out suc)) { ex = new SqliteVisualizerException(Resources.ErrMsg_FuncEvalFailed, r.FullName); return; } columnCount = (int)suc.Address.Value; }); workList.Execute(); if (ex != null) { throw ex; } } finally { if (expr != null) { expr.Close(); } } return(columnCount); }
private bool EvaluateExpression(string expression, Action <DkmSuccessEvaluationResult> onSuccess) { var workList = DkmWorkList.Create(null); var success = false; _inspectionContext.EvaluateExpression(workList, CppExpression(expression), _frame, res => { var resObj = res.ResultObject; var result = resObj as DkmSuccessEvaluationResult; if (result != null) { success = true; onSuccess(result); } resObj.Close(); }); workList.Execute(); return(success); }
private void FinalizeQuery() { DkmLanguageExpression expr = null; try { SqliteVisualizerException ex = null; DkmWorkList workList = DkmWorkList.Create(null); expr = this.AddFuncEval( workList, $"sqlite3_finalize(*(sqlite3_stmt **){this.procMemForQuery})", (r) => { DkmSuccessEvaluationResult suc; if (!this.VerifySuccess(r, out suc)) { ex = new SqliteVisualizerException(Resources.ErrMsg_FuncEvalFailed, r.FullName); return; } }); workList.Execute(); if (ex != null) { throw ex; } } finally { if (expr != null) { expr.Close(); } } }
private void PrepareQuery() { DkmLanguageExpression expr = null; try { SqliteVisualizerException ex = null; DkmWorkList workList = DkmWorkList.Create(null); expr = this.AddFuncEval( workList, $"sqlite3_prepare({sqliteInstanceName}, \"{query}\", {query.Length + 1}, (sqlite3_stmt **){this.procMemForQuery}, nullptr)", (r) => { DkmSuccessEvaluationResult suc; if (!this.VerifySuccess(r, out suc)) { ex = new SqliteVisualizerException(Resources.ErrMsg_PrepareFailed, r.FullName); return; } }); workList.Execute(); if (ex != null) { throw ex; } } finally { if (expr != null) { expr.Close(); } } }
public void OnBeginStepOut(DkmThread thread) { // When we're stepping out while in Python code, there are two possibilities. Either the stack looks like this: // // PythonFrame1 // PythonFrame2 // // or else it looks like this: // // PythonFrame // [Native to Python transition] // NativeFrame // // In both cases, we use native breakpoints on the return address to catch the end of step-out operation. // For Python-to-native step-out, this is the only option. For Python-to-Python, it would seem that TraceFunc // can detect it via PyTrace_RETURN, but it doesn't actually know whether the return is to Python or to // native at the point where it's reported - and, in any case, we need to let PyEval_EvalFrameEx to return // before reporting the completion of that step-out (otherwise we will show the returning frame in call stack). // Find the destination for step-out by walking the call stack and finding either the first native frame // outside of Python and helper DLLs, or the second Python frame. var inspectionSession = DkmInspectionSession.Create(_process, null); var frameFormatOptions = new DkmFrameFormatOptions(DkmVariableInfoFlags.None, DkmFrameNameFormatOptions.None, DkmEvaluationFlags.None, 10000, 10); var stackContext = DkmStackContext.Create(inspectionSession, thread, DkmCallStackFilterOptions.None, frameFormatOptions, null, null); DkmStackFrame frame = null; for (int pyFrameCount = 0; pyFrameCount != 2;) { DkmStackFrame[] frames = null; var workList = DkmWorkList.Create(null); stackContext.GetNextFrames(workList, 1, (result) => { frames = result.Frames; }); workList.Execute(); if (frames == null || frames.Length != 1) { return; } frame = frames[0]; var frameModuleInstance = frame.ModuleInstance; if (frameModuleInstance is DkmNativeModuleInstance && frameModuleInstance != _pyrtInfo.DLLs.Python && frameModuleInstance != _pyrtInfo.DLLs.DebuggerHelper && frameModuleInstance != _pyrtInfo.DLLs.CTypes) { break; } else if (frame.RuntimeInstance != null && frame.RuntimeInstance.Id.RuntimeType == Guids.PythonRuntimeTypeGuid) { ++pyFrameCount; } } var nativeAddr = frame.InstructionAddress as DkmNativeInstructionAddress; if (nativeAddr == null) { var customAddr = frame.InstructionAddress as DkmCustomInstructionAddress; if (customAddr == null) { return; } var loc = new SourceLocation(customAddr.AdditionalData, thread.Process); nativeAddr = loc.NativeAddress; if (nativeAddr == null) { return; } } var bp = DkmRuntimeInstructionBreakpoint.Create(Guids.PythonStepTargetSourceGuid, thread, nativeAddr, false, null); bp.Enable(); _stepOutTargetBreakpoints.Add(bp); }
/// <summary> /// Retrieves active statements from the debuggee process. /// Shall only be called while in debug mode. /// Can be invoked on any thread. /// </summary> public Task <ImmutableArray <ActiveStatementDebugInfo> > GetActiveStatementsAsync(CancellationToken cancellationToken) { using (DebuggerComponent.ManagedEditAndContinueService()) { // TODO: return empty outside of debug session. // https://github.com/dotnet/roslyn/issues/24325 int unexpectedError = 0; var completion = new TaskCompletionSource <ImmutableArray <ActiveStatementDebugInfo> >(); var builders = default(ArrayBuilder <ArrayBuilder <ActiveStatementDebugInfo> >); int pendingRuntimes = 0; int runtimeCount = 0; var workList = DkmWorkList.Create(CompletionRoutine: _ => { completion.TrySetException(new InvalidOperationException($"Unexpected error enumerating active statements: 0x{unexpectedError:X8}")); }); void CancelWork() { if (builders != null) { FreeBuilders(builders); builders = null; // TODO: DkmWorkList.Cancel doesn't currently work when invoked on the completion callback. // We continue execute all the queued callbacks -- they will be no-ops. // See https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems/edit/562781. // // workList.Cancel(); // make sure we cancel with the token we received from the caller: completion.TrySetCanceled(cancellationToken); } } foreach (var process in DkmProcess.GetProcesses()) { foreach (var runtimeInstance in process.GetRuntimeInstances()) { if (runtimeInstance.TagValue == DkmRuntimeInstance.Tag.ClrRuntimeInstance) { var clrRuntimeInstance = (DkmClrRuntimeInstance)runtimeInstance; int runtimeIndex = runtimeCount; runtimeCount++; clrRuntimeInstance.GetActiveStatements(workList, activeStatementsResult => { if (cancellationToken.IsCancellationRequested) { CancelWork(); return; } if (activeStatementsResult.ErrorCode != 0) { unexpectedError = activeStatementsResult.ErrorCode; return; } // group active statement by instruction and aggregate flags and threads: var instructionMap = PooledDictionary <ActiveInstructionId, (DkmInstructionSymbol Symbol, ArrayBuilder <Guid> Threads, int Index, ActiveStatementFlags Flags)> .GetInstance(); GroupActiveStatementsByInstructionId(instructionMap, activeStatementsResult.ActiveStatements); int pendingStatements = instructionMap.Count; builders[runtimeIndex] = ArrayBuilder <ActiveStatementDebugInfo> .GetInstance(pendingStatements); builders[runtimeIndex].Count = pendingStatements; foreach (var(instructionId, (symbol, threads, index, flags)) in instructionMap) { var immutableThreads = threads.ToImmutableAndFree(); symbol.GetSourcePosition(workList, DkmSourcePositionFlags.None, InspectionSession: null, sourcePositionResult => { if (cancellationToken.IsCancellationRequested) { CancelWork(); return; } int errorCode = sourcePositionResult.ErrorCode; if (errorCode != 0) { unexpectedError = errorCode; } DkmSourcePosition position; string documentNameOpt; LinePositionSpan span; if (errorCode == 0 && (position = sourcePositionResult.SourcePosition) != null) { documentNameOpt = position.DocumentName; span = ToLinePositionSpan(position.TextSpan); } else { // The debugger can't determine source location for the active statement. // The PDB might not be available or the statement is in a method that doesn't have debug information. documentNameOpt = null; span = default; } builders[runtimeIndex][index] = new ActiveStatementDebugInfo( instructionId, documentNameOpt, span, immutableThreads, flags); // the last active statement of the current runtime has been processed: if (Interlocked.Decrement(ref pendingStatements) == 0) { // the last active statement of the last runtime has been processed: if (Interlocked.Decrement(ref pendingRuntimes) == 0) { completion.TrySetResult(builders.ToFlattenedImmutableArrayAndFree()); } } }); } instructionMap.Free(); });
public Tuple <ulong, ulong, ulong>[] GetThreadStackTrace(uint threadId, byte[] threadContextBytes) { return(ExecuteOnDkmInitializedThread(() => { DkmThread thread = GetThread(threadId); List <DkmStackFrame> frames = new List <DkmStackFrame>(); DkmProcess process = thread.Process; using (DkmInspectionSession dkmInspectionSession = DkmInspectionSession.Create(process, null)) { using (DkmStackContext dkmStackContext = DkmStackContext.Create(dkmInspectionSession, thread, DkmCallStackFilterOptions.None, new DkmFrameFormatOptions(), new System.Collections.ObjectModel.ReadOnlyCollection <byte>(threadContextBytes), null)) { bool done = false; while (!done) { DkmWorkList dkmWorkList = DkmWorkList.Create(null); dkmStackContext.GetNextFrames(dkmWorkList, int.MaxValue, (ar) => { frames.AddRange(ar.Frames); done = ar.Frames.Length == 0; }); dkmWorkList.Execute(); } } } threads[(int)threadId].Frames.Value = frames.ToArray(); Tuple <ulong, ulong, ulong>[] result = new Tuple <ulong, ulong, ulong> [frames.Count]; for (int i = 0; i < result.Length; i++) { ulong stackOffset, instructionOffset; switch (frames[i].Registers.TagValue) { case DkmFrameRegisters.Tag.X64Registers: { DkmX64FrameRegisters registers = (DkmX64FrameRegisters)frames[i].Registers; instructionOffset = registers.Rip; stackOffset = registers.Rsp; } break; case DkmFrameRegisters.Tag.X86Registers: { DkmX86FrameRegisters registers = (DkmX86FrameRegisters)frames[i].Registers; instructionOffset = registers.Eip; stackOffset = registers.Esp; } break; default: throw new NotImplementedException("Unexpected DkmFrameRegisters.Tag"); } bool found = false; ulong frameOffset = 0; for (int j = 0; !found && j < frames[i].Registers.UnwoundRegisters.Count; j++) { switch ((Dia2Lib.CV_HREG_e)frames[i].Registers.UnwoundRegisters[j].Identifier) { case Dia2Lib.CV_HREG_e.CV_AMD64_EBP: case Dia2Lib.CV_HREG_e.CV_AMD64_RBP: { byte[] bytes = frames[i].Registers.UnwoundRegisters[j].Value.ToArray(); found = true; frameOffset = bytes.Length == 8 ? BitConverter.ToUInt64(bytes, 0) : BitConverter.ToUInt32(bytes, 0); break; } } } if (instructionOffset != frames[i].InstructionAddress.CPUInstructionPart.InstructionPointer) { throw new Exception("Instruction offset is not the same?"); } result[i] = Tuple.Create(instructionOffset, stackOffset, frameOffset); } return result; })); }
/// <summary> /// Retrieves active statements from the debuggee process. /// Shall only be called while in debug mode. /// Can be invoked on any thread. /// </summary> public Task <ImmutableArray <ActiveStatementDebugInfo> > GetActiveStatementsAsync(CancellationToken cancellationToken) { using (DebuggerComponent.ManagedEditAndContinueService()) { // TODO: return empty outside of debug session. // https://github.com/dotnet/roslyn/issues/24325 var completion = new TaskCompletionSource <ImmutableArray <ActiveStatementDebugInfo> >(); var builders = default(ArrayBuilder <ArrayBuilder <ActiveStatementDebugInfo> >); int pendingRuntimes = 0; int runtimeCount = 0; // No exception should be thrown in case of errors on the debugger side. // The debugger is responsible to provide telemetry for error cases. // The callback should not be called, but it's there to guarantee that the task completes and a hang is avoided. var workList = DkmWorkList.Create(_ => { completion.TrySetResult(ImmutableArray <ActiveStatementDebugInfo> .Empty); }); void CancelWork() { if (builders != null) { FreeBuilders(builders); builders = null; workList.Cancel(blockOnCompletion: false); // make sure we cancel with the token we received from the caller: completion.TrySetCanceled(cancellationToken); } } foreach (var process in DkmProcess.GetProcesses()) { foreach (var runtimeInstance in process.GetRuntimeInstances()) { if (runtimeInstance.TagValue == DkmRuntimeInstance.Tag.ClrRuntimeInstance) { var clrRuntimeInstance = (DkmClrRuntimeInstance)runtimeInstance; int runtimeIndex = runtimeCount; runtimeCount++; clrRuntimeInstance.GetActiveStatements(workList, activeStatementsResult => { try { if (cancellationToken.IsCancellationRequested) { CancelWork(); return; } var localBuilders = builders; if (localBuilders == null) // e.g. cancelled { return; } if (activeStatementsResult.ErrorCode != 0) { localBuilders[runtimeIndex] = ArrayBuilder <ActiveStatementDebugInfo> .GetInstance(0); // the last active statement of the last runtime has been processed: if (Interlocked.Decrement(ref pendingRuntimes) == 0) { completion.TrySetResult(localBuilders.ToFlattenedImmutableArrayAndFree()); } return; } // group active statement by instruction and aggregate flags and threads: var instructionMap = PooledDictionary <ActiveInstructionId, (DkmInstructionSymbol Symbol, ArrayBuilder <Guid> Threads, int Index, ActiveStatementFlags Flags)> .GetInstance(); GroupActiveStatementsByInstructionId(instructionMap, activeStatementsResult.ActiveStatements); int pendingStatements = instructionMap.Count; localBuilders[runtimeIndex] = ArrayBuilder <ActiveStatementDebugInfo> .GetInstance(pendingStatements); localBuilders[runtimeIndex].Count = pendingStatements; if (instructionMap.Count == 0) { if (Interlocked.Decrement(ref pendingRuntimes) == 0) { completion.TrySetResult(localBuilders.ToFlattenedImmutableArrayAndFree()); } return; } foreach (var(instructionId, (symbol, threads, index, flags)) in instructionMap) { var immutableThreads = threads.ToImmutableAndFree(); symbol.GetSourcePosition(workList, DkmSourcePositionFlags.None, InspectionSession: null, sourcePositionResult => { try { if (cancellationToken.IsCancellationRequested) { CancelWork(); return; } DkmSourcePosition position; string documentNameOpt; LinePositionSpan span; if (sourcePositionResult.ErrorCode == 0 && (position = sourcePositionResult.SourcePosition) != null) { documentNameOpt = position.DocumentName; span = ToLinePositionSpan(position.TextSpan); } else { // The debugger can't determine source location for the active statement. // The PDB might not be available or the statement is in a method that doesn't have debug information. documentNameOpt = null; span = default; } localBuilders[runtimeIndex][index] = new ActiveStatementDebugInfo( instructionId, documentNameOpt, span, immutableThreads, flags); // the last active statement of the current runtime has been processed: if (Interlocked.Decrement(ref pendingStatements) == 0) { // the last active statement of the last runtime has been processed: if (Interlocked.Decrement(ref pendingRuntimes) == 0) { completion.TrySetResult(localBuilders.ToFlattenedImmutableArrayAndFree()); } } } catch (Exception e) { completion.TrySetException(e); } }); } instructionMap.Free(); }
private bool TryGetRow(out string[] row) { // Move to the next row bool moreRows = false; var exprs = new List <DkmLanguageExpression>(); try { const int SQLITE_ROW = 100; SqliteVisualizerException ex = null; DkmWorkList workList = DkmWorkList.Create(null); DkmLanguageExpression expr = this.AddFuncEval( workList, $"sqlite3_step(*(sqlite3_stmt **){this.procMemForQuery})", (r) => { DkmSuccessEvaluationResult suc; if (!this.VerifySuccess(r, out suc)) { ex = new SqliteVisualizerException(Resources.ErrMsg_FuncEvalFailed, r.FullName); return; } moreRows = SQLITE_ROW == (int)suc.Address.Value; }); exprs.Add(expr); workList.Execute(); if (ex != null) { throw ex; } } finally { foreach (var e in exprs) { e.Close(); } exprs.Clear(); } if (!moreRows) { row = new string[0]; return(false); } // Read each column in the row var rowLocal = new string[ColumnNames.Count()]; try { SqliteVisualizerException ex = null; DkmProcess process = this.inspectionContext.Thread.Process; DkmWorkList workList = DkmWorkList.Create(null); for (int i = 0; i < rowLocal.Length; i++) { var i_local = i; var e = this.AddFuncEval( workList, $"sqlite3_column_text(*(sqlite3_stmt **){this.procMemForQuery}, {i})", (r) => { DkmSuccessEvaluationResult suc; if (!this.VerifySuccess(r, out suc)) { ex = ex ?? new SqliteVisualizerException(Resources.ErrMsg_FuncEvalFailed, r.FullName); return; } ulong address = suc.Address.Value; byte[] stringMaybe = process.ReadMemoryString(address, DkmReadMemoryFlags.None, 1, 1024); int len = stringMaybe.Length; if (len > 0) { // The debugger null terminates all strings, but encoding doesn't strip null when creating a string len--; rowLocal[i_local] = Encoding.UTF8.GetString(stringMaybe, 0, len); } }); exprs.Add(e); } workList.Execute(); if (ex != null) { throw ex; } } finally { foreach (var e in exprs) { e.Close(); } } row = rowLocal; return(true); }