Skip to content

Commit 4e0fa30

Browse files
authored
Merge branch 'main' into betterconsoleservice
2 parents e355cac + 7e8e386 commit 4e0fa30

13 files changed

Lines changed: 544 additions & 11 deletions

File tree

documentation/design-docs/diagnostics-client-library.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ namespace Microsoft.Diagnostics.Client
327327
long keywords = 0,
328328
IDictionary<string, string> arguments = null)
329329

330+
// Adds a per-provider Event ID filter. Using this overload starts the session with
331+
// CollectTracing5 (requires a .NET 10+ target runtime).
332+
public EventPipeProvider(
333+
string name,
334+
EventLevel eventLevel,
335+
long keywords,
336+
IDictionary<string, string> arguments,
337+
EventPipeProviderEventFilter eventFilter)
338+
330339
public long Keywords { get; }
331340

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

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

347+
public EventPipeProviderEventFilter EventFilter { get; }
348+
338349
public override string ToString();
339350

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

346357
public static bool operator !=(Provider left, Provider right);
347358
}
359+
360+
// An optional per-provider filter on Event IDs, applied by the runtime after the keyword/level
361+
// filter. Available on runtimes that support CollectTracing5 (.NET 10+).
362+
public class EventPipeProviderEventFilter
363+
{
364+
// enable=true: eventIds is an allow-list (only those IDs are enabled).
365+
// enable=false: eventIds is a deny-list (every ID except those is enabled; an empty
366+
// deny-list therefore enables all events).
367+
public EventPipeProviderEventFilter(bool enable, IReadOnlyList<uint> eventIds)
368+
369+
public bool Enable { get; }
370+
371+
public IReadOnlyList<uint> EventIds { get; }
372+
}
348373
}
349374
```
350375

documentation/design-docs/ipc-protocol.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ enum class EventPipeCommandId : uint8_t
287287
CollectTracing3 = 0x04, // create/start a given session with/without collecting stacks
288288
CollectTracing4 = 0x05, // create/start a given session with specific rundown keyword
289289
CollectTracing5 = 0x06, // create/start a given session with/without user_events
290+
CollectTracing6 = 0x07, // create/start a given session with a specific buffering mode
290291
}
291292
```
292293
See: [EventPipe Commands](#EventPipe-Commands)
@@ -348,6 +349,7 @@ enum class EventPipeCommandId : uint8_t
348349
CollectTracing3 = 0x04, // create/start a given session with/without collecting stacks
349350
CollectTracing4 = 0x05, // create/start a given session with specific rundown keyword
350351
CollectTracing5 = 0x06, // create/start a given session with/without user_events
352+
CollectTracing6 = 0x07, // create/start a given session with a specific buffering mode
351353
}
352354
```
353355
EventPipe Payloads are encoded with the following rules:
@@ -732,6 +734,44 @@ A Streaming Session started with `CollectTracing5` is followed by an Optional Co
732734

733735
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)
734736

737+
### `CollectTracing6`
738+
739+
Command Code: `0x0207`
740+
741+
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.
742+
743+
> Note available for .NET 11.0 and later.
744+
745+
#### Inputs:
746+
747+
Header: `{ Magic; 20 + Payload Size; 0x0207; 0x0000 }`
748+
749+
#### Streaming Session Payload:
750+
* `uint session_type`: 0
751+
* `uint streaming_circularBufferMB`: Specifies the size of the Streaming session's circular buffer used for buffering event data.
752+
* `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
753+
* `ulong rundownKeyword`: Indicates the keyword for the rundown provider
754+
* `bool requestStackwalk`: Indicates whether stacktrace information should be recorded.
755+
* `array<streaming_provider_config> providers`: The providers to turn on for the session
756+
* `uint sessionBufferMode`: Selects the session's buffering behavior. `0` = Drop (default): the buffer drops the newest events when it overflows (lossy). `1` = Block: producers block until the reader frees buffer capacity instead of dropping events (non-lossy).
757+
758+
The `streaming_provider_config` and its `event_filter` are encoded exactly as in [`CollectTracing5`](#collecttracing5).
759+
760+
#### User_events Session Payload:
761+
762+
Identical to the [`CollectTracing5`](#collecttracing5) user_events session payload; it has no `sessionBufferMode` field.
763+
764+
#### Returns (as an IPC Message Payload):
765+
766+
Header: `{ Magic; 28; 0xFF00; 0x0000; }`
767+
768+
`CollectTracing6` returns:
769+
* `ulong sessionId`: the ID for the EventPipe Session started
770+
771+
A Streaming Session started with `CollectTracing6` is followed by an Optional Continuation of a `nettrace` format stream of events.
772+
773+
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)
774+
735775
## EventPipe Payload Serialization Examples
736776

737777
### Event_filter

src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,9 @@ internal static bool ValidateResponseMessage(IpcMessage responseMessage, string
885885
}
886886
throw new UnsupportedCommandException($"{operationName} failed - Invalid command argument.");
887887

888+
case (uint)DiagnosticsIpcError.BadEncoding:
889+
throw new BadEncodingException($"{operationName} failed - the target runtime rejected the request as invalid (bad encoding), likely because it does not support one or more of the configured options. Retry with options supported by the target runtime's version.");
890+
888891
case (uint)DiagnosticsIpcError.NotSupported:
889892
message = $"{operationName} - Not supported by this runtime.";
890893
break;

src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClientExceptions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,12 @@ public class ProfilerAlreadyActiveException : ServerErrorException
4141
{
4242
public ProfilerAlreadyActiveException(string msg) : base(msg) { }
4343
}
44+
45+
// When the runtime cannot decode the command payload (DiagnosticsIpcError.BadEncoding). From a well-formed
46+
// client this usually means the target runtime is too old to understand a configured option value (e.g. a
47+
// newer buffering mode) and rejects it while parsing.
48+
public class BadEncodingException : ServerErrorException
49+
{
50+
public BadEncodingException(string msg) : base(msg) { }
51+
}
4452
}

