From a52ce0217671797e346be41a62a1cc2bfbd7f9d7 Mon Sep 17 00:00:00 2001 From: Mukul Sabharwal Date: Thu, 17 Sep 2020 22:52:05 -0700 Subject: [PATCH] Add Process Metadata Events --- .../ProcessMetadataEventSource.cs | 52 ++ .../ProcessMetadataTraceEventParser.cs | 683 ++++++++++++++++++ src/TraceEvent/TraceEvent.cs | 18 + src/TraceEvent/TraceLog.cs | 139 +++- 4 files changed, 866 insertions(+), 26 deletions(-) create mode 100644 src/TraceEvent/EventSources/ProcessMetadataEventSource.cs create mode 100644 src/TraceEvent/Parsers/ProcessMetadataTraceEventParser.cs diff --git a/src/TraceEvent/EventSources/ProcessMetadataEventSource.cs b/src/TraceEvent/EventSources/ProcessMetadataEventSource.cs new file mode 100644 index 000000000..9c74acf97 --- /dev/null +++ b/src/TraceEvent/EventSources/ProcessMetadataEventSource.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics.Tracing; + +namespace Microsoft.Diagnostics.Tracing +{ + [EventSource(Name = "ProcessMetadataEventSource")] + public sealed class ProcessMetadataEventSource : EventSource + { + public class Tasks + { + public const EventTask Process = (EventTask)1; + public const EventTask Thread = (EventTask)2; + public const EventTask Module = (EventTask)3; + } + + [Event(1, Opcode = EventOpcode.Start, Task = Tasks.Process)] + public void ProcessStart(long ProcessId, long ParentProcessId, string Executable, string CommandLine) + { + this.WriteEvent(1, ProcessId, ParentProcessId, Executable, CommandLine); + } + + [Event(2, Opcode = EventOpcode.Stop, Task = Tasks.Process)] + public void ProcessExit(long ProcessId, long ParentProcessId, int ExitCode, string Executable, string CommandLine) + { + this.WriteEvent(2, ProcessId, ParentProcessId, ExitCode, Executable, CommandLine); + } + + [Event(3, Opcode = EventOpcode.Start, Task = Tasks.Thread)] + public void ThreadCreate(long ProcessId, long ThreadId, ulong StackBaseAddress, string ThreadName) + { + this.WriteEvent(3, ProcessId, ThreadId, StackBaseAddress, ThreadName); + } + + [Event(4, Opcode = EventOpcode.Stop, Task = Tasks.Thread)] + public void ThreadDestroy(long ProcessId, long ThreadId, ulong StackBaseAddress, string ThreadName) + { + this.WriteEvent(4, ProcessId, ThreadId, StackBaseAddress, ThreadName); + } + + [Event(5, Opcode = EventOpcode.Start, Task = Tasks.Module)] + public void ModuleLoad(long ProcessId, ulong LoadAddress, long ModuleSize, Guid DebugGuid, int DebugAge, string ModuleFilePath, string DebugModuleFileName) + { + this.WriteEvent(5, ProcessId, LoadAddress, ModuleSize, DebugGuid, DebugAge, ModuleFilePath, DebugModuleFileName); + } + + [Event(6, Opcode = EventOpcode.Stop, Task = Tasks.Module)] + public void ModuleUnload(long ProcessId, long LoadAddress, long ModuleSize, string ModuleFilePath) + { + this.WriteEvent(6, ProcessId, LoadAddress, ModuleSize, ModuleFilePath); + } + } +} \ No newline at end of file diff --git a/src/TraceEvent/Parsers/ProcessMetadataTraceEventParser.cs b/src/TraceEvent/Parsers/ProcessMetadataTraceEventParser.cs new file mode 100644 index 000000000..4c7c5d062 --- /dev/null +++ b/src/TraceEvent/Parsers/ProcessMetadataTraceEventParser.cs @@ -0,0 +1,683 @@ +// +using System; +using System.Diagnostics; +using System.Diagnostics.Tracing; +using System.Text; +using Microsoft.Diagnostics.Tracing; +using Address = System.UInt64; + +#pragma warning disable 1591 // disable warnings on XML comments not being present + +// This code was automatically generated by the TraceParserGen tool, which converts +// an ETW event manifest into strongly typed C# classes. +namespace Microsoft.Diagnostics.Tracing.Parsers +{ + using Microsoft.Diagnostics.Tracing.Parsers.ProcessMetadataEventSource; + + [System.CodeDom.Compiler.GeneratedCode("traceparsergen", "2.0")] + public sealed class ProcessMetadataEventSourceTraceEventParser : TraceEventParser + { + public static string ProviderName = "ProcessMetadataEventSource"; + public static Guid ProviderGuid = new Guid(unchecked((int)0xa0aec25c), unchecked((short)0xe018), unchecked((short)0x5ee2), 0x59, 0x94, 0xb2, 0x88, 0x6f, 0xdb, 0x33, 0x3d); + + public enum Keywords : long + { + Session3 = 0x100000000000, + Session2 = 0x200000000000, + Session1 = 0x400000000000, + Session0 = 0x800000000000, + }; + + public ProcessMetadataEventSourceTraceEventParser(TraceEventSource source) : base(source) { } + + public event Action EventSourceMessage + { + add + { + RegisterTemplate(new EventSourceMessageArgsTraceData(value, 0, 65534, "EventSourceMessage", Guid.Empty, 0, "", ProviderGuid, ProviderName)); + } + remove + { + source.UnregisterEventTemplate(value, 0, Guid.Empty); + } + } + public event Action ModuleStart + { + add + { + RegisterTemplate(new ModuleLoadArgsTraceData(value, 5, 3, "Module", Guid.Empty, 1, "Start", ProviderGuid, ProviderName)); + } + remove + { + source.UnregisterEventTemplate(value, 5, Guid.Empty); + } + } + public event Action ModuleStop + { + add + { + RegisterTemplate(new ModuleUnloadArgsTraceData(value, 6, 3, "Module", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName)); + } + remove + { + source.UnregisterEventTemplate(value, 6, Guid.Empty); + } + } + public event Action ProcessStart + { + add + { + RegisterTemplate(new ProcessStartArgsTraceData(value, 1, 1, "Process", Guid.Empty, 1, "Start", ProviderGuid, ProviderName)); + } + remove + { + source.UnregisterEventTemplate(value, 1, Guid.Empty); + } + } + public event Action ProcessStop + { + add + { + RegisterTemplate(new ProcessExitArgsTraceData(value, 2, 1, "Process", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName)); + } + remove + { + source.UnregisterEventTemplate(value, 2, Guid.Empty); + } + } + public event Action ThreadStart + { + add + { + RegisterTemplate(new ThreadCreateArgsTraceData(value, 3, 2, "Thread", Guid.Empty, 1, "Start", ProviderGuid, ProviderName)); + } + remove + { + source.UnregisterEventTemplate(value, 3, Guid.Empty); + } + } + public event Action ThreadStop + { + add + { + RegisterTemplate(new ThreadDestroyArgsTraceData(value, 4, 2, "Thread", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName)); + } + remove + { + source.UnregisterEventTemplate(value, 4, Guid.Empty); + } + } + + #region private + protected override string GetProviderName() { return ProviderName; } + + static private EventSourceMessageArgsTraceData EventSourceMessageTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new EventSourceMessageArgsTraceData(action, 0, 65534, "EventSourceMessage", Guid.Empty, 0, "", ProviderGuid, ProviderName); + } + static private ModuleLoadArgsTraceData ModuleStartTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new ModuleLoadArgsTraceData(action, 5, 3, "Module", Guid.Empty, 1, "Start", ProviderGuid, ProviderName); + } + static private ModuleUnloadArgsTraceData ModuleStopTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new ModuleUnloadArgsTraceData(action, 6, 3, "Module", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName); + } + static private ProcessStartArgsTraceData ProcessStartTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new ProcessStartArgsTraceData(action, 1, 1, "Process", Guid.Empty, 1, "Start", ProviderGuid, ProviderName); + } + static private ProcessExitArgsTraceData ProcessStopTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new ProcessExitArgsTraceData(action, 2, 1, "Process", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName); + } + static private ThreadCreateArgsTraceData ThreadStartTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new ThreadCreateArgsTraceData(action, 3, 2, "Thread", Guid.Empty, 1, "Start", ProviderGuid, ProviderName); + } + static private ThreadDestroyArgsTraceData ThreadStopTemplate(Action action) + { // action, eventid, taskid, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName + return new ThreadDestroyArgsTraceData(action, 4, 2, "Thread", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName); + } + + static private volatile TraceEvent[] s_templates; + protected internal override void EnumerateTemplates(Func eventsToObserve, Action callback) + { + if (s_templates == null) + { + var templates = new TraceEvent[7]; + templates[0] = new EventSourceMessageArgsTraceData(null, 0, 65534, "EventSourceMessage", Guid.Empty, 0, "", ProviderGuid, ProviderName); + templates[1] = new ProcessStartArgsTraceData(null, 1, 1, "Process", Guid.Empty, 1, "Start", ProviderGuid, ProviderName); + templates[2] = new ProcessExitArgsTraceData(null, 2, 1, "Process", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName); + templates[3] = new ThreadCreateArgsTraceData(null, 3, 2, "Thread", Guid.Empty, 1, "Start", ProviderGuid, ProviderName); + templates[4] = new ThreadDestroyArgsTraceData(null, 4, 2, "Thread", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName); + templates[5] = new ModuleLoadArgsTraceData(null, 5, 3, "Module", Guid.Empty, 1, "Start", ProviderGuid, ProviderName); + templates[6] = new ModuleUnloadArgsTraceData(null, 6, 3, "Module", Guid.Empty, 2, "Stop", ProviderGuid, ProviderName); + s_templates = templates; + } + foreach (var template in s_templates) + if (eventsToObserve == null || eventsToObserve(template.ProviderName, template.EventName) == EventFilterResponse.AcceptEvent) + callback(template); + } + + private void RegisterTemplate(TraceEvent template) + { + Debug.Assert(template.ProviderGuid == MicrosoftAntimalwareEngineTraceEventParser.ProviderGuid); + source.RegisterEventTemplate(template); + } + + #endregion + } +} + +namespace Microsoft.Diagnostics.Tracing.Parsers.ProcessMetadataEventSource +{ + public sealed class EventSourceMessageArgsTraceData : TraceEvent + { + public string message { get { return GetUnicodeStringAt(0); } } + + #region Private + internal EventSourceMessageArgsTraceData(Action action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + Action = action; + } + protected internal override void Dispatch() + { + Action(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != SkipUnicodeString(0))); + Debug.Assert(!(Version > 0 && EventDataLength < SkipUnicodeString(0))); + } + protected internal override Delegate Target + { + get { return Action; } + set { Action = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "message", message); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "message" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return message; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + public static ulong GetKeywords() { return 0; } + public static string GetProviderName() { return "ProcessMetadataEventSource"; } + public static Guid GetProviderGuid() { return new Guid("a0aec25c-e018-5ee2-5994-b2886fdb333d"); } + private event Action Action; + #endregion + } + public sealed class ModuleLoadArgsTraceData : TraceEvent + { + public long ProcessId { get { return GetInt64At(0); } } + public long LoadAddress { get { return GetInt64At(8); } } + public long ModuleSize { get { return GetInt64At(16); } } + public Guid DebugGuid { get { return GetGuidAt(24); } } + public int DebugAge { get { return GetInt32At(40); } } + public string ModuleFilePath { get { return GetUnicodeStringAt(44); } } + public string DebugModuleFileName { get { return GetUnicodeStringAt(SkipUnicodeString(44)); } } + + #region Private + internal ModuleLoadArgsTraceData(Action action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + Action = action; + } + protected internal override void Dispatch() + { + Action(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != SkipUnicodeString(SkipUnicodeString(44)))); + Debug.Assert(!(Version > 0 && EventDataLength < SkipUnicodeString(SkipUnicodeString(44)))); + } + protected internal override Delegate Target + { + get { return Action; } + set { Action = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "ProcessId", ProcessId); + XmlAttrib(sb, "LoadAddress", LoadAddress); + XmlAttrib(sb, "ModuleSize", ModuleSize); + XmlAttrib(sb, "DebugGuid", DebugGuid); + XmlAttrib(sb, "DebugAge", DebugAge); + XmlAttrib(sb, "ModuleFilePath", ModuleFilePath); + XmlAttrib(sb, "DebugModuleFileName", DebugModuleFileName); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "ProcessId", "LoadAddress", "ModuleSize", "DebugGuid", "DebugAge", "ModuleFilePath", "DebugModuleFileName" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return ProcessId; + case 1: + return LoadAddress; + case 2: + return ModuleSize; + case 3: + return DebugGuid; + case 4: + return DebugAge; + case 5: + return ModuleFilePath; + case 6: + return DebugModuleFileName; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + public static ulong GetKeywords() { return 0; } + public static string GetProviderName() { return "ProcessMetadataEventSource"; } + public static Guid GetProviderGuid() { return new Guid("a0aec25c-e018-5ee2-5994-b2886fdb333d"); } + private event Action Action; + #endregion + } + public sealed class ModuleUnloadArgsTraceData : TraceEvent + { + public long ProcessId { get { return GetInt64At(0); } } + public long LoadAddress { get { return GetInt64At(8); } } + public long ModuleSize { get { return GetInt64At(16); } } + public string ModuleFilePath { get { return GetUnicodeStringAt(24); } } + + #region Private + internal ModuleUnloadArgsTraceData(Action action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + Action = action; + } + protected internal override void Dispatch() + { + Action(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != SkipUnicodeString(24))); + Debug.Assert(!(Version > 0 && EventDataLength < SkipUnicodeString(24))); + } + protected internal override Delegate Target + { + get { return Action; } + set { Action = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "ProcessId", ProcessId); + XmlAttrib(sb, "LoadAddress", LoadAddress); + XmlAttrib(sb, "ModuleSize", ModuleSize); + XmlAttrib(sb, "ModuleFilePath", ModuleFilePath); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "ProcessId", "LoadAddress", "ModuleSize", "ModuleFilePath" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return ProcessId; + case 1: + return LoadAddress; + case 2: + return ModuleSize; + case 3: + return ModuleFilePath; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + public static ulong GetKeywords() { return 0; } + public static string GetProviderName() { return "ProcessMetadataEventSource"; } + public static Guid GetProviderGuid() { return new Guid("a0aec25c-e018-5ee2-5994-b2886fdb333d"); } + private event Action Action; + #endregion + } + public sealed class ProcessStartArgsTraceData : TraceEvent + { + public long ProcessId { get { return GetInt64At(0); } } + public long ParentProcessId { get { return GetInt64At(8); } } + public string Executable { get { return GetUnicodeStringAt(16); } } + public string CommandLine { get { return GetUnicodeStringAt(SkipUnicodeString(16)); } } + + #region Private + internal ProcessStartArgsTraceData(Action action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + Action = action; + } + protected internal override void Dispatch() + { + Action(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != SkipUnicodeString(SkipUnicodeString(16)))); + Debug.Assert(!(Version > 0 && EventDataLength < SkipUnicodeString(SkipUnicodeString(16)))); + } + protected internal override Delegate Target + { + get { return Action; } + set { Action = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "ProcessId", ProcessId); + XmlAttrib(sb, "ParentProcessId", ParentProcessId); + XmlAttrib(sb, "Executable", Executable); + XmlAttrib(sb, "CommandLine", CommandLine); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "ProcessId", "ParentProcessId", "Executable", "CommandLine" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return ProcessId; + case 1: + return ParentProcessId; + case 2: + return Executable; + case 3: + return CommandLine; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + public static ulong GetKeywords() { return 0; } + public static string GetProviderName() { return "ProcessMetadataEventSource"; } + public static Guid GetProviderGuid() { return new Guid("a0aec25c-e018-5ee2-5994-b2886fdb333d"); } + private event Action Action; + #endregion + } + public sealed class ProcessExitArgsTraceData : TraceEvent + { + public long ProcessId { get { return GetInt64At(0); } } + public long ParentProcessId { get { return GetInt64At(8); } } + public int ExitCode { get { return GetInt32At(16); } } + public string Executable { get { return GetUnicodeStringAt(20); } } + public string CommandLine { get { return GetUnicodeStringAt(SkipUnicodeString(20)); } } + + #region Private + internal ProcessExitArgsTraceData(Action action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + Action = action; + } + protected internal override void Dispatch() + { + Action(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != SkipUnicodeString(SkipUnicodeString(20)))); + Debug.Assert(!(Version > 0 && EventDataLength < SkipUnicodeString(SkipUnicodeString(20)))); + } + protected internal override Delegate Target + { + get { return Action; } + set { Action = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "ProcessId", ProcessId); + XmlAttrib(sb, "ParentProcessId", ParentProcessId); + XmlAttrib(sb, "ExitCode", ExitCode); + XmlAttrib(sb, "Executable", Executable); + XmlAttrib(sb, "CommandLine", CommandLine); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "ProcessId", "ParentProcessId", "ExitCode", "Executable", "CommandLine" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return ProcessId; + case 1: + return ParentProcessId; + case 2: + return ExitCode; + case 3: + return Executable; + case 4: + return CommandLine; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + public static ulong GetKeywords() { return 0; } + public static string GetProviderName() { return "ProcessMetadataEventSource"; } + public static Guid GetProviderGuid() { return new Guid("a0aec25c-e018-5ee2-5994-b2886fdb333d"); } + private event Action Action; + #endregion + } + public sealed class ThreadCreateArgsTraceData : TraceEvent + { + public long ProcessId { get { return GetInt64At(0); } } + public long ThreadId { get { return GetInt64At(8); } } + public long StackBaseAddress { get { return GetInt64At(16); } } + public string ThreadName { get { return GetUnicodeStringAt(24); } } + + #region Private + internal ThreadCreateArgsTraceData(Action action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + Action = action; + } + protected internal override void Dispatch() + { + Action(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != SkipUnicodeString(24))); + Debug.Assert(!(Version > 0 && EventDataLength < SkipUnicodeString(24))); + } + protected internal override Delegate Target + { + get { return Action; } + set { Action = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "ProcessId", ProcessId); + XmlAttrib(sb, "ThreadId", ThreadId); + XmlAttrib(sb, "StackBaseAddress", StackBaseAddress); + XmlAttrib(sb, "ThreadName", ThreadName); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "ProcessId", "ThreadId", "StackBaseAddress", "ThreadName" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return ProcessId; + case 1: + return ThreadId; + case 2: + return StackBaseAddress; + case 3: + return ThreadName; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + public static ulong GetKeywords() { return 0; } + public static string GetProviderName() { return "ProcessMetadataEventSource"; } + public static Guid GetProviderGuid() { return new Guid("a0aec25c-e018-5ee2-5994-b2886fdb333d"); } + private event Action Action; + #endregion + } + public sealed class ThreadDestroyArgsTraceData : TraceEvent + { + public long ProcessId { get { return GetInt64At(0); } } + public long ThreadId { get { return GetInt64At(8); } } + public long StackBaseAddress { get { return GetInt64At(16); } } + public string ThreadName { get { return GetUnicodeStringAt(24); } } + + #region Private + internal ThreadDestroyArgsTraceData(Action action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) + : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName) + { + Action = action; + } + protected internal override void Dispatch() + { + Action(this); + } + protected internal override void Validate() + { + Debug.Assert(!(Version == 0 && EventDataLength != SkipUnicodeString(24))); + Debug.Assert(!(Version > 0 && EventDataLength < SkipUnicodeString(24))); + } + protected internal override Delegate Target + { + get { return Action; } + set { Action = (Action)value; } + } + public override StringBuilder ToXml(StringBuilder sb) + { + Prefix(sb); + XmlAttrib(sb, "ProcessId", ProcessId); + XmlAttrib(sb, "ThreadId", ThreadId); + XmlAttrib(sb, "StackBaseAddress", StackBaseAddress); + XmlAttrib(sb, "ThreadName", ThreadName); + sb.Append("/>"); + return sb; + } + + public override string[] PayloadNames + { + get + { + if (payloadNames == null) + payloadNames = new string[] { "ProcessId", "ThreadId", "StackBaseAddress", "ThreadName" }; + return payloadNames; + } + } + + public override object PayloadValue(int index) + { + switch (index) + { + case 0: + return ProcessId; + case 1: + return ThreadId; + case 2: + return StackBaseAddress; + case 3: + return ThreadName; + default: + Debug.Assert(false, "Bad field index"); + return null; + } + } + + public static ulong GetKeywords() { return 0; } + public static string GetProviderName() { return "ProcessMetadataEventSource"; } + public static Guid GetProviderGuid() { return new Guid("a0aec25c-e018-5ee2-5994-b2886fdb333d"); } + private event Action Action; + #endregion + } +} diff --git a/src/TraceEvent/TraceEvent.cs b/src/TraceEvent/TraceEvent.cs index 157fa0b53..8dffd7c94 100644 --- a/src/TraceEvent/TraceEvent.cs +++ b/src/TraceEvent/TraceEvent.cs @@ -194,6 +194,23 @@ public KernelTraceEventParser Kernel } } + /// + /// For convenience, we provide a property returns a ProcessMetadataTraceEventParser that knows + /// how to parse all the Process Metadata events into callbacks. + /// + public ProcessMetadataEventSourceTraceEventParser ProcessMetadata + { + get + { + if (_ProcessMetadata == null) + { + _ProcessMetadata = new ProcessMetadataEventSourceTraceEventParser(this); + } + + return _ProcessMetadata; + } + } + #if !NOT_WINDOWS && !NO_DYNAMIC_TRACEEVENTPARSER /// /// For convenience, we provide a property returns a DynamicTraceEventParser that knows @@ -422,6 +439,7 @@ string ITraceParserServices.ProviderNameForGuid(Guid taskOrProviderGuid) internal /*protected*/ bool useClassicETW; internal /*protected*/ ClrTraceEventParser _CLR; internal /*protected*/ KernelTraceEventParser _Kernel; + internal /*protected*/ ProcessMetadataEventSourceTraceEventParser _ProcessMetadata; #if !NOT_WINDOWS && !NO_DYNAMIC_TRACEEVENTPARSER internal /*protected*/ DynamicTraceEventParser _Dynamic; internal /*protected*/ RegisteredTraceEventParser _Registered; diff --git a/src/TraceEvent/TraceLog.cs b/src/TraceEvent/TraceLog.cs index d6586c568..c59a27a73 100644 --- a/src/TraceEvent/TraceLog.cs +++ b/src/TraceEvent/TraceLog.cs @@ -30,6 +30,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Microsoft.Diagnostics.Tracing.Parsers.ProcessMetadataEventSource; using Address = System.UInt64; @@ -899,6 +900,7 @@ internal static void CreateFromTraceEventSource(TraceEventDispatcher source, str var dynamicParser = source.Dynamic; var clrParser = source.Clr; var kernelParser = source.Kernel; + var processMetadataParser = source.ProcessMetadata; // Get all the users data from the original source. Note that this happens by reference, which means // that even though we have not built up the state yet (since we have not scanned the data yet), it will @@ -1135,13 +1137,13 @@ private unsafe void SetupCallbacks(TraceEventDispatcher rawEvents) // Process level events. kernelParser.ProcessStartGroup += delegate (ProcessTraceData data) { - processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC, data.Opcode == TraceEventOpcode.Start).ProcessStart(data); + processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC, data.Opcode == TraceEventOpcode.Start).ProcessStart(data, data.CommandLine, data.ImageFileName, data.ParentID); // Don't filter them out (not that many, useful for finding command line) }; kernelParser.ProcessEndGroup += delegate (ProcessTraceData data) { - processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC).ProcessEnd(data); + processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC).ProcessEnd(data, data.CommandLine, data.ImageFileName, data.ParentID, data.ExitStatus); // Don't filter them out (not that many, useful for finding command line) }; // Thread level events @@ -1234,7 +1236,7 @@ private unsafe void SetupCallbacks(TraceEventDispatcher rawEvents) } } - var moduleFile = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC).LoadedModules.ImageLoadOrUnload(data, isLoad, fileName); + var moduleFile = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC).LoadedModules.ImageLoadOrUnload(data, data.ImageBase, data.ImageSize, isLoad, fileName); // TODO review: is using the timestamp the best way to make the association if (lastDbgData != null && data.TimeStampQPC == lastDbgData.TimeStampQPC) { @@ -1786,6 +1788,95 @@ private unsafe void SetupCallbacks(TraceEventDispatcher rawEvents) sampleProfileInterval100ns = data.OldInterval; } }; + + var processMetadataParser = rawEvents.ProcessMetadata; + + processMetadataParser.ProcessStart += delegate(ProcessStartArgsTraceData data) + { + Processes.GetOrCreateProcess((int)data.ProcessId, data.TimeStampQPC, isProcessStartEvent: true).ProcessStart(data, data.CommandLine, data.Executable, (int)data.ParentProcessId); + }; + + processMetadataParser.ProcessStop += delegate (ProcessExitArgsTraceData data) + { + Processes.GetOrCreateProcess((int)data.ProcessId, data.TimeStampQPC).ProcessEnd(data, data.CommandLine, data.Executable, (int)data.ParentProcessId, data.ExitCode); + }; + + processMetadataParser.ThreadStart += delegate (ThreadCreateArgsTraceData data) + { + TraceProcess process = processes.GetOrCreateProcess((int)data.ProcessId, data.TimeStampQPC); + thread = Threads.GetOrCreateThread((int)data.ThreadId, data.TimeStampQPC, process, data.Opcode == TraceEventOpcode.Start || data.Opcode == TraceEventOpcode.DataCollectionStart); + thread.startTimeQPC = data.TimeStampQPC; + thread.userStackBase = (Address)data.StackBaseAddress; + if (data.Opcode == TraceEventOpcode.DataCollectionStart) + { + bookKeepingEvent = true; + thread.startTimeQPC = sessionStartTimeQPC; + } + else if (data.Opcode == TraceEventOpcode.Start) + { + var threadProc = thread.Process; + if (!threadProc.anyThreads) + { + // We saw a real process start (not a DCStart or a non at all) + if (sessionStartTimeQPC < threadProc.startTimeQPC && threadProc.startTimeQPC < data.TimeStampQPC) + { + thread.threadInfo = "Startup Thread"; + } + + threadProc.anyThreads = true; + } + } + }; + + processMetadataParser.ThreadStop += delegate (ThreadDestroyArgsTraceData data) + { + TraceProcess process = processes.GetOrCreateProcess((int)data.ProcessId, data.TimeStampQPC); + thread = Threads.GetOrCreateThread((int)data.ThreadId, data.TimeStampQPC, process); + if (thread.process == null) + { + thread.process = process; + } + + if (data.ThreadName.Length > 0) + { + CategorizeThread(data, data.ThreadName); + } + + Debug.Assert(thread.process == process, "Different events disagree on the process object!"); + DebugWarn(thread.endTimeQPC == long.MaxValue || thread.ThreadID == 0, "Thread end on a terminated thread " + data.ThreadId + " that ended at " + QPCTimeToRelMSec(thread.endTimeQPC), data); + DebugWarn(thread.Process.endTimeQPC == long.MaxValue, "Thread ending on ended process", data); + thread.endTimeQPC = data.TimeStampQPC; + thread.userStackBase = (Address)data.StackBaseAddress; + if (data.Opcode == TraceEventOpcode.DataCollectionStop) + { + thread.endTimeQPC = sessionEndTimeQPC; + bookKeepingEvent = true; + bookeepingEventThatMayHaveStack = true; + } + + // Keep threadIDtoThread table under control by removing old entries. + if (IsRealTime) + { + Threads.threadIDtoThread.Remove((Address)data.ThreadId); + } + }; + + processMetadataParser.ModuleStart += delegate(ModuleLoadArgsTraceData data) + { + var isLoad = (data.Opcode == (TraceEventOpcode)10) || (data.Opcode == TraceEventOpcode.DataCollectionStart); + + var moduleFile = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC).LoadedModules.ImageLoadOrUnload(data, (ulong)data.LoadAddress, (int)data.ModuleSize, isLoad, data.ModuleFilePath); + moduleFile.pdbName = data.DebugModuleFileName; + moduleFile.pdbSignature = data.DebugGuid; + moduleFile.pdbAge = data.DebugAge; + hasPdbInfo = true; + }; + + processMetadataParser.ModuleStop += delegate (ModuleUnloadArgsTraceData data) + { + var isLoad = (data.Opcode == (TraceEventOpcode)10) || (data.Opcode == TraceEventOpcode.DataCollectionStart); + var moduleFile = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC).LoadedModules.ImageLoadOrUnload(data, (ulong)data.LoadAddress, (int)data.ModuleSize, isLoad, data.ModuleFilePath); + }; } /// @@ -5566,7 +5657,7 @@ public override string ToString() // #ProcessHandlersCalledFromTraceLog // // called from TraceLog.CopyRawEvents - internal void ProcessStart(ProcessTraceData data) + internal void ProcessStart(TraceEvent data, string cmdLine, string moduleFileName, int parentId) { Log.DebugWarn(parentID == 0, "Events for process happen before process start. PrevEventTime: " + StartTimeRelativeMsec.ToString("f4"), data); @@ -5580,21 +5671,21 @@ internal void ProcessStart(ProcessTraceData data) Debug.Assert(endTimeQPC == long.MaxValue); // We would create a new Process record otherwise startTimeQPC = data.TimeStampQPC; } - commandLine = data.CommandLine; - imageFileName = data.ImageFileName; - parentID = data.ParentID; + commandLine = cmdLine; + imageFileName = moduleFileName; + parentID = parentId; } - internal void ProcessEnd(ProcessTraceData data) + internal void ProcessEnd(TraceEvent data, string cmdLine, string moduleFileName, int parentId, int exitCode) { if (commandLine.Length == 0) { - commandLine = data.CommandLine; + commandLine = cmdLine; } - imageFileName = data.ImageFileName; // Always overwrite as we might have guessed via the image loads - if (parentID == 0 && data.ParentID != 0) + imageFileName = moduleFileName; // Always overwrite as we might have guessed via the image loads + if (parentID == 0 && parentId != 0) { - parentID = data.ParentID; + parentID = parentId; } if (data.Opcode != TraceEventOpcode.DataCollectionStop) @@ -5603,7 +5694,7 @@ internal void ProcessEnd(ProcessTraceData data) // Only set the exit code if it really is a process exit (not a DCStop). if (data.Opcode == TraceEventOpcode.Stop) { - exitStatus = data.ExitStatus; + exitStatus = exitCode; } endTimeQPC = data.TimeStampQPC; @@ -6523,26 +6614,22 @@ internal TraceLoadedModule GetLoadedModule(string fileName, long timeQPC) } // #ModuleHandlersCalledFromTraceLog - internal TraceModuleFile ImageLoadOrUnload(ImageLoadTraceData data, bool isLoad, string dataFileName = null) + internal TraceModuleFile ImageLoadOrUnload(TraceEvent data, Address imageBase, int imageSize, bool isLoad, string dataFileName) { int index; - if (dataFileName == null) - { - dataFileName = data.FileName; - } - TraceLoadedModule module = FindModuleAndIndexContainingAddress(data.ImageBase, data.TimeStampQPC, out index); + TraceLoadedModule module = FindModuleAndIndexContainingAddress(imageBase, data.TimeStampQPC, out index); if (module == null) { // We need to make a new module - TraceModuleFile newModuleFile = process.Log.ModuleFiles.GetOrCreateModuleFile(dataFileName, data.ImageBase); - newModuleFile.imageSize = data.ImageSize; - module = new TraceLoadedModule(process, newModuleFile, data.ImageBase); + TraceModuleFile newModuleFile = process.Log.ModuleFiles.GetOrCreateModuleFile(dataFileName, imageBase); + newModuleFile.imageSize = imageSize; + module = new TraceLoadedModule(process, newModuleFile, imageBase); InsertAndSetOverlap(index + 1, module); } // If we load a module higher than 32 bits can do, then we must be a 64 bit process. - if (!process.loadedAModuleHigh && (ulong)data.ImageBase >= 0x100000000L) + if (!process.loadedAModuleHigh && (ulong)imageBase >= 0x100000000L) { // On win8 ntdll gets loaded into 32 bit processes so ignore it if (!dataFileName.EndsWith("ntdll.dll", StringComparison.OrdinalIgnoreCase)) @@ -6569,14 +6656,14 @@ internal TraceModuleFile ImageLoadOrUnload(ImageLoadTraceData data, bool isLoad, int start2 = dataFileName.Length - len; process.Log.DebugWarn(string.Compare(module.ModuleFile.FilePath, start1, dataFileName, start2, len, StringComparison.OrdinalIgnoreCase) == 0, "Filename Load/Unload mismatch.\r\n FILE1: " + module.ModuleFile.FilePath, data); - process.Log.DebugWarn(module.ModuleFile.ImageSize == 0 || module.ModuleFile.ImageSize == data.ImageSize, + process.Log.DebugWarn(module.ModuleFile.ImageSize == 0 || module.ModuleFile.ImageSize == imageSize, "ImageSize not consistent over all Loads Size 0x" + module.ModuleFile.ImageSize.ToString("x"), data); /* TODO this one fails. decide what to do about it. process.Log.DebugWarn(module.ModuleFile.DefaultBase == 0 || module.ModuleFile.DefaultBase == data.DefaultBase, "DefaultBase not consistent over all Loads Size 0x" + module.ModuleFile.DefaultBase.ToString("x"), data); ***/ - moduleFile.imageSize = data.ImageSize; + moduleFile.imageSize = imageSize; if (isLoad) { process.Log.DebugWarn(module.loadTimeQPC == 0 || data.Opcode == TraceEventOpcode.DataCollectionStart, "Events for module happened before load. PrevEventTime: " + module.LoadTimeRelativeMSec.ToString("f4"), data); @@ -6610,7 +6697,7 @@ internal TraceModuleFile ImageLoadOrUnload(ImageLoadTraceData data, bool isLoad, } // Look for all code addresses those that don't have modules that are in my range are assumed to be mine. - Process.Log.CodeAddresses.ForAllUnresolvedCodeAddressesInRange(process, data.ImageBase, data.ImageSize, false, + Process.Log.CodeAddresses.ForAllUnresolvedCodeAddressesInRange(process, imageBase, imageSize, false, delegate (ref Microsoft.Diagnostics.Tracing.Etlx.TraceCodeAddresses.CodeAddressInfo info) { info.SetModuleFileIndex(moduleFile);