Skip to content

Commit

Permalink
Add Thread Start/Stop and samplingId
Browse files Browse the repository at this point in the history
  • Loading branch information
xoofx committed Dec 22, 2024
1 parent 56a2808 commit f6715e3
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 30 deletions.
31 changes: 19 additions & 12 deletions src/Ultra.Core/Parser/UltraNativeCallstackTraceEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal sealed class UltraNativeCallstackTraceEvent : TraceEvent
{
private static readonly string[] _payloadNames =
[
nameof(SamplingId),
nameof(FrameThreadId),
nameof(ThreadState),
nameof(ThreadCpuUsage),
Expand All @@ -26,17 +27,21 @@ internal UltraNativeCallstackTraceEvent(Action<UltraNativeCallstackTraceEvent>?
_target = target;
}

public ulong FrameThreadId => (ulong)GetInt64At(0);
public ulong SamplingId => (ulong)GetInt64At(0);

public UltraSamplerThreadState ThreadState => (UltraSamplerThreadState)GetInt32At(8);
public ulong FrameThreadId => (ulong)GetInt64At(8);

public double ThreadCpuUsage => GetInt32At(12) / 1000.0;
public UltraSamplerThreadState ThreadState => (UltraSamplerThreadState)GetInt32At(16);

public int PreviousFrameCount => GetInt32At(16);
public int ThreadCpuUsageAsInt => GetInt32At(20);

public double ThreadCpuUsage => ThreadCpuUsageAsInt / 1000.0;

public int PreviousFrameCount => GetInt32At(24);

public int FrameSize => GetInt32At(20);
public int FrameSize => GetInt32At(28);

public unsafe ReadOnlySpan<ulong> FrameAddresses => new((byte*)DataStart + 24, FrameSize / sizeof(ulong));
public unsafe ReadOnlySpan<ulong> FrameAddresses => new((byte*)DataStart + 32, FrameSize / sizeof(ulong));

/// <inheritdoc />

Expand All @@ -45,16 +50,18 @@ public override object PayloadValue(int index)
switch (index)
{
case 0:
return FrameThreadId;
return SamplingId;
case 1:
return (int)ThreadState;
return FrameThreadId;
case 2:
return GetInt32At(12);
return (int)ThreadState;
case 3:
return PreviousFrameCount;
return ThreadCpuUsageAsInt;
case 4:
return FrameSize;
return PreviousFrameCount;
case 5:
return FrameSize;
case 6:
return FrameAddresses.ToArray();
default:
throw new ArgumentOutOfRangeException(nameof(index));
Expand All @@ -80,4 +87,4 @@ protected override void Dispatch()
protected override void Validate()
{
}
}
}
79 changes: 79 additions & 0 deletions src/Ultra.Core/Parser/UltraNativeThreadStartTraceEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// 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.

using System.Text;
using Microsoft.Diagnostics.Tracing;
using Ultra.Sampler;

namespace Ultra.Core;

internal sealed class UltraNativeThreadStartTraceEvent : TraceEvent
{
private static readonly string[] _payloadNames =
[
nameof(SamplingId),
nameof(FrameThreadId),
nameof(ThreadNameSize),
nameof(ThreadName),
];

private Action<UltraNativeThreadStartTraceEvent>? _target;

internal UltraNativeThreadStartTraceEvent(Action<UltraNativeThreadStartTraceEvent>? target, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName)
{
_target = target;
}

public ulong SamplingId => (ulong)GetInt64At(0);

public ulong FrameThreadId => (ulong)GetInt64At(8);

public int ThreadNameSize => GetInt32At(16);

public unsafe byte* ThreadNamePointer => (byte*)DataStart + 24 + ThreadNameSize;

public unsafe string ThreadName => Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ThreadNamePointer, ThreadNameSize));

/// <inheritdoc />

public override object PayloadValue(int index)
{
switch (index)
{
case 0:
return SamplingId;
case 1:
return FrameThreadId;
case 2:
return ThreadNameSize;
case 3:
unsafe
{
return new ReadOnlySpan<byte>(ThreadNamePointer, ThreadNameSize).ToArray();
}
default:
throw new ArgumentOutOfRangeException(nameof(index));
}
}

public override string[] PayloadNames => _payloadNames;

/// <inheritdoc />
protected override Delegate? Target
{
get => _target;
set => _target = (Action<UltraNativeThreadStartTraceEvent>?)value;
}

/// <inheritdoc />
protected override void Dispatch()
{
_target?.Invoke(this);
}

/// <inheritdoc />
protected override void Validate()
{
}
}
62 changes: 62 additions & 0 deletions src/Ultra.Core/Parser/UltraNativeThreadStopTraceEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.

using Microsoft.Diagnostics.Tracing;

namespace Ultra.Core;

internal sealed class UltraNativeThreadStopTraceEvent : TraceEvent
{
private static readonly string[] _payloadNames =
[
nameof(SamplingId),
nameof(FrameThreadId),
];

private Action<UltraNativeThreadStopTraceEvent>? _target;

internal UltraNativeThreadStopTraceEvent(Action<UltraNativeThreadStopTraceEvent>? target, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) : base(eventID, task, taskName, taskGuid, opcode, opcodeName, providerGuid, providerName)
{
_target = target;
}

public ulong SamplingId => (ulong)GetInt64At(0);

public ulong FrameThreadId => (ulong)GetInt64At(8);

/// <inheritdoc />

public override object PayloadValue(int index)
{
switch (index)
{
case 0:
return SamplingId;
case 1:
return FrameThreadId;
default:
throw new ArgumentOutOfRangeException(nameof(index));
}
}

public override string[] PayloadNames => _payloadNames;

/// <inheritdoc />
protected override Delegate? Target
{
get => _target;
set => _target = (Action<UltraNativeThreadStopTraceEvent>?)value;
}

/// <inheritdoc />
protected override void Dispatch()
{
_target?.Invoke(this);
}

/// <inheritdoc />
protected override void Validate()
{
}
}
26 changes: 23 additions & 3 deletions src/Ultra.Core/Parser/UltraSamplerParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,19 @@ public event Action<UltraNativeModuleTraceEvent> EventNativeModule
add => source.RegisterEventTemplate(CreateUltraNativeModuleTraceEvent(value));
remove => source.UnregisterEventTemplate(value, UltraSamplerConstants.NativeModuleEventId, ProviderGuid);
}


public event Action<UltraNativeThreadStartTraceEvent> EventNativeThreadStart
{
add => source.RegisterEventTemplate(CreateUltraNativeThreadStartTraceEvent(value));
remove => source.UnregisterEventTemplate(value, UltraSamplerConstants.NativeThreadStartEventId, ProviderGuid);
}

public event Action<UltraNativeThreadStopTraceEvent> EventNativeThreadStop
{
add => source.RegisterEventTemplate(CreateUltraNativeThreadStopTraceEvent(value));
remove => source.UnregisterEventTemplate(value, UltraSamplerConstants.NativeThreadStopEventId, ProviderGuid);
}

/// <inheritdoc />
protected override string GetProviderName() => UltraSamplerConstants.ProviderName;

Expand All @@ -44,6 +56,8 @@ protected override void EnumerateTemplates(Func<string, string, EventFilterRespo
[
CreateUltraNativeCallstackTraceEvent(null),
CreateUltraNativeModuleTraceEvent(null),
CreateUltraNativeThreadStartTraceEvent(null),
CreateUltraNativeThreadStopTraceEvent(null)
];
}

Expand All @@ -57,8 +71,14 @@ protected override void EnumerateTemplates(Func<string, string, EventFilterRespo
}

private static TraceEvent CreateUltraNativeCallstackTraceEvent(Action<UltraNativeCallstackTraceEvent>? value)
=> new UltraNativeCallstackTraceEvent(value, UltraSamplerConstants.NativeCallStackEventId, 0, "OnNativeCallStack", Guid.Empty, 0, "OnNativeCallStack", ProviderGuid, ProviderName);
=> new UltraNativeCallstackTraceEvent(value, UltraSamplerConstants.NativeCallStackEventId, 0, "NativeCallStack", Guid.Empty, 0, "NativeCallStack", ProviderGuid, ProviderName);

private static TraceEvent CreateUltraNativeModuleTraceEvent(Action<UltraNativeModuleTraceEvent>? value)
=> new UltraNativeModuleTraceEvent(value, UltraSamplerConstants.NativeModuleEventId, 0, "OnNativeModule", Guid.Empty, 0, "OnNativeModule", ProviderGuid, ProviderName);
=> new UltraNativeModuleTraceEvent(value, UltraSamplerConstants.NativeModuleEventId, 0, "NativeModule", Guid.Empty, 0, "NativeModule", ProviderGuid, ProviderName);

private static TraceEvent CreateUltraNativeThreadStartTraceEvent(Action<UltraNativeThreadStartTraceEvent>? value)
=> new UltraNativeThreadStartTraceEvent(value, UltraSamplerConstants.NativeThreadStartEventId, 0, "NativeThreadStart", Guid.Empty, 0, "NativeThreadStart", ProviderGuid, ProviderName);

