示例#1
0
文件: Tracer.cs 项目: svetek/Profiler
        public static void Patch(int version, EngineIntrinsics context, PSHostUserInterface ui)
        {
            Clear();

            var uiFieldName = version >= 7 ? "_externalUI" : "externalUI";
            // we get InternalHostUserInterface, grab external ui from that and replace it with ours
            var externalUIField = ui.GetType().GetField(uiFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
            var externalUI      = (PSHostUserInterface)externalUIField.GetValue(ui);

            // replace it with out patched up UI that writes to profiler on debug
            externalUIField.SetValue(ui, new ProfilerUI(externalUI));

            ResetUI = () =>
            {
                externalUIField.SetValue(ui, externalUI);
            };

            // getting MethodInfo of context._context.Debugger.TraceLine
            var bf = BindingFlags.NonPublic | BindingFlags.Instance;
            var contextInternal = context.GetType().GetField("_context", bf).GetValue(context);
            var debugger        = contextInternal.GetType().GetProperty("Debugger", bf).GetValue(contextInternal);
            var debuggerType    = debugger.GetType();

            var callStackField = debuggerType.GetField("_callStack", BindingFlags.Instance | BindingFlags.NonPublic);
            var _callStack     = callStackField.GetValue(debugger);

            var callStackType = _callStack.GetType();

            var countProperty = callStackType.GetProperty("Count", BindingFlags.Instance | BindingFlags.NonPublic);
            var getCount      = countProperty.GetMethod;
            var empty         = new object[0];
            var stack         = callStackField.GetValue(debugger);
            var initialLevel  = (int)getCount.Invoke(stack, empty);

            var lastFunctionContextMethod = callStackType.GetMethod("LastFunctionContext", BindingFlags.Instance | BindingFlags.NonPublic);

            object functionContext1        = lastFunctionContextMethod.Invoke(callStackField.GetValue(debugger), empty);
            var    functionContextType     = functionContext1.GetType();
            var    scriptBlockField        = functionContextType.GetField("_scriptBlock", BindingFlags.Instance | BindingFlags.NonPublic);
            var    currentPositionProperty = functionContextType.GetProperty("CurrentPosition", BindingFlags.Instance | BindingFlags.NonPublic);

            var scriptBlock1 = (ScriptBlock)scriptBlockField.GetValue(functionContext1);
            var extent1      = (IScriptExtent)currentPositionProperty.GetValue(functionContext1);

            TraceLine = () =>
            {
                var    callStack       = callStackField.GetValue(debugger);
                var    level           = (int)getCount.Invoke(callStack, empty) - initialLevel;
                object functionContext = lastFunctionContextMethod.Invoke(callStack, empty);
                var    scriptBlock     = (ScriptBlock)scriptBlockField.GetValue(functionContext);
                var    extent          = (IScriptExtent)currentPositionProperty.GetValue(functionContext);

                Trace(extent, scriptBlock, level);
            };

            // Add another event to the top apart from the scriptblock invocation
            // in Trace-ScriptInternal, this makes it more consistently work on first
            // run. Without this, the triggering line sometimes does not show up as 99.9%
            TraceLine();
        }
示例#2
0
        /// <summary>
        /// Enable tracing by patch the current session by replacing the UI host with another that triggers tracing on every statement. Set-PSDebug -Trace 1 needs to be called before this, and Set-PSDebug -Off needs to be called after Unpatch.
        /// </summary>
        /// <param name="powerShellVersion">Major PSVersion that is used `$PSVersionTable.PSVersion.Major`</param>
        /// <param name="context">ExecutionContext to be used `$ExecutionContext`</param>
        /// <param name="ui">UIHost to be replaced. `$host.UI`</param>
        /// <param name="tracer">The tracer to be used. For example ProfilerTracer.</param>
        public static void Patch(int powerShellVersion, EngineIntrinsics context, PSHostUserInterface ui, ITracer tracer)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (ui is null)
            {
                throw new ArgumentNullException(nameof(ui));
            }

            Tracer1 = tracer ?? throw new ArgumentNullException(nameof(tracer));

            var uiFieldName = powerShellVersion >= 6 ? "_externalUI" : "externalUI";
            // we get InternalHostUserInterface, grab external ui from that and replace it with ours
            var externalUIField = ui.GetType().GetField(uiFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
            var externalUI      = (PSHostUserInterface)externalUIField.GetValue(ui);

            // replace it with out patched up UI that writes to profiler on debug
            externalUIField.SetValue(ui, new TracerHostUI(externalUI, (message) => TraceLine(message, false)));

            ResetUI = () =>
            {
                externalUIField.SetValue(ui, externalUI);
            };

            // getting MethodInfo of context._context.Debugger.TraceLine
            var bf = BindingFlags.NonPublic | BindingFlags.Instance;
            var contextInternal = context.GetType().GetField("_context", bf).GetValue(context);
            var debugger        = contextInternal.GetType().GetProperty("Debugger", bf).GetValue(contextInternal);
            var debuggerType    = debugger.GetType();

            var callStackField = debuggerType.GetField("_callStack", BindingFlags.Instance | BindingFlags.NonPublic);
            var _callStack     = callStackField.GetValue(debugger);

            var callStackType = _callStack.GetType();

            var countBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;

            if (powerShellVersion == 3)
            {
                // in PowerShell 3 callstack is List<CallStackInfo> not a struct CallStackList
                // Count is public property
                countBindingFlags = BindingFlags.Instance | BindingFlags.Public;
            }
            var countProperty = callStackType.GetProperty("Count", countBindingFlags);
            var getCount      = countProperty.GetMethod;
            var empty         = new object[0];
            var stack         = callStackField.GetValue(debugger);
            var initialLevel  = (int)getCount.Invoke(stack, empty);

            if (powerShellVersion == 3)
            {
                // we do the same operation as in the TraceLineAction below, but here
                // we resolve the static things like types and properties, and then in the
                // action we just use them to get the live data without the overhead of looking
                // up properties all the time. This might be internally done in the reflection code
                // did not measure the impact, and it is probably done for us in the reflection api itself
                // in modern verisons of runtime
                var callStack1              = callStackField.GetValue(debugger);
                var callStackList1          = (NonGeneric.IList)callStack1;
                var level1                  = callStackList1.Count - initialLevel;
                var last1                   = callStackList1[callStackList1.Count - 1];
                var lastType                = last1.GetType();
                var functionContextProperty = lastType.GetProperty("FunctionContext", BindingFlags.NonPublic | BindingFlags.Instance);
                var functionContext1        = functionContextProperty.GetValue(last1);
                var functionContextType     = functionContext1.GetType();

                var scriptBlockField        = functionContextType.GetField("_scriptBlock", BindingFlags.Instance | BindingFlags.NonPublic);
                var currentPositionProperty = functionContextType.GetProperty("CurrentPosition", BindingFlags.Instance | BindingFlags.NonPublic);

                var scriptBlock1 = (ScriptBlock)scriptBlockField.GetValue(functionContext1);
                var extent1      = (IScriptExtent)currentPositionProperty.GetValue(functionContext1);

                GetTraceLineInfo = () =>
                {
                    var callStack       = callStackField.GetValue(debugger);
                    var callStackList   = (NonGeneric.IList)callStack;
                    var level           = callStackList.Count - initialLevel;
                    var last            = callStackList[callStackList.Count - 1];
                    var functionContext = functionContextProperty.GetValue(last);

                    var scriptBlock = (ScriptBlock)scriptBlockField.GetValue(functionContext);
                    var extent      = (IScriptExtent)currentPositionProperty.GetValue(functionContext);

                    return(new TraceLineInfo(extent, scriptBlock, level));
                };
            }
            else
            {
                var lastFunctionContextMethod = callStackType.GetMethod("LastFunctionContext", BindingFlags.Instance | BindingFlags.NonPublic);

                object functionContext1        = lastFunctionContextMethod.Invoke(callStackField.GetValue(debugger), empty);
                var    functionContextType     = functionContext1.GetType();
                var    scriptBlockField        = functionContextType.GetField("_scriptBlock", BindingFlags.Instance | BindingFlags.NonPublic);
                var    currentPositionProperty = functionContextType.GetProperty("CurrentPosition", BindingFlags.Instance | BindingFlags.NonPublic);

                var scriptBlock1 = (ScriptBlock)scriptBlockField.GetValue(functionContext1);
                var extent1      = (IScriptExtent)currentPositionProperty.GetValue(functionContext1);

                GetTraceLineInfo = () =>
                {
                    var    callStack       = callStackField.GetValue(debugger);
                    var    level           = (int)getCount.Invoke(callStack, empty) - initialLevel;
                    object functionContext = lastFunctionContextMethod.Invoke(callStack, empty);
                    var    scriptBlock     = (ScriptBlock)scriptBlockField.GetValue(functionContext);
                    var    extent          = (IScriptExtent)currentPositionProperty.GetValue(functionContext);

                    return(new TraceLineInfo(extent, scriptBlock, level));
                };
            }

#pragma warning disable CS0618 // Type or member is obsolete
            IsEnabled = true;
#pragma warning restore CS0618 // Type or member is obsolete

            // Add another event to the top apart from the scriptblock invocation
            // in Trace-ScriptInternal, this makes it more consistently work on first
            // run. Without this, the triggering line sometimes does not show up as 99.9%
            TraceLine();
        }