Skip to content

Commit 74f23ea

Browse files
committed
Fix sampler and EventSource
1 parent 9b6898d commit 74f23ea

8 files changed

+144
-35
lines changed

src/Ultra.Core/UltraProfilerEventPipe.cs

+43-9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics;
66
using System.Diagnostics.Tracing;
77
using System.IO.Enumeration;
8+
using System.Net.Sockets;
89
using ByteSizeLib;
910
using Microsoft.Diagnostics.NETCore.Client;
1011
using Microsoft.Diagnostics.Tracing;
@@ -117,7 +118,7 @@ private static void SetupUltraSampler(ProcessStartInfo startInfo)
117118
value = ultraSamplerPath;
118119
}
119120

120-
Console.WriteLine($"DYLD_INSERT_LIBRARIES={value}");
121+
//Console.WriteLine($"DYLD_INSERT_LIBRARIES={value}");
121122
startInfo.Environment[key] = value;
122123
}
123124

@@ -156,6 +157,7 @@ private UltraSamplerProfilerState(UltraProfilerOptions options, DiagnosticsClien
156157
public static async Task<UltraSamplerProfilerState> Connect(string baseName, int pid, CancellationToken token, UltraProfilerOptions options)
157158
{
158159

160+
//var ultraTempFolder = Path.Combine(Path.GetTempPath(), ".ultra");
159161
var ultraTempFolder = Path.Combine(Path.GetTempPath(), ".ultra");
160162

161163
var pattern = $"dotnet-diagnostic-{pid}-*";
@@ -189,15 +191,16 @@ public static async Task<UltraSamplerProfilerState> Connect(string baseName, int
189191
}
190192

191193
var diagnosticClientMain = new DiagnosticsClient(pid);
192-
var diagnosticClientUltra = await DiagnosticsClientConnector.FromDiagnosticPort(ultraDiagnosticPortSocket, token);
194+
//DiagnosticsClient? diagnosticClientMain = null;
195+
var diagnosticClientUltra = (await DiagnosticsClientConnector.FromDiagnosticPort(ultraDiagnosticPortSocket, token).ConfigureAwait(false))!.Instance;
193196

194197
var timeoutSource = new CancellationTokenSource();
195198
var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutSource.Token);
196199

197200
try
198201
{
199202
timeoutSource.CancelAfter(1000);
200-
await diagnosticClientUltra!.Instance.WaitForConnectionAsync(linkedCancellationTokenSource.Token).ConfigureAwait(false);
203+
await diagnosticClientUltra!.WaitForConnectionAsync(linkedCancellationTokenSource.Token).ConfigureAwait(false);
201204
await diagnosticClientMain.WaitForConnectionAsync(linkedCancellationTokenSource.Token).ConfigureAwait(false);
202205
}
203206
catch (OperationCanceledException) when (timeoutSource.IsCancellationRequested)
@@ -209,7 +212,7 @@ public static async Task<UltraSamplerProfilerState> Connect(string baseName, int
209212
throw;
210213
}
211214

212-
return new UltraSamplerProfilerState(options, diagnosticClientMain, diagnosticClientUltra.Instance, baseName, pid, token);
215+
return new UltraSamplerProfilerState(options, diagnosticClientMain, diagnosticClientUltra, baseName, pid, token);
213216
}
214217

215218
public long TotalFileLength()
@@ -228,7 +231,8 @@ public long TotalFileLength()
228231
public async Task StartProfiling()
229232
{
230233
var ultraEventProvider = new EventPipeProvider(UltraSamplerParser.Name, EventLevel.Verbose);
231-
_ultraSession = await _ultraDiagnosticsClient.StartEventPipeSessionAsync([ultraEventProvider], true, 256, _token).ConfigureAwait(false);
234+
var config = new EventPipeSessionConfiguration([ultraEventProvider], 256, false, true);
235+
_ultraSession = await _ultraDiagnosticsClient.StartEventPipeSessionAsync(config, _token).ConfigureAwait(false);
232236
_ultraEventStreamCopyTask = _ultraSession.EventStream.CopyToAsync(_ultraNetTraceFileStream, _token);
233237

234238
if (_mainDiagnosticsClient is not null)
@@ -264,14 +268,44 @@ public async Task Stop()
264268
await _mainEventStreamCopyTask.ConfigureAwait(false);
265269
}
266270

