Skip to content

Commit 235915e

Browse files
authored
Add event to RuntimeEventSource for AppContext switches (#57303)
Fixes #56142
1 parent 2a1d100 commit 235915e

File tree

7 files changed

+278
-92
lines changed

7 files changed

+278
-92
lines changed

src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
5+
using System.Diagnostics;
46
using System.Diagnostics.CodeAnalysis;
7+
using System.Diagnostics.Tracing;
58
using System.IO;
69
using System.Reflection;
710

@@ -30,5 +33,52 @@ private static string GetBaseDirectoryCore()
3033

3134
return directory;
3235
}
36+
37+
internal static void LogSwitchValues(RuntimeEventSource ev)
38+
{
39+
if (s_switches is not null)
40+
{
41+
lock (s_switches)
42+
{
43+
foreach (KeyValuePair<string, bool> kvp in s_switches)
44+
{
45+
// Convert bool to int because it's cheaper to log (no boxing)
46+
ev.LogAppContextSwitch(kvp.Key, kvp.Value ? 1 : 0);
47+
}
48+
}
49+
}
50+
51+
if (s_dataStore is not null)
52+
{
53+
lock (s_dataStore)
54+
{
55+
if (s_switches is not null)
56+
{
57+
lock (s_switches)
58+
{
59+
LogDataStore(ev, s_switches);
60+
}
61+
}
62+
else
63+
{
64+
LogDataStore(ev, null);
65+
}
66+
67+
static void LogDataStore(RuntimeEventSource ev, Dictionary<string, bool>? switches)
68+
{
69+
Debug.Assert(s_dataStore is not null);
70+
foreach (KeyValuePair<string, object?> kvp in s_dataStore)
71+
{
72+
if (kvp.Value is string s &&
73+
bool.TryParse(s, out bool isEnabled) &&
74+
switches?.ContainsKey(kvp.Key) != true)
75+
{
76+
ev.LogAppContextSwitch(kvp.Key, isEnabled ? 1 : 0);
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}
3383
}
3484
}

src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ internal sealed partial class RuntimeEventSource : EventSource
1414
{
1515
internal const string EventSourceName = "System.Runtime";
1616

17+
public static class Keywords
18+
{
19+
public const EventKeywords AppContext = (EventKeywords)0x1;
20+
}
21+
1722
private static RuntimeEventSource? s_RuntimeEventSource;
1823
private PollingCounter? _gcHeapSizeCounter;
1924
private IncrementingPollingCounter? _gen0GCCounter;
@@ -50,6 +55,17 @@ public static void Initialize()
5055
// as you can't make a constructor partial.
5156
private RuntimeEventSource(int _) { }
5257

58+
private enum EventId : int
59+
{
60+
AppContextSwitch = 1
61+
}
62+
63+
[Event((int)EventId.AppContextSwitch, Level = EventLevel.Informational, Keywords = Keywords.AppContext)]
64+
internal void LogAppContextSwitch(string switchName, int value)
65+
{
66+
base.WriteEvent((int)EventId.AppContextSwitch, switchName, value);
67+
}
68+
5369
protected override void OnEventCommand(EventCommandEventArgs command)
5470
{
5571
if (command.Command == EventCommand.Enable)
@@ -87,6 +103,8 @@ protected override void OnEventCommand(EventCommandEventArgs command)
87103
_ilBytesJittedCounter ??= new PollingCounter("il-bytes-jitted", this, () => System.Runtime.JitInfo.GetCompiledILBytes()) { DisplayName = "IL Bytes Jitted", DisplayUnits = "B" };
88104
_methodsJittedCounter ??= new PollingCounter("methods-jitted-count", this, () => System.Runtime.JitInfo.GetCompiledMethodCount()) { DisplayName = "Number of Methods Jitted" };
89105
_jitTimeCounter ??= new IncrementingPollingCounter("time-in-jit", this, () => System.Runtime.JitInfo.GetCompilationTime().TotalMilliseconds) { DisplayName = "Time spent in JIT", DisplayUnits = "ms", DisplayRateTimeScale = new TimeSpan(0, 0, 1) };
106+
107+
AppContext.LogSwitchValues(this);
90108
}
91109

92110
}

src/tests/tracing/eventcounter/runtimecounters.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ protected override void OnEventSourceCreated(EventSource source)
5454
{
5555
Dictionary<string, string> refreshInterval = new Dictionary<string, string>();
5656
refreshInterval.Add("EventCounterIntervalSec", "1");
57-
EnableEvents(source, EventLevel.Informational, (EventKeywords)(-1), refreshInterval);
57+
EnableEvents(source, EventLevel.Informational,
58+
(EventKeywords)(-1 & (~1 /* RuntimeEventSource.Keywords.AppContext */)),
59+
refreshInterval);
5860
}
5961
}
6062

@@ -100,7 +102,7 @@ public static int Main(string[] args)
100102
// Create an EventListener.
101103
using (RuntimeCounterListener myListener = new RuntimeCounterListener())
102104
{
103-
Thread.Sleep(3000);
105+
Thread.Sleep(3000);
104106
if (myListener.Verify())
105107
{
106108
Console.WriteLine("Test passed");
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Diagnostics.Tracing;
7+
using System.Runtime.CompilerServices;
8+
using System.Threading;
9+
using Tracing.Tests.Common;
10+
11+
namespace Tracing.Tests
12+
{
13+
public sealed class NativeRuntimeEventSourceTest
14+
{
15+
static int Main(string[] args)
16+
{
17+
SimpleEventListener.EnableKeywords = (EventKeywords)0;
18+
using (SimpleEventListener noEventsListener = new SimpleEventListener("NoEvents"))
19+
{
20+
// Create an EventListener.
21+
SimpleEventListener.EnableKeywords = (EventKeywords)0x4c14fccbd;
22+
using (SimpleEventListener listener = new SimpleEventListener("Simple"))
23+
{
24+
// Trigger the allocator task.
25+
System.Threading.Tasks.Task.Run(new Action(Allocator));
26+
27+
// Wait for events.
28+
Thread.Sleep(1000);
29+
30+
// Generate some GC events.
31+
GC.Collect(2, GCCollectionMode.Forced);
32+
33+
// Wait for more events.
34+
Thread.Sleep(1000);
35+
36+
// Ensure that we've seen some events.
37+
Assert.True("listener.EventCount > 0", listener.EventCount > 0);
38+
}
39+
40+
// Generate some more GC events.
41+
GC.Collect(2, GCCollectionMode.Forced);
42+
43+
// Ensure that we've seen no events.
44+
Assert.True("noEventsListener.EventCount == 0", noEventsListener.EventCount == 0);
45+
}
46+
47+
return 100;
48+
}
49+
50+
private static void Allocator()
51+
{
52+
while (true)
53+
{
54+
for(int i=0; i<1000; i++)
55+
GC.KeepAlive(new object());
56+
57+
Thread.Sleep(10);
58+
}
59+
}
60+
}
61+
62+
internal sealed class SimpleEventListener : EventListener
63+
{
64+
private string m_name;
65+
66+
// Keep track of the set of keywords to be enabled.
67+
public static EventKeywords EnableKeywords
68+
{
69+
get;
70+
set;
71+
}
72+
73+
public SimpleEventListener(string name)
74+
{
75+
m_name = name;
76+
}
77+
78+
public int EventCount { get; private set; } = 0;
79+
80+
protected override void OnEventSourceCreated(EventSource eventSource)
81+
{
82+
if (eventSource.Name.Equals("Microsoft-Windows-DotNETRuntime"))
83+
{
84+
if (EnableKeywords != 0)
85+
{
86+
// Enable events.
87+
EnableEvents(eventSource, EventLevel.Verbose, EnableKeywords);
88+
}
89+
else
90+
{
91+
// Enable the provider, but not any keywords, so we should get no events as long as no rundown occurs.
92+
EnableEvents(eventSource, EventLevel.Critical, EnableKeywords);
93+
}
94+
}
95+
}
96+
97+
protected override void OnEventWritten(EventWrittenEventArgs eventData)
98+
{
99+
Console.WriteLine($"[{m_name}] ThreadID = {eventData.OSThreadId} ID = {eventData.EventId} Name = {eventData.EventName}");
100+
Console.WriteLine($"TimeStamp: {eventData.TimeStamp.ToLocalTime()}");
101+
Console.WriteLine($"LocalTime: {DateTime.Now}");
102+
Console.WriteLine($"Difference: {DateTime.UtcNow - eventData.TimeStamp}");
103+
Assert.True("eventData.TimeStamp <= DateTime.UtcNow", eventData.TimeStamp <= DateTime.UtcNow);
104+
for (int i = 0; i < eventData.Payload.Count; i++)
105+
{
106+
string payloadString = eventData.Payload[i] != null ? eventData.Payload[i].ToString() : string.Empty;
107+
Console.WriteLine($"\tName = \"{eventData.PayloadNames[i]}\" Value = \"{payloadString}\"");
108+
}
109+
Console.WriteLine("\n");
110+
111+
EventCount++;
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)