PowerShell / PowerShellEditorServices

A common platform for PowerShell development support in any editor or application!
MIT License
632 stars 215 forks source link

Debugger hangs when displaying certain variables #708

Open powercode opened 6 years ago

powercode commented 6 years ago

Steps to reproduce:

class X  {
    [string] ToString() {return $this.AMethod()}
    [string] AMethod() { return "XXX AMethod" }

        # this works if ToString is changed to the code below
    #[string] ToString() {return "xxx"}
}

class Z {
    [X] $X = [X]::new()
}

$z = [Z]::new()
$z

Put a breakpoint on the last line and try to view $z in the debugger.

The thing that breaks is invoking a member method (static or instance) while evaluating ToString(). Accessing properties in ToString works fine, however.

It seems to be connected to events (PSEngineEvent.OnScriptBlockInvoke) being raised while an event is being processed. Not sure if this is a supported scenario. @BrucePay do you know the intention here?

Possibly related is also this code in scriptblock.cs:

            ExecutionContext context = LocalPipeline.GetExecutionContextFromTLS();

            if (SessionStateInternal != null && SessionStateInternal.ExecutionContext != context)
            {
                context = SessionStateInternal.ExecutionContext;
                shouldGenerateEvent = true;
            }

shouldGenerateEvent is set to true if SessionStateInternal.ExecutionContext != LocalPipeline.GetExecutionContextFromTLS() .

shouldGenerateEvent is what causes the invocation of PSEngineEvent.OnScriptBlockInvoke that is hanging.

Callstack

    System.Private.CoreLib.dll!System.Threading.ManualResetEventSlim.Wait(int = 0x000000fa, System.Threading.CancellationToken) Unknown No symbols loaded.
    System.Private.CoreLib.dll!System.Threading.ManualResetEventSlim.Wait(int)  Unknown No symbols loaded.
>   System.Management.Automation.dll!System.Management.Automation.PSLocalEventManager.ProcessNewEvent(System.Management.Automation.PSEventArgs, bool, bool = true)  Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.ScriptBlock.InvokeWithPipe(bool, System.Management.Automation.ScriptBlock.ErrorHandlingBehavior, object, object, object, System.Management.Automation.Internal.Pipe, System.Management.Automation.InvocationInfo, bool, System.Collections.Generic.List<System.Management.Automation.PSVariable>, System.Collections.Generic.Dictionary<string, System.Management.Automation.ScriptBlock>, object[])  Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.ScriptBlock.InvokeAsMemberFunctionT<string>(object, object[]) Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.Internal.ScriptBlockMemberMethodWrapper.InvokeHelperT<string>(object, object, object[])   Unknown No symbols loaded.
    [Lightweight Function]      Annotated Frame
    System.Linq.Expressions.dll!System.Dynamic.UpdateDelegates.UpdateAndExecute1<object, object>(System.Runtime.CompilerServices.CallSite = {System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, object, object>>}, object = {X})    Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.Interpreter.DynamicInstruction<object, object>.Run(System.Management.Automation.Interpreter.InterpretedFrame) Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(System.Management.Automation.Interpreter.InterpretedFrame = {System.Management.Automation.Interpreter.InterpretedFrame})  Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(System.Management.Automation.Interpreter.InterpretedFrame = {System.Management.Automation.Interpreter.InterpretedFrame})  Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.Interpreter.Interpreter.Run(System.Management.Automation.Interpreter.InterpretedFrame = {System.Management.Automation.Interpreter.InterpretedFrame})  Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.Interpreter.LightLambda.RunVoid1<System.Management.Automation.Language.FunctionContext>(System.Management.Automation.Language.FunctionContext)    Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.ScriptBlock.InvokeWithPipeImpl(System.Management.Automation.ScriptBlockClauseToInvoke, bool = true, System.Collections.Generic.Dictionary<string, System.Management.Automation.ScriptBlock>, System.Collections.Generic.List<System.Management.Automation.PSVariable>, System.Management.Automation.ScriptBlock.ErrorHandlingBehavior, object, object, object, System.Management.Automation.Internal.Pipe, System.Management.Automation.InvocationInfo, object[]) Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.ScriptBlock.OnScriptBlockInvokeEventHandler(object, System.Management.Automation.PSEventArgs) Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.PSLocalEventManager.InvokeAction(System.Management.Automation.EventAction = {System.Management.Automation.EventAction}, out bool = false) Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.PSLocalEventManager.ProcessPendingActionsImpl()   Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.PSLocalEventManager.ProcessNewEvent(System.Management.Automation.PSEventArgs, bool, bool = true)  Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.ScriptBlock.InvokeWithPipe(bool, System.Management.Automation.ScriptBlock.ErrorHandlingBehavior, object, object, object, System.Management.Automation.Internal.Pipe, System.Management.Automation.InvocationInfo, bool, System.Collections.Generic.List<System.Management.Automation.PSVariable>, System.Collections.Generic.Dictionary<string, System.Management.Automation.ScriptBlock>, object[])  Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.ScriptBlock.InvokeAsMemberFunctionT<string>(object, object[]) Unknown No symbols loaded.
    System.Management.Automation.dll!System.Management.Automation.Internal.ScriptBlockMemberMethodWrapper.InvokeHelperT<string>(object, object, object[])   Unknown No symbols loaded.
    Microsoft.PowerShell.EditorServices.dll!Microsoft.PowerShell.EditorServices.Utility.ObjectExtensions.SafeToString(object)   Unknown No symbols loaded.
    Microsoft.PowerShell.EditorServices.dll!Microsoft.PowerShell.EditorServices.VariableDetails.GetValueStringAndType(object = {X}, bool, out string)   Unknown No symbols loaded.
    Microsoft.PowerShell.EditorServices.dll!Microsoft.PowerShell.EditorServices.VariableDetails.VariableDetails(string, object) Unknown No symbols loaded.
    Microsoft.PowerShell.EditorServices.dll!Microsoft.PowerShell.EditorServices.VariableDetails.AddDotNetProperties(object = {Z}, System.Collections.Generic.List<Microsoft.PowerShell.EditorServices.VariableDetails> = Count = 0x00000000)    Unknown No symbols loaded.
    Microsoft.PowerShell.EditorServices.dll!Microsoft.PowerShell.EditorServices.VariableDetails.GetChildren(object, Microsoft.PowerShell.EditorServices.Utility.ILogger = {Microsoft.PowerShell.EditorServices.Utility.PsesLogger}) Unknown No symbols loaded.
    Microsoft.PowerShell.EditorServices.dll!Microsoft.PowerShell.EditorServices.VariableDetails.GetChildren(Microsoft.PowerShell.EditorServices.Utility.ILogger)    Unknown No symbols loaded.
    Microsoft.PowerShell.EditorServices.dll!Microsoft.PowerShell.EditorServices.DebugService.GetVariables(int)  Unknown No symbols loaded.
SeeminglyScience commented 6 years ago

Looks like when ToString() is called to get the VariableDetails.ValueString of the property X we're on a different thread. I'm surprised it actually creates the event though. I wouldn't have thought that thread would have Runspace.DefaultRunspace set, in which case I'd expect a PSInvalidOperationException instead.

I'm not sure there's a good way to distinguish a PSMethod backed by a ScriptBlockMemberMethodWrapper and a pure IL PSMethod, so we may have to just make sure we're always on the pipeline thread while creating the VariableDetails object.

Really interesting issue @powercode, thanks for the excellent report!