Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/Core/src/Diagnostics/DiagnosticsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ public DiagnosticsManager(IEnumerable<IDiagnosticMetrics> metrics, IEnumerable<I

public ActivitySource ActivitySource { get; }

public bool HasActivityListeners => ActivitySource.HasListeners();

public Meter? Meter { get; }

public void GetTags(object source, out TagList tagList)
Expand Down
2 changes: 0 additions & 2 deletions src/Core/src/Diagnostics/IDiagnosticsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ internal interface IDiagnosticsManager
{
ActivitySource ActivitySource { get; }

bool HasActivityListeners { get; }

Meter? Meter { get; }

void GetTags(object source, out TagList tagList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,52 +10,18 @@ internal static class DiagnosticInstrumentation
/// </summary>
/// <param name="view">The view to instrument.</param>
/// <returns>Returns an instance of <see cref="LayoutMeasureInstrumentation"/> if instrumentation is supported; otherwise, null.</returns>
public static LayoutMeasureInstrumentation? StartLayoutMeasure(IView view)
{
if (!RuntimeFeature.IsMeterSupported)
{
return null;
}

var diagnostics = view.GetMauiDiagnostics();
if (diagnostics is null)
{
return null;
}

var metrics = diagnostics.GetMetrics<LayoutDiagnosticMetrics>();
if (!diagnostics.HasActivityListeners && metrics?.IsMeasureEnabled != true)
{
return null;
}

return new LayoutMeasureInstrumentation(view, diagnostics, metrics);
}
public static LayoutMeasureInstrumentation? StartLayoutMeasure(IView view) =>
RuntimeFeature.IsMeterSupported
? new LayoutMeasureInstrumentation(view)
: null;

/// <summary>
/// Starts layout arrange instrumentation for the specified view.
/// </summary>
/// <param name="view">The view to instrument.</param>
/// <returns>Returns an instance of <see cref="LayoutArrangeInstrumentation"/> if instrumentation is supported; otherwise, null.</returns>
public static LayoutArrangeInstrumentation? StartLayoutArrange(IView view)
{
if (!RuntimeFeature.IsMeterSupported)
{
return null;
}

var diagnostics = view.GetMauiDiagnostics();
if (diagnostics is null)
{
return null;
}

var metrics = diagnostics.GetMetrics<LayoutDiagnosticMetrics>();
if (!diagnostics.HasActivityListeners && metrics?.IsArrangeEnabled != true)
{
return null;
}

return new LayoutArrangeInstrumentation(view, diagnostics, metrics);
}
public static LayoutArrangeInstrumentation? StartLayoutArrange(IView view) =>
RuntimeFeature.IsMeterSupported
? new LayoutArrangeInstrumentation(view)
: null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,21 @@ namespace Microsoft.Maui.Diagnostics;
/// <summary>
/// Instrumentation for the layout arrange phase of a view.
/// </summary>
readonly struct LayoutArrangeInstrumentation : System.IDisposable
readonly struct LayoutArrangeInstrumentation(IView view) : IDiagnosticInstrumentation
{
readonly IView _view;
readonly IDiagnosticsManager _diagnostics;
readonly LayoutDiagnosticMetrics? _metrics;
readonly Activity? _activity;
readonly bool _metricsDurationStarted;
readonly long _metricsStartTimestamp;

public LayoutArrangeInstrumentation(IView view, IDiagnosticsManager diagnostics, LayoutDiagnosticMetrics? metrics)
{
_view = view;
_diagnostics = diagnostics;
_metrics = metrics;

if (diagnostics.HasActivityListeners)
{
diagnostics.GetTags(view, out var tagList);
_activity = diagnostics.ActivitySource.StartActivity(
ActivityKind.Internal,
name: $"Arrange {view.GetType().Name}",
tags: tagList);
}
else
{
_activity = null;
}

_metricsDurationStarted = metrics?.IsArrangeDurationEnabled == true;
_metricsStartTimestamp = _metricsDurationStarted
? Stopwatch.GetTimestamp()
: 0;
}
readonly Activity? _activity = view.StartDiagnosticActivity("Arrange");

/// <summary>
/// Disposes the instrumentation and stops the diagnostic activity.
/// </summary>
public void Dispose()
{
var metrics = _metrics;
var recordDuration = _metricsDurationStarted && metrics?.IsArrangeDurationEnabled == true;
var duration = recordDuration
? LayoutDiagnosticMetrics.GetElapsedNanoseconds(_metricsStartTimestamp)
: 0;

_activity?.Stop();
public void Dispose() =>
view.StopDiagnostics(_activity, this);

if (metrics?.IsArrangeEnabled == true)
{
_diagnostics.GetTags(_view, out var tagList);
metrics.RecordArrange(duration, recordDuration, in tagList);
}

_activity?.Dispose();
}
/// <summary>
/// Records the stopping of the instrumentation and publishes various metrics.
/// </summary>
/// <param name="diagnostics">The <see cref="IDiagnosticsManager"/> instance.</param>
/// <param name="tagList">The tags associated with the instrumentation.</param>
public void Stopped(IDiagnosticsManager diagnostics, in TagList tagList) =>
diagnostics.GetMetrics<LayoutDiagnosticMetrics>()?.RecordArrange(_activity?.Duration, in tagList);
}
63 changes: 19 additions & 44 deletions src/Core/src/Diagnostics/Instrumentation/LayoutDiagnosticMetrics.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Diagnostics;
using System.Diagnostics.Metrics;

Expand Down Expand Up @@ -28,18 +29,6 @@ internal class LayoutDiagnosticMetrics : IDiagnosticMetrics
/// </summary>
internal Histogram<int>? ArrangeHistogram { get; private set; }

internal bool IsMeasureEnabled =>
MeasureCounter?.Enabled == true ||
MeasureHistogram?.Enabled == true;

internal bool IsMeasureDurationEnabled => MeasureHistogram?.Enabled == true;

internal bool IsArrangeEnabled =>
ArrangeCounter?.Enabled == true ||
ArrangeHistogram?.Enabled == true;

internal bool IsArrangeDurationEnabled => ArrangeHistogram?.Enabled == true;

/// <inheritdoc/>
public void Create(Meter meter)
{
Expand All @@ -53,52 +42,38 @@ public void Create(Meter meter)
/// <summary>
/// Records a measure operation with an optional duration and associated tags.
/// </summary>
/// <param name="duration">The duration of the measure operation in nanoseconds.</param>
/// <param name="recordDuration">Whether a duration should be recorded.</param>
/// <param name="duration">The duration of the measure operation.</param>
/// <param name="tagList">The tags associated with the measure operation.</param>
public void RecordMeasure(int duration, bool recordDuration, in TagList tagList)
public void RecordMeasure(TimeSpan? duration, in TagList tagList)
{
if (MeasureCounter?.Enabled == true)
{
MeasureCounter.Add(1, tagList);
}
MeasureCounter?.Add(1, tagList);

if (recordDuration && MeasureHistogram?.Enabled == true)
if (duration is not null)
{
MeasureHistogram.Record(duration, tagList);
#if NET9_0_OR_GREATER
MeasureHistogram?.Record((int)duration.Value.TotalNanoseconds, tagList);
#else
MeasureHistogram?.Record((int)(duration.Value.TotalMilliseconds * 1_000_000), tagList);
#endif
}
}

/// <summary>
/// Records an arrange operation with an optional duration and associated tags.
/// </summary>
/// <param name="duration">The duration of the arrange operation in nanoseconds.</param>
/// <param name="recordDuration">Whether a duration should be recorded.</param>
/// <param name="duration">The duration of the arrange operation.</param>
/// <param name="tagList">The tags associated with the arrange operation.</param>
public void RecordArrange(int duration, bool recordDuration, in TagList tagList)
public void RecordArrange(TimeSpan? duration, in TagList tagList)
{
if (ArrangeCounter?.Enabled == true)
{
ArrangeCounter.Add(1, tagList);
}
ArrangeCounter?.Add(1, tagList);

if (recordDuration && ArrangeHistogram?.Enabled == true)
if (duration is not null)
{
ArrangeHistogram.Record(duration, tagList);
#if NET9_0_OR_GREATER
ArrangeHistogram?.Record((int)duration.Value.TotalNanoseconds, tagList);
#else
ArrangeHistogram?.Record((int)(duration.Value.TotalMilliseconds * 1_000_000), tagList);
#endif
}
}

internal static int GetElapsedNanoseconds(long startTimestamp)
{
var elapsedTimestamp = Stopwatch.GetTimestamp() - startTimestamp;
if (elapsedTimestamp <= 0)
{
return 0;
}

var elapsedNanoseconds = elapsedTimestamp * (1_000_000_000.0 / Stopwatch.Frequency);
return elapsedNanoseconds >= int.MaxValue
? int.MaxValue
: (int)elapsedNanoseconds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,21 @@ namespace Microsoft.Maui.Diagnostics;
/// <summary>
/// Instrumentation for measuring layout operations in a view.
/// </summary>
readonly struct LayoutMeasureInstrumentation : System.IDisposable
readonly struct LayoutMeasureInstrumentation(IView view) : IDiagnosticInstrumentation
{
readonly IView _view;
readonly IDiagnosticsManager _diagnostics;
readonly LayoutDiagnosticMetrics? _metrics;
readonly Activity? _activity;
readonly bool _metricsDurationStarted;
readonly long _metricsStartTimestamp;

public LayoutMeasureInstrumentation(IView view, IDiagnosticsManager diagnostics, LayoutDiagnosticMetrics? metrics)
{
_view = view;
_diagnostics = diagnostics;
_metrics = metrics;

if (diagnostics.HasActivityListeners)
{
diagnostics.GetTags(view, out var tagList);
_activity = diagnostics.ActivitySource.StartActivity(
ActivityKind.Internal,
name: $"Measure {view.GetType().Name}",
tags: tagList);
}
else
{
_activity = null;
}

_metricsDurationStarted = metrics?.IsMeasureDurationEnabled == true;
_metricsStartTimestamp = _metricsDurationStarted
? Stopwatch.GetTimestamp()
: 0;
}
readonly Activity? _activity = view.StartDiagnosticActivity("Measure");

/// <summary>
/// Disposes the instrumentation and stops the diagnostic activity.
/// </summary>
public void Dispose()
{
var metrics = _metrics;
var recordDuration = _metricsDurationStarted && metrics?.IsMeasureDurationEnabled == true;
var duration = recordDuration
? LayoutDiagnosticMetrics.GetElapsedNanoseconds(_metricsStartTimestamp)
: 0;

_activity?.Stop();
public void Dispose() =>
view.StopDiagnostics(_activity, this);

if (metrics?.IsMeasureEnabled == true)
{
_diagnostics.GetTags(_view, out var tagList);
metrics.RecordMeasure(duration, recordDuration, in tagList);
}

_activity?.Dispose();
}
/// <summary>
/// Records the stopping of the instrumentation and publishes various metrics.
/// </summary>
/// <param name="diagnostics">The <see cref="IDiagnosticsManager"/> instance.</param>
/// <param name="tagList">The tags associated with the instrumentation.</param>
public void Stopped(IDiagnosticsManager diagnostics, in TagList tagList) =>
diagnostics.GetMetrics<LayoutDiagnosticMetrics>()?.RecordMeasure(_activity?.Duration, in tagList);
}
Loading
Loading