Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions documentation/design-docs/diagnostics-client-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,15 @@ namespace Microsoft.Diagnostics.Client
long keywords = 0,
IDictionary<string, string> arguments = null)

// Adds a per-provider Event ID filter. Using this overload starts the session with
// CollectTracing5 (requires a .NET 10+ target runtime).
public EventPipeProvider(
string name,
EventLevel eventLevel,
long keywords,
IDictionary<string, string> arguments,
EventPipeProviderEventFilter eventFilter)

public long Keywords { get; }

public EventLevel EventLevel { get; }
Expand All @@ -335,6 +344,8 @@ namespace Microsoft.Diagnostics.Client

public IDictionary<string, string> Arguments { get; }

public EventPipeProviderEventFilter EventFilter { get; }

public override string ToString();

public override bool Equals(object obj);
Expand All @@ -345,6 +356,20 @@ namespace Microsoft.Diagnostics.Client

public static bool operator !=(Provider left, Provider right);
}

// An optional per-provider filter on Event IDs, applied by the runtime after the keyword/level
// filter. Available on runtimes that support CollectTracing5 (.NET 10+).
public class EventPipeProviderEventFilter
{
// enable=true: eventIds is an allow-list (only those IDs are enabled).
// enable=false: eventIds is a deny-list (every ID except those is enabled; an empty
// deny-list therefore enables all events).
public EventPipeProviderEventFilter(bool enable, IReadOnlyList<uint> eventIds)

public bool Enable { get; }

public IReadOnlyList<uint> EventIds { get; }
}
}
```

Expand Down
40 changes: 40 additions & 0 deletions documentation/design-docs/ipc-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ enum class EventPipeCommandId : uint8_t
CollectTracing3 = 0x04, // create/start a given session with/without collecting stacks
CollectTracing4 = 0x05, // create/start a given session with specific rundown keyword
CollectTracing5 = 0x06, // create/start a given session with/without user_events
CollectTracing6 = 0x07, // create/start a given session with a specific buffering mode
}
```
See: [EventPipe Commands](#EventPipe-Commands)
Expand Down Expand Up @@ -348,6 +349,7 @@ enum class EventPipeCommandId : uint8_t
CollectTracing3 = 0x04, // create/start a given session with/without collecting stacks
CollectTracing4 = 0x05, // create/start a given session with specific rundown keyword
CollectTracing5 = 0x06, // create/start a given session with/without user_events
CollectTracing6 = 0x07, // create/start a given session with a specific buffering mode
}
```
EventPipe Payloads are encoded with the following rules:
Expand Down Expand Up @@ -732,6 +734,44 @@ A Streaming Session started with `CollectTracing5` is followed by an Optional Co

A User_events Session started with `CollectTracing5` expects the Optional Continuation to contain another message passing along the SCM_RIGHTS `user_events_data` file descriptor. See [details](#passing_file_descriptor)

### `CollectTracing6`

Command Code: `0x0207`

The `CollectTracing6` command is an extension of the `CollectTracing5` command. It has all the capabilities of `CollectTracing5` and adds a trailing `sessionBufferMode` field to the **streaming session payload** that selects how the runtime's per-session event buffer behaves when it fills faster than the session is drained. The user_events session payload is unchanged from `CollectTracing5`, since a user_events session does not use the buffer manager.

> Note available for .NET 11.0 and later.

#### Inputs:

Header: `{ Magic; 20 + Payload Size; 0x0207; 0x0000 }`

#### Streaming Session Payload:
* `uint session_type`: 0
* `uint streaming_circularBufferMB`: Specifies the size of the Streaming session's circular buffer used for buffering event data.
* `uint streaming_format`: 0 for the legacy NetPerf format and 1 for the NetTrace V4 format. Specifies the format in which event data will be serialized into the IPC Stream
* `ulong rundownKeyword`: Indicates the keyword for the rundown provider
* `bool requestStackwalk`: Indicates whether stacktrace information should be recorded.
* `array<streaming_provider_config> providers`: The providers to turn on for the session
* `uint sessionBufferMode`: Selects the session's buffering behavior. `0` = Drop (default): the circular buffer drops events when it overflows (lossy). `1` = Block: producers block until the reader frees buffer capacity instead of dropping events (non-lossy).

The `streaming_provider_config` and its `event_filter` are encoded exactly as in [`CollectTracing5`](#collecttracing5).

#### User_events Session Payload:

Identical to the [`CollectTracing5`](#collecttracing5) user_events session payload; it has no `sessionBufferMode` field.

#### Returns (as an IPC Message Payload):

Header: `{ Magic; 28; 0xFF00; 0x0000; }`

`CollectTracing6` returns:
* `ulong sessionId`: the ID for the EventPipe Session started

A Streaming Session started with `CollectTracing6` is followed by an Optional Continuation of a `nettrace` format stream of events.

A User_events Session started with `CollectTracing6` expects the Optional Continuation to contain another message passing along the SCM_RIGHTS `user_events_data` file descriptor. See [details](#passing_file_descriptor)

## EventPipe Payload Serialization Examples

### Event_filter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,52 @@

namespace Microsoft.Diagnostics.NETCore.Client
{
/// <summary>
/// An optional per-provider filter on Event IDs, applied by the runtime after the keyword/level
/// filter. Requires a target runtime that supports CollectTracing5 (.NET 10+).
/// </summary>
public sealed class EventPipeProviderEventFilter
{
/// <param name="enable">
/// When true, <paramref name="eventIds"/> is an allow-list: only those Event IDs are enabled.
/// When false, it is a deny-list: every Event ID except those is enabled (so an empty list with
/// enable=false enables all events).
/// </param>
/// <param name="eventIds">The Event IDs to enable or disable, per <paramref name="enable"/>.</param>
public EventPipeProviderEventFilter(bool enable, IReadOnlyList<uint> eventIds)
{
Enable = enable;
EventIds = eventIds ?? (IReadOnlyList<uint>)System.Array.Empty<uint>();
}

public bool Enable { get; }

public IReadOnlyList<uint> EventIds { get; }
}

public sealed class EventPipeProvider
{
public EventPipeProvider(string name, EventLevel eventLevel, long keywords = 0xF00000000000, IDictionary<string, string> arguments = null)
: this(name, eventLevel, keywords, arguments, eventFilter: null)
{
}

/// <summary>
/// Creates a provider that additionally filters which Event IDs are enabled. Using this overload
/// starts the session with CollectTracing5 (requires a .NET 10+ target runtime).
/// </summary>
/// <param name="name">The provider name.</param>
/// <param name="eventLevel">The verbosity level to enable.</param>
/// <param name="keywords">A bitmask of keywords to enable.</param>
/// <param name="arguments">Optional provider arguments, or null.</param>
/// <param name="eventFilter">The per-provider Event ID filter applied after the keyword/level filter.</param>
public EventPipeProvider(string name, EventLevel eventLevel, long keywords, IDictionary<string, string> arguments, EventPipeProviderEventFilter eventFilter)
{
Name = name;
EventLevel = eventLevel;
Keywords = keywords;
Arguments = arguments;
EventFilter = eventFilter;
}

public long Keywords { get; }
Expand All @@ -25,6 +63,12 @@ public EventPipeProvider(string name, EventLevel eventLevel, long keywords = 0xF

public IDictionary<string, string> Arguments { get; }

/// <summary>
/// An optional filter on this provider's Event IDs, applied after the keyword/level filter.
/// Setting it causes the session to be started with CollectTracing5 (requires a .NET 10+ target).
/// </summary>
public EventPipeProviderEventFilter EventFilter { get; }

public override string ToString()
{
return $"{Name}:0x{Keywords:X16}:{(uint)EventLevel}{(Arguments == null ? "" : $":{GetArgumentString()}")}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,25 @@ public async Task StopAsync(CancellationToken cancellationToken)
}
}

private static IpcMessage CreateStartMessage(EventPipeSessionConfiguration config)
// Internal for unit testing of the version/command selection logic.
internal static IpcMessage CreateStartMessage(EventPipeSessionConfiguration config)
{
// To keep backward compatibility with older runtimes we only use newer serialization format when needed
EventPipeCommandId command;
byte[] payload;
if (config.RundownKeyword != DefaultRundownKeyword && config.RundownKeyword != 0)
if (config.BufferingMode != EventPipeBufferingMode.Default)
{
// V6 adds an opt-in session buffering mode (its payload also carries any event filters)
command = EventPipeCommandId.CollectTracing6;
payload = config.SerializeV6();
}
else if (HasEventFilter(config))
{
// V5 adds a per-provider event-id filter (and a session-type prefix)
command = EventPipeCommandId.CollectTracing5;
payload = config.SerializeV5();
}
Comment thread
mdh1418 marked this conversation as resolved.
else if (config.RundownKeyword != DefaultRundownKeyword && config.RundownKeyword != 0)
{
// V4 has added support to specify rundown keyword
command = EventPipeCommandId.CollectTracing4;
Expand All @@ -118,6 +131,20 @@ private static IpcMessage CreateStartMessage(EventPipeSessionConfiguration confi
return new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)command, payload);
}

// A per-provider Event ID filter is available on CollectTracing5 and later.
private static bool HasEventFilter(EventPipeSessionConfiguration config)
{
foreach (EventPipeProvider provider in config.Providers)
{
if (provider.EventFilter != null)
{
return true;
}
}

return false;
}

private static EventPipeSession CreateSessionFromResponse(IpcEndpoint endpoint, ref IpcResponse? response, string operationName)
{
try
Expand Down
Loading
Loading