| File: Log\RoslynEventSource.cs | Web Access |
| Project: src\roslyn\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Diagnostics; using System.Diagnostics.Tracing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.Internal.Log; /// <summary> /// This EventSource exposes our events to ETW. /// RoslynEventSource GUID is {bf965e67-c7fb-5c5b-d98f-cdf68f8154c2}. /// /// When updating this class, use the following to also update Main\Source\Test\Performance\Log\RoslynEventSourceParser.cs: /// Main\Tools\Source\TraceParserGen\bin\Debug\TraceParserGen.exe Microsoft.CodeAnalysis.Workspaces.dll -eventsource:RoslynEventSource /// /// Use this command to register the ETW manifest on any machine where you need to decode events in xperf/etlstackbrowse: /// "\\clrmain\tools\managed\etw\eventRegister\bin\Debug\eventRegister.exe" Microsoft.CodeAnalysis.Workspaces.dll /// </summary> [EventSource(Name = "RoslynEventSource")] internal sealed partial class RoslynEventSource : EventSource { // might not "enabled" but we always have this singleton alive public static readonly RoslynEventSource Instance = new(); private readonly bool _initialized; private RoslynEventSource() => _initialized = true; // Do not change the parameter order for this method: it must match the parameter order // for WriteEvent() that's being invoked inside it. This is necessary because the ETW schema // generation process will reflect over WriteRoslynEvent() to determine how to pack event // data. If the orders are different, the generated schema will be wrong, and any perf // tools will deserialize event data fields in the wrong order. // // The WriteEvent overloads are optimized for either: // Up to 3 integer parameters // 1 string and up to 2 integer parameters // There's also a params object[] overload that is much slower and should be avoided [Event(1)] public void Log(string message, FunctionId functionId) => WriteEvent(1, message ?? string.Empty, (int)functionId); [Event(2)] public void BlockStart(string message, FunctionId functionId, int blockId) => WriteEvent(2, message ?? string.Empty, (int)functionId, blockId); [Event(3)] public void BlockStop(FunctionId functionId, int tick, int blockId) => WriteEvent(3, (int)functionId, tick, blockId); [Event(4)] public void SendFunctionDefinitions(string definitions) => WriteEvent(4, definitions); [Event(5)] public void BlockCanceled(FunctionId functionId, int tick, int blockId) => WriteEvent(5, (int)functionId, tick, blockId); [NonEvent] protected override void OnEventCommand(EventCommandEventArgs command) { base.OnEventCommand(command); if (command.Command == EventCommand.SendManifest || command.Command != EventCommand.Disable || FunctionDefinitionRequested(command)) { if (!_initialized) { // We're still in the constructor, need to defer sending until we've finished initializing Task.Yield().GetAwaiter().OnCompleted(FireAndForgetSendFunctionDefinitions); return; } SendFunctionDefinitions(); } // Cannot inline this local function as a lambda because we need NonEventAttribute applied. [NonEvent] void FireAndForgetSendFunctionDefinitions() { _ = Task.Run(SendFunctionDefinitions); } } [NonEvent] private static bool FunctionDefinitionRequested(EventCommandEventArgs command) { return command.Arguments != null && command.Arguments.Keys.FirstOrDefault() == "SendFunctionDefinitions"; } [NonEvent] private void SendFunctionDefinitions() => SendFunctionDefinitions(GenerateFunctionDefinitions()); [NonEvent] public static string GenerateFunctionDefinitions() { var output = new StringBuilder(); var functions = from f in typeof(FunctionId).GetFields() where !f.IsSpecialName select f; var assembly = typeof(RoslynEventSource).Assembly; var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); output.AppendLine(fvi.ProductVersion); foreach (var function in functions) { var value = (int)function.GetRawConstantValue(); #if DEBUG var name = "Debug_" + function.Name; #else var name = function.Name; #endif var goal = (from attr in function.GetCustomAttributes(false) where attr is PerfGoalAttribute select ((PerfGoalAttribute)attr).InteractionClass).DefaultIfEmpty(InteractionClass.Undefined).First(); output.Append(value.ToString(System.Globalization.CultureInfo.InvariantCulture)); output.Append(' '); output.Append(name); output.Append(' '); output.AppendLine(goal.ToString()); } // Note that changing the format of this output string will break any ETW listeners // that don't have a direct reference to Microsoft.CodeAnalysis.Workspaces.dll return output.ToString(); } }