267-
if (_ultraSession is not null)
271+
await SafeStopAsync(_ultraSession, _token);
272+
_ultraSession = null;
273+
274+
await SafeStopAsync(_mainSession, _token);
275+
_mainSession = null;
276+
}
277+
278+
private static async Task SafeStopAsync(EventPipeSession? session, CancellationToken token)
279+
{
280+
if (session is null) return;
281+
282+
try
268283
{
269-
await _ultraSession.StopAsync(_token).ConfigureAwait(false);
284+
await session.StopAsync(token).ConfigureAwait(false);
270285
}
286+
catch (EndOfStreamException)
287+
{
271288

272-
if (_mainSession is not null)
289+
}
290+
catch (TimeoutException)
291+
{
292+
293+
}
294+
catch (OperationCanceledException)
273295
{
274-
await _mainSession.StopAsync(_token).ConfigureAwait(false);
296+
297+
}
298+
catch (PlatformNotSupportedException)
299+
{
300+
301+
}
302+
catch (ServerNotAvailableException)
303+
{
304+
305+
}
306+
catch (SocketException)
307+
{
308+
275309
}
276310
}
277311

src/Ultra.Sampler/MacOS/MacOSLibSystem.cs

+31-1
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,38 @@ public struct uuid_command {
343343
public uint cmd; /* LC_UUID */
344344
public uint cmdsize; /* sizeof(struct uuid_command) */
345345
public Guid uuid; /* the 128-bit uuid */
346-
};
346+
}
347+
348+
public unsafe struct segment_command_64 { /* for 64-bit architectures */
349+
public uint cmd; /* LC_SEGMENT_64 */
350+
public uint cmdsize; /* includes sizeof section_64 structs */
351+
public fixed byte segname[16]; /* segment name */
352+
public ulong vmaddr; /* memory address of this segment */
353+
public ulong vmsize; /* memory size of this segment */
354+
public ulong fileoff; /* file offset of this segment */
355+
public ulong filesize; /* amount to map from the file */
356+
public int maxprot; /* maximum VM protection */
357+
public int initprot; /* initial VM protection */
358+
public uint nsects; /* number of sections in segment */
359+
public uint flags; /* flags */
360+
}
361+
362+
public unsafe struct section_64 { /* for 64-bit architectures */
363+
public fixed byte sectname[16]; /* name of this section */
364+
public fixed byte segname[16]; /* segment this section goes in */
365+
public ulong addr; /* memory address of this section */
366+
public ulong size; /* size in bytes of this section */
367+
public uint offset; /* file offset of this section */
368+
public uint align; /* section alignment (power of 2) */
369+
public uint reloff; /* file offset of relocation entries */
370+
public uint nreloc; /* number of relocation entries */
371+
public uint flags; /* flags (section type and attributes)*/
372+
public uint reserved1; /* reserved (for offset or index) */
373+
public uint reserved2; /* reserved (for count or sizeof) */
374+
public uint reserved3; /* reserved */
375+
}
347376

348377
public const uint LC_UUID = 0x1b;
378+
public const uint LC_SEGMENT_64 = 0x19;
349379

350380
}

src/Ultra.Sampler/MacOS/MacOSUltraSampler.cs

+44-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// Licensed under the BSD-Clause 2 license.
33
// See license.txt file in the project root for full license information.
44

5+
using System.Collections;
56
using System.Diagnostics;
7+
using System.Diagnostics.Tracing;
68
using System.Runtime.CompilerServices;
79
using System.Runtime.InteropServices;
810
using XenoAtom.Collections;
@@ -24,6 +26,7 @@ internal unsafe class MacOSUltraSampler : UltraSampler
2426
private bool _initializingModules;
2527
private readonly object _moduleEventLock = new();
2628
private int _nextModuleEventIndexToLog;
29+
private readonly UltraSamplerSource _samplerEventSource;
2730

2831
private readonly MacOSLibSystem.dyld_register_callback _callbackDyldAdded;
2932
private readonly MacOSLibSystem.dyld_register_callback _callbackDyldRemoved;
@@ -40,6 +43,9 @@ public MacOSUltraSampler()
4043
MacOSLibSystem._dyld_register_func_for_add_image(Marshal.GetFunctionPointerForDelegate(_callbackDyldAdded));
4144
_initializingModules = false;
4245
MacOSLibSystem._dyld_register_func_for_remove_image(Marshal.GetFunctionPointerForDelegate(_callbackDyldRemoved));
46+
47+
// Make sure to use the instance to trigger the constructor of the EventSource so that it is registered in the runtime!
48+
_samplerEventSource = UltraSamplerSource.Log;
4349
}
4450

4551
protected override void StartImpl()
@@ -140,6 +146,7 @@ private void AddModuleEvent(NativeModuleEventKind kind, nint loadAddress)
140146
var path = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)info.dli_fname);
141147
evt.Path = path.ToArray();
142148
evt.TimestampUtc = DateTime.UtcNow;
149+
evt.Size = GetMaximumCodeAddress(loadAddress);
143150

