Perfetto plugin - Added tables for per-process memory and system memory (#35)
Added table that displays memory for each process based on /proc//status Added table that displays overall system memory based on /proc/meminfo Reorganized table groups into "Perfetto - Android", "Perfetto - Events", and "Perfetto - System" Reorganized namespaces and PerfettoPluginConstants
This commit is contained in:
Родитель
52a1f4c2a1
Коммит
5e97a0749d
|
@ -8,9 +8,10 @@ using Microsoft.Performance.SDK.Extensibility;
|
|||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.DataCookers
|
||||
namespace PerfettoCds.Pipeline.CompositeDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Pulls data from multiple individual SQL tables and joins them to create a CPU counters event.
|
||||
|
|
|
@ -8,9 +8,10 @@ using Microsoft.Performance.SDK.Extensibility;
|
|||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.DataCookers
|
||||
namespace PerfettoCds.Pipeline.CompositeDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Pulls data from multiple individual SQL tables and joins them to create a a CPU frequency event. CPU frequency events
|
||||
|
|
|
@ -8,9 +8,10 @@ using Microsoft.Performance.SDK.Extensibility;
|
|||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.DataCookers
|
||||
namespace PerfettoCds.Pipeline.CompositeDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Pulls data from multiple individual SQL tables and joins them to create a CPU scheduling event
|
||||
|
|
|
@ -8,9 +8,10 @@ using Microsoft.Performance.SDK.Extensibility;
|
|||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.DataCookers
|
||||
namespace PerfettoCds.Pipeline.CompositeDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Pulls data from multiple individual SQL tables and joins them to create an Ftrace Perfetto event
|
||||
|
|
|
@ -13,8 +13,9 @@ using PerfettoProcessor;
|
|||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using System.IO;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
|
||||
namespace PerfettoCds.Pipeline.DataCookers
|
||||
namespace PerfettoCds.Pipeline.CompositeDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// XML deserialized EventProvider
|
||||
|
|
|
@ -8,9 +8,10 @@ using Microsoft.Performance.SDK.Extensibility;
|
|||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.DataCookers
|
||||
namespace PerfettoCds.Pipeline.CompositeDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Pulls data from multiple individual SQL tables and joins them to create events for logcat output
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Performance.SDK;
|
||||
using Microsoft.Performance.SDK.Extensibility;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.CompositeDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Pulls data from multiple individual SQL tables and joins them to create a process memory event. Process
|
||||
/// memory events list different memory counts per process
|
||||
/// </summary>
|
||||
public sealed class PerfettoProcessMemoryEventCooker : CookedDataReflector, ICompositeDataCookerDescriptor
|
||||
{
|
||||
public static readonly DataCookerPath DataCookerPath = PerfettoPluginConstants.ProcessMemoryEventCookerPath;
|
||||
|
||||
public string Description => "Process memory composite cooker";
|
||||
|
||||
public DataCookerPath Path => DataCookerPath;
|
||||
|
||||
// Declare all of the cookers that are used by this CompositeCooker.
|
||||
public IReadOnlyCollection<DataCookerPath> RequiredDataCookers => new[]
|
||||
{
|
||||
PerfettoPluginConstants.CounterCookerPath,
|
||||
PerfettoPluginConstants.ProcessCounterTrackCookerPath,
|
||||
PerfettoPluginConstants.ProcessCookerPath
|
||||
};
|
||||
|
||||
[DataOutput]
|
||||
public ProcessedEventData<PerfettoProcessMemoryEvent> ProcessMemoryEvents { get; }
|
||||
|
||||
public PerfettoProcessMemoryEventCooker() : base(PerfettoPluginConstants.ProcessMemoryEventCookerPath)
|
||||
{
|
||||
this.ProcessMemoryEvents =
|
||||
new ProcessedEventData<PerfettoProcessMemoryEvent>();
|
||||
}
|
||||
|
||||
public void OnDataAvailable(IDataExtensionRetrieval requiredData)
|
||||
{
|
||||
// Gather the data from all the SQL tables
|
||||
var counterData = requiredData.QueryOutput<ProcessedEventData<PerfettoCounterEvent>>(new DataOutputPath(PerfettoPluginConstants.CounterCookerPath, nameof(PerfettoCounterCooker.CounterEvents)));
|
||||
var processCounterTrackData = requiredData.QueryOutput<ProcessedEventData<PerfettoProcessCounterTrackEvent>>(new DataOutputPath(PerfettoPluginConstants.ProcessCounterTrackCookerPath, nameof(PerfettoProcessCounterTrackCooker.ProcessCounterTrackEvents)));
|
||||
var processData = requiredData.QueryOutput<ProcessedEventData<PerfettoProcessEvent>>(new DataOutputPath(PerfettoPluginConstants.ProcessCookerPath, nameof(PerfettoProcessCooker.ProcessEvents)));
|
||||
|
||||
// Join them all together
|
||||
// Counter table contains the memory count value, timestamp
|
||||
// ProcessCounterTrack contains the UPID and memory type name. All the memory types we care about start with "mem."
|
||||
// Process contains the process name
|
||||
var joined = from counter in counterData
|
||||
join processCounterTrack in processCounterTrackData on counter.TrackId equals processCounterTrack.Id
|
||||
join process in processData on processCounterTrack.Upid equals process.Upid
|
||||
where processCounterTrack.Name.StartsWith("mem.")
|
||||
orderby counter.Timestamp ascending
|
||||
select new { counter, processCounterTrack, process };
|
||||
|
||||
// Create events out of the joined results
|
||||
foreach (var processGroup in joined.GroupBy(x => x.processCounterTrack.Upid))
|
||||
{
|
||||
var timeGroups = processGroup.GroupBy(z => z.counter.RelativeTimestamp);
|
||||
|
||||
for (int i = 0; i < timeGroups.Count(); i++)
|
||||
{
|
||||
var timeGroup = timeGroups.ElementAt(i);
|
||||
|
||||
var ts = timeGroup.Key;
|
||||
var processName = $"{timeGroup.ElementAt(0).process.Name} {processGroup.Key}";
|
||||
long nextTs = ts;
|
||||
if (i < timeGroups.Count() - 1)
|
||||
{
|
||||
// Need to look ahead in the future at the next event to get the timestamp so that we can calculate the duration
|
||||
nextTs = timeGroups.ElementAt(i + 1).Key;
|
||||
}
|
||||
|
||||
double virt = 0.0, rss = 0.0, rssAnon = 0.0, rssFile = 0.0, rssShMem = 0.0, rssHwm = 0.0, swap = 0.0, locked = 0.0;
|
||||
// Gather each type of memory
|
||||
foreach (var thing in timeGroup)
|
||||
{
|
||||
switch (thing.processCounterTrack.Name)
|
||||
{
|
||||
case "mem.virt":
|
||||
virt = thing.counter.FloatValue;
|
||||
break;
|
||||
case "mem.rss":
|
||||
rss = thing.counter.FloatValue;
|
||||
break;
|
||||
case "mem.rss.anon":
|
||||
rssAnon = thing.counter.FloatValue;
|
||||
break;
|
||||
case "mem.rss.file":
|
||||
rssFile = thing.counter.FloatValue;
|
||||
break;
|
||||
case "mem.rss.shmem":
|
||||
rssShMem = thing.counter.FloatValue;
|
||||
break;
|
||||
case "mem.rss.watermark":
|
||||
rssHwm = thing.counter.FloatValue;
|
||||
break;
|
||||
case "mem.locked":
|
||||
locked = thing.counter.FloatValue;
|
||||
break;
|
||||
case "mem.swap":
|
||||
swap = thing.counter.FloatValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PerfettoProcessMemoryEvent ev = new PerfettoProcessMemoryEvent
|
||||
(
|
||||
processName,
|
||||
new Timestamp(ts),
|
||||
new TimestampDelta(nextTs - ts),
|
||||
rssAnon,
|
||||
rssShMem,
|
||||
rssFile,
|
||||
rssHwm,
|
||||
rss,
|
||||
locked,
|
||||
swap,
|
||||
virt
|
||||
);
|
||||
this.ProcessMemoryEvents.AddEvent(ev);
|
||||
}
|
||||
}
|
||||
this.ProcessMemoryEvents.FinalizeData();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Performance.SDK;
|
||||
using Microsoft.Performance.SDK.Extensibility;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.CompositeDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Pulls data from multiple individual SQL tables and joins them to create a system memory event. System
|
||||
/// memory events capture periodic system memory counts
|
||||
/// </summary>
|
||||
public sealed class PerfettoSystemMemoryEventCooker : CookedDataReflector, ICompositeDataCookerDescriptor
|
||||
{
|
||||
public static readonly DataCookerPath DataCookerPath = PerfettoPluginConstants.SystemMemoryEventCookerPath;
|
||||
|
||||
public string Description => "System memory composite cooker";
|
||||
|
||||
public DataCookerPath Path => DataCookerPath;
|
||||
|
||||
// Declare all of the cookers that are used by this CompositeCooker.
|
||||
public IReadOnlyCollection<DataCookerPath> RequiredDataCookers => new[]
|
||||
{
|
||||
PerfettoPluginConstants.CounterCookerPath,
|
||||
PerfettoPluginConstants.CounterTrackCookerPath
|
||||
};
|
||||
|
||||
[DataOutput]
|
||||
public ProcessedEventData<PerfettoSystemMemoryEvent> SystemMemoryEvents { get; }
|
||||
|
||||
// Perfetto captures memory counts from /proc/meminfo and outputs events with
|
||||
// the following names.
|
||||
// Set sys_stats_counters.h in Perfetto repo.
|
||||
public HashSet<string> MemoryTypes = new HashSet<string>() {
|
||||
"MemUnspecified",
|
||||
"MemTotal",
|
||||
"MemFree",
|
||||
"MemAvailable",
|
||||
"Buffers",
|
||||
"Cached",
|
||||
"SwapCached",
|
||||
"Active",
|
||||
"Inactive",
|
||||
"Active(anon)",
|
||||
"Inactive(anon)",
|
||||
"Active(file)",
|
||||
"Inactive(file)",
|
||||
"Unevictable",
|
||||
"Mlocked",
|
||||
"SwapTotal",
|
||||
"SwapFree",
|
||||
"Dirty",
|
||||
"Writeback",
|
||||
"AnonPages",
|
||||
"Mapped",
|
||||
"Shmem",
|
||||
"Slab",
|
||||
"SReclaimable",
|
||||
"SUnreclaim",
|
||||
"KernelStack",
|
||||
"PageTables",
|
||||
"CommitLimit",
|
||||
"Committed_AS",
|
||||
"VmallocTotal",
|
||||
"VmallocUsed",
|
||||
"VmallocChunk",
|
||||
"CmaTotal",
|
||||
"CmaFree"
|
||||
};
|
||||
|
||||
public PerfettoSystemMemoryEventCooker() : base(PerfettoPluginConstants.SystemMemoryEventCookerPath)
|
||||
{
|
||||
this.SystemMemoryEvents =
|
||||
new ProcessedEventData<PerfettoSystemMemoryEvent>();
|
||||
}
|
||||
|
||||
public void OnDataAvailable(IDataExtensionRetrieval requiredData)
|
||||
{
|
||||
// Gather the data from all the SQL tables
|
||||
var counterData = requiredData.QueryOutput<ProcessedEventData<PerfettoCounterEvent>>(new DataOutputPath(PerfettoPluginConstants.CounterCookerPath, nameof(PerfettoCounterCooker.CounterEvents)));
|
||||
var counterTrackData = requiredData.QueryOutput<ProcessedEventData<PerfettoCounterTrackEvent>>(new DataOutputPath(PerfettoPluginConstants.CounterTrackCookerPath, nameof(PerfettoCounterTrackCooker.CounterTrackEvents)));
|
||||
|
||||
// Join them all together
|
||||
// Counter table contains the memory count value, timestamp
|
||||
// counterTrackData contains the name of the memory type
|
||||
// Process contains the process name
|
||||
var joined = from counter in counterData
|
||||
join counterTrack in counterTrackData on counter.TrackId equals counterTrack.Id
|
||||
where MemoryTypes.Contains(counterTrack.Name)
|
||||
orderby counter.Timestamp ascending
|
||||
select new { counter, counterTrack };
|
||||
|
||||
// Create events out of the joined results
|
||||
foreach (var memoryGroup in joined.GroupBy(x => x.counterTrack.Name))
|
||||
{
|
||||
string memoryType = memoryGroup.Key;
|
||||
|
||||
for(int i = 0; i < memoryGroup.Count(); i++)
|
||||
{
|
||||
var thing = memoryGroup.ElementAt(i);
|
||||
double val = thing.counter.FloatValue;
|
||||
var ts = thing.counter.RelativeTimestamp;
|
||||
|
||||
long nextTs = ts;
|
||||
if (i < memoryGroup.Count() - 1)
|
||||
{
|
||||
// Need to look ahead in the future at the next event to get the timestamp so that we can calculate the duration
|
||||
nextTs = memoryGroup.ElementAt(i + 1).counter.RelativeTimestamp;
|
||||
}
|
||||
|
||||
PerfettoSystemMemoryEvent ev = new PerfettoSystemMemoryEvent
|
||||
(
|
||||
val,
|
||||
memoryType,
|
||||
new Timestamp(ts),
|
||||
new TimestampDelta(nextTs - ts)
|
||||
);
|
||||
this.SystemMemoryEvents.AddEvent(ev);
|
||||
}
|
||||
|
||||
}
|
||||
this.SystemMemoryEvents.FinalizeData();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using Microsoft.Performance.SDK;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PerfettoCds.Pipeline.DataOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// A event that represents several memory values for a process at a point in time
|
||||
/// </summary>
|
||||
public readonly struct PerfettoProcessMemoryEvent
|
||||
{
|
||||
public string ProcessName { get; }
|
||||
public Timestamp StartTimestamp { get; }
|
||||
public TimestampDelta Duration { get; }
|
||||
|
||||
/// Resident set size - anonymous memory
|
||||
public double RssAnon { get; }
|
||||
/// Resident set size - shared memory
|
||||
public double RssShMem { get; }
|
||||
/// Resident set size - file mappings
|
||||
public double RssFile { get; }
|
||||
/// Resident set size - Peak (high water mark)
|
||||
public double RssHwm { get; }
|
||||
/// Resident set size - Sum of anon, file, ShMem
|
||||
public double Rss { get; }
|
||||
/// Locked memory size
|
||||
public double Locked { get; }
|
||||
/// Swapped out VM size by anonymous private pages
|
||||
public double Swap { get; }
|
||||
/// Peak virtual memory size
|
||||
public double Virt { get; }
|
||||
|
||||
public PerfettoProcessMemoryEvent(string processName, Timestamp startTimestamp, TimestampDelta duration,
|
||||
double rssAnon,
|
||||
double rssShMem,
|
||||
double rssFile,
|
||||
double rssHwm,
|
||||
double rss,
|
||||
double locked,
|
||||
double swap,
|
||||
double virt)
|
||||
{
|
||||
this.ProcessName = processName;
|
||||
this.StartTimestamp = startTimestamp;
|
||||
this.Duration = duration;
|
||||
|
||||
this.RssAnon = rssAnon;
|
||||
this.Locked = locked;
|
||||
this.RssShMem = rssShMem;
|
||||
this.RssFile = rssFile;
|
||||
this.RssHwm = rssHwm;
|
||||
this.Rss = rss;
|
||||
this.Swap = swap;
|
||||
this.Virt = virt;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using Microsoft.Performance.SDK;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PerfettoCds.Pipeline.DataOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// A event that represents a single system memory value at a point in time
|
||||
/// </summary>
|
||||
public readonly struct PerfettoSystemMemoryEvent
|
||||
{
|
||||
public double Value { get; }
|
||||
public string MemoryType { get; }
|
||||
public Timestamp StartTimestamp { get; }
|
||||
public TimestampDelta Duration { get; }
|
||||
|
||||
public PerfettoSystemMemoryEvent(double value, string memoryType, Timestamp startTimestamp, TimestampDelta duration)
|
||||
{
|
||||
this.Value = value;
|
||||
this.MemoryType = memoryType;
|
||||
this.StartTimestamp = startTimestamp;
|
||||
this.Duration = duration;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using Microsoft.Performance.SDK.Extensibility;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using PerfettoCds.Pipeline.SourceDataCookers;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
|
@ -13,24 +16,28 @@ namespace PerfettoCds
|
|||
public const string ParserId = "PerfettoSourceParser";
|
||||
|
||||
// ID for source data cookers
|
||||
public const string SliceCookerId = "PerfettoSliceCooker";
|
||||
public const string ArgCookerId = "PerfettoArgCooker";
|
||||
public const string ThreadCookerId = "PerfettoThreadCooker";
|
||||
public const string ThreadTrackCookerId = "PerfettoThreadCookerId";
|
||||
public const string ProcessCookerId = "PerfettoProcessCooker";
|
||||
public const string SchedSliceCookerId = "PerfettoSchedSliceCooker";
|
||||
public const string AndroidLogCookerId = "PerfettoAndroidLogCooker";
|
||||
public const string RawCookerId = "PerfettoRawCooker";
|
||||
public const string CounterCookerId = "PerfettoCounterCooker";
|
||||
public const string CpuCounterTrackCookerId = "PerfettoCpuCounterTrackCooker";
|
||||
public const string SliceCookerId = nameof(PerfettoSliceCooker);
|
||||
public const string ArgCookerId = nameof(PerfettoArgCooker);
|
||||
public const string ThreadCookerId = nameof(PerfettoThreadCooker);
|
||||
public const string ThreadTrackCookerId = nameof(PerfettoThreadTrackCooker);
|
||||
public const string ProcessCookerId = nameof(PerfettoProcessCooker);
|
||||
public const string SchedSliceCookerId = nameof(PerfettoSchedSliceCooker);
|
||||
public const string AndroidLogCookerId = nameof(PerfettoAndroidLogCooker);
|
||||
public const string RawCookerId = nameof(PerfettoRawCooker);
|
||||
public const string CounterCookerId = nameof(PerfettoCounterCooker);
|
||||
public const string CpuCounterTrackCookerId = nameof(PerfettoCpuCounterTrackCooker);
|
||||
public const string ProcessCounterTrackCookerId = nameof(PerfettoProcessCounterTrackCooker);
|
||||
public const string CounterTrackCookerId = nameof(PerfettoCounterTrackCooker);
|
||||
|
||||
// ID for composite data cookers
|
||||
public const string GenericEventCookerId = "PerfettoGenericEventCooker";
|
||||
public const string CpuSchedEventCookerId = "PerfettoCpuSchedEventCooker";
|
||||
public const string LogcatEventCookerId = "PerfettoLogcatEventCooker";
|
||||
public const string FtraceEventCookerId = "PerfettoFtraceEventCooker";
|
||||
public const string CpuFrequencyEventCookerId = "PerfettoCpuFrequencyEventCooker";
|
||||
public const string CpuCountersEventCookerId = "PerfettoCpuCountersEventCooker";
|
||||
public const string GenericEventCookerId = nameof(PerfettoGenericEventCooker);
|
||||
public const string CpuSchedEventCookerId = nameof(PerfettoCpuSchedEventCooker);
|
||||
public const string LogcatEventCookerId = nameof(PerfettoLogcatEventCooker);
|
||||
public const string FtraceEventCookerId = nameof(PerfettoFtraceEventCooker);
|
||||
public const string CpuFrequencyEventCookerId = nameof(PerfettoCpuFrequencyEventCooker);
|
||||
public const string CpuCountersEventCookerId = nameof(PerfettoCpuCountersEventCooker);
|
||||
public const string ProcessMemoryEventCookerId = nameof(PerfettoProcessMemoryEventCooker);
|
||||
public const string SystemMemoryEventCookerId = nameof(PerfettoSystemMemoryEventCooker);
|
||||
|
||||
// Events for source cookers
|
||||
public const string SliceEvent = PerfettoSliceEvent.Key;
|
||||
|
@ -43,14 +50,18 @@ namespace PerfettoCds
|
|||
public const string RawEvent = PerfettoRawEvent.Key;
|
||||
public const string CounterEvent = PerfettoCounterEvent.Key;
|
||||
public const string CpuCounterTrackEvent = PerfettoCpuCounterTrackEvent.Key;
|
||||
public const string ProcessCounterTrackEvent = PerfettoProcessCounterTrackEvent.Key;
|
||||
public const string CounterTrackEvent = PerfettoCounterTrackEvent.Key;
|
||||
|
||||
// Output events for composite cookers
|
||||
public const string GenericEvent = "PerfettoGenericEvent";
|
||||
public const string CpuSchedEvent = "PerfettoCpuSchedEvent";
|
||||
public const string LogcatEvent = "PerfettoLogcatEvent";
|
||||
public const string FtraceEvent = "PerfettoFtraceEvent";
|
||||
public const string CpuFrequencyEvent = "PerfettoCpuFrequencyEvent";
|
||||
public const string CpuCountersEvent = "PerfettoCpuCountersEvent";
|
||||
public const string GenericEvent = nameof(PerfettoGenericEvent);
|
||||
public const string CpuSchedEvent = nameof(PerfettoCpuSchedEvent);
|
||||
public const string LogcatEvent = nameof(PerfettoLogcatEvent);
|
||||
public const string FtraceEvent = nameof(PerfettoFtraceEvent);
|
||||
public const string CpuFrequencyEvent = nameof(PerfettoCpuFrequencyEvent);
|
||||
public const string CpuCountersEvent = nameof(PerfettoCpuCountersEvent);
|
||||
public const string ProcessMemoryEvent = nameof(PerfettoProcessMemoryEvent);
|
||||
public const string SystemMemoryEvent = nameof(PerfettoSystemMemoryEvent);
|
||||
|
||||
// Paths for source cookers
|
||||
public static readonly DataCookerPath SliceCookerPath =
|
||||
|
@ -73,6 +84,10 @@ namespace PerfettoCds
|
|||
new DataCookerPath(PerfettoPluginConstants.ParserId, PerfettoPluginConstants.CounterCookerId);
|
||||
public static readonly DataCookerPath CpuCounterTrackCookerPath =
|
||||
new DataCookerPath(PerfettoPluginConstants.ParserId, PerfettoPluginConstants.CpuCounterTrackCookerId);
|
||||
public static readonly DataCookerPath ProcessCounterTrackCookerPath =
|
||||
new DataCookerPath(PerfettoPluginConstants.ParserId, PerfettoPluginConstants.ProcessCounterTrackCookerId);
|
||||
public static readonly DataCookerPath CounterTrackCookerPath =
|
||||
new DataCookerPath(PerfettoPluginConstants.ParserId, PerfettoPluginConstants.CounterTrackCookerId);
|
||||
|
||||
// Paths for composite cookers
|
||||
public static readonly DataCookerPath GenericEventCookerPath =
|
||||
|
@ -87,6 +102,10 @@ namespace PerfettoCds
|
|||
new DataCookerPath(PerfettoPluginConstants.CpuFrequencyEventCookerId);
|
||||
public static readonly DataCookerPath CpuCountersEventCookerPath =
|
||||
new DataCookerPath(PerfettoPluginConstants.CpuCountersEventCookerId);
|
||||
public static readonly DataCookerPath ProcessMemoryEventCookerPath =
|
||||
new DataCookerPath(PerfettoPluginConstants.ProcessMemoryEventCookerId);
|
||||
public static readonly DataCookerPath SystemMemoryEventCookerPath =
|
||||
new DataCookerPath(PerfettoPluginConstants.SystemMemoryEventCookerId);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,9 @@ namespace PerfettoCds
|
|||
new PerfettoAndroidLogEvent(),
|
||||
new PerfettoRawEvent(),
|
||||
new PerfettoCpuCounterTrackEvent(),
|
||||
new PerfettoCounterEvent()
|
||||
new PerfettoCounterEvent(),
|
||||
new PerfettoProcessCounterTrackEvent(),
|
||||
new PerfettoCounterTrackEvent()
|
||||
};
|
||||
|
||||
// Increment progress for each table queried.
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the Android_Logs table in Perfetto traces
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the Args table in Perfetto traces
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the Counter table in Perfetto traces
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using Microsoft.Performance.SDK;
|
||||
using Microsoft.Performance.SDK.Extensibility;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking.SourceDataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the CounterTrack table in Perfetto traces
|
||||
/// </summary>
|
||||
public sealed class PerfettoCounterTrackCooker : BaseSourceDataCooker<PerfettoSqlEventKeyed, PerfettoSourceParser, string>
|
||||
{
|
||||
public override string Description => "Processes events from the counter_track Perfetto SQL table";
|
||||
|
||||
//
|
||||
// The data this cooker outputs. Tables or other cookers can query for this data
|
||||
// via the SDK runtime
|
||||
//
|
||||
[DataOutput]
|
||||
public ProcessedEventData<PerfettoCounterTrackEvent> CounterTrackEvents { get; }
|
||||
|
||||
// Instructs runtime to only send events with the given keys this data cooker
|
||||
public override ReadOnlyHashSet<string> DataKeys =>
|
||||
new ReadOnlyHashSet<string>(new HashSet<string> { PerfettoPluginConstants.CounterTrackEvent });
|
||||
|
||||
public PerfettoCounterTrackCooker() : base(PerfettoPluginConstants.CounterTrackCookerPath)
|
||||
{
|
||||
this.CounterTrackEvents = new ProcessedEventData<PerfettoCounterTrackEvent>();
|
||||
}
|
||||
|
||||
public override DataProcessingResult CookDataElement(PerfettoSqlEventKeyed perfettoEvent, PerfettoSourceParser context, CancellationToken cancellationToken)
|
||||
{
|
||||
var newEvent = (PerfettoCounterTrackEvent)perfettoEvent.SqlEvent;
|
||||
this.CounterTrackEvents.AddEvent(newEvent);
|
||||
|
||||
return DataProcessingResult.Processed;
|
||||
}
|
||||
|
||||
public override void EndDataCooking(CancellationToken cancellationToken)
|
||||
{
|
||||
base.EndDataCooking(cancellationToken);
|
||||
this.CounterTrackEvents.FinalizeData();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the CpuCounterTrack table in Perfetto traces
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the Process table in Perfetto traces
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using Microsoft.Performance.SDK;
|
||||
using Microsoft.Performance.SDK.Extensibility;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking.SourceDataCooking;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the ProcessCounterTrack table in Perfetto traces
|
||||
/// </summary>
|
||||
public sealed class PerfettoProcessCounterTrackCooker : BaseSourceDataCooker<PerfettoSqlEventKeyed, PerfettoSourceParser, string>
|
||||
{
|
||||
public override string Description => "Processes events from the process_counter_track Perfetto SQL table";
|
||||
|
||||
//
|
||||
// The data this cooker outputs. Tables or other cookers can query for this data
|
||||
// via the SDK runtime
|
||||
//
|
||||
[DataOutput]
|
||||
public ProcessedEventData<PerfettoProcessCounterTrackEvent> ProcessCounterTrackEvents { get; }
|
||||
|
||||
// Instructs runtime to only send events with the given keys this data cooker
|
||||
public override ReadOnlyHashSet<string> DataKeys =>
|
||||
new ReadOnlyHashSet<string>(new HashSet<string> { PerfettoPluginConstants.ProcessCounterTrackEvent });
|
||||
|
||||
|
||||
public PerfettoProcessCounterTrackCooker() : base(PerfettoPluginConstants.ProcessCounterTrackCookerPath)
|
||||
{
|
||||
this.ProcessCounterTrackEvents =
|
||||
new ProcessedEventData<PerfettoProcessCounterTrackEvent>();
|
||||
}
|
||||
|
||||
public override DataProcessingResult CookDataElement(PerfettoSqlEventKeyed perfettoEvent, PerfettoSourceParser context, CancellationToken cancellationToken)
|
||||
{
|
||||
this.ProcessCounterTrackEvents.AddEvent((PerfettoProcessCounterTrackEvent)perfettoEvent.SqlEvent);
|
||||
|
||||
return DataProcessingResult.Processed;
|
||||
}
|
||||
|
||||
public override void EndDataCooking(CancellationToken cancellationToken)
|
||||
{
|
||||
base.EndDataCooking(cancellationToken);
|
||||
this.ProcessCounterTrackEvents.FinalizeData();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the Raw table in Perfetto traces
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the SchedSlice table in Perfetto traces
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the Slice table in Perfetto traces
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the Thread table in Perfetto traces
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using PerfettoCds.Pipeline.Events;
|
||||
using PerfettoProcessor;
|
||||
|
||||
namespace PerfettoCds
|
||||
namespace PerfettoCds.Pipeline.SourceDataCookers
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooks the data from the ThreadTrack table in Perfetto traces
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Security.Cryptography;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using Microsoft.Performance.SDK;
|
||||
using PerfettoCds.Pipeline.DataCookers;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using System.Linq;
|
||||
|
||||
namespace PerfettoCds.Pipeline.Tables
|
||||
|
@ -20,7 +20,7 @@ namespace PerfettoCds.Pipeline.Tables
|
|||
Guid.Parse("{cc2db5d6-5abb-4094-b8c0-475a2f4d9946}"),
|
||||
"Perfetto CPU Counters (coarse)",
|
||||
"Displays coarse CPU usage based on /proc/stat counters",
|
||||
"Perfetto",
|
||||
"Perfetto - System",
|
||||
requiredDataCookers: new List<DataCookerPath> { PerfettoPluginConstants.CpuCountersEventCookerPath }
|
||||
);
|
||||
|
||||
|
@ -123,7 +123,7 @@ namespace PerfettoCds.Pipeline.Tables
|
|||
tableGenerator.AddColumn(CountColumn, Projection.Constant<int>(1));
|
||||
|
||||
// Only display the total CPU usage column
|
||||
var cpuUsageConfig = new TableConfiguration("Perfetto CPU usage")
|
||||
var cpuUsageConfig = new TableConfiguration("Perfetto CPU Usage")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Security.Cryptography;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using Microsoft.Performance.SDK;
|
||||
using PerfettoCds.Pipeline.DataCookers;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using System.Linq;
|
||||
|
||||
namespace PerfettoCds.Pipeline.Tables
|
||||
|
@ -20,7 +20,7 @@ namespace PerfettoCds.Pipeline.Tables
|
|||
Guid.Parse("{5b9689d4-617c-484c-9b0a-c7242565ec13}"),
|
||||
"Perfetto CPU Frequency Scaling",
|
||||
"Displays CPU frequency scaling events and idle states for CPUs. Idle CPUs show a frequency of 0.",
|
||||
"Perfetto",
|
||||
"Perfetto - System",
|
||||
requiredDataCookers: new List<DataCookerPath> { PerfettoPluginConstants.CpuFrequencyEventCookerPath }
|
||||
);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Security.Cryptography;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using Microsoft.Performance.SDK;
|
||||
using PerfettoCds.Pipeline.DataCookers;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using System.Linq;
|
||||
|
||||
namespace PerfettoCds.Pipeline.Tables
|
||||
|
@ -20,7 +20,7 @@ namespace PerfettoCds.Pipeline.Tables
|
|||
Guid.Parse("{db17169e-afe5-41f6-ba24-511af1d869f9}"),
|
||||
"Perfetto CPU Scheduler Events",
|
||||
"Displays CPU scheduling events for processes and threads",
|
||||
"Perfetto",
|
||||
"Perfetto - System",
|
||||
requiredDataCookers: new List<DataCookerPath> { PerfettoPluginConstants.CpuSchedEventCookerPath }
|
||||
);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using Microsoft.Performance.SDK;
|
||||
using PerfettoCds.Pipeline.DataCookers;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using Utilities;
|
||||
|
||||
namespace PerfettoCds.Pipeline.Tables
|
||||
|
@ -21,7 +21,7 @@ namespace PerfettoCds.Pipeline.Tables
|
|||
Guid.Parse("{96beb7a0-5a9e-4713-b1f7-4ee74d27851c}"),
|
||||
"Perfetto Ftrace Events",
|
||||
"All Ftrace events in the Perfetto trace",
|
||||
"Perfetto",
|
||||
"Perfetto - Events",
|
||||
requiredDataCookers: new List<DataCookerPath> { PerfettoPluginConstants.FtraceEventCookerPath }
|
||||
);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using Microsoft.Performance.SDK;
|
||||
using PerfettoCds.Pipeline.DataCookers;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using Utilities;
|
||||
|
||||
namespace PerfettoCds.Pipeline.Tables
|
||||
|
@ -21,7 +21,7 @@ namespace PerfettoCds.Pipeline.Tables
|
|||
Guid.Parse("{506777b6-f1a3-437a-b976-bc48190450b6}"),
|
||||
"Perfetto Generic Events",
|
||||
"All app/component events in the Perfetto trace",
|
||||
"Perfetto",
|
||||
"Perfetto - Events",
|
||||
requiredDataCookers: new List<DataCookerPath> { PerfettoPluginConstants.GenericEventCookerPath }
|
||||
);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Security.Cryptography;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using Microsoft.Performance.SDK;
|
||||
using PerfettoCds.Pipeline.DataCookers;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
|
||||
namespace PerfettoCds.Pipeline.Tables
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace PerfettoCds.Pipeline.Tables
|
|||
Guid.Parse("{1b25fe8d-887c-4de9-850f-284eb4c28ad7}"),
|
||||
"Android Logcat Events",
|
||||
"All logcat events/messages in the Perfetto trace",
|
||||
"Android",
|
||||
"Perfetto - Android",
|
||||
requiredDataCookers: new List<DataCookerPath> { PerfettoPluginConstants.LogcatEventCookerPath }
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using Microsoft.Performance.SDK.Extensibility;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using Microsoft.Performance.SDK;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using System.Linq;
|
||||
|
||||
namespace PerfettoCds.Pipeline.Tables
|
||||
{
|
||||
[Table]
|
||||
public class PerfettoProcessMemoryTable
|
||||
{
|
||||
public static TableDescriptor TableDescriptor => new TableDescriptor(
|
||||
Guid.Parse("{80d5ef1d-a24f-472c-83be-707b03239d35}"),
|
||||
"Perfetto Process Memory",
|
||||
"Displays per process memory counts gathered from /proc/<pid>/status",
|
||||
"Perfetto - System",
|
||||
requiredDataCookers: new List<DataCookerPath> { PerfettoPluginConstants.ProcessMemoryEventCookerPath }
|
||||
);
|
||||
|
||||
private static readonly ColumnConfiguration ProcessNameColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{5f47812f-85ab-42e8-bae3-1e7bf377a689}"), "Process", "Process name"),
|
||||
new UIHints { Width = 210 });
|
||||
private static readonly ColumnConfiguration StartTimestampColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{4b2c0e42-04ee-4e4f-916f-bf7065f34018}"), "StartTimestamp", "Start timestamp for the memory event"),
|
||||
new UIHints { Width = 120 });
|
||||
private static readonly ColumnConfiguration DurationColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{1db8a444-3bcc-4787-bc4c-f8ffd25ccf98}"), "Duration", "Start timestamp for the memory sample"),
|
||||
new UIHints { Width = 120 });
|
||||
|
||||
private static readonly ColumnConfiguration RssAnonColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{59db9b2a-09aa-42c5-9da7-631a507f0dbc}"), "RssAnonymous(kb)", "Resident set size - anonymous memory"),
|
||||
new UIHints { Width = 120, AggregationMode = AggregationMode.Max });
|
||||
private static readonly ColumnConfiguration RssShMemColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{2969f7a3-54b3-492c-a393-1bd937389bd2}"), "RssSharedMem(kb)", "Resident set size - shared memory"),
|
||||
new UIHints { Width = 120, AggregationMode = AggregationMode.Max });
|
||||
private static readonly ColumnConfiguration RssFileColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{96a12428-279e-4ef7-830b-89268c1d90cf}"), "RssFile(kb)", "Resident set size - file mappings"),
|
||||
new UIHints { Width = 120, AggregationMode = AggregationMode.Max });
|
||||
private static readonly ColumnConfiguration RssHwmColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{88f8d32e-c884-4263-9712-44166aee1f95}"), "RssHighWatermark(kb)", "Resident set size - peak (high water mark)"),
|
||||
new UIHints { Width = 120, AggregationMode = AggregationMode.Max });
|
||||
private static readonly ColumnConfiguration RssColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{1ea71b65-4a32-4fc0-8a87-273073a51aa9}"), "Rss(kb)", "Resident set size - sum of anon, file, shared mem"),
|
||||
new UIHints { Width = 120, AggregationMode = AggregationMode.Max });
|
||||
private static readonly ColumnConfiguration LockedColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{b9a65a94-f421-40cf-840e-74dffb84857f}"), "Locked(kb)", "Locked memory size"),
|
||||
new UIHints { Width = 120, AggregationMode = AggregationMode.Max });
|
||||
private static readonly ColumnConfiguration SwapColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{f0ebf9e8-39a1-44b7-8bf0-5f11ff6a8089}"), "Swap(kb)", "Swapped out VM size by anonymous private pages"),
|
||||
new UIHints { Width = 120, AggregationMode = AggregationMode.Max });
|
||||
private static readonly ColumnConfiguration VirtColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{83d575ba-2c24-46f7-901e-57241f72b918}"), "Virtual(kb)", "Peak virtual memory size"),
|
||||
new UIHints { Width = 120, AggregationMode = AggregationMode.Max });
|
||||
|
||||
public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieval tableData)
|
||||
{
|
||||
// Get data from the cooker
|
||||
var events = tableData.QueryOutput<ProcessedEventData<PerfettoProcessMemoryEvent>>(
|
||||
new DataOutputPath(PerfettoPluginConstants.ProcessMemoryEventCookerPath, nameof(PerfettoProcessMemoryEventCooker.ProcessMemoryEvents)));
|
||||
|
||||
var tableGenerator = tableBuilder.SetRowCount((int)events.Count);
|
||||
var baseProjection = Projection.Index(events);
|
||||
|
||||
tableGenerator.AddColumn(StartTimestampColumn, baseProjection.Compose(x => x.StartTimestamp));
|
||||
tableGenerator.AddColumn(ProcessNameColumn, baseProjection.Compose(x => x.ProcessName));
|
||||
tableGenerator.AddColumn(DurationColumn, baseProjection.Compose(x => x.Duration));
|
||||
|
||||
tableGenerator.AddColumn(RssAnonColumn, baseProjection.Compose(x => x.RssAnon));
|
||||
tableGenerator.AddColumn(LockedColumn, baseProjection.Compose(x => x.Locked));
|
||||
tableGenerator.AddColumn(RssShMemColumn, baseProjection.Compose(x => x.RssShMem));
|
||||
tableGenerator.AddColumn(RssFileColumn, baseProjection.Compose(x => x.RssFile));
|
||||
tableGenerator.AddColumn(RssHwmColumn, baseProjection.Compose(x => x.RssHwm));
|
||||
tableGenerator.AddColumn(RssColumn, baseProjection.Compose(x => x.Rss));
|
||||
tableGenerator.AddColumn(SwapColumn, baseProjection.Compose(x => x.Swap));
|
||||
tableGenerator.AddColumn(VirtColumn, baseProjection.Compose(x => x.Virt));
|
||||
|
||||
// Virtual
|
||||
var virtConfig = new TableConfiguration("Virtual")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
ProcessNameColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
RssAnonColumn,
|
||||
RssShMemColumn,
|
||||
RssFileColumn,
|
||||
RssHwmColumn,
|
||||
RssColumn,
|
||||
LockedColumn,
|
||||
SwapColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
VirtColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
virtConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
virtConfig.AddColumnRole(ColumnRole.ResourceId, ProcessNameColumn);
|
||||
virtConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
// Swap
|
||||
var swapConfig = new TableConfiguration("Swap")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
ProcessNameColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
RssAnonColumn,
|
||||
RssShMemColumn,
|
||||
RssFileColumn,
|
||||
RssHwmColumn,
|
||||
RssColumn,
|
||||
LockedColumn,
|
||||
VirtColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
SwapColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
swapConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
swapConfig.AddColumnRole(ColumnRole.ResourceId, ProcessNameColumn);
|
||||
swapConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
// Locked
|
||||
var lockedConfig = new TableConfiguration("Locked")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
ProcessNameColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
RssAnonColumn,
|
||||
RssShMemColumn,
|
||||
RssFileColumn,
|
||||
RssHwmColumn,
|
||||
RssColumn,
|
||||
SwapColumn,
|
||||
VirtColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
LockedColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
lockedConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
lockedConfig.AddColumnRole(ColumnRole.ResourceId, ProcessNameColumn);
|
||||
lockedConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
// Rss
|
||||
var rssConfig = new TableConfiguration("RSS (sum of anon, file, shared mem)")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
ProcessNameColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
RssAnonColumn,
|
||||
RssShMemColumn,
|
||||
RssFileColumn,
|
||||
RssHwmColumn,
|
||||
LockedColumn,
|
||||
SwapColumn,
|
||||
VirtColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
RssColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
rssConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
rssConfig.AddColumnRole(ColumnRole.ResourceId, ProcessNameColumn);
|
||||
rssConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
// rssHwm
|
||||
var rssHwmConfig = new TableConfiguration("RSS Peak (high water mark)")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
ProcessNameColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
RssAnonColumn,
|
||||
RssShMemColumn,
|
||||
RssFileColumn,
|
||||
RssColumn,
|
||||
LockedColumn,
|
||||
SwapColumn,
|
||||
VirtColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
RssHwmColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
rssHwmConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
rssHwmConfig.AddColumnRole(ColumnRole.ResourceId, ProcessNameColumn);
|
||||
rssHwmConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
// rssFile
|
||||
var rssFileConfig = new TableConfiguration("RSS File")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
ProcessNameColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
RssAnonColumn,
|
||||
RssShMemColumn,
|
||||
RssHwmColumn,
|
||||
RssColumn,
|
||||
LockedColumn,
|
||||
SwapColumn,
|
||||
VirtColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
RssFileColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
rssFileConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
rssFileConfig.AddColumnRole(ColumnRole.ResourceId, ProcessNameColumn);
|
||||
rssFileConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
// rssShMem
|
||||
var rssShMemConfig = new TableConfiguration("RSS Shared Memory")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
ProcessNameColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
RssAnonColumn,
|
||||
RssFileColumn,
|
||||
RssHwmColumn,
|
||||
RssColumn,
|
||||
LockedColumn,
|
||||
SwapColumn,
|
||||
VirtColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
RssShMemColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
rssShMemConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
rssShMemConfig.AddColumnRole(ColumnRole.ResourceId, ProcessNameColumn);
|
||||
rssShMemConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
// rssAnon
|
||||
var rssAnonConfig = new TableConfiguration("RSS Anonymous")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
ProcessNameColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
RssShMemColumn,
|
||||
RssFileColumn,
|
||||
RssHwmColumn,
|
||||
RssColumn,
|
||||
LockedColumn,
|
||||
SwapColumn,
|
||||
VirtColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
RssAnonColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
rssAnonConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
rssAnonConfig.AddColumnRole(ColumnRole.ResourceId, ProcessNameColumn);
|
||||
rssAnonConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
tableBuilder.AddTableConfiguration(virtConfig)
|
||||
.AddTableConfiguration(virtConfig)
|
||||
.AddTableConfiguration(swapConfig)
|
||||
.AddTableConfiguration(lockedConfig)
|
||||
.AddTableConfiguration(rssConfig)
|
||||
.AddTableConfiguration(rssHwmConfig)
|
||||
.AddTableConfiguration(rssFileConfig)
|
||||
.AddTableConfiguration(rssShMemConfig)
|
||||
.AddTableConfiguration(rssAnonConfig)
|
||||
.SetDefaultTableConfiguration(virtConfig);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using Microsoft.Performance.SDK.Extensibility;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using Microsoft.Performance.SDK;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using System.Linq;
|
||||
|
||||
namespace PerfettoCds.Pipeline.Tables
|
||||
{
|
||||
[Table]
|
||||
public class PerfettoSystemMemoryTable
|
||||
{
|
||||
public static TableDescriptor TableDescriptor => new TableDescriptor(
|
||||
Guid.Parse("{edbd3ddd-5610-4929-a85f-f9ca6eceb9b2}"),
|
||||
"Perfetto System Memory",
|
||||
"Displays system memory counts gathered from /proc/meminfo",
|
||||
"Perfetto - System",
|
||||
requiredDataCookers: new List<DataCookerPath> { PerfettoPluginConstants.SystemMemoryEventCookerPath }
|
||||
);
|
||||
|
||||
private static readonly ColumnConfiguration MemoryTypeColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{ac84aa1d-ea66-46d2-8fc3-8ead853f81b4}"), "MemoryType", "Type of memory"),
|
||||
new UIHints { Width = 210, });
|
||||
private static readonly ColumnConfiguration MemoryValueColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{3ada3dda-2893-4366-b1a7-a5fe8344e17b}"), "MemoryValue(kb)", "Memory value"),
|
||||
new UIHints { Width = 210, AggregationMode = AggregationMode.Max});
|
||||
|
||||
private static readonly ColumnConfiguration StartTimestampColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{6a9f870f-103c-461c-b909-9b098fe3695f}"), "StartTimestamp", "Start timestamp for the memory event"),
|
||||
new UIHints { Width = 120 });
|
||||
|
||||
private static readonly ColumnConfiguration DurationColumn = new ColumnConfiguration(
|
||||
new ColumnMetadata(new Guid("{bade2ff2-0a7c-4358-a736-058163739ae4}"), "Duration", "Start timestamp for the memory sample"),
|
||||
new UIHints { Width = 120 });
|
||||
|
||||
public static void BuildTable(ITableBuilder tableBuilder, IDataExtensionRetrieval tableData)
|
||||
{
|
||||
// Get data from the cooker
|
||||
var events = tableData.QueryOutput<ProcessedEventData<PerfettoSystemMemoryEvent>>(
|
||||
new DataOutputPath(PerfettoPluginConstants.SystemMemoryEventCookerPath, nameof(PerfettoSystemMemoryEventCooker.SystemMemoryEvents)));
|
||||
|
||||
var tableGenerator = tableBuilder.SetRowCount((int)events.Count);
|
||||
var baseProjection = Projection.Index(events);
|
||||
|
||||
tableGenerator.AddColumn(StartTimestampColumn, baseProjection.Compose(x => x.StartTimestamp));
|
||||
tableGenerator.AddColumn(MemoryTypeColumn, baseProjection.Compose(x => x.MemoryType));
|
||||
tableGenerator.AddColumn(MemoryValueColumn, baseProjection.Compose(x => x.Value));
|
||||
tableGenerator.AddColumn(DurationColumn, baseProjection.Compose(x => x.Duration));
|
||||
|
||||
// Virtual
|
||||
var tableConfig = new TableConfiguration("System Memory")
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
MemoryTypeColumn,
|
||||
TableConfiguration.PivotColumn, // Columns before this get pivotted on
|
||||
StartTimestampColumn,
|
||||
DurationColumn,
|
||||
TableConfiguration.GraphColumn, // Columns after this get graphed
|
||||
MemoryValueColumn
|
||||
},
|
||||
Layout = TableLayoutStyle.GraphAndTable,
|
||||
ChartType = ChartType.Line
|
||||
};
|
||||
tableConfig.AddColumnRole(ColumnRole.StartTime, StartTimestampColumn.Metadata.Guid);
|
||||
tableConfig.AddColumnRole(ColumnRole.Duration, DurationColumn);
|
||||
|
||||
tableBuilder.AddTableConfiguration(tableConfig)
|
||||
.SetDefaultTableConfiguration(tableConfig);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using System;
|
||||
using Perfetto.Protos;
|
||||
|
||||
namespace PerfettoProcessor
|
||||
{
|
||||
public class PerfettoCounterTrackEvent : PerfettoSqlEvent
|
||||
{
|
||||
public const string Key = "PerfettoCounterTrackEvent";
|
||||
|
||||
public static string SqlQuery = "select id, name from counter_track";
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public override string GetSqlQuery()
|
||||
{
|
||||
return SqlQuery;
|
||||
}
|
||||
|
||||
public override string GetEventKey()
|
||||
{
|
||||
return Key;
|
||||
}
|
||||
|
||||
public override void ProcessCell(string colName,
|
||||
QueryResult.Types.CellsBatch.Types.CellType cellType,
|
||||
QueryResult.Types.CellsBatch batch,
|
||||
string[] stringCells,
|
||||
CellCounters counters)
|
||||
{
|
||||
var col = colName.ToLower();
|
||||
switch (cellType)
|
||||
{
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellInvalid:
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellNull:
|
||||
break;
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellVarint:
|
||||
var longVal = batch.VarintCells[counters.IntCounter++];
|
||||
switch (col)
|
||||
{
|
||||
case "id":
|
||||
Id = longVal;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellFloat64:
|
||||
break;
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellString:
|
||||
var strVal = stringCells[counters.StringCounter++];
|
||||
switch (col)
|
||||
{
|
||||
case "name":
|
||||
Name = strVal;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellBlob:
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unexpected CellType");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
using System;
|
||||
using Perfetto.Protos;
|
||||
|
||||
namespace PerfettoProcessor
|
||||
{
|
||||
public class PerfettoProcessCounterTrackEvent : PerfettoSqlEvent
|
||||
{
|
||||
public const string Key = "PerfettoProcessCounterTrackEvent";
|
||||
|
||||
public static string SqlQuery = "select name, id, upid from process_counter_track";
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public long Upid { get; set; }
|
||||
|
||||
public override string GetSqlQuery()
|
||||
{
|
||||
return SqlQuery;
|
||||
}
|
||||
|
||||
public override string GetEventKey()
|
||||
{
|
||||
return Key;
|
||||
}
|
||||
|
||||
public override void ProcessCell(string colName,
|
||||
QueryResult.Types.CellsBatch.Types.CellType cellType,
|
||||
QueryResult.Types.CellsBatch batch,
|
||||
string[] stringCells,
|
||||
CellCounters counters)
|
||||
{
|
||||
var col = colName.ToLower();
|
||||
switch (cellType)
|
||||
{
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellInvalid:
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellNull:
|
||||
break;
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellVarint:
|
||||
var longVal = batch.VarintCells[counters.IntCounter++];
|
||||
switch (col)
|
||||
{
|
||||
case "id":
|
||||
Id = longVal;
|
||||
break;
|
||||
case "upid":
|
||||
Upid = longVal;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellFloat64:
|
||||
break;
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellString:
|
||||
var strVal = stringCells[counters.StringCounter++];
|
||||
switch (col)
|
||||
{
|
||||
case "name":
|
||||
Name = strVal;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Perfetto.Protos.QueryResult.Types.CellsBatch.Types.CellType.CellBlob:
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unexpected CellType");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -282,6 +282,12 @@ namespace PerfettoProcessor
|
|||
case PerfettoMetadataEvent.Key:
|
||||
ev = new PerfettoMetadataEvent();
|
||||
break;
|
||||
case PerfettoProcessCounterTrackEvent.Key:
|
||||
ev = new PerfettoProcessCounterTrackEvent();
|
||||
break;
|
||||
case PerfettoCounterTrackEvent.Key:
|
||||
ev = new PerfettoCounterTrackEvent();
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid event type");
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ using Microsoft.Performance.SDK.Processing;
|
|||
using Microsoft.Performance.Toolkit.Engine;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using PerfettoCds;
|
||||
using PerfettoCds.Pipeline.DataCookers;
|
||||
using PerfettoCds.Pipeline.CompositeDataCookers;
|
||||
using PerfettoCds.Pipeline.DataOutput;
|
||||
using System.IO;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче