-
Notifications
You must be signed in to change notification settings - Fork 33
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 all 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,51 @@ | ||
# Microsoft Performance Tools Linux / Android - Developer Information | ||
|
||
# Prerequisites | ||
|
||
See [Readme Dev PreReqs](Readme.md#Dev%20prereqs) | ||
|
||
# Code Editing | ||
|
||
## Entire Project | ||
If working on the entire project, or editing .sln or .csproj files | ||
[Visual Studio](https://visualstudio.microsoft.com/) is recommended | ||
|
||
Open [Microsoft-Perf-Tools-Linux-Android.sln](Microsoft-Perf-Tools-Linux-Android.sln) in VS | ||
|
||
## Single Files | ||
Use your favorite editor | ||
|
||
## Build & Test | ||
|
||
### Cross Platform Cmd-Line | ||
- ```dotnet build``` | ||
- ```dotnet test``` | ||
|
||
### IDE | ||
- VS Build Solution or Build Project | ||
|
||
# Debugging & Testing | ||
|
||
## Dev inner loop | ||
It's often fastest to debug the Unit Test examples since they wrap the plugins. This method keeps runtime overhead to a minimum. See the various *UnitTest projects | ||
|
||
- VS Test Explorer is a great way to visualize / run / debug tests. Test -> Test Explorer | ||
|
||
## Plugin visualization and trace testing | ||
- After getting some stabilization in a plugin, it's often fastest to test or investigate multiple traces using a GUI. | ||
|
||
- The plugins are not tied to any specific GUI. However the GUI does need to support the [Microsoft Performance Toolkit SDK](https://github.com/microsoft/microsoft-performance-toolkit-sdk) | ||
|
||
### WPA GUI | ||
|
||
- Using VS2022 Launch Profiles | ||
- To Start WPA with your plugin (doesn't auto-open file) | ||
- Executable | ||
- "C:\PATH\TO\wpa.exe" | ||
- Command line arguments - | ||
- -addsearchdir "C:\src\Microsoft-Performance-Tools-Linux-Android\ThePlugin\bin\Debug" | ||
- To Start WPA with your plugin AND auto-open file | ||
- Executable | ||
- "C:\PATH\TO\wpa.exe" | ||
- Command line arguments - | ||
- -addsearchdir "C:\src\Microsoft-Performance-Tools-Linux-Android\ThePlugin\bin\Debug" -i "C:\PATH\TO\tracefile.ext" |
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.1"> | ||
<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,119 @@ | ||
// 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) | ||
{ | ||
const string ReadPastEndOfStreamExceptionMessage = "Read past end of stream."; // Trace can be partially written but still have data - https://github.com/microsoft/perfview/issues/1637 | ||
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? |
||
|
||
var tmpEtlx = Path.Combine(Path.GetTempPath(), Path.GetFileName(path) + ".etlx"); | ||
var traceEventProcessor = new TraceEventProcessor(); | ||
try | ||
{ | ||
string traceLogPath = TraceLog.CreateFromEventPipeDataFile(path, tmpEtlx); | ||
using (TraceLog traceLog = new TraceLog(traceLogPath)) | ||
{ | ||
TraceLogEventSource source = traceLog.Events.GetSource(); | ||
|
||
contentDictionary[path] = traceEventProcessor; | ||
source.AllEvents += traceEventProcessor.ProcessTraceEvent; | ||
source.Process(); | ||
this.dataSourceInfo = new DataSourceInfo(0, source.SessionDuration.Ticks * 100, source.SessionStartTime.ToUniversalTime()); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
if (e.Message != ReadPastEndOfStreamExceptionMessage || !traceEventProcessor.HasTraceData()) | ||
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. If a failure occurs when calling CreateFromEventPipeDataFile, you might consider attempting to re-process the file with ContinueOnError==true. If this succeed,s you should tell users that there was an error, and you stopped at the point in the file where the error occurred, so the file might not contain all of the expected data. |
||
{ | ||
throw; | ||
} | ||
} | ||
finally | ||
{ | ||
try | ||
{ | ||
File.Delete(tmpEtlx); | ||
} | ||
catch (Exception) | ||
{ | ||
} | ||
} | ||
} | ||
|
||
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; | ||
} | ||
} | ||
} |
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?