From c84fc9c889160fc6b38f45223092afc5b94b70cf Mon Sep 17 00:00:00 2001 From: lordmilko Date: Sat, 23 Dec 2023 15:47:02 +1100 Subject: [PATCH] -Fix Import-DbgProfilerStackTrace not working -Fix Trace-DbgProfilerStack not being allowed to create global sessions -Warn when attempting to operate on the last trace when no last trace exists --- README.md | 2 +- .../Cmdlets/Base/StackFrameCmdlet.cs | 15 ++++-- .../Cmdlets/ExportDbgProfilerStackTrace.cs | 9 +++- .../Cmdlets/ImportDbgProfilerStackTrace.cs | 3 +- .../Cmdlets/TraceDbgProfilerStack.cs | 4 ++ src/DebugTools/Memory/DllInjector.cs | 6 --- src/DebugTools/Profiler/ProfilerSession.cs | 16 ++++-- .../FileEtwProfilerReaderConfig.cs | 0 .../Config/FileXmlProfilerReaderConfig.cs | 18 +++++++ .../{ => Config}/IProfilerReaderConfig.cs | 0 .../{ => Config}/LiveProfilerReaderConfig.cs | 0 .../Profiler/Reader/FileXmlProfilerReader.cs | 52 +++++++++++++++++++ .../Profiler/Target/EtwFileProfilerTarget.cs | 2 +- .../Profiler/Target/XmlFileProfilerTarget.cs | 36 +++++++++++++ .../Profiler/XmlFileProfilerSession.cs | 14 ----- ...cs => InProcessLocalDbgSessionProvider.cs} | 4 +- .../Local/LocalDbgSessionProviderFactory.cs | 2 +- .../Local/ProfilerLocalDbgSessionProvider.cs | 15 ++++-- 18 files changed, 158 insertions(+), 40 deletions(-) rename src/DebugTools/Profiler/Reader/{ => Config}/FileEtwProfilerReaderConfig.cs (100%) create mode 100644 src/DebugTools/Profiler/Reader/Config/FileXmlProfilerReaderConfig.cs rename src/DebugTools/Profiler/Reader/{ => Config}/IProfilerReaderConfig.cs (100%) rename src/DebugTools/Profiler/Reader/{ => Config}/LiveProfilerReaderConfig.cs (100%) create mode 100644 src/DebugTools/Profiler/Reader/FileXmlProfilerReader.cs create mode 100644 src/DebugTools/Profiler/Target/XmlFileProfilerTarget.cs delete mode 100644 src/DebugTools/Profiler/XmlFileProfilerSession.cs rename src/DebugTools/Session/Local/{InProessLocalDbgSessionProvider.cs => InProcessLocalDbgSessionProvider.cs} (85%) diff --git a/README.md b/README.md index 574a81a..49fa7fd 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ We can assume that the "gci" command we executed was passed as a string to a var ```powershell # Show the call stacks of any methods where the value "gci" was passed in (either as a raw string or # a sub-member). Only show the first invocation of each method, and exclude namespace gunk from the -# resulting output +# resulting output (Tip: you can also specify parameter -Simple which implies -Unique -ExcludeNamespace) C:\> Show-DbgProfilerStackTrace -StringValue gci -Unique -ExcludeNamespace ``` diff --git a/src/DebugTools.PowerShell/Cmdlets/Base/StackFrameCmdlet.cs b/src/DebugTools.PowerShell/Cmdlets/Base/StackFrameCmdlet.cs index f07e5bd..73f95d9 100644 --- a/src/DebugTools.PowerShell/Cmdlets/Base/StackFrameCmdlet.cs +++ b/src/DebugTools.PowerShell/Cmdlets/Base/StackFrameCmdlet.cs @@ -20,14 +20,19 @@ protected override void ProcessRecordEx() { var frames = Session.LastTrace; - foreach (var frame in frames) + if (frames == null) + WriteWarning("Last trace is empty. Did you forget to record a trace?"); + else { - Frame = frame.Root; + foreach (var frame in frames) + { + Frame = frame.Root; - if (ShouldIgnore()) - continue; + if (ShouldIgnore()) + continue; - DoProcessRecordEx(); + DoProcessRecordEx(); + } } } } diff --git a/src/DebugTools.PowerShell/Cmdlets/ExportDbgProfilerStackTrace.cs b/src/DebugTools.PowerShell/Cmdlets/ExportDbgProfilerStackTrace.cs index dde0652..3e9e7f4 100644 --- a/src/DebugTools.PowerShell/Cmdlets/ExportDbgProfilerStackTrace.cs +++ b/src/DebugTools.PowerShell/Cmdlets/ExportDbgProfilerStackTrace.cs @@ -26,9 +26,14 @@ protected override void ProcessRecordEx() frames.Add(Frame); else { - foreach (var frame in Session.LastTrace) + if (Session.LastTrace == null) + WriteWarning("Last trace is empty. Did you forget to record a trace?"); + else { - frames.Add(frame.Root); + foreach (var frame in Session.LastTrace) + { + frames.Add(frame.Root); + } } } } diff --git a/src/DebugTools.PowerShell/Cmdlets/ImportDbgProfilerStackTrace.cs b/src/DebugTools.PowerShell/Cmdlets/ImportDbgProfilerStackTrace.cs index f7b1139..e2eafb6 100644 --- a/src/DebugTools.PowerShell/Cmdlets/ImportDbgProfilerStackTrace.cs +++ b/src/DebugTools.PowerShell/Cmdlets/ImportDbgProfilerStackTrace.cs @@ -23,7 +23,8 @@ protected override void ProcessRecord() { var results = reader.Read(fs); - var session = new XmlFileProfilerSession(Path); + var session = new ProfilerSession(new FileXmlProfilerReaderConfig(Path)); + session.Start(default); var threads = new List(); diff --git a/src/DebugTools.PowerShell/Cmdlets/TraceDbgProfilerStack.cs b/src/DebugTools.PowerShell/Cmdlets/TraceDbgProfilerStack.cs index 16efb4d..79c58c4 100644 --- a/src/DebugTools.PowerShell/Cmdlets/TraceDbgProfilerStack.cs +++ b/src/DebugTools.PowerShell/Cmdlets/TraceDbgProfilerStack.cs @@ -6,6 +6,10 @@ namespace DebugTools.PowerShell.Cmdlets [Cmdlet(VerbsDiagnostic.Trace, "DbgProfilerStack")] public class TraceDbgProfilerStack : ProfilerSessionCmdlet { + public TraceDbgProfilerStack() : base(true) + { + } + protected override void ProcessRecordEx() { using (CtrlCHandler()) diff --git a/src/DebugTools/Memory/DllInjector.cs b/src/DebugTools/Memory/DllInjector.cs index f065a83..f1765fd 100644 --- a/src/DebugTools/Memory/DllInjector.cs +++ b/src/DebugTools/Memory/DllInjector.cs @@ -21,12 +21,6 @@ public class DllInjector private DataTargetMemoryStream dataTargetStream; private Dictionary peFiles = new Dictionary(); - public static int Test(string args) - { - Debug.WriteLine("success!"); - return 0; - } - public DllInjector(Process process) { this.process = process; diff --git a/src/DebugTools/Profiler/ProfilerSession.cs b/src/DebugTools/Profiler/ProfilerSession.cs index 5808d41..555bf88 100644 --- a/src/DebugTools/Profiler/ProfilerSession.cs +++ b/src/DebugTools/Profiler/ProfilerSession.cs @@ -10,9 +10,9 @@ namespace DebugTools.Profiler { public partial class ProfilerSession : IDisposable { - public virtual int? PID => Target.ProcessId; + public int? PID => Target.ProcessId; - public virtual string Name => Target.Name; + public string Name => Target.Name; public ProfilerSessionType Type { get; } @@ -80,6 +80,15 @@ public ProfilerSession(IProfilerReaderConfig config) switch (config.SessionType) { + case ProfilerSessionType.Normal: + case ProfilerSessionType.Global: + Reader = new LiveEtwProfilerReader((LiveProfilerReaderConfig) config); + break; + + case ProfilerSessionType.XmlFile: + Reader = new FileXmlProfilerReader((FileXmlProfilerReaderConfig) config); + break; + case ProfilerSessionType.EtwFile: Reader = new FileEtwProfilerReader((FileEtwProfilerReaderConfig) config); break; @@ -89,8 +98,7 @@ public ProfilerSession(IProfilerReaderConfig config) break; default: - Reader = new LiveEtwProfilerReader((LiveProfilerReaderConfig) config); - break; + throw new NotImplementedException($"Don't know how to handle {nameof(ProfilerSessionType)} '{config.SessionType}'."); } SetEventHandlers(); diff --git a/src/DebugTools/Profiler/Reader/FileEtwProfilerReaderConfig.cs b/src/DebugTools/Profiler/Reader/Config/FileEtwProfilerReaderConfig.cs similarity index 100% rename from src/DebugTools/Profiler/Reader/FileEtwProfilerReaderConfig.cs rename to src/DebugTools/Profiler/Reader/Config/FileEtwProfilerReaderConfig.cs diff --git a/src/DebugTools/Profiler/Reader/Config/FileXmlProfilerReaderConfig.cs b/src/DebugTools/Profiler/Reader/Config/FileXmlProfilerReaderConfig.cs new file mode 100644 index 0000000..3ee2dba --- /dev/null +++ b/src/DebugTools/Profiler/Reader/Config/FileXmlProfilerReaderConfig.cs @@ -0,0 +1,18 @@ +using System; + +namespace DebugTools.Profiler +{ + class FileXmlProfilerReaderConfig : IProfilerReaderConfig + { + public ProfilerSessionType SessionType => ProfilerSessionType.XmlFile; + + public ProfilerSetting[] Settings => Array.Empty(); + + public string Path { get; } + + public FileXmlProfilerReaderConfig(string path) + { + Path = path; + } + } +} diff --git a/src/DebugTools/Profiler/Reader/IProfilerReaderConfig.cs b/src/DebugTools/Profiler/Reader/Config/IProfilerReaderConfig.cs similarity index 100% rename from src/DebugTools/Profiler/Reader/IProfilerReaderConfig.cs rename to src/DebugTools/Profiler/Reader/Config/IProfilerReaderConfig.cs diff --git a/src/DebugTools/Profiler/Reader/LiveProfilerReaderConfig.cs b/src/DebugTools/Profiler/Reader/Config/LiveProfilerReaderConfig.cs similarity index 100% rename from src/DebugTools/Profiler/Reader/LiveProfilerReaderConfig.cs rename to src/DebugTools/Profiler/Reader/Config/LiveProfilerReaderConfig.cs diff --git a/src/DebugTools/Profiler/Reader/FileXmlProfilerReader.cs b/src/DebugTools/Profiler/Reader/FileXmlProfilerReader.cs new file mode 100644 index 0000000..1d69077 --- /dev/null +++ b/src/DebugTools/Profiler/Reader/FileXmlProfilerReader.cs @@ -0,0 +1,52 @@ +using System; +using DebugTools.Tracing; + +namespace DebugTools.Profiler +{ + class FileXmlProfilerReader : IProfilerReader + { + public IProfilerReaderConfig Config { get; } + + public FileXmlProfilerReader(FileXmlProfilerReaderConfig config) + { + Config = config; + } + + public void Initialize() => throw new NotImplementedException(); + + public void InitializeGlobal() => throw new NotImplementedException(); + + public void Execute() => throw new NotImplementedException(); + + public void Stop() => throw new NotImplementedException(); + + public IProfilerTarget CreateTarget() => new XmlFileProfilerTarget((FileXmlProfilerReaderConfig) Config); + + public void Dispose() + { + } + +#pragma warning disable CS0067 + public event Action MethodInfo; + public event Action MethodInfoDetailed; + public event Action ModuleLoaded; + public event Action CallEnter; + public event Action CallLeave; + public event Action Tailcall; + public event Action CallEnterDetailed; + public event Action CallLeaveDetailed; + public event Action TailcallDetailed; + public event Action ManagedToUnmanaged; + public event Action UnmanagedToManaged; + public event Action Exception; + public event Action ExceptionFrameUnwind; + public event Action ExceptionCompleted; + public event Action StaticFieldValue; + public event Action ThreadCreate; + public event Action ThreadDestroy; + public event Action ThreadName; + public event Action Shutdown; + public event Action Completed; +#pragma warning enable CS0067 + } +} diff --git a/src/DebugTools/Profiler/Target/EtwFileProfilerTarget.cs b/src/DebugTools/Profiler/Target/EtwFileProfilerTarget.cs index 9e3c87d..9a92103 100644 --- a/src/DebugTools/Profiler/Target/EtwFileProfilerTarget.cs +++ b/src/DebugTools/Profiler/Target/EtwFileProfilerTarget.cs @@ -6,7 +6,7 @@ namespace DebugTools.Profiler class EtwFileProfilerTarget : IProfilerTarget { public string Name => config.FileName; - public int? ProcessId { get; } + public int? ProcessId => null; public bool IsAlive => false; //Must be false so that GetImplicitProfilerSession doesn't see us private FileEtwProfilerReaderConfig config; diff --git a/src/DebugTools/Profiler/Target/XmlFileProfilerTarget.cs b/src/DebugTools/Profiler/Target/XmlFileProfilerTarget.cs new file mode 100644 index 0000000..c341e9c --- /dev/null +++ b/src/DebugTools/Profiler/Target/XmlFileProfilerTarget.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; + +namespace DebugTools.Profiler +{ + class XmlFileProfilerTarget : IProfilerTarget + { + public string Name => config.Path; + public int? ProcessId => null; + public bool IsAlive => false; + + private FileXmlProfilerReaderConfig config; + + public XmlFileProfilerTarget(FileXmlProfilerReaderConfig config) + { + this.config = config; + } + + public void Start(Action startCallback, CancellationToken cancellationToken) + { + } + + public void SetExitHandler(Action onTargetExit) + { + } + + public void ExecuteCommand(MessageType messageType, object value) + { + throw new NotSupportedException(); + } + + public void Dispose() + { + } + } +} diff --git a/src/DebugTools/Profiler/XmlFileProfilerSession.cs b/src/DebugTools/Profiler/XmlFileProfilerSession.cs deleted file mode 100644 index 5189c0d..0000000 --- a/src/DebugTools/Profiler/XmlFileProfilerSession.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace DebugTools.Profiler -{ - public class XmlFileProfilerSession : ProfilerSession - { - public override int? PID => null; - - public override string Name { get; } - - public XmlFileProfilerSession(string fileName) : base(new LiveProfilerReaderConfig(ProfilerSessionType.XmlFile, null)) - { - Name = fileName; - } - } -} diff --git a/src/DebugTools/Session/Local/InProessLocalDbgSessionProvider.cs b/src/DebugTools/Session/Local/InProcessLocalDbgSessionProvider.cs similarity index 85% rename from src/DebugTools/Session/Local/InProessLocalDbgSessionProvider.cs rename to src/DebugTools/Session/Local/InProcessLocalDbgSessionProvider.cs index 4030984..e5c30e7 100644 --- a/src/DebugTools/Session/Local/InProessLocalDbgSessionProvider.cs +++ b/src/DebugTools/Session/Local/InProcessLocalDbgSessionProvider.cs @@ -5,9 +5,9 @@ namespace DebugTools { - class InProessLocalDbgSessionProvider : LocalDbgSessionProvider + class InProcessLocalDbgSessionProvider : LocalDbgSessionProvider { - public InProessLocalDbgSessionProvider() : base(DbgSessionType.InProcess, "Process") + public InProcessLocalDbgSessionProvider() : base(DbgSessionType.InProcess, "Process") { } diff --git a/src/DebugTools/Session/Local/LocalDbgSessionProviderFactory.cs b/src/DebugTools/Session/Local/LocalDbgSessionProviderFactory.cs index 1834ef2..6902a49 100644 --- a/src/DebugTools/Session/Local/LocalDbgSessionProviderFactory.cs +++ b/src/DebugTools/Session/Local/LocalDbgSessionProviderFactory.cs @@ -21,7 +21,7 @@ public LocalDbgSessionProviderFactory() { typeof(LocalSOSProcess), new SOSLocalDbgSessionProvider() }, { typeof(ProfilerSession), new ProfilerLocalDbgSessionProvider() }, { typeof(LocalUiSession), new UiLocalDbgSessionProvider() }, - { typeof(InjectedHostSession), new InProessLocalDbgSessionProvider() } + { typeof(InjectedHostSession), new InProcessLocalDbgSessionProvider() } }; store = new LocalDbgSessionStore(); diff --git a/src/DebugTools/Session/Local/ProfilerLocalDbgSessionProvider.cs b/src/DebugTools/Session/Local/ProfilerLocalDbgSessionProvider.cs index 074edd1..8557175 100644 --- a/src/DebugTools/Session/Local/ProfilerLocalDbgSessionProvider.cs +++ b/src/DebugTools/Session/Local/ProfilerLocalDbgSessionProvider.cs @@ -67,7 +67,7 @@ public override ProfilerSession GetOrCreateSpecial(object context) else { if (profilerContext.Mandatory) - throw new InvalidOperationException("Attempted to retrieve a the global profiler session, however one did not exist"); + throw new InvalidOperationException("Attempted to retrieve the global profiler session, however one did not exist"); } return global; @@ -109,8 +109,7 @@ protected override bool TryGetFallbackSubSession(ProfilerSession[] dead, out Pro return true; } - //Special sessions are either file based or global. We don't want to allow implicitly returning file - //sessions, so we only check for global + //Special sessions are either file based or global. First, prefer global if it exists var global = specialSessions.SingleOrDefault(s => s.Type == ProfilerSessionType.Global); @@ -120,6 +119,16 @@ protected override bool TryGetFallbackSubSession(ProfilerSession[] dead, out Pro return true; } + //No global, we'll take anything we can get + + var special = specialSessions.FirstOrDefault(); + + if (special != null) + { + subSession = special; + return true; + } + subSession = null; return false; }