-
Notifications
You must be signed in to change notification settings - Fork 32
Dotnet-trace (.netrace) support #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 8 commits
ab48b31
a36fa9a
2bc897b
6188397
3401409
6063c0b
63ad24d
51b9b73
0cd5b7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
using Microsoft.Diagnostics.Tracing; | ||
using Microsoft.Diagnostics.Tracing.Etlx; | ||
using Microsoft.Performance.SDK; | ||
using System; | ||
using Utilities; | ||
|
||
namespace DotNetEventPipe.DataOutputTypes | ||
{ | ||
/// <summary> | ||
/// A GenericEvent | ||
/// </summary> | ||
public class GenericEvent | ||
{ | ||
public string EventName { get; } | ||
public TraceEventID ID { get; } | ||
public TraceEventKeyword Keywords { get; } | ||
public TraceEventLevel Level { get; } | ||
public TraceEventOpcode Opcode { get; } | ||
public string OpcodeName { get; } | ||
public string[] PayloadNames { get; } | ||
public object[] PayloadValues { get; } | ||
public Guid ProviderGuid { get; } | ||
public string ProviderName { get; } | ||
public int ProcessID { get; } | ||
public string ProcessName { get; } | ||
public int ProcessorNumber { get; } | ||
public int ThreadID { get; } | ||
public Timestamp Timestamp { get; } | ||
public string[] CallStack { get; } | ||
/// <summary> | ||
/// filename of the binary / library for the instruction pointer | ||
/// </summary> | ||
public TraceModuleFile Module { get; } | ||
/// <summary> | ||
/// Functionname of the instruction pointer | ||
/// </summary> | ||
public string FullMethodName { get; } | ||
|
||
public GenericEvent(string eventName, TraceEventID id, TraceEventKeyword keywords, TraceEventLevel level, TraceEventOpcode opcode, string opcodeName, string[] payloadNames, object[] payloadValues, Guid providerGuid, string providerName, int processID, string processName, int processorNumber, int threadID, Timestamp timestamp, string[] callStack, TraceModuleFile module, string fullMethodName) | ||
{ | ||
EventName = Common.StringIntern(eventName); | ||
ID = id; | ||
Keywords = keywords; | ||
Level = level; | ||
Opcode = opcode; | ||
OpcodeName = Common.StringIntern(opcodeName); | ||
PayloadNames = payloadNames; | ||
PayloadValues = payloadValues; | ||
ProviderGuid = providerGuid; | ||
ProviderName = providerName; | ||
ProcessID = processID; | ||
ProcessName = Common.StringIntern(processName); | ||
ProcessorNumber = processorNumber; | ||
ThreadID = threadID; | ||
Timestamp = timestamp; | ||
CallStack = callStack; | ||
Module = module; | ||
FullMethodName = Common.StringIntern(fullMethodName); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
using Microsoft.Diagnostics.Tracing.Etlx; | ||
using Microsoft.Performance.SDK; | ||
using Utilities; | ||
|
||
namespace DotNetEventPipe.DataOutputTypes | ||
{ | ||
/// <summary> | ||
/// A CPU sampling event that samples at some interval | ||
/// Shows process and threads, and stacks that were running on which CPUs at specific times. | ||
/// </summary> | ||
public readonly struct ThreadSamplingEvent | ||
{ | ||
public int ProcessID { get; } | ||
public string ProcessName { get; } | ||
public int ProcessorNumber { get; } | ||
public int ThreadID { get; } | ||
public Timestamp Timestamp { get; } | ||
public string[] CallStack { get; } | ||
/// <summary> | ||
/// filename of the binary / library for the instruction pointer | ||
/// </summary> | ||
public TraceModuleFile Module { get; } | ||
/// <summary> | ||
/// Functionname of the instruction pointer | ||
/// </summary> | ||
public string FullMethodName { get; } | ||
|
||
public ThreadSamplingEvent( | ||
int processID, | ||
string processName, | ||
int processorNumber, | ||
int threadID, | ||
Timestamp timestamp, | ||
string[] callStack, | ||
TraceModuleFile module, | ||
string fullMethodName | ||
) | ||
{ | ||
ProcessID = processID; | ||
ProcessName = Common.StringIntern(processName); | ||
ProcessorNumber = processorNumber; | ||
ThreadID = threadID; | ||
Timestamp = timestamp; | ||
CallStack = callStack; // Cache whole common stack?? | ||
Module = module; | ||
FullMethodName = Common.StringIntern(fullMethodName); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Microsoft.Diagnostics.Tracing.Etlx; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
|
||
namespace DotnetEventpipe.DataOutputTypes | ||
{ | ||
public class TraceCallStackProcessed | ||
{ | ||
public string[] CallStack { get; set; } | ||
public TraceModuleFile Module { get; set; } | ||
public string FullMethodName { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.1</TargetFramework> | ||
<Version>1.0.0</Version> | ||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||
<Authors>Microsoft</Authors> | ||
<Company>Microsoft Corp.</Company> | ||
<Product>Performance ToolKit</Product> | ||
<Description>Contains the .NET Trace datasource plugin.</Description> | ||
<PackageId>Microsoft.Performance.Toolkit.Plugins.DotNetEvent</PackageId> | ||
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright> | ||
<RepositoryUrl>https://github.com/microsoft/Microsoft-Performance-Tools-Linux-Android</RepositoryUrl> | ||
<PackageProjectUrl>https://github.com/microsoft/Microsoft-Performance-Tools-Linux-Android</PackageProjectUrl> | ||
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<None Include="..\LICENSE.txt"> | ||
<Pack>True</Pack> | ||
<PackagePath></PackagePath> | ||
</None> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.0.0"> | ||
<GeneratePathProperty>True</GeneratePathProperty> | ||
</PackageReference> | ||
<PackageReference Include="Microsoft.Performance.SDK" Version="1.0.16" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Utilities\Utilities.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<TraceEventWorkaround Include="$(PkgMicrosoft_Diagnostics_Tracing_Traceevent)\lib\netstandard2.0\Microsoft.Diagnostics.Tracing.TraceEvent.dll;$(PkgMicrosoft_Diagnostics_Tracing_Traceevent)\lib\netstandard2.0\Microsoft.Diagnostics.FastSerialization.dll" /> | ||
</ItemGroup> | ||
<Target Name="CopyRulesToTarget" AfterTargets="Build"> | ||
<Copy SourceFiles="@(TraceEventWorkaround)" DestinationFolder="$(TargetDir)" /> | ||
</Target> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using DotNetEventPipe.Tables; | ||
using Microsoft.Diagnostics.Tracing; | ||
using Microsoft.Performance.SDK.Processing; | ||
using Microsoft.Diagnostics.Tracing.EventPipe; | ||
using Microsoft.Diagnostics.Tracing.Etlx; | ||
|
||
namespace DotNetEventPipe | ||
{ | ||
public sealed class DotnetTraceDataProcessor | ||
: CustomDataProcessor | ||
{ | ||
private readonly string[] filePaths; | ||
private IReadOnlyDictionary<string, TraceEventProcessor> fileContent; | ||
private DataSourceInfo dataSourceInfo; | ||
|
||
public DotnetTraceDataProcessor( | ||
string[] filePaths, | ||
ProcessorOptions options, | ||
IApplicationEnvironment applicationEnvironment, | ||
IProcessorEnvironment processorEnvironment) | ||
: base(options, applicationEnvironment, processorEnvironment) | ||
{ | ||
// | ||
// Assign the files array to a readonly backing field. | ||
// | ||
|
||
this.filePaths = filePaths; | ||
} | ||
|
||
public override DataSourceInfo GetDataSourceInfo() | ||
{ | ||
// The DataSourceInfo is used to tell analzyer the time range of the data(if applicable) and any other relevant data for rendering / synchronizing. | ||
|
||
return this.dataSourceInfo; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: extra line |
||
} | ||
|
||
protected override Task ProcessAsyncCore( | ||
IProgress<int> progress, | ||
CancellationToken cancellationToken) | ||
{ | ||
var contentDictionary = new Dictionary<string, TraceEventProcessor>(); | ||
|
||
foreach (var path in this.filePaths) | ||
{ | ||
var traceStartTime = DateTime.UtcNow.Date; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not needed? |
||
|
||
// EventPipeEventSource doesn't expose the callstacks - https://github.com/Microsoft/perfview/blob/main/src/TraceEvent/EventPipe/EventPipeFormat.md | ||
// But currently it's SessionDuration, SessionStartTime are correct | ||
// Can remove when when this is released - https://github.com/microsoft/perfview/pull/1635 | ||
var dotnetFileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add using |
||
using (var traceSource = new EventPipeEventSource(dotnetFileStream)) | ||
{ | ||
traceSource.Process(); | ||
this.dataSourceInfo = new DataSourceInfo(0, traceSource.SessionDuration.Ticks * 100, traceSource.SessionStartTime.ToUniversalTime()); | ||
} | ||
|
||
var tmpEtlx = Path.Combine(Path.GetTempPath(), Path.GetFileName(path) + ".etlx"); | ||
|
||
string traceLogPath = TraceLog.CreateFromEventPipeDataFile(path, tmpEtlx); | ||
using (TraceLog traceLog = new TraceLog(traceLogPath)) | ||
{ | ||
TraceLogEventSource source = traceLog.Events.GetSource(); | ||
|
||
var traceEventProcessor = new TraceEventProcessor(); | ||
contentDictionary[path] = traceEventProcessor; | ||
source.AllEvents += traceEventProcessor.ProcessTraceEvent; | ||
source.Process(); | ||
// Below will work when this is released - https://github.com/microsoft/perfview/pull/1635 | ||
//this.dataSourceInfo = new DataSourceInfo(0, source.SessionDuration.Ticks * 100, source.SessionStartTime.ToUniversalTime()); | ||
} | ||
File.Delete(tmpEtlx); | ||
} | ||
|
||
this.fileContent = new ReadOnlyDictionary<string, TraceEventProcessor>(contentDictionary); | ||
|
||
return Task.CompletedTask; | ||
} | ||
|
||
protected override void BuildTableCore( | ||
TableDescriptor tableDescriptor, | ||
ITableBuilder tableBuilder) | ||
{ | ||
// | ||
// Instantiate the table, and pass the tableBuilder to it. | ||
// | ||
|
||
var table = this.InstantiateTable(tableDescriptor.Type); | ||
table.Build(tableBuilder); | ||
} | ||
|
||
private TraceEventTableBase InstantiateTable(Type tableType) | ||
{ | ||
// | ||
// This private method is added to activate the given table type and pass in the file content. | ||
// | ||
|
||
var instance = Activator.CreateInstance(tableType, new[] { this.fileContent, }); | ||
return (TraceEventTableBase)instance; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using Microsoft.Performance.SDK.Processing; | ||
|
||
namespace DotNetEventPipe | ||
{ | ||
[ProcessingSource( | ||
"{890C0A11-011E-43E1-AE28-7E1A903A6633}", // The GUID must be unique for your Custom Data Source. You can use Visual Studio's Tools -> Create Guid… tool to create a new GUID | ||
".NET (dotnet-trace)", // The Custom Data Source MUST have a name | ||
@".net trace EventPipe")] // The Custom Data Source MUST have a description | ||
[FileDataSource( | ||
".nettrace", // A file extension is REQUIRED | ||
"dotnet-trace")] // A description is OPTIONAL. The description is what appears in the file open menu to help users understand what the file type actually is. | ||
|
||
// | ||
// There are two methods to creating a Custom Data Source that is recognized by UI: | ||
// 1. Using the helper abstract base classes | ||
// 2. Implementing the raw interfaces | ||
// This sample demonstrates method 1 where the ProcessingSource abstract class | ||
// helps provide a public parameterless constructor and implement the IProcessingSource interface | ||
// | ||
|
||
public class DotnetTraceProcessingSource | ||
: ProcessingSource | ||
{ | ||
private IApplicationEnvironment applicationEnvironment; | ||
|
||
public override ProcessingSourceInfo GetAboutInfo() | ||
{ | ||
return new ProcessingSourceInfo() | ||
{ | ||
ProjectInfo = new ProjectInfo() { Uri = "https://aka.ms/linuxperftools" }, | ||
}; | ||
} | ||
|
||
protected override void SetApplicationEnvironmentCore(IApplicationEnvironment applicationEnvironment) | ||
{ | ||
// | ||
// Saves the given application environment into this instance | ||
// | ||
|
||
this.applicationEnvironment = applicationEnvironment; | ||
} | ||
|
||
protected override bool IsDataSourceSupportedCore(IDataSource dataSource) | ||
{ | ||
return dataSource.IsFile() && Path.GetExtension(dataSource.Uri.LocalPath) == ".nettrace"; | ||
} | ||
|
||
protected override ICustomDataProcessor CreateProcessorCore( | ||
IEnumerable<IDataSource> dataSources, | ||
IProcessorEnvironment processorEnvironment, | ||
ProcessorOptions options) | ||
{ | ||
// | ||
// Create a new instance implementing ICustomDataProcessor here to process the specified data sources. | ||
// Note that you can have more advanced logic here to create different processors if you would like based on the file, or any other criteria. | ||
// You are not restricted to always returning the same type from this method. | ||
// | ||
|
||
return new DotnetTraceDataProcessor( | ||
dataSources.Select(x => x.Uri.LocalPath).ToArray(), | ||
options, | ||
this.applicationEnvironment, | ||
processorEnvironment); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe make readonly?