Skip to content

Commit

Permalink
Refactor code to support decoding nettrace files
Browse files Browse the repository at this point in the history
  • Loading branch information
xoofx committed Dec 13, 2024
1 parent e1909b9 commit 03e8a6b
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 127 deletions.
19 changes: 16 additions & 3 deletions src/Ultra.Core/DiagnosticPortSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See license.txt file in the project root for full license information.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Net.Sockets;
using Microsoft.Diagnostics.NETCore.Client;
Expand All @@ -29,7 +30,7 @@ internal class DiagnosticPortSession
private FileStream? _nettraceFileStream;
private Task? _eventStreamCopyTask;
private bool _disposed;

public DiagnosticPortSession(int pid, bool sampler, string baseName, CancellationToken token)
{
_pid = pid;
Expand All @@ -40,6 +41,18 @@ public DiagnosticPortSession(int pid, bool sampler, string baseName, Cancellatio
_connectTask = ConnectAndStartProfilingImpl(pid, sampler, baseName, token);
}

public bool TryGetNettraceFilePathIfExists([NotNullWhen(true)] out string? nettraceFilePath)
{
if (_nettraceFilePath is null || !File.Exists(_nettraceFilePath))
{
nettraceFilePath = null;
return false;
}

nettraceFilePath = _nettraceFilePath;
return nettraceFilePath is not null;
}

private async Task ConnectAndStartProfilingImpl(int pid, bool sampler, string baseName, CancellationToken token)
{
CancellationTokenSource linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, _cancelConnectSource.Token);
Expand Down Expand Up @@ -91,7 +104,7 @@ public async Task StartProfiling(CancellationToken token)
{


_nettraceFilePath = Path.Combine(Environment.CurrentDirectory, $"{_baseName}_{(_sampler ? "sampler" : "main")}_{_pid}.nettrace");
_nettraceFilePath = Path.Combine(Environment.CurrentDirectory, $"{_baseName}_{(_sampler ? "sampler" : "clr")}_{_pid}.nettrace");
_nettraceFileStream = new FileStream(_nettraceFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, 65536, FileOptions.Asynchronous);

long keywords = -1;
Expand All @@ -100,7 +113,7 @@ public async Task StartProfiling(CancellationToken token)

if (!_sampler)
{
providerName = "Microsoft-Windows-DotNETRuntime";
providerName = ClrTraceEventParser.ProviderName;
keywords = (long)(
ClrTraceEventParser.Keywords.JITSymbols |
ClrTraceEventParser.Keywords.Exception |
Expand Down
4 changes: 2 additions & 2 deletions src/Ultra.Core/Ultra.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
</PackageReference>
</ItemGroup>

<ItemGroup>
<!--<ItemGroup>
<Content Include="..\Ultra.Sampler\libUltraSampler.dylib">
<Link>libUltraSampler.dylib</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand All @@ -53,5 +53,5 @@
<Link>libUltraSamplerHook.dylib</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</ItemGroup>-->
</Project>
105 changes: 105 additions & 0 deletions src/Ultra.Core/UltraConverterToFirefox.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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;

/// <summary>
/// Converts a list of trace files (one ETL file or a list of nettrace files) to a Firefox profile.
/// </summary>
public abstract class UltraConverterToFirefox : IDisposable
{
private protected readonly List<UltraProfilerTraceFile> TraceFiles;
private protected readonly UltraProfilerOptions Options;
private protected FirefoxProfiler.Profile ProfilerResult;

/// <summary>
/// A generic other category.
/// </summary>
public const int CategoryOther = 0;

/// <summary>
/// The kernel category.
/// </summary>
public const int CategoryKernel = 1;

/// <summary>
/// The native category.
/// </summary>
public const int CategoryNative = 2;

/// <summary>
/// The managed category.
/// </summary>
public const int CategoryManaged = 3;

/// <summary>
/// The GC category.
/// </summary>
public const int CategoryGc = 4;

/// <summary>
/// The JIT category.
/// </summary>
public const int CategoryJit = 5;

/// <summary>
/// The CLR category.
/// </summary>
public const int CategoryClr = 6;

private protected UltraConverterToFirefox(List<UltraProfilerTraceFile> traceFiles, UltraProfilerOptions options)
{
TraceFiles = traceFiles;
this.Options = options;
ProfilerResult = new FirefoxProfiler.Profile(); // Create an empty profile (not used and override by the derived class)
}

/// <inheritdoc />
public abstract void Dispose();

/// <summary>
/// Converts a list of trace files (one ETL file, or a list of nettrace files) to a Firefox profile.
/// </summary>
/// <param name="traceFiles">The list of trace files to convert.</param>
/// <param name="options">The options used for converting.</param>
/// <param name="processIds">The list of process ids to extract from the trace files.</param>
/// <returns>The converted Firefox profile.</returns>
public static FirefoxProfiler.Profile Convert(List<UltraProfilerTraceFile> traceFiles, UltraProfilerOptions options, List<int> processIds)
{
var extensions = traceFiles.Select(x => Path.GetExtension(x.FileName)).ToHashSet(StringComparer.OrdinalIgnoreCase);

if (extensions.Count != 1)
{
throw new ArgumentException($"All trace files must have the same extension. Instead got [{string.Join(", ", extensions)}]");
}

var extension = extensions.First();
if (extension == ".etl")
{
if (traceFiles.Count > 1)
{
throw new ArgumentException("Only one ETL file is supported");
}

using var converter = new UltraConverterToFirefoxEtw(traceFiles, options);
return converter.Convert(processIds);
}
else if (extension == ".nettrace")
{
throw new NotImplementedException();
}
else
{
throw new ArgumentException($"Unsupported trace file extension [{extension}]");
}
}

private FirefoxProfiler.Profile Convert(List<int> processIds)
{
ConvertImpl(processIds);
return ProfilerResult;
}

private protected abstract void ConvertImpl(List<int> processIds);
}
Loading

0 comments on commit 03e8a6b

Please sign in to comment.