From 56a28083d448abb43aa0c414d62f3026e72409b4 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 21 Dec 2024 23:57:14 +0100 Subject: [PATCH] Add sealed. Refactor modules --- .../Markers/GCAllocationTickTraceMarker.cs | 2 +- .../Model/Markers/GCHeapStatsTraceMarker.cs | 2 +- .../GCRestartExecutionEngineTraceMarker.cs | 2 +- .../GCSuspendExecutionEngineTraceMarker.cs | 2 +- src/Ultra.Core/Model/Markers/GCTraceMarker.cs | 2 +- .../Model/Markers/JitCompileTraceMarker.cs | 2 +- src/Ultra.Core/Model/UCallStackList.cs | 2 +- src/Ultra.Core/Model/UCodeAddressList.cs | 2 +- src/Ultra.Core/Model/UTraceManagedMethod.cs | 2 +- .../Model/UTraceManagedMethodList.cs | 2 +- src/Ultra.Core/Model/UTraceManagedModule.cs | 4 +-- src/Ultra.Core/Model/UTraceModule.cs | 21 +++++++++++ src/Ultra.Core/Model/UTraceModuleFile.cs | 12 +------ src/Ultra.Core/Model/UTraceModuleList.cs | 28 +++++++-------- ...eLoadedModule.cs => UTraceNativeModule.cs} | 2 +- src/Ultra.Core/Model/UTraceProcess.cs | 4 +-- src/Ultra.Core/Model/UTraceSession.cs | 36 +++++++++++++++++++ src/Ultra.Core/Model/UTraceThread.cs | 2 +- src/Ultra.Core/Model/UTraceThreadList.cs | 2 +- .../Parser/UltraEventPipeProcessor.cs | 36 +++++++++++++++---- 20 files changed, 118 insertions(+), 49 deletions(-) create mode 100644 src/Ultra.Core/Model/UTraceModule.cs rename src/Ultra.Core/Model/{UTraceLoadedModule.cs => UTraceNativeModule.cs} (68%) create mode 100644 src/Ultra.Core/Model/UTraceSession.cs diff --git a/src/Ultra.Core/Model/Markers/GCAllocationTickTraceMarker.cs b/src/Ultra.Core/Model/Markers/GCAllocationTickTraceMarker.cs index a701cb4..8e487b1 100644 --- a/src/Ultra.Core/Model/Markers/GCAllocationTickTraceMarker.cs +++ b/src/Ultra.Core/Model/Markers/GCAllocationTickTraceMarker.cs @@ -7,7 +7,7 @@ namespace Ultra.Core.Model; /// /// Represents a garbage collection allocation tick profile marker. /// -public record GCAllocationTickTraceMarker : UTraceMarker +public sealed record GCAllocationTickTraceMarker : UTraceMarker { /// /// Gets or sets the amount of memory allocated. diff --git a/src/Ultra.Core/Model/Markers/GCHeapStatsTraceMarker.cs b/src/Ultra.Core/Model/Markers/GCHeapStatsTraceMarker.cs index b0307a1..1280c75 100644 --- a/src/Ultra.Core/Model/Markers/GCHeapStatsTraceMarker.cs +++ b/src/Ultra.Core/Model/Markers/GCHeapStatsTraceMarker.cs @@ -7,7 +7,7 @@ namespace Ultra.Core.Model; /// /// Represents a garbage collection heap stats event marker payload for Firefox Profiler. /// -public record GCHeapStatsTraceMarker : UTraceMarker +public sealed record GCHeapStatsTraceMarker : UTraceMarker { /// /// Gets or sets the total heap size. diff --git a/src/Ultra.Core/Model/Markers/GCRestartExecutionEngineTraceMarker.cs b/src/Ultra.Core/Model/Markers/GCRestartExecutionEngineTraceMarker.cs index c034b8a..02d64a7 100644 --- a/src/Ultra.Core/Model/Markers/GCRestartExecutionEngineTraceMarker.cs +++ b/src/Ultra.Core/Model/Markers/GCRestartExecutionEngineTraceMarker.cs @@ -7,4 +7,4 @@ namespace Ultra.Core.Model; /// /// Represents an event that indicates the .NET garbage collector has restarted the execution engine. /// -public record GCRestartExecutionEngineTraceMarker : UTraceMarker; \ No newline at end of file +public sealed record GCRestartExecutionEngineTraceMarker : UTraceMarker; \ No newline at end of file diff --git a/src/Ultra.Core/Model/Markers/GCSuspendExecutionEngineTraceMarker.cs b/src/Ultra.Core/Model/Markers/GCSuspendExecutionEngineTraceMarker.cs index aa08703..97a3066 100644 --- a/src/Ultra.Core/Model/Markers/GCSuspendExecutionEngineTraceMarker.cs +++ b/src/Ultra.Core/Model/Markers/GCSuspendExecutionEngineTraceMarker.cs @@ -7,7 +7,7 @@ namespace Ultra.Core.Model; /// /// Represents an event that marks the suspension of the execution engine by the garbage collector. /// -public record GCSuspendExecutionEngineTraceMarker : UTraceMarker +public sealed record GCSuspendExecutionEngineTraceMarker : UTraceMarker { /// /// Gets or sets the reason for the suspension. diff --git a/src/Ultra.Core/Model/Markers/GCTraceMarker.cs b/src/Ultra.Core/Model/Markers/GCTraceMarker.cs index 9135379..34349ac 100644 --- a/src/Ultra.Core/Model/Markers/GCTraceMarker.cs +++ b/src/Ultra.Core/Model/Markers/GCTraceMarker.cs @@ -7,7 +7,7 @@ namespace Ultra.Core.Model; /// /// Represents a garbage collection event marker payload for Firefox Profiler. /// -public record GCTraceMarker : UTraceMarker +public sealed record GCTraceMarker : UTraceMarker { /// /// Gets or sets the reason for the garbage collection. diff --git a/src/Ultra.Core/Model/Markers/JitCompileTraceMarker.cs b/src/Ultra.Core/Model/Markers/JitCompileTraceMarker.cs index 6d56c41..011c087 100644 --- a/src/Ultra.Core/Model/Markers/JitCompileTraceMarker.cs +++ b/src/Ultra.Core/Model/Markers/JitCompileTraceMarker.cs @@ -7,7 +7,7 @@ namespace Ultra.Core.Model; /// /// Represents a JIT compile event marker payload for the Firefox Profiler. /// -public record JitCompileTraceMarker : UTraceMarker +public sealed record JitCompileTraceMarker : UTraceMarker { /// /// Gets or sets the full name of the method. diff --git a/src/Ultra.Core/Model/UCallStackList.cs b/src/Ultra.Core/Model/UCallStackList.cs index de10f82..7e93506 100644 --- a/src/Ultra.Core/Model/UCallStackList.cs +++ b/src/Ultra.Core/Model/UCallStackList.cs @@ -9,7 +9,7 @@ namespace Ultra.Core.Model; /// /// Represents a list of instances. /// -public class UCallStackList : UGenericList +public sealed class UCallStackList : UGenericList { private UnsafeDictionary _uniqueStacks = new(65536); diff --git a/src/Ultra.Core/Model/UCodeAddressList.cs b/src/Ultra.Core/Model/UCodeAddressList.cs index 51f3e01..c3a868d 100644 --- a/src/Ultra.Core/Model/UCodeAddressList.cs +++ b/src/Ultra.Core/Model/UCodeAddressList.cs @@ -7,7 +7,7 @@ namespace Ultra.Core.Model; /// /// Represents a list of each associated with a unique . /// -public class UCodeAddressList : UGenericList +public sealed class UCodeAddressList : UGenericList { private readonly Dictionary _mapAddressToIndex = new(); diff --git a/src/Ultra.Core/Model/UTraceManagedMethod.cs b/src/Ultra.Core/Model/UTraceManagedMethod.cs index 25c4bff..a64fec7 100644 --- a/src/Ultra.Core/Model/UTraceManagedMethod.cs +++ b/src/Ultra.Core/Model/UTraceManagedMethod.cs @@ -9,7 +9,7 @@ namespace Ultra.Core.Model; /// /// Represents a managed method in a traced process. /// -public record UTraceManagedMethod(int ThreadID, long ModuleID, long MethodID, string MethodNamespace, string MethodName, string MethodSignature, int MethodToken, MethodFlags MethodFlags, UAddress MethodStartAddress, USize MethodSize) : UTraceMethod(CreateFullName(MethodNamespace, MethodName), MethodStartAddress, MethodSize) +public sealed record UTraceManagedMethod(int ThreadID, long ModuleID, long MethodID, string MethodNamespace, string MethodName, string MethodSignature, int MethodToken, MethodFlags MethodFlags, UAddress MethodStartAddress, USize MethodSize) : UTraceMethod(CreateFullName(MethodNamespace, MethodName), MethodStartAddress, MethodSize) { /// /// Gets or sets the native IL offsets for the method. diff --git a/src/Ultra.Core/Model/UTraceManagedMethodList.cs b/src/Ultra.Core/Model/UTraceManagedMethodList.cs index 5756cae..c98f628 100644 --- a/src/Ultra.Core/Model/UTraceManagedMethodList.cs +++ b/src/Ultra.Core/Model/UTraceManagedMethodList.cs @@ -11,7 +11,7 @@ namespace Ultra.Core.Model; /// /// Represents a list of instances. /// -public class UTraceManagedMethodList : UGenericList +public sealed class UTraceManagedMethodList : UGenericList { private UnsafeDictionary _mapMethodAddressToMethodIndex = new(); private UnsafeDictionary _mapManagedMethodIDToMethodIndex = new(); diff --git a/src/Ultra.Core/Model/UTraceManagedModule.cs b/src/Ultra.Core/Model/UTraceManagedModule.cs index 25e0b1c..0ec7f7d 100644 --- a/src/Ultra.Core/Model/UTraceManagedModule.cs +++ b/src/Ultra.Core/Model/UTraceManagedModule.cs @@ -7,10 +7,10 @@ namespace Ultra.Core.Model; /// /// Represents a managed module in the traced process. /// -public record UTraceManagedModule(long ModuleID, long AssemblyId, UTraceModuleFile ModuleFile, UAddress BaseAddress, USize CodeSize) : UTraceLoadedModule(ModuleFile, BaseAddress, CodeSize) +public sealed record UTraceManagedModule(long ModuleID, long AssemblyId, UTraceModuleFile ModuleFile) : UTraceModule(ModuleFile) { /// /// Gets or sets the native module if available. /// - public UTraceLoadedModule? NativeModule { get; set; } + public UTraceNativeModule? NativeModule { get; set; } } \ No newline at end of file diff --git a/src/Ultra.Core/Model/UTraceModule.cs b/src/Ultra.Core/Model/UTraceModule.cs new file mode 100644 index 0000000..fdecf8d --- /dev/null +++ b/src/Ultra.Core/Model/UTraceModule.cs @@ -0,0 +1,21 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// Licensed under the BSD-Clause 2 license. +// See license.txt file in the project root for full license information. + +namespace Ultra.Core.Model; + +/// +/// Represents a loaded module in the traced process. +/// +public abstract record UTraceModule(UTraceModuleFile ModuleFile) +{ + /// + /// Gets or sets the load time of the module. Time is relative to the start of the session. + /// + public UTimeSpan LoadTime { get; set; } + + /// + /// Gets or sets the unload time of the module. Time is relative to the start of the session. + /// + public UTimeSpan UnloadTime { get; set; } +} diff --git a/src/Ultra.Core/Model/UTraceModuleFile.cs b/src/Ultra.Core/Model/UTraceModuleFile.cs index 17a6dac..c9d07ef 100644 --- a/src/Ultra.Core/Model/UTraceModuleFile.cs +++ b/src/Ultra.Core/Model/UTraceModuleFile.cs @@ -7,7 +7,7 @@ namespace Ultra.Core.Model; /// /// Represents a module file that is loaded in the traced process. /// -public record UTraceModuleFile(string FilePath) +public sealed record UTraceModuleFile(string FilePath) { /// /// Gets or sets the module file index. @@ -23,14 +23,4 @@ public record UTraceModuleFile(string FilePath) /// Gets or sets the symbol file path for the module. /// public string? SymbolFilePath { get; set; } - - /// - /// Gets or sets the load time of the module. Time is relative to the start of the session. - /// - public UTimeSpan LoadTime { get; set; } - - /// - /// Gets or sets the unload time of the module. Time is relative to the start of the session. - /// - public UTimeSpan UnloadTime { get; set; } } \ No newline at end of file diff --git a/src/Ultra.Core/Model/UTraceModuleList.cs b/src/Ultra.Core/Model/UTraceModuleList.cs index b4fa5bb..b19ff67 100644 --- a/src/Ultra.Core/Model/UTraceModuleList.cs +++ b/src/Ultra.Core/Model/UTraceModuleList.cs @@ -8,9 +8,9 @@ namespace Ultra.Core.Model; /// -/// Represents a list of instances. +/// Represents a list of instances. /// -public class UTraceModuleList : UGenericList +public sealed class UTraceModuleList : UGenericList { private UnsafeDictionary _mapModulePathToIndex = new(); private UnsafeList _moduleFiles = new(); @@ -64,19 +64,19 @@ public bool TryGetManagedModule(long moduleID, [NotNullWhen(true)] out UTraceMan /// /// Gets or creates a loaded module based on the file path, base address, and code size. /// - /// The file path of the module. /// The base address of the module. /// The size of the code. - /// The instance. - public UTraceLoadedModule GetOrCreateLoadedModule(string filePath, UAddress baseAddress, USize codeSize) + /// The file path of the module. + /// The instance. + public UTraceModule GetOrCreateNativeModule(UAddress baseAddress, USize codeSize, string moduleFilePath) { if (_mapModuleAddressToLoadedModule.TryGetValue(baseAddress, out var loadedModuleIndex)) { return List[loadedModuleIndex]; } - var moduleFile = GetOrCreateModuleFile(filePath); - var loadedModule = new UTraceLoadedModule(moduleFile, baseAddress, codeSize); + var moduleFile = GetOrCreateModuleFile(moduleFilePath); + var loadedModule = new UTraceNativeModule(moduleFile, baseAddress, codeSize); loadedModuleIndex = List.Count; _mapModuleAddressToLoadedModule.Add(baseAddress, loadedModuleIndex); List.Add(loadedModule); @@ -93,14 +93,14 @@ public UTraceLoadedModule GetOrCreateLoadedModule(string filePath, UAddress base /// The address of the module. /// The module found, if any. /// True if the module was found, otherwise false. - public bool TryFindModuleByAddress(UAddress address, [NotNullWhen(true)] out UTraceLoadedModule? module) + public bool TryFindNativeModuleByAddress(UAddress address, [NotNullWhen(true)] out UTraceNativeModule? module) { var ranges = _loadedModuleAddressRanges.AsSpan(); var comparer = new UAddressRangeFinder(address); var index = ranges.BinarySearch(comparer); if (index >= 0) { - module = List[ranges[index].Index]; + module = (UTraceNativeModule)List[ranges[index].Index]; return true; } module = null; @@ -112,18 +112,16 @@ public bool TryFindModuleByAddress(UAddress address, [NotNullWhen(true)] out UTr /// /// The module ID. /// The assembly ID. - /// The file path of the module. - /// The base address of the module. - /// The size of the module. + /// The file path of the module. /// The instance. - public UTraceManagedModule GetOrCreateManagedModule(long moduleID, long assemblyId, string filePath, UAddress baseAddress, USize codeSize) + public UTraceManagedModule GetOrCreateManagedModule(long moduleID, long assemblyId, string moduleFilePath) { if (_mapModuleIDToManagedModule.TryGetValue(moduleID, out var managedModuleIndex)) { return (UTraceManagedModule)List[managedModuleIndex]; } - var moduleFile = GetOrCreateModuleFile(filePath); - var managedModule = new UTraceManagedModule(moduleID, assemblyId, moduleFile, baseAddress, codeSize); + var moduleFile = GetOrCreateModuleFile(moduleFilePath); + var managedModule = new UTraceManagedModule(moduleID, assemblyId, moduleFile); _mapModuleIDToManagedModule.Add(moduleID, List.Count); List.Add(managedModule); return managedModule; diff --git a/src/Ultra.Core/Model/UTraceLoadedModule.cs b/src/Ultra.Core/Model/UTraceNativeModule.cs similarity index 68% rename from src/Ultra.Core/Model/UTraceLoadedModule.cs rename to src/Ultra.Core/Model/UTraceNativeModule.cs index 37e7401..d109df6 100644 --- a/src/Ultra.Core/Model/UTraceLoadedModule.cs +++ b/src/Ultra.Core/Model/UTraceNativeModule.cs @@ -7,4 +7,4 @@ namespace Ultra.Core.Model; /// /// Represents a loaded module in the traced process. /// -public record UTraceLoadedModule(UTraceModuleFile ModuleFile, UAddress BaseAddress, USize CodeSize); \ No newline at end of file +public sealed record UTraceNativeModule(UTraceModuleFile ModuleFile, UAddress BaseAddress, USize CodeSize) : UTraceModule(ModuleFile); \ No newline at end of file diff --git a/src/Ultra.Core/Model/UTraceProcess.cs b/src/Ultra.Core/Model/UTraceProcess.cs index 1202afd..6d7cee7 100644 --- a/src/Ultra.Core/Model/UTraceProcess.cs +++ b/src/Ultra.Core/Model/UTraceProcess.cs @@ -11,7 +11,7 @@ namespace Ultra.Core.Model; /// /// Represents a process being traced. /// -public class UTraceProcess +public sealed class UTraceProcess { /// /// Gets or sets the process ID. @@ -52,4 +52,4 @@ public class UTraceProcess /// Gets the list of call stacks in the traced process. /// public UCallStackList CallStacks { get; } = new(); -} +} \ No newline at end of file diff --git a/src/Ultra.Core/Model/UTraceSession.cs b/src/Ultra.Core/Model/UTraceSession.cs new file mode 100644 index 0000000..06f9dd6 --- /dev/null +++ b/src/Ultra.Core/Model/UTraceSession.cs @@ -0,0 +1,36 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// Licensed under the BSD-Clause 2 license. +// See license.txt file in the project root for full license information. + +namespace Ultra.Core.Model; + +/// +/// Represents a session of tracing. +/// +public sealed class UTraceSession +{ + /// + /// Gets or sets the number of processor used for the session. + /// + public int NumberOfProcessors { get; set; } + + /// + /// Gets or sets the CPU speed in MHz. + /// + public int CpuSpeedMHz { get; set; } + + /// + /// Gets or sets the start time of the session. + /// + public DateTime StartTime { get; set; } + + /// + /// Gets or sets the duration of the session. + /// + public UTimeSpan Duration { get; set; } + + /// + /// Gets or sets the process being traced. + /// + public List Processes { get; } = new(); +} \ No newline at end of file diff --git a/src/Ultra.Core/Model/UTraceThread.cs b/src/Ultra.Core/Model/UTraceThread.cs index 0729f7d..6a78acd 100644 --- a/src/Ultra.Core/Model/UTraceThread.cs +++ b/src/Ultra.Core/Model/UTraceThread.cs @@ -9,7 +9,7 @@ namespace Ultra.Core.Model; /// /// Represents a thread in a traced process. /// -public record UTraceThread(ulong ThreadID) +public sealed record UTraceThread(ulong ThreadID) { private UnsafeList _samples = new(1024); diff --git a/src/Ultra.Core/Model/UTraceThreadList.cs b/src/Ultra.Core/Model/UTraceThreadList.cs index 93f9844..715c82b 100644 --- a/src/Ultra.Core/Model/UTraceThreadList.cs +++ b/src/Ultra.Core/Model/UTraceThreadList.cs @@ -10,7 +10,7 @@ namespace Ultra.Core.Model; /// /// Represents a list of instances. /// -public class UTraceThreadList : UGenericList +public sealed class UTraceThreadList : UGenericList { private readonly Dictionary _mapThreadIDToIndex = new(); diff --git a/src/Ultra.Core/Parser/UltraEventPipeProcessor.cs b/src/Ultra.Core/Parser/UltraEventPipeProcessor.cs index df6fa16..8d7db99 100644 --- a/src/Ultra.Core/Parser/UltraEventPipeProcessor.cs +++ b/src/Ultra.Core/Parser/UltraEventPipeProcessor.cs @@ -137,24 +137,40 @@ private void ProcessMethodLoadVerbose(MethodLoadUnloadVerboseTraceData method) private void ProcessModuleLoadUnload(ModuleLoadUnloadTraceData data, bool isLoad, bool isDCStartStop) { + var module = _modules.GetOrCreateManagedModule(data.ModuleID, data.AssemblyID, data.ModuleILPath); + + module.ModuleFile.SymbolUuid = data.ManagedPdbSignature; + module.ModuleFile.SymbolFilePath = data.ManagedPdbBuildPath; + + if (!isDCStartStop) + { + if (isLoad) + { + module.LoadTime = UTimeSpan.FromMilliseconds(data.TimeStampRelativeMSec); + } + else + { + module.UnloadTime = UTimeSpan.FromMilliseconds(data.TimeStampRelativeMSec); + } + } } private void SamplerParserOnEventNativeModule(UltraNativeModuleTraceEvent evt) { if (evt.ModulePath is not null) { - var module = _modules.GetOrCreateLoadedModule(evt.ModulePath, evt.LoadAddress, evt.Size); + var module = _modules.GetOrCreateNativeModule(evt.LoadAddress, evt.Size, evt.ModulePath); if (evt.NativeModuleEventKind == UltraSamplerNativeModuleEventKind.Unloaded) { // TODO: how to support remove? //_mapModuleNameToIndex.Remove(evt.ModulePath); - module.ModuleFile.UnloadTime = UTimeSpan.FromMilliseconds(evt.TimeStampRelativeMSec); + module.UnloadTime = UTimeSpan.FromMilliseconds(evt.TimeStampRelativeMSec); } else { module.ModuleFile.SymbolUuid = evt.Uuid; - module.ModuleFile.LoadTime = UTimeSpan.FromMilliseconds(evt.TimeStampRelativeMSec); + module.LoadTime = UTimeSpan.FromMilliseconds(evt.TimeStampRelativeMSec); } } @@ -192,7 +208,7 @@ private void PrintCallStack(UltraNativeCallstackTraceEvent callstackTraceEvent) //} } - public UTraceProcess Run() + public UTraceSession Run() { // Run CLR if available _clrEventSource?.Process(); @@ -201,8 +217,16 @@ public UTraceProcess Run() // Run sampler before CLR _samplerEventSource.Process(); - - return _process; + + var session = new UTraceSession(); + session.Processes.Add(_process); + + session.NumberOfProcessors = _samplerEventSource.NumberOfProcessors; + session.StartTime = _samplerEventSource.SessionStartTime; + session.Duration = _samplerEventSource.SessionDuration; + session.CpuSpeedMHz = _samplerEventSource.CpuSpeedMHz; + + return session; } private ThreadSamplerState GetThreadSamplingState(ulong threadID)