/// <summary>
        /// Finds a module of given MVID in one of the processes being debugged and returns its baseline metadata.
        /// Shall only be called while in debug mode.
        /// Shall only be called on MTA thread.
        /// </summary>
        public DebuggeeModuleInfo TryGetBaselineModuleInfo(Guid mvid)
        {
            Contract.ThrowIfFalse(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA);

            return(_baselineMetadata.GetOrAdd(mvid, m =>
            {
                using (DebuggerComponent.ManagedEditAndContinueService())
                {
                    var clrModuleInstance = FindClrModuleInstance(m);
                    if (clrModuleInstance == null)
                    {
                        return default;
                    }

                    if (!TryGetBaselineModuleInfo(clrModuleInstance, out var metadata, out var symReader))
                    {
                        return default;
                    }

                    // hook up a callback on module unload (the call blocks until the message is processed):
                    DkmCustomMessage.Create(
                        Connection: clrModuleInstance.Process.Connection,
                        Process: clrModuleInstance.Process,
                        SourceId: DebuggerService.MessageSourceId,
                        MessageCode: 0,
                        Parameter1: this,
                        Parameter2: clrModuleInstance).SendLower();

                    return new DebuggeeModuleInfo(metadata, symReader);
                }
Example #2
0
        /// <summary>
        /// Finds a module of given MVID in one of the processes being debugged and returns its baseline metadata.
        /// Shall only be called while in debug mode.
        /// </summary>
        public ModuleMetadata TryGetBaselineMetadata(Guid mvid)
        {
            return(_baselineMetadata.GetOrAdd(mvid, m =>
            {
                using (DebuggerComponent.ManagedEditAndContinueService())
                {
                    var clrModuleInstance = FindClrModuleInstance(m);
                    if (clrModuleInstance == null)
                    {
                        return default;
                    }

                    var metadata = GetBaselineModuleMetadata(clrModuleInstance);
                    if (metadata == null)
                    {
                        return default;
                    }

                    // hook up a callback on module unload (the call blocks until the message is processed):
                    DkmCustomMessage.Create(
                        Connection: clrModuleInstance.Process.Connection,
                        Process: clrModuleInstance.Process,
                        SourceId: DebuggerService.MessageSourceId,
                        MessageCode: 0,
                        Parameter1: this,
                        Parameter2: clrModuleInstance).SendLower();

                    return metadata;
                }
            }));
        }
        public void StartDebugging(DebugSessionOptions options)
        {
            _debuggingService.OnBeforeDebuggingStateChanged(DebuggingState.Design, DebuggingState.Run);

            if ((options & DebugSessionOptions.EditAndContinueDisabled) == 0)
            {
                _encService = _workspace.Services.GetRequiredService <IEditAndContinueWorkspaceService>();

                // hook up a callbacks (the call blocks until the message is processed):
                using (DebuggerComponent.ManagedEditAndContinueService())
                {
                    DkmCustomMessage.Create(
                        Connection: null,
                        Process: null,
                        SourceId: DebuggerService.MessageSourceId,
                        MessageCode: 0,
                        Parameter1: _encService,
                        Parameter2: null).SendLower();
                }

                _encService.StartDebuggingSession();
            }
            else
            {
                _encService = null;
            }
        }
        /// <summary>
        /// Called by the debugger when a debugging session starts and managed debugging is being used.
        /// </summary>
        public void StartDebugging()
        {
            // Hook up a callbacks (the call blocks until the message is processed).
            using (DebuggerComponent.ManagedEditAndContinueService())
            {
                DkmCustomMessage.Create(
                    Connection: null,
                    Process: null,
                    SourceId: DebuggerService.MessageSourceId,
                    MessageCode: 0,
                    Parameter1: this,
                    Parameter2: null).SendLower();
            }

            _debuggingService.OnBeforeDebuggingStateChanged(DebuggingState.Design, DebuggingState.Run);

            _encService.StartDebuggingSession();
        }
        /// <summary>
        /// Finds a module of given MVID in one of the processes being debugged and returns its baseline metadata.
        /// Shall only be called while in debug mode.
        /// Shall only be called on MTA thread.
        /// </summary>
        /// <returns>Null, if the module with the specified MVID is not found.</returns>
        public DebuggeeModuleInfo TryGetBaselineModuleInfo(Guid mvid)
        {
            Contract.ThrowIfFalse(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA);

            return(_baselineMetadata.GetOrAdd(mvid, m =>
            {
                using (DebuggerComponent.ManagedEditAndContinueService())
                {
                    var clrModuleInstance = EnumerateClrModuleInstances(m).FirstOrDefault();
                    if (clrModuleInstance == null)
                    {
                        return null;
                    }

                    // The DebuggeeModuleInfo holds on a pointer to module metadata and on a SymReader instance.
                    //
                    // The module metadata lifetime is that of the module instance, so we need to stop using the module
                    // as soon as the module instance is unloaded. We do so by associating a DataItem with the module
                    // instance and removing the metadata from our cache when the DataItem is disposed.
                    //
                    // The SymReader instance is shared across all instancese of the module.
                    if (!clrModuleInstance.TryGetModuleInfo(out var info))
                    {
                        return null;
                    }

                    // hook up a callback on module unload (the call blocks until the message is processed):
                    DkmCustomMessage.Create(
                        Connection: clrModuleInstance.Process.Connection,
                        Process: clrModuleInstance.Process,
                        SourceId: DebuggerService.MessageSourceId,
                        MessageCode: 0,
                        Parameter1: this,
                        Parameter2: clrModuleInstance).SendLower();

                    return info;
                }
        /// <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();
                            });
        /// <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();
                                }