private static TraceEvent CreateUltraNativeThreadStopTraceEvent(Action<UltraNativeThreadStopTraceEvent>? value)
=> new UltraNativeThreadStopTraceEvent(value, UltraSamplerConstants.NativeThreadStopEventId, 0, "NativeThreadStop", Guid.Empty, 0, "NativeThreadStop", ProviderGuid, ProviderName);
}
22 changes: 20 additions & 2 deletions src/Ultra.Sampler/MacOS/MacOSUltraSampler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal unsafe class MacOSUltraSampler : UltraSampler
private Thread? _samplerThread;
private ulong _samplerThreadId;
private readonly AutoResetEvent _samplerResumeThreadEvent;
private ulong _samplingId;

// Frames information
private const int MaximumFrames = 4096;
Expand Down Expand Up @@ -148,7 +149,7 @@ private unsafe void RunImpl()
}
catch (Exception ex)
{
Console.Error.WriteLine($"Ultra-Sampler unexpected exception while sampling: {ex}");
Console.Error.WriteLine($"Ultra-Sampler stopped. Unexpected exception while sampling: {ex}");
}
}

Expand Down Expand Up @@ -354,6 +355,18 @@ private unsafe void Sample(MacOS.MacOSLibSystem.mach_port_t rootTask, NativeSamp
continue;
}

// If thread was not active before, simulate a thread start event with its name
if (!_activeThreadIds.Contains(threadInfo.thread_id))
{
var threadName = new ReadOnlySpan<byte>(threadExtendedInfo.pth_name, 64);
var length = threadName.IndexOf((byte)0);
if (length < 0)
{
length = 64;
}
_samplerEventSource.NativeThreadStart(_samplingId, threadInfo.thread_id, length, threadExtendedInfo.pth_name);
}

// -------------------------------------------------------------------
// Suspend the thread
// -------------------------------------------------------------------
Expand Down Expand Up @@ -385,7 +398,7 @@ private unsafe void Sample(MacOS.MacOSLibSystem.mach_port_t rootTask, NativeSamp
frameCount -= sameFrameCount;

// Long only the delta frames
samplingDelegate(threadInfo.thread_id, (int)threadExtendedInfo.pth_run_state, (int)threadExtendedInfo.pth_cpu_usage, sameFrameCount, frameCount * sizeof(ulong), (byte*)pFrames);
samplingDelegate(_samplingId, threadInfo.thread_id, (int)threadExtendedInfo.pth_run_state, (int)threadExtendedInfo.pth_cpu_usage, sameFrameCount, frameCount * sizeof(ulong), (byte*)pFrames);
}

// Cleanup threads that are no longer active
Expand All @@ -397,11 +410,16 @@ private unsafe void Sample(MacOS.MacOSLibSystem.mach_port_t rootTask, NativeSamp
{
_freeCompressedFramesIndices.Add(compressedFrameIndex);
}

_samplerEventSource.NativeThreadStop(_samplingId, threadInfo.thread_id);
}
}

// Swap the active and current thread ids
(_currentThreadIds, _activeThreadIds) = (_activeThreadIds, _currentThreadIds);

// Increment the sampling id
_samplingId++;
}

private int ComputeSameFrameCount(ulong threadId, int frameCount, ulong* frames)
Expand Down
2 changes: 1 addition & 1 deletion src/Ultra.Sampler/MacOS/NativeSamplingDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

namespace Ultra.Sampler.MacOS;

internal unsafe delegate void NativeSamplingDelegate(ulong threadId, int threadState, int threadCpuUsage, int previousFrameCount, int deltaFrameSizeInBytes, byte* deltaFrames);
internal unsafe delegate void NativeSamplingDelegate(ulong samplingId, ulong threadId, int threadState, int threadCpuUsage, int previousFrameCount, int deltaFrameSizeInBytes, byte* deltaFrames);
8 changes: 8 additions & 0 deletions src/Ultra.Sampler/UltraSamplerConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ public static class UltraSamplerConstants

public const int NativeModuleEventId = 2;

public const int NativeThreadStartEventId = 3;

public const int NativeThreadStopEventId = 4;

public const int TaskNativeCallStackEventId = 1;

public const int TaskNativeModuleEventId = 2;

public const int TaskNativeThreadStartEventId = 3;

public const int TaskNativeThreadStopEventId = 4;
}
Loading

0 comments on commit f6715e3

Please sign in to comment.