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
1 change: 0 additions & 1 deletion PerfettoCds/PerfettoCds.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

<Target Name="DownloadTraceProcessorShell">
<!-- Download the trace_processor.exe from Perfetto's GitHub release and copy it to the output directory-->
<Copy SourceFiles="$(PkgGoogle_Protobuf)\lib\netstandard2.0\Google.Protobuf.dll" DestinationFolder="$(TargetDir)" />
Comment thread
KyleStorck marked this conversation as resolved.
<DownloadFile SourceUrl="https://github.com/google/perfetto/releases/download/v18.0/windows-amd64.zip" DestinationFolder="$(TargetDir)" />
<Unzip SourceFiles="$(TargetDir)\windows-amd64.zip" DestinationFolder="$(TargetDir)" />
<Copy SourceFiles="$(TargetDir)\windows-amd64\trace_processor_shell.exe" DestinationFolder="$(TargetDir)" />
Expand Down
2 changes: 1 addition & 1 deletion PerfettoProcessor/PerfettoTraceProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private bool IsPortAvailable(int port)
return !rpcResult.Msg[0].Status.HumanReadableVersion.Contains("Perfetto");
}
}
catch (HttpRequestException e)
catch (HttpRequestException)
{
// Exception here is the connection refused message, meaning the RPC server has not been initialized
// Which means this port is not being used by another trace_processor_shell
Expand Down
146 changes: 78 additions & 68 deletions PerfettoUnitTest/PerfettoUnitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,93 +12,103 @@ namespace PerfettoUnitTest
[TestClass]
public class PerfettoUnitTest
{
public static bool IsTraceProcessed = false;
public static object IsTraceProcessedLock = new object();

private static RuntimeExecutionResults RuntimeExecutionResults;

private static DataCookerPath PerfettoGenericEventDataCookerPath;
private static DataCookerPath PerfettoSliceCookerPath;
private static DataCookerPath PerfettoArgCookerPath;
private static DataCookerPath PerfettoThreadTrackCookerPath;
private static DataCookerPath PerfettoThreadCookerPath;
private static DataCookerPath PerfettoProcessCookerPath;

public static void ProcessTrace()
public static void LoadTrace(string traceFilename)
{
lock (IsTraceProcessedLock)
{
if (!IsTraceProcessed)
{
// Input data
string perfettoTrace = @"..\..\..\..\TestData\Perfetto\sample.perfetto-trace";
var perfettoDataPath = new FileInfo(perfettoTrace);
Assert.IsTrue(perfettoDataPath.Exists);

// Approach #1 - Engine - Doesn't test tables UI but tests processing
var runtime = Engine.Create();

runtime.AddFile(perfettoDataPath.FullName);

// Enable our various types of data cookers
var perfettoSliceCooker = new PerfettoSliceCooker();
PerfettoSliceCookerPath = perfettoSliceCooker.Path;
runtime.EnableCooker(PerfettoSliceCookerPath);

var perfettoArgCooker = new PerfettoArgCooker();
PerfettoArgCookerPath = perfettoArgCooker.Path;
runtime.EnableCooker(PerfettoArgCookerPath);
// Input data
var perfettoDataPath = new FileInfo(traceFilename);
Assert.IsTrue(perfettoDataPath.Exists);

// Start the SDK engine
var runtime = Engine.Create();
runtime.AddFile(perfettoDataPath.FullName);

// Enable the source data cookers
runtime.EnableCooker(PerfettoPluginConstants.SliceCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.ArgCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.ThreadTrackCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.ThreadCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.ProcessCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.SchedSliceCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.RawCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.CounterCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.CpuCounterTrackCookerPath);

// Enable the composite data cookers
runtime.EnableCooker(PerfettoPluginConstants.GenericEventCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.CpuSchedEventCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.LogcatEventCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.FtraceEventCookerPath);
runtime.EnableCooker(PerfettoPluginConstants.CpuFrequencyEventCookerPath);

// Process our data.
RuntimeExecutionResults = runtime.Process();
}
}

var perfettoThreadTrackCooker = new PerfettoThreadTrackCooker();
PerfettoThreadTrackCookerPath = perfettoThreadTrackCooker.Path;
runtime.EnableCooker(PerfettoThreadTrackCookerPath);
[TestMethod]
public void TestAndroidTrace()
{
LoadTrace(@"..\..\..\..\TestData\Perfetto\test_trace.perfetto-trace");

var perfettoThreadCooker = new PerfettoThreadCooker();
PerfettoThreadCookerPath = perfettoThreadCooker.Path;
runtime.EnableCooker(PerfettoThreadCookerPath);
var genericEventData = RuntimeExecutionResults.QueryOutput<ProcessedEventData<PerfettoGenericEvent>>(
new DataOutputPath(
PerfettoPluginConstants.GenericEventCookerPath,
nameof(PerfettoGenericEventCooker.GenericEvents)));
Assert.IsTrue(genericEventData.Count == 1);
Assert.IsTrue(genericEventData[0].EventName == "Hello Trace");
Assert.IsTrue(genericEventData[0].Thread == "TraceLogApiTest 20855");

var perfettoProcessCooker = new PerfettoProcessCooker();
PerfettoProcessCookerPath = perfettoProcessCooker.Path;
runtime.EnableCooker(PerfettoProcessCookerPath);
var cpuSchedEventData = RuntimeExecutionResults.QueryOutput<ProcessedEventData<PerfettoCpuSchedEvent>>(
new DataOutputPath(
PerfettoPluginConstants.CpuSchedEventCookerPath,
nameof(PerfettoCpuSchedEventCooker.CpuSchedEvents)));
Assert.IsTrue(cpuSchedEventData.Count == 15267);
Assert.IsTrue(cpuSchedEventData[0].ThreadName == "kworker/u17:9");
Assert.IsTrue(cpuSchedEventData[1].EndState == "Task Dead");

var perfettoGenericEventDataCooker = new PerfettoGenericEventCooker();
PerfettoGenericEventDataCookerPath = perfettoGenericEventDataCooker.Path;
runtime.EnableCooker(PerfettoGenericEventDataCookerPath);
var ftraceEventData = RuntimeExecutionResults.QueryOutput<ProcessedEventData<PerfettoFtraceEvent>>(
new DataOutputPath(
PerfettoPluginConstants.FtraceEventCookerPath,
nameof(PerfettoFtraceEventCooker.FtraceEvents)));
Assert.IsTrue(ftraceEventData.Count == 35877);
Assert.IsTrue(ftraceEventData[0].ThreadName == "swapper");
Assert.IsTrue(ftraceEventData[1].Cpu == 3);

//
// Process our data.
//
RuntimeExecutionResults = runtime.Process();
IsTraceProcessed = true;
}
}
var cpuFreqEventData = RuntimeExecutionResults.QueryOutput<ProcessedEventData<PerfettoCpuFrequencyEvent>>(
new DataOutputPath(
PerfettoPluginConstants.CpuFrequencyEventCookerPath,
nameof(PerfettoCpuFrequencyEventCooker.CpuFrequencyEvents)));
Assert.IsTrue(cpuFreqEventData.Count == 11855);
Assert.IsTrue(cpuFreqEventData[0].CpuNum == 3);
Assert.IsTrue(cpuFreqEventData[1].Name == "cpuidle");
}
/// <summary>
/// PerfettoGenericEvents gather data from multiple source cookers. Valid PerfettoGenericEvents ensure
/// that the cookers below it also worked successfully.
/// </summary>

[TestMethod]
public void TestPerfettoGenericEvents()
public void TestChromeTrace()
{
ProcessTrace();
LoadTrace(@"..\..\..\..\TestData\Perfetto\chrome.pftrace");

var eventData = RuntimeExecutionResults.QueryOutput<ProcessedEventData<PerfettoGenericEvent>>(
var genericEventData = RuntimeExecutionResults.QueryOutput<ProcessedEventData<PerfettoGenericEvent>>(
new DataOutputPath(
PerfettoGenericEventDataCookerPath,
PerfettoPluginConstants.GenericEventCookerPath,
nameof(PerfettoGenericEventCooker.GenericEvents)));
Assert.IsTrue(genericEventData.Count == 144194);
Assert.IsTrue(genericEventData[0].EventName == "WebContentsImpl::DidReceiveInputEvent");
Assert.IsTrue(genericEventData[0].Thread == "CrBrowserMain 14448");

Assert.IsTrue(eventData.Count == 3);

// Ensures join with slices table worked
Assert.IsTrue(eventData[0].EventName == "name1");

// Ensures joins with thread_track, thread, and process table worked
Assert.IsTrue(eventData[1].Thread == "t1 1");
Assert.IsTrue(eventData[2].Process == " 5");

// Ensures join with args table worked
Assert.IsTrue(eventData[0].ArgKeys.Count == 1);
Assert.IsTrue(eventData[0].ArgKeys[0] == "chrome_user_event.action");
var logcatEventData = RuntimeExecutionResults.QueryOutput<ProcessedEventData<PerfettoLogcatEvent>>(
new DataOutputPath(
PerfettoPluginConstants.LogcatEventCookerPath,
nameof(PerfettoLogcatEventCooker.LogcatEvents)));
Assert.IsTrue(logcatEventData.Count == 43);
Assert.IsTrue(logcatEventData[0].Message == "type: 97 score: 0.8\n");
Assert.IsTrue(logcatEventData[1].ProcessName == "Browser");
}
}
}
13 changes: 13 additions & 0 deletions PerfettoUnitTest/PerfettoUnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,17 @@
<ProjectReference Include="..\PerfettoProcessor\PerfettoProcessor.csproj" />
</ItemGroup>

