blob: 10c7e41e4b30101f87e99e05e18c755701a48717 [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {TraceImpl} from '../core/trace_impl';
import {globals} from '../frontend/globals';
import {TrackNode} from '../public/workspace';
const MEM_DMA_COUNTER_NAME = 'mem.dma_heap';
const MEM_DMA = 'mem.dma_buffer';
const MEM_ION = 'mem.ion';
const F2FS_IOSTAT_TAG = 'f2fs_iostat.';
const F2FS_IOSTAT_GROUP_NAME = 'f2fs_iostat';
const F2FS_IOSTAT_LAT_TAG = 'f2fs_iostat_latency.';
const F2FS_IOSTAT_LAT_GROUP_NAME = 'f2fs_iostat_latency';
const DISK_IOSTAT_TAG = 'diskstat.';
const DISK_IOSTAT_GROUP_NAME = 'diskstat';
const BUDDY_INFO_TAG = 'mem.buddyinfo';
const UFS_CMD_TAG_REGEX = new RegExp('^io.ufs.command.tag.*$');
const UFS_CMD_TAG_GROUP = 'io.ufs.command.tags';
// NB: Userspace wakelocks start with "WakeLock" not "Wakelock".
const KERNEL_WAKELOCK_REGEX = new RegExp('^Wakelock.*$');
const KERNEL_WAKELOCK_GROUP = 'Kernel wakelocks';
const NETWORK_TRACK_REGEX = new RegExp('^.* (Received|Transmitted)( KB)?$');
const NETWORK_TRACK_GROUP = 'Networking';
const ENTITY_RESIDENCY_REGEX = new RegExp('^Entity residency:');
const ENTITY_RESIDENCY_GROUP = 'Entity residency';
const UCLAMP_REGEX = new RegExp('^UCLAMP_');
const UCLAMP_GROUP = 'Scheduler Utilization Clamping';
const POWER_RAILS_GROUP = 'Power Rails';
const POWER_RAILS_REGEX = new RegExp('^power.');
const FREQUENCY_GROUP = 'Frequency Scaling';
const TEMPERATURE_REGEX = new RegExp('^.* Temperature$');
const TEMPERATURE_GROUP = 'Temperature';
const IRQ_GROUP = 'IRQs';
const IRQ_REGEX = new RegExp('^(Irq|SoftIrq) Cpu.*');
const CHROME_TRACK_REGEX = new RegExp('^Chrome.*|^InputLatency::.*');
const CHROME_TRACK_GROUP = 'Chrome Global Tracks';
const MISC_GROUP = 'Misc Global Tracks';
function groupGlobalIonTracks(): void {
const ionTracks: TrackNode[] = [];
let hasSummary = false;
for (const track of globals.workspace.children) {
if (track.hasChildren) continue;
const isIon = track.title.startsWith(MEM_ION);
const isIonCounter = track.title === MEM_ION;
const isDmaHeapCounter = track.title === MEM_DMA_COUNTER_NAME;
const isDmaBuffferSlices = track.title === MEM_DMA;
if (isIon || isIonCounter || isDmaHeapCounter || isDmaBuffferSlices) {
ionTracks.push(track);
}
hasSummary = hasSummary || isIonCounter;
hasSummary = hasSummary || isDmaHeapCounter;
}
if (ionTracks.length === 0 || !hasSummary) {
return;
}
let group: TrackNode | undefined;
for (const track of ionTracks) {
if (!group && [MEM_DMA_COUNTER_NAME, MEM_ION].includes(track.title)) {
globals.workspace.removeChild(track);
group = new TrackNode({
title: track.title,
uri: track.uri,
isSummary: true,
});
globals.workspace.addChildInOrder(group);
} else {
group?.addChildInOrder(track);
}
}
}
function groupGlobalIostatTracks(tag: string, groupName: string): void {
const devMap = new Map<string, TrackNode>();
for (const track of globals.workspace.children) {
if (track.hasChildren) continue;
if (track.title.startsWith(tag)) {
const name = track.title.split('.', 3);
const key = name[1];
let parentGroup = devMap.get(key);
if (!parentGroup) {
const group = new TrackNode({title: groupName, isSummary: true});
globals.workspace.addChildInOrder(group);
devMap.set(key, group);
parentGroup = group;
}
track.title = name[2];
parentGroup.addChildInOrder(track);
}
}
}
function groupGlobalBuddyInfoTracks(): void {
const devMap = new Map<string, TrackNode>();
for (const track of globals.workspace.children) {
if (track.hasChildren) continue;
if (track.title.startsWith(BUDDY_INFO_TAG)) {
const tokens = track.title.split('[');
const node = tokens[1].slice(0, -1);
const zone = tokens[2].slice(0, -1);
const size = tokens[3].slice(0, -1);
const groupName = 'Buddyinfo: Node: ' + node + ' Zone: ' + zone;
if (!devMap.has(groupName)) {
const group = new TrackNode({title: groupName, isSummary: true});
devMap.set(groupName, group);
globals.workspace.addChildInOrder(group);
}
track.title = 'Chunk size: ' + size;
const group = devMap.get(groupName)!;
group.addChildInOrder(track);
}
}
}
function groupFrequencyTracks(groupName: string): void {
const group = new TrackNode({title: groupName, isSummary: true});
for (const track of globals.workspace.children) {
if (track.hasChildren) continue;
// Group all the frequency tracks together (except the CPU and GPU
// frequency ones).
if (
track.title.endsWith('Frequency') &&
!track.title.startsWith('Cpu') &&
!track.title.startsWith('Gpu')
) {
group.addChildInOrder(track);
}
}
if (group.children.length > 0) {
globals.workspace.addChildInOrder(group);
}
}
function groupMiscNonAllowlistedTracks(groupName: string): void {
// List of allowlisted track names.
const ALLOWLIST_REGEXES = [
new RegExp('^Cpu .*$', 'i'),
new RegExp('^Gpu .*$', 'i'),
new RegExp('^Trace Triggers$'),
new RegExp('^Android App Startups$'),
new RegExp('^Device State.*$'),
new RegExp('^Android logs$'),
];
const group = new TrackNode({title: groupName, isSummary: true});
for (const track of globals.workspace.children) {
if (track.hasChildren) continue;
let allowlisted = false;
for (const regex of ALLOWLIST_REGEXES) {
allowlisted = allowlisted || regex.test(track.title);
}
if (allowlisted) {
continue;
}
group.addChildInOrder(track);
}
if (group.children.length > 0) {
globals.workspace.addChildInOrder(group);
}
}
function groupTracksByRegex(regex: RegExp, groupName: string): void {
const group = new TrackNode({title: groupName, isSummary: true});
for (const track of globals.workspace.children) {
if (track.hasChildren) continue;
if (regex.test(track.title)) {
group.addChildInOrder(track);
}
}
if (group.children.length > 0) {
globals.workspace.addChildInOrder(group);
}
}
export async function decideTracks(trace: TraceImpl): Promise<void> {
groupGlobalIonTracks();
groupGlobalIostatTracks(F2FS_IOSTAT_TAG, F2FS_IOSTAT_GROUP_NAME);
groupGlobalIostatTracks(F2FS_IOSTAT_LAT_TAG, F2FS_IOSTAT_LAT_GROUP_NAME);
groupGlobalIostatTracks(DISK_IOSTAT_TAG, DISK_IOSTAT_GROUP_NAME);
groupTracksByRegex(UFS_CMD_TAG_REGEX, UFS_CMD_TAG_GROUP);
groupGlobalBuddyInfoTracks();
groupTracksByRegex(KERNEL_WAKELOCK_REGEX, KERNEL_WAKELOCK_GROUP);
groupTracksByRegex(NETWORK_TRACK_REGEX, NETWORK_TRACK_GROUP);
groupTracksByRegex(ENTITY_RESIDENCY_REGEX, ENTITY_RESIDENCY_GROUP);
groupTracksByRegex(UCLAMP_REGEX, UCLAMP_GROUP);
groupFrequencyTracks(FREQUENCY_GROUP);
groupTracksByRegex(POWER_RAILS_REGEX, POWER_RAILS_GROUP);
groupTracksByRegex(TEMPERATURE_REGEX, TEMPERATURE_GROUP);
groupTracksByRegex(IRQ_REGEX, IRQ_GROUP);
groupTracksByRegex(CHROME_TRACK_REGEX, CHROME_TRACK_GROUP);
groupMiscNonAllowlistedTracks(MISC_GROUP);
// Move groups underneath tracks
Array.from(trace.workspace.children)
.sort((a, b) => {
// Get the index in the order array
const indexA = a.hasChildren ? 1 : 0;
const indexB = b.hasChildren ? 1 : 0;
return indexA - indexB;
})
.forEach((n) => trace.workspace.addChildLast(n));
// If there is only one group, expand it
const rootLevelChildren = trace.workspace.children;
if (rootLevelChildren.length === 1 && rootLevelChildren[0].hasChildren) {
rootLevelChildren[0].expand();
}
}