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)