<Target Name="DownloadTraceProcessorShell">
<!-- Download the trace_processor.exe from Perfetto's GitHub release and copy it to the output directory-->
<DownloadFile SourceUrl="https://github.com/google/perfetto/releases/download/v18.0/windows-amd64.zip" DestinationFolder="$(TargetDir)" />
<Unzip SourceFiles="$(TargetDir)\windows-amd64.zip" DestinationFolder="$(TargetDir)" />
<Copy SourceFiles="$(TargetDir)\windows-amd64\trace_processor_shell.exe" DestinationFolder="$(TargetDir)" />
<RemoveDir Directories="$(TargetDir)\windows-amd64"/>
<Delete Files="$(TargetDir)\windows-amd64.zip"/>
</Target>

<Target Name="CopyRulesToTarget" AfterTargets="Build">
<!-- Download trace_processor_shell to the output directory if it doesn't exist -->
<CallTarget Condition="!Exists('$(TargetDir)\trace_processor_shell.exe')" Targets="DownloadTraceProcessorShell" />
</Target>
</Project>
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Microsoft Performance Tools Linux

> This repo contains various Linux Performance Analysis tools built with the [Microsoft Performance Toolkit SDK](https://github.com/microsoft/microsoft-performance-toolkit-sdk).
> This repo contains various Linux and Android Performance Analysis tools built with the [Microsoft Performance Toolkit SDK](https://github.com/microsoft/microsoft-performance-toolkit-sdk).

> Tools are built with open source .NET Core and can be run on the cmd-line or in the WPA GUI. All the logs that are supported are open source.

>Not only are the raw logs parsed, but a lot of smart post processing / correlation is done to make your life easier as a perf analyst. We hope you can solve & debug tough issues on you or your customers systems with this toolset!

> Tracing supported: [LTTng](https://lttng.org) (Kernel CPU scheduling, Processes, Threads, Block IO/Disk, Syscalls, File events, etc), [perf](https://perf.wiki.kernel.org/) CPU Sampling(cpu-clock)
> Tracing supported: [LTTng](https://lttng.org) (Kernel CPU scheduling, Processes, Threads, Block IO/Disk, Syscalls, File events, etc), [perf](https://perf.wiki.kernel.org/) CPU Sampling(cpu-clock), [Perfetto](https://perfetto.dev/)

> Logs supported: [Dmesg](https://en.wikipedia.org/wiki/Dmesg), [Cloud-Init](https://cloud-init.io/), [WaLinuxAgent](https://github.com/Azure/WALinuxAgent)

Expand Down Expand Up @@ -73,18 +73,25 @@ The tools can be run in several modes:
- (Coming soon) (Windows) Command-line dumping to a text format (say CSV)

# How to capture a trace or logs
Please see [Linux Trace Log Capture](LinuxTraceLogCapture.md)
- Linux
- Please see [Linux Trace Log Capture](LinuxTraceLogCapture.md)
- Perfetto
- Android - Please see [Record traces on Android](https://perfetto.dev/docs/quickstart/android-tracing)
- Linux - Please see [Record traces on Linux](https://perfetto.dev/docs/quickstart/linux-tracing)

# How to load the logs in the UI
Once you gather the data, there is a tiny bit of prep needed to open them in a single unified timeline (like the screenshot above)

- LTTng - If you just need to open only a LTTng trace by itself in folder format
- WPA -> Open -> Folder (Select CTF folder)
- Note: Requires >= WPA ADK xxxxx - WPA 10.?.?.?
- Unified (LTTng or other multiple different logs files together)
- If you want to open other logs together in single timeline - Copy other Linux logs you want to open to single folder
- Note: Requires >= WPA ADK xxxxx - WPA 10.?.?.?
- Perfetto
- WPA -> Open -> Folder (Select Perfetto trace file)
- Note: The Perfetto plugin explicitly supports the _.perfetto-trace_ and _.pftrace_ file types, but it does support more (e.g. Protobuf, Chrome JSON). You just need to rename to one of the stated supported types
- Unified (LTTng, Perfetto, or other multiple different logs files together)
- If you want to open multiple logs together in single timeline - Copy all trace files and logs you want to open to single folder
- Example: You want to open in the same timeline: LTTng, Perf CPU Sampling, Dmesg
- Ensure that the Linux CTF folder/trace is zipped and renamed to .ctf in the same folder (hack so open Unified works)
- Ensure that the Linux CTF folder/trace is zipped and renamed to .ctf in the same folder (hack so open Unified works)
- WPA -> File -> Open -> Multi-select all files and choose "Open Unified"

# How do I use WPA in general?
Expand Down
Binary file added TestData/Perfetto/chrome.pftrace
Binary file not shown.
Binary file removed TestData/Perfetto/sample.perfetto-trace
Binary file not shown.
Binary file added TestData/Perfetto/test_trace.perfetto-trace
Binary file not shown.
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ jobs:
inputs:
SourceFolder: 'PerfettoCds/bin/$(BuildConfiguration)/netstandard2.1'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/Microsoft-Performance-Tools-Linux/PerfettoAddin'
TargetFolder: '$(Build.ArtifactStagingDirectory)/Microsoft-Performance-Tools-Linux/MicrosoftPerfToolkitAddins/Perfetto'

- task: CopyFiles@2
displayName: Copy Cloud-init Build to Output Artifacts
Expand Down