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);
                    }
                }
            }
        }
Esempio n. 2
0
            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);
            }
Esempio n. 3
0
        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();
                }
            }
        }
Esempio n. 5
0
 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);
     }
 }
Esempio n. 6
0
        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);
        }
Esempio n. 8
0
        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();
                            });
Esempio n. 13
0
        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);
        }