Skip to content

Commit 4b83ad8

Browse files
committed
PerfDataExtension - Decoding support for perf.data
Create PerfDataExtension project with decoding support for perf.data files. Includes a generic events table with details view and a simple files metadata table.
1 parent 278b8c2 commit 4b83ad8

12 files changed

+3029
-0
lines changed

Microsoft-Perf-Tools-Linux-Android.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LTTngCdsUnitTest", "LTTngCd
7373
EndProject
7474
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{A42944EE-FFCC-4544-9F17-2BBFC94DFC3B}"
7575
EndProject
76+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfDataExtension", "PerfDataExtension\PerfDataExtension.csproj", "{02B6DB71-E796-4AFE-9E18-334E2F803393}"
77+
EndProject
7678
Global
7779
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7880
Debug|Any CPU = Debug|Any CPU
@@ -163,6 +165,10 @@ Global
163165
{CD7E9DD5-B97F-4B86-999E-92F86A1C1138}.Debug|Any CPU.Build.0 = Debug|Any CPU
164166
{CD7E9DD5-B97F-4B86-999E-92F86A1C1138}.Release|Any CPU.ActiveCfg = Release|Any CPU
165167
{CD7E9DD5-B97F-4B86-999E-92F86A1C1138}.Release|Any CPU.Build.0 = Release|Any CPU
168+
{02B6DB71-E796-4AFE-9E18-334E2F803393}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
169+
{02B6DB71-E796-4AFE-9E18-334E2F803393}.Debug|Any CPU.Build.0 = Debug|Any CPU
170+
{02B6DB71-E796-4AFE-9E18-334E2F803393}.Release|Any CPU.ActiveCfg = Release|Any CPU
171+
{02B6DB71-E796-4AFE-9E18-334E2F803393}.Release|Any CPU.Build.0 = Release|Any CPU
166172
EndGlobalSection
167173
GlobalSection(SolutionProperties) = preSolution
168174
HideSolutionNode = FALSE

PerfDataExtension/PerfDataEvent.cs