144151
lock (_moduleEventLock)
145152
{
@@ -163,7 +170,7 @@ private void NotifyPendingNativeModuleEvents()
163170
for(; _nextModuleEventIndexToLog < events.Length; _nextModuleEventIndexToLog++)
164171
{
165172
var evt = events[_nextModuleEventIndexToLog];
166-
UltraSamplerSource.Log.OnNativeModuleEvent(evt.Kind, evt.LoadAddress, evt.Path, evt.TimestampUtc.Ticks);
173+
UltraSamplerSource.Log.OnNativeModuleEvent(evt.Kind, evt.LoadAddress, evt.Size, evt.Path, evt.TimestampUtc.Ticks);
167174
}
168175
}
169176
}
@@ -190,6 +197,41 @@ private static bool TryGetUuidFromMacHeader(nint headerPtr, out Guid guid)
190197
return false;
191198
}
192199

200+
private static ulong GetMaximumCodeAddress(nint headerPtr)
201+
{
202+
ulong startAddress = 0;
203+
204+
ulong size = 0;
205+
var header = (MacOSLibSystem.mach_header_64*)headerPtr;
206+
if (header->magic != MacOSLibSystem.MH_MAGIC_64) throw new InvalidOperationException("Invalid magic header");
207+
208+
var nbCommands = header->ncmds;
209+
var commands = (MacOSLibSystem.load_command*)((byte*)header + sizeof(MacOSLibSystem.mach_header_64));
210+
for(uint i = 0; i < nbCommands; i++)
211+
{
212+
ref var command = ref commands[i];
213+
if (command.cmd == MacOSLibSystem.LC_SEGMENT_64)
214+
{
215+
ref var segment = ref Unsafe.As<MacOSLibSystem.load_command,MacOSLibSystem.segment_command_64>(ref command);
216+
if (segment.vmaddr != 0)
217+
{
218+
if (startAddress == 0)
219+
{
220+
startAddress = segment.vmaddr;
221+
}
222+
223+
var newSize = (ulong)((long)segment.vmaddr + (long)segment.vmsize - (long)startAddress);
224+
if (newSize > size)
225+
{
226+
size = newSize;
227+
}
228+
}
229+
}
230+
}
231+
232+
return size;
233+
}
234+
193235
public void Sample(NativeCallstackDelegate nativeCallstack)
194236
{
195237
MacOS.MacOSLibSystem.task_for_pid(MacOS.MacOSLibSystem.mach_task_self(), Process.GetCurrentProcess().Id, out var rootTask)
@@ -251,7 +293,7 @@ private static unsafe void Sample(MacOS.MacOSLibSystem.mach_port_t rootTask, ulo
251293

252294
//Console.WriteLine($"sp: 0x{armThreadState.__sp:X8}, fp: 0x{armThreadState.__fp:X8}, lr: 0x{armThreadState.__lr:X8}");
253295
int frameCount = WalkNativeCallStack(armThreadState.__sp, armThreadState.__fp, armThreadState.__lr, pFrames);
254-
nativeCallstack(threadInfo.thread_id, pFrames, frameCount);
296+
nativeCallstack(threadInfo.thread_id, (nint)pFrames, frameCount);
255297
}
256298
finally
257299
{

src/Ultra.Sampler/MacOS/NativeCallstackDelegate.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
namespace Ultra.Sampler.MacOS;
66

7-
public unsafe delegate void NativeCallstackDelegate(ulong threadId, ulong* pFrames, int frameCount);
7+
public unsafe delegate void NativeCallstackDelegate(ulong threadId, nint pFrames, int frameCount);

src/Ultra.Sampler/MacOS/NativeModuleEvent.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ internal struct NativeModuleEvent
1010
{
1111
public NativeModuleEventKind Kind;
1212
public ulong LoadAddress;
13+
public ulong Size;
1314
public byte[]? Path;
1415
public DateTime TimestampUtc;
1516

1617
public override string ToString()
1718
{
18-
return $"{nameof(LoadAddress)}: 0x{LoadAddress:X8}, {nameof(Path)}: {Encoding.UTF8.GetString(Path ?? [])}, {nameof(TimestampUtc)}: {TimestampUtc:O}";
19+
return $"{nameof(LoadAddress)}: 0x{LoadAddress:X8}, {nameof(Size)}: {Size}, {nameof(Path)}: {Encoding.UTF8.GetString(Path ?? [])}, {nameof(TimestampUtc)}: {TimestampUtc:O}";
1920
}
2021
}

src/Ultra.Sampler/UltraSamplerParser.cs

-6
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,4 @@ public static class UltraSamplerParser
2727
public const int NativeCallStackEvent = 1;
2828

2929
public const int NativeModuleEvent = 2;
30-
31-
32-
public static void TestId(Guid guid)
33-
{
34-
guid = Id;
35-
}
3630
}

src/Ultra.Sampler/UltraSamplerSource.cs

+22-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the BSD-Clause 2 license.
33
// See license.txt file in the project root for full license information.
44

5+
using System.Diagnostics.CodeAnalysis;
56
using System.Diagnostics.Tracing;
67
using System.Runtime.CompilerServices;
78
using Ultra.Sampler.MacOS;
@@ -17,38 +18,40 @@ private UltraSamplerSource()
1718
{
1819
}
1920

20-
[Event(UltraSamplerParser.NativeCallStackEvent, Level = EventLevel.Verbose, Message = "NativeCallstackEvent Thread {0} with {2} frames")]
21-
[SkipLocalsInit]
22-
public unsafe void OnNativeCallstack(ulong threadId, ulong* pFrames, int count)
21+
[Event(UltraSamplerParser.NativeCallStackEvent, Level = EventLevel.Informational)]
22+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
23+
public unsafe void OnNativeCallstack(ulong threadId, nint pFrames, int count)
2324
{
24-
25-
Unsafe.SkipInit(out EventData2 evt);
25+
EventData2 evt = default;
2626
evt.Data1.DataPointer = (nint)(void*)&threadId;
2727
evt.Data1.Size = sizeof(ulong);
2828
evt.Data2.DataPointer = (nint)pFrames;
2929
evt.Data2.Size = count * sizeof(ulong);
3030
WriteEventCore(UltraSamplerParser.NativeCallStackEvent, 2, &evt.Data1);
3131
}
3232

33-
[Event(UltraSamplerParser.NativeModuleEvent, Level = EventLevel.Verbose, Message = "NativeModuleEvent {0} LoadAddress: {1}")]
34-
[SkipLocalsInit]
35-
public unsafe void OnNativeModuleEvent(NativeModuleEventKind nativeModuleEventKind, ulong loadAddress, byte[]? modulePathUtf8, long timestampUtc)
33+
[Event(UltraSamplerParser.NativeModuleEvent, Level = EventLevel.Informational)]
34+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
35+
public unsafe void OnNativeModuleEvent(NativeModuleEventKind nativeModuleEventKind, ulong loadAddress, ulong size, byte[]? modulePathUtf8, long timestampUtc)
3636
{
37-
Unsafe.SkipInit(out EventData4 evt);
37+
EventData5 evt = default;
3838
evt.Data1.DataPointer = (nint)(void*)&nativeModuleEventKind;
3939
evt.Data1.Size = sizeof(int);
4040
evt.Data2.DataPointer = (nint)(void*)&loadAddress;
4141
evt.Data2.Size = sizeof(ulong);
42+
evt.Data3.DataPointer = (nint)(void*)&size;
43+
evt.Data3.Size = sizeof(ulong);
4244
fixed (byte* evtPathPtr = modulePathUtf8)
4345
{
44-
evt.Data3.DataPointer = (nint)evtPathPtr;
45-
evt.Data3.Size = modulePathUtf8?.Length ?? 0;
46-
evt.Data4.DataPointer = (nint)(void*)&timestampUtc;
47-
evt.Data4.Size = sizeof(long);
46+
evt.Data4.DataPointer = (nint)evtPathPtr;
47+
evt.Data4.Size = modulePathUtf8?.Length ?? 0;
48+
evt.Data5.DataPointer = (nint)(void*)&timestampUtc;
49+
evt.Data5.Size = sizeof(long);
4850
WriteEventCore(UltraSamplerParser.NativeModuleEvent, 4, &evt.Data1);
4951
}
5052
}
5153

54+
[NonEvent]
5255
protected override void OnEventCommand(EventCommandEventArgs command)
5356
{
5457
if (command.Command == EventCommand.Enable)
@@ -58,6 +61,9 @@ protected override void OnEventCommand(EventCommandEventArgs command)
5861
else if (command.Command == EventCommand.Disable)
5962
{
6063
UltraSampler.Instance.Disable();
64+
65+
// Wait a bit to let the sampler thread finishing
66+
Thread.Sleep(100);
6167
}
6268
}
6369

@@ -68,7 +74,7 @@ private struct EventData2
6874
public EventData Data2;
6975
}
7076

71-
private struct EventData4
77+
private struct EventData5
7278
{
7379
public EventData Data1;
7480

@@ -77,5 +83,7 @@ private struct EventData4
7783
public EventData Data3;
7884

7985
public EventData Data4;
86+
87+
public EventData Data5;
8088
}
8189
}

src/Ultra.Sampler/ultra_sampler_indirect.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ extern "C" {
4545
free(new_tmpdir);
4646

4747
pid_t pid = getpid();
48-
printf("Current Process pid: %d tmpdir: %s\n", pid, getenv("TMPDIR"));
48+
//printf("Current Process pid: %d tmpdir: %s\n", pid, getenv("TMPDIR"));
4949

5050
// Call the arbitrary function
5151
func();

0 commit comments

Comments
 (0)