-
Notifications
You must be signed in to change notification settings - Fork 712
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Process Metadata Events #1276
base: main
Are you sure you want to change the base?
Conversation
2a29458
to
a52ce02
Compare
@brianrob Could you take a look at this one? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @mjsabby. Some comments and questions.
} | ||
|
||
[Event(2, Opcode = EventOpcode.Stop, Task = Tasks.Process)] | ||
public void ProcessExit(long ProcessId, long ParentProcessId, int ExitCode, string Executable, string CommandLine) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Can we call this ProcessStop
to match the start/stop naming guidance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
[EventSource(Name = "ProcessMetadataEventSource")] | ||
public sealed class ProcessMetadataEventSource : EventSource | ||
{ | ||
public class Tasks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if we need them yet, but should we have keywords for Process, Thread, and Module?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I intentionally steered away from them, because it would seem like process metadata is a sort of monolith. I'm happy to add them now or later if you see it differently.
} | ||
|
||
[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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the DebugGuid a PDBGuid, or were you going for a cross-platform representation, and avoiding the term PDB? If so, is Guid used on Linux? I know for shared objects it's the build id.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As luck has it, ELF Debug IDs are 20-bytes. so I contemplated making this a byte[] but then thought about MachO which is also a GUID without any age, so I thought we could encode it as 0. And we could hack into the ELF IDs as guid + age to take the 20 bytes.
I just picked this and not a byte array because it was more convenient. @noahfalk thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha - yes, interested in @noahfalk's thoughts here. Do we have any other precedent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be tempted to encode it as a byte[] or string. Fixed size types would be a little more performant but given these shouldn't be frequent events I'd favor an encoding that feels straightforward.
If the goal is to be able to look up the images and symbols on a symbol server then the data we need is:
format - PE/ELF/MachO
- PE - filesize, timestamp, filename, debug signature, age, and the codeview debug directory major/minor version (major=0x100,minor=504D is a sentinel value that indicates portable pdb is supported - PE spec).
- ELF - build-id and filename
- MachO - uuid and name
} | ||
|
||
[Event(3, Opcode = EventOpcode.Start, Task = Tasks.Thread)] | ||
public void ThreadCreate(long ProcessId, long ThreadId, ulong StackBaseAddress, string ThreadName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: For the process and thread events, I'm interpreting that these events are for when the action occurs (e.g. when a thread is created we trigger a ThreadCreate event). What about cases where the thread already existed when the trace starts. Do we need a way to encapsulate that? I know that you're looking to avoid rundown - do you have a recommendation on how to handle this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rundown events or capture state events would have the same format, so I thought we could overload this, but I can also just add a specific thing for "Rundown", or maybe a more neutral term for catch up events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think it would be worth knowing if the thread has existed longer than the trace or not. Perhaps something similar to Thread/DCStart? Also, if you don't mind, let's change to Thread/Start and Thread/Stop top match existing precedent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a way to encapsulate that? I know that you're looking to avoid rundown - do you have a recommendation on how to handle this?
I agree that it would be worthwhile to distinguish information that is snapshotting the current state of the system from events that are indicating a state change is occuring at a specific point in time. Differently named events are certainly one way to represent that but I am going to suggest we hold off exploring too deeply before we've come to some decision whether we want the capability to slice regions out of a trace file and process the events in that time range in isolation. That slicing functionality seems like a pretty useful capability generally and I know its something Bing in particular benefits from, but it doesn't appear to play well with rundown style approaches that place a large number of stateful events at only a single point in the file. I think most formats that do support that kind of slicing wind up having something like keyframes, up-to-date snapshots of information that are repeated on a regular cadence.
|
||
processMetadataParser.ThreadStart += delegate (ThreadCreateArgsTraceData data) | ||
{ | ||
TraceProcess process = processes.GetOrCreateProcess((int)data.ProcessId, data.TimeStampQPC); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we work these more complicated Threading and module load events into helper functions and then just call them from here and from the ETW equivalents? There's enough here that it would be nice to only have one version of these.
@@ -30,6 +30,7 @@ | |||
using System.Text.RegularExpressions; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using Microsoft.Diagnostics.Tracing.Parsers.ProcessMetadataEventSource; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that in one of your other PRs, you had done some work to expose process name inside of the EventPipe
source. Do you think that would be worth plumbing in here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@noahfalk can you take a look at this as well? Would like to get your thoughts, especially on the moduleload abstractions. |
Its in my todo list now : ) |
namespace Microsoft.Diagnostics.Tracing | ||
{ | ||
[EventSource(Name = "ProcessMetadataEventSource")] | ||
public sealed class ProcessMetadataEventSource : EventSource |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this EventSource ever get created or invoked when I run TraceEvent? I couldn't find any code elsewhere that used it though I suspect you needed it to create the parser generated code?
It is still useful to use as a reference for what would get emitted that we can talk about, but if its only role was going to a reference then maybe we can put the code somewhere else? @brianrob do we have any precedent around this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is only for reference.
|
||
namespace Microsoft.Diagnostics.Tracing | ||
{ | ||
[EventSource(Name = "ProcessMetadataEventSource")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name typically wouldn't have "EventSource" at the end of it. Most of our naming conventions either do Microsoft-bla-bla or we use a dotted namespace name. Neither of these feel appropriate here however so I am trying to think what we should do. I don't think we need to block on it though.
} | ||
|
||
[Event(1, Opcode = EventOpcode.Start, Task = Tasks.Process)] | ||
public void ProcessStart(long ProcessId, long ParentProcessId, string Executable, string CommandLine) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does CommandLine include the executable? If it does do we need Executable as a separate field?
} | ||
|
||
[Event(3, Opcode = EventOpcode.Start, Task = Tasks.Thread)] | ||
public void ThreadCreate(long ProcessId, long ThreadId, ulong StackBaseAddress, string ThreadName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a way to encapsulate that? I know that you're looking to avoid rundown - do you have a recommendation on how to handle this?
I agree that it would be worthwhile to distinguish information that is snapshotting the current state of the system from events that are indicating a state change is occuring at a specific point in time. Differently named events are certainly one way to represent that but I am going to suggest we hold off exploring too deeply before we've come to some decision whether we want the capability to slice regions out of a trace file and process the events in that time range in isolation. That slicing functionality seems like a pretty useful capability generally and I know its something Bing in particular benefits from, but it doesn't appear to play well with rundown style approaches that place a large number of stateful events at only a single point in the file. I think most formats that do support that kind of slicing wind up having something like keyframes, up-to-date snapshots of information that are repeated on a regular cadence.
[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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a definition of what string to expect in ThreadName? For example .Net has a Thread.Name property and Linux has pthread_getname_np() but I don't know if the strings returned by those APIs are guaranteed to be equal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting a name change? I suppose it could be either, but I think adding two thread name events would be overkill.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am fine with the argument name (and keeping it singular). I am just trying to sort out what data do we expect to be provided? On Windows we have precedent from the kernel events but on all other platforms we have to define what is the equivalent data we are going to populate this with.
} | ||
|
||
[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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be tempted to encode it as a byte[] or string. Fixed size types would be a little more performant but given these shouldn't be frequent events I'd favor an encoding that feels straightforward.
If the goal is to be able to look up the images and symbols on a symbol server then the data we need is:
format - PE/ELF/MachO
- PE - filesize, timestamp, filename, debug signature, age, and the codeview debug directory major/minor version (major=0x100,minor=504D is a sentinel value that indicates portable pdb is supported - PE spec).
- ELF - build-id and filename
- MachO - uuid and name
using Microsoft.Diagnostics.Tracing.Parsers.ProcessMetadataEventSource; | ||
|
||
[System.CodeDom.Compiler.GeneratedCode("traceparsergen", "2.0")] | ||
public sealed class ProcessMetadataEventSourceTraceEventParser : TraceEventParser |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized above I said we didn't need to block on the name, but that probably also means we need to avoid putting the name in new public API surface. Can we switch to internal for now or should we just try to sort out the naming right away?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine but I don't have an opinion, I think it should be the .net name.
/// For convenience, we provide a property returns a ProcessMetadataTraceEventParser that knows | ||
/// how to parse all the Process Metadata events into callbacks. | ||
/// </summary> | ||
public ProcessMetadataEventSourceTraceEventParser ProcessMetadata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the comment above, another spot where the name is currently showing up in public API surface
Fixes #1265