Lines changed: 793 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.1</TargetFramework>
5+
<OutputType>Library</OutputType>
6+
<Version>0.1.2</Version>
7+
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
8+
<Authors>Microsoft</Authors>
9+
<Company>Microsoft</Company>
10+
<Product>Performance ToolKit</Product>
11+
<AssemblyName>Microsoft.Performance.Toolkit.Plugins.PerfDataExtension</AssemblyName>
12+
<RootNamespace>Microsoft.Performance.Toolkit.Plugins.PerfDataExtension</RootNamespace>
13+
<Title>Microsoft Linux Tracepoints Decode for Microsoft Performance Toolkit</Title>
14+
<Description>Microsoft Performance Toolkit support for Linux Tracepoints, including perf.data files, tracefs formats, and EventHeader events.</Description>
15+
<PackageId>Microsoft.Performance.Toolkit.Plugins.PerfDataExtension</PackageId>
16+
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
17+
<RepositoryUrl>https://github.com/microsoft/Microsoft-Performance-Tools-Linux-Android</RepositoryUrl>
18+
<PackageProjectUrl>https://github.com/microsoft/Microsoft-Performance-Tools-Linux-Android</PackageProjectUrl>
19+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
20+
</PropertyGroup>
21+
22+
<ItemGroup>
23+
<PackageReference Include="Microsoft.LinuxTracepoints.Decode" Version="0.1.2.*">
24+
<IncludeAssets></IncludeAssets>
25+
</PackageReference>
26+
<PackageReference Include="Microsoft.Performance.SDK" Version="1.1.13" />
27+
</ItemGroup>
28+
29+
<PropertyGroup>
30+
<Nullable>enable</Nullable>
31+
<WarningLevel>9999</WarningLevel>
32+
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
33+
<EnableNETAnalyzers>True</EnableNETAnalyzers>
34+
<AnalysisLevel>latest-recommended</AnalysisLevel>
35+
36+
<PackageReadmeFile>README.md</PackageReadmeFile>
37+
<PackageTags>Tracepoints;Perf;Perf.data</PackageTags>
38+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
39+
</PropertyGroup>
40+
41+
<ItemGroup>
42+
<None Update="README.md">
43+
<Pack>True</Pack>
44+
<PackagePath>\</PackagePath>
45+
</None>
46+
</ItemGroup>
47+
48+
</Project>
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace Microsoft.Performance.Toolkit.Plugins.PerfDataExtension
5+
{
6+
using Microsoft.LinuxTracepoints.Decode;
7+
using System.Collections.ObjectModel;
8+
using System;
9+
using Debug = System.Diagnostics.Debug;
10+
using System.Collections.Generic;
11+
12+
/// <summary>
13+
/// Information about a perf.data file. Used as the context object for events.
14+
/// </summary>
15+
public class PerfDataFileInfo
16+
{
17+
private readonly ReadOnlyMemory<byte>[] headers = new ReadOnlyMemory<byte>[(int)PerfHeaderIndex.LastFeature];
18+
19+
protected PerfDataFileInfo(string filename, PerfByteReader byteReader)
20+
{
21+
this.FileName = filename;
22+
this.ByteReader = byteReader;
23+
}
24+
25+
/// <summary>
26+
/// The filename from which this trace data was loaded.
27+
/// </summary>
28+
public string FileName { get; }
29+
30+
/// <summary>
31+
/// Gets a byte reader configured for the byte order of the file's events.
32+
/// Same as new PerfByteReader(FromBigEndian).
33+
/// </summary>
34+
public PerfByteReader ByteReader { get; }
35+
36+
/// <summary>
37+
/// Returns true if the file's events are in big-endian byte order, false if
38+
/// the events are in little-endian byte order. Same as ByteReader.FromBigEndian.
39+
/// </summary>
40+
public bool FromBigEndian => this.ByteReader.FromBigEndian;
41+
42+
/// <summary>
43+
/// True if we've finished parsing the trace file's headers.
44+
/// This becomes true when we see a FinishedInit event or a Sample event.
45+
/// </summary>
46+
public bool HeaderAttributesAvailable { get; private set; }
47+
48+
/// <summary>
49+
/// True if we've finished parsing the trace file.
50+
/// </summary>
51+
public bool FileAttributesAvailable { get; private set; }
52+
53+
/// <summary>
54+
/// True if we've finished parsing all trace files in the session.
55+
/// </summary>
56+
public bool SessionAttributesAvailable { get; private set; }
57+
58+
/// <summary>
59+
/// Gets the value of the PERF_HEADER_HOSTNAME header, or "" if not present.
60+
/// <br/>
61+
/// Not available until trace's headers are parsed (HeaderAttributesAvailable).
62+
/// </summary>
63+
public string HostName { get; private set; } = "";
64+
65+
/// <summary>
66+
/// Gets the value of the PERF_HEADER_OSRELEASE header, or "" if not present.
67+
/// <br/>
68+
/// Not available until trace's headers are parsed (HeaderAttributesAvailable).
69+
/// </summary>
70+
public string OSRelease { get; private set; } = "";
71+
72+
/// <summary>
73+
/// Gets the value of the PERF_HEADER_ARCH header, or "" if not present.
74+
/// <br/>
75+
/// Not available until trace's headers are parsed (HeaderAttributesAvailable).
76+
/// </summary>
77+
public string Arch { get; private set; } = "";
78+
79+
/// <summary>
80+
/// Gets the value of the PERF_HEADER_NRCPUS header "available" field, or 0 if not present.
81+
/// <br/>
82+
/// Not available until trace's headers are parsed (HeaderAttributesAvailable).
83+
/// </summary>
84+
public uint CpusAvailable { get; private set; }
85+
86+
/// <summary>
87+
/// Gets the value of the PERF_HEADER_NRCPUS header "online" field, or 0 if not present.
88+
/// <br/>
89+
/// Not available until trace's headers are parsed (HeaderAttributesAvailable).
90+
/// </summary>
91+
public uint CpusOnline { get; private set; }
92+
93+
/// <summary>
94+
/// Returns the clockid of the file timestamp, e.g. CLOCK_MONOTONIC.
95+
/// Returns uint.MaxValue if the file timestamp clockid is unknown.
96+
/// <br/>
97+
/// Not available until we finish parsing the trace's headers (HeaderAttributesAvailable).
98+
/// </summary>
99+
public uint ClockId { get; private set; } = uint.MaxValue;
100+
101+
/// <summary>
102+
/// Returns the CLOCK_REALTIME value that corresponds to an event timestamp
103+
/// of 0 for this file. Returns UnixEpoch (1970) if the file did not contain
104+
/// clock offset information.
105+
/// <br/>
106+
/// Not available until we finish parsing the trace's headers (HeaderAttributesAvailable).
107+
/// </summary>
108+
public PerfTimeSpec ClockOffset { get; private set; }
109+
110+
/// <summary>
111+
/// Number of events in this file.
112+
/// <br/>
113+
/// Not available until trace's file is parsed (FileAttributesAvailable).
114+
/// </summary>
115+
public uint EventCount { get; private set; }
116+
117+
/// <summary>
118+
/// File-relative timestamp of the first event in this file
119+
/// (nanoseconds since ClockOffset).
120+
/// FirstEventTime > LastEventTime means the file contained no time-stamped events.
121+
/// <br/>
122+
/// Not available until trace's file is parsed (FileAttributesAvailable).
123+
/// </summary>
124+
public ulong FirstEventTime { get; private set; } = ulong.MaxValue;
125+
126+
/// <summary>
127+
/// File-relative timestamp of the last event in this file
128+
/// (nanoseconds since ClockOffset).
129+
/// FirstEventTime > LastEventTime means the file contained no time-stamped events.
130+
/// <br/>
131+
/// Not available until trace's file is parsed (FileAttributesAvailable).
132+
/// </summary>
133+
public ulong LastEventTime { get; private set; } = ulong.MinValue;
134+
135+
/// <summary>
136+
/// Returns ClockOffset + FirstEventTime.
137+
/// <br/>
138+
/// Not available until trace's file is parsed (FileAttributesAvailable).
139+
/// </summary>
140+
public PerfTimeSpec FirstEventTimeSpec => this.ClockOffset.AddNanoseconds(this.FirstEventTime);
141+
142+
/// <summary>
143+
/// Returns ClockOffset + LastEventTime.
144+
/// <br/>
145+
/// Not available until trace's file is parsed (FileAttributesAvailable).
146+
/// </summary>
147+
public PerfTimeSpec LastEventTimeSpec => this.ClockOffset.AddNanoseconds(this.LastEventTime);
148+
149+
/// <summary>
150+
/// SessionTimestampOffset = ClockOffset - Session start time.
151+
/// SessionTimestampOffset must be added to an event's file-relative
152+
/// timestamp to get the event's session-relative timestamp.
153+
/// <br/>
154+
/// Not available until all trace files are parsed (SessionAttributesAvailable).
155+
/// </summary>
156+
public long SessionTimestampOffset { get; private set; }
157+
158+
/// <summary>
159+
/// Returns the LongSize parsed from a PERF_HEADER_TRACING_DATA header,
160+
/// or 0 if no PERF_HEADER_TRACING_DATA has been parsed.
161+
/// </summary>
162+
public byte TracingDataLongSize { get; private set; }
163+
164+
/// <summary>
165+
/// Returns the PageSize parsed from a PERF_HEADER_TRACING_DATA header,
166+
/// or 0 if no PERF_HEADER_TRACING_DATA has been parsed.
167+
/// </summary>
168+
public int TracingDataPageSize { get; private set; }
169+
170+
/// <summary>
171+
/// Returns the header_page parsed from a PERF_HEADER_TRACING_DATA header,
172+
/// or empty if no PERF_HEADER_TRACING_DATA has been parsed.
173+
/// </summary>
174+
public ReadOnlyMemory<byte> TracingDataHeaderPage { get; private set; }
175+
176+
/// <summary>
177+
/// Returns the header_event parsed from a PERF_HEADER_TRACING_DATA header,
178+
/// or empty if no PERF_HEADER_TRACING_DATA has been parsed.
179+
/// </summary>
180+
public ReadOnlyMemory<byte> TracingDataHeaderEvent { get; private set; }
181+
182+
/// <summary>
183+
/// Returns the ftraces parsed from a PERF_HEADER_TRACING_DATA header,
184+
/// or empty if no PERF_HEADER_TRACING_DATA has been parsed.
185+
/// </summary>
186+
public ReadOnlyCollection<ReadOnlyMemory<byte>> TracingDataFtraces { get; private set; } =
187+
new ReadOnlyCollection<ReadOnlyMemory<byte>>(Array.Empty<ReadOnlyMemory<byte>>());
188+
189+
/// <summary>
190+
/// Returns the kallsyms parsed from a PERF_HEADER_TRACING_DATA header,
191+
/// or empty if no PERF_HEADER_TRACING_DATA has been parsed.
192+
/// </summary>
193+
public ReadOnlyMemory<byte> TracingDataKallsyms { get; private set; }
194+
195+
/// <summary>
196+
/// Returns the printk parsed from a PERF_HEADER_TRACING_DATA header,
197+
/// or empty if no PERF_HEADER_TRACING_DATA has been parsed.
198+
/// </summary>
199+
public ReadOnlyMemory<byte> TracingDataPrintk { get; private set; }
200+
201+
/// <summary>
202+
/// Returns the saved_cmdline parsed from a PERF_HEADER_TRACING_DATA header,
203+
/// or empty if no PERF_HEADER_TRACING_DATA has been parsed.
204+
/// </summary>
205+
public ReadOnlyMemory<byte> TracingDataSavedCmdLine { get; private set; }
206+
207+
/// <summary>
208+
/// Returns the raw data from the specified header. Data is in file-endian
209+
/// byte order (use ByteReader to do byte-swapping as appropriate).
210+
/// Returns empty if the requested header was not loaded from the file.
211+
/// </summary>
212+
public ReadOnlyMemory<byte> Header(PerfHeaderIndex headerIndex)
213+
{
214+
return (uint)headerIndex < (uint)this.headers.Length
215+
? this.headers[(uint)headerIndex]
216+
: default;
217+
}
218+
219+
/// <summary>
220+
/// Assumes the specified header is a nul-terminated Latin1 string.
221+
/// Returns a new string with the value of the header.
222+
/// </summary>
223+
public string HeaderString(PerfHeaderIndex headerIndex)
224+
{
225+
var header = Header(headerIndex).Span;
226+
if (header.Length <= 4)
227+
{
228+
return "";
229+
}
230+
else
231+
{
232+
// Starts with a 4-byte length, followed by a nul-terminated string.
233+
header = header.Slice(4);
234+
var nul = header.IndexOf((byte)0);
235+
return PerfConvert.EncodingLatin1.GetString(header.Slice(0, nul >= 0 ? nul : header.Length));
236+
}
237+
}
238+
239+
protected void SetHeaderAttributes(PerfDataFileReader reader)
240+
{
241+
Debug.Assert(!this.HeaderAttributesAvailable, "Header attributes already set");
242+
this.HeaderAttributesAvailable = true;
243+
244+
this.HostName = reader.HeaderString(PerfHeaderIndex.Hostname);
245+
this.OSRelease = reader.HeaderString(PerfHeaderIndex.OSRelease);
246+
this.Arch = reader.HeaderString(PerfHeaderIndex.Arch);
247+
248+
var nrCpus = reader.Header(PerfHeaderIndex.NrCpus).Span;
249+
if (nrCpus.Length >= 8)
250+
{
251+
this.CpusAvailable = this.ByteReader.ReadU32(nrCpus);
252+
this.CpusOnline = this.ByteReader.ReadU32(nrCpus.Slice(4));
253+
}
254+
255+
this.ClockId = reader.SessionInfo.ClockId;
256+
this.ClockOffset = reader.SessionInfo.ClockOffset;
257+
258+
this.TracingDataLongSize = reader.TracingDataLongSize;
259+
this.TracingDataPageSize = reader.TracingDataPageSize;
260+
this.TracingDataHeaderPage = CloneMemory(reader.TracingDataHeaderPage);
261+
this.TracingDataHeaderEvent = CloneMemory(reader.TracingDataHeaderEvent);
262+
263+
var readerFtraces = reader.TracingDataFtraces;
264+
if (readerFtraces.Count != 0)
265+
{
266+
var newFtraces = new ReadOnlyMemory<byte>[readerFtraces.Count];
267+
for (int i = 0; i < newFtraces.Length; i++)
268+
{
269+
newFtraces[i] = CloneMemory(readerFtraces[i]);
270+
}
271+
this.TracingDataFtraces = new ReadOnlyCollection<ReadOnlyMemory<byte>>(newFtraces);
272+
}
273+
274+
this.TracingDataKallsyms = CloneMemory(reader.TracingDataKallsyms);
275+
this.TracingDataPrintk = CloneMemory(reader.TracingDataPrintk);
276+
this.TracingDataSavedCmdLine = CloneMemory(reader.TracingDataSavedCmdLine);
277+
278+
for (int i = 0; i < this.headers.Length; i += 1)
279+
{
280+
this.headers[i] = CloneMemory(reader.Header((PerfHeaderIndex)i));
281+
}
282+
}
283+
284+
protected void SetFileAttributes(ulong firstEventTime, ulong lastEventTime, uint eventCount)
285+
{
286+
Debug.Assert(!this.FileAttributesAvailable, "File attributes already set");
287+
this.FileAttributesAvailable = true;
288+
289+
this.EventCount = eventCount;
290+
this.FirstEventTime = firstEventTime;
291+
this.LastEventTime = lastEventTime;
292+
}
293+
294+
protected void SetSessionAttributes(long sessionTimestampOffset)
295+
{
296+
Debug.Assert(!this.SessionAttributesAvailable, "Session attributes already set");
297+
this.SessionAttributesAvailable = true;
298+
299+
this.SessionTimestampOffset = sessionTimestampOffset;
300+
}
301+
302+
private static ReadOnlyMemory<T> CloneMemory<T>(ReadOnlyMemory<T> memory)
303+
{
304+
return memory.Length == 0
305+
? default
306+
: memory.ToArray();
307+
}
308+
}
309+
}

0 commit comments

Comments
 (0)