src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeProvider.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,52 @@
77

88
namespace Microsoft.Diagnostics.NETCore.Client
99
{
10+
/// <summary>
11+
/// An optional per-provider filter on Event IDs, applied by the runtime after the keyword/level
12+
/// filter. Requires a target runtime that supports CollectTracing5 (.NET 10+).
13+
/// </summary>
14+
public sealed class EventPipeProviderEventFilter
15+
{
16+
/// <param name="enable">
17+
/// When true, <paramref name="eventIds"/> is an allow-list: only those Event IDs are enabled.
18+
/// When false, it is a deny-list: every Event ID except those is enabled (so an empty list with
19+
/// enable=false enables all events).
20+
/// </param>
21+
/// <param name="eventIds">The Event IDs to enable or disable, per <paramref name="enable"/>.</param>
22+
public EventPipeProviderEventFilter(bool enable, IReadOnlyList<uint> eventIds)
23+
{
24+
Enable = enable;
25+
EventIds = eventIds ?? (IReadOnlyList<uint>)System.Array.Empty<uint>();
26+
}
27+
28+
public bool Enable { get; }
29+
30+
public IReadOnlyList<uint> EventIds { get; }
31+
}
32+
1033
public sealed class EventPipeProvider
1134
{
1235
public EventPipeProvider(string name, EventLevel eventLevel, long keywords = 0xF00000000000, IDictionary<string, string> arguments = null)
36+
: this(name, eventLevel, keywords, arguments, eventFilter: null)
37+
{
38+
}
39+
40+
/// <summary>
41+
/// Creates a provider that additionally filters which Event IDs are enabled. Using this overload
42+
/// starts the session with CollectTracing5 (requires a .NET 10+ target runtime).
43+
/// </summary>
44+
/// <param name="name">The provider name.</param>
45+
/// <param name="eventLevel">The verbosity level to enable.</param>
46+
/// <param name="keywords">A bitmask of keywords to enable.</param>
47+
/// <param name="arguments">Optional provider arguments, or null.</param>
48+
/// <param name="eventFilter">The per-provider Event ID filter applied after the keyword/level filter.</param>
49+
public EventPipeProvider(string name, EventLevel eventLevel, long keywords, IDictionary<string, string> arguments, EventPipeProviderEventFilter eventFilter)
1350
{
1451
Name = name;
1552
EventLevel = eventLevel;
1653
Keywords = keywords;
1754
Arguments = arguments;
55+
EventFilter = eventFilter;
1856
}
1957

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

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

66+
/// <summary>
67+
/// An optional filter on this provider's Event IDs, applied after the keyword/level filter.
68+
/// Setting it causes the session to be started with CollectTracing5 (requires a .NET 10+ target).
69+
/// </summary>
70+
public EventPipeProviderEventFilter EventFilter { get; }
71+
2872
public override string ToString()
2973
{
3074
return $"{Name}:0x{Keywords:X16}:{(uint)EventLevel}{(Arguments == null ? "" : $":{GetArgumentString()}")}";

src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/EventPipeSession.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,25 @@ public async Task StopAsync(CancellationToken cancellationToken)
9292
}
9393
}
9494

95-
private static IpcMessage CreateStartMessage(EventPipeSessionConfiguration config)
95+
// Internal for unit testing of the version/command selection logic.
96+
internal static IpcMessage CreateStartMessage(EventPipeSessionConfiguration config)
9697
{
9798
// To keep backward compatibility with older runtimes we only use newer serialization format when needed
9899
EventPipeCommandId command;
99100
byte[] payload;
100-
if (config.RundownKeyword != DefaultRundownKeyword && config.RundownKeyword != 0)
101+
if (config.BufferingMode != EventPipeBufferingMode.Drop)
102+
{
103+
// V6 adds an opt-in session buffering mode
104+
command = EventPipeCommandId.CollectTracing6;
105+
payload = config.SerializeV6();
106+
}
107+
else if (HasEventFilter(config))
108+
{
109+
// V5 adds a per-provider event-id filter (and a session-type prefix)
110+
command = EventPipeCommandId.CollectTracing5;
111+
payload = config.SerializeV5();
112+
}
113+
else if (config.RundownKeyword != DefaultRundownKeyword && config.RundownKeyword != 0)
101114
{
102115
// V4 has added support to specify rundown keyword
103116
command = EventPipeCommandId.CollectTracing4;
@@ -118,6 +131,20 @@ private static IpcMessage CreateStartMessage(EventPipeSessionConfiguration confi
118131
return new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)command, payload);
119132
}
120133

134+
// A per-provider Event ID filter is available on CollectTracing5 and later.
135+
private static bool HasEventFilter(EventPipeSessionConfiguration config)
136+
{
137+
foreach (EventPipeProvider provider in config.Providers)
138+
{
139+
if (provider.EventFilter != null)
140+
{
141+
return true;
142+
}
143+
}
144+
145+
return false;
146+
}
147+
121148
private static EventPipeSession CreateSessionFromResponse(IpcEndpoint endpoint, ref IpcResponse? response, string operationName)
122149
{
123150
try

0 commit comments

Comments
 (0)