1// Copyright (C) 2023 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import m from 'mithril'; 16 17import {duration, TimeSpan} from '../../base/time'; 18import {Engine} from '../../public'; 19import { 20 LONG, 21 NUM_NULL, 22 STR, 23 STR_NULL, 24} from '../../trace_processor/query_result'; 25import {TreeNode} from '../../widgets/tree'; 26import {Utid} from '../sql_types'; 27import {DurationWidget} from '../widgets/duration'; 28 29// An individual node of the thread state breakdown tree. 30class Node { 31 parent?: Node; 32 children: Map<string, Node>; 33 dur: duration; 34 startsCollapsed: boolean = true; 35 36 constructor(parent?: Node) { 37 this.parent = parent; 38 this.children = new Map(); 39 this.dur = 0n; 40 } 41 42 getOrCreateChild(name: string) { 43 let child = this.children.get(name); 44 if (!child) { 45 child = new Node(this); 46 this.children.set(name, child); 47 } 48 return child; 49 } 50 51 addDuration(dur: duration) { 52 let node: Node | undefined = this; 53 while (node !== undefined) { 54 node.dur += dur; 55 node = node.parent; 56 } 57 } 58} 59 60// Thread state breakdown data (tree). 61// Can be passed to ThreadStateBreakdownTreeNode to be rendered as a part of a 62// tree. 63export interface BreakdownByThreadState { 64 root: Node; 65} 66 67// Compute a breakdown of thread states for a given thread for a given time 68// interval. 69export async function breakDownIntervalByThreadState( 70 engine: Engine, 71 range: TimeSpan, 72 utid: Utid, 73): Promise<BreakdownByThreadState> { 74 // TODO(altimin): this probably should share some code with pivot tables when 75 // we actually get some pivot tables we like. 76 const query = await engine.query(` 77 INCLUDE PERFETTO MODULE sched.time_in_state; 78 INCLUDE PERFETTO MODULE sched.states; 79 INCLUDE PERFETTO MODULE cpu.size; 80 81 SELECT 82 sched_state_io_to_human_readable_string(state, io_wait) as state, 83 state AS rawState, 84 cpu_guess_core_type(cpu) AS cpuType, 85 cpu, 86 blocked_function AS blockedFunction, 87 dur 88 FROM sched_time_in_state_and_cpu_for_thread_in_interval(${range.start}, ${range.duration}, ${utid}); 89 `); 90 const it = query.iter({ 91 state: STR, 92 rawState: STR, 93 cpuType: STR_NULL, 94 cpu: NUM_NULL, 95 blockedFunction: STR_NULL, 96 dur: LONG, 97 }); 98 const root = new Node(); 99 for (; it.valid(); it.next()) { 100 let currentNode = root; 101 currentNode = currentNode.getOrCreateChild(it.state); 102 // If the CPU time is not null, add it to the breakdown. 103 if (it.cpuType !== null) { 104 currentNode = currentNode.getOrCreateChild(it.cpuType); 105 } 106 if (it.cpu !== null) { 107 currentNode = currentNode.getOrCreateChild(`CPU ${it.cpu}`); 108 } 109 if (it.blockedFunction !== null) { 110 currentNode = currentNode.getOrCreateChild(`${it.blockedFunction}`); 111 } 112 currentNode.addDuration(it.dur); 113 } 114 return { 115 root, 116 }; 117} 118 119function renderChildren(node: Node, totalDur: duration): m.Child[] { 120 const res = Array.from(node.children.entries()).map(([name, child]) => 121 renderNode(child, name, totalDur), 122 ); 123 return res; 124} 125 126function renderNode(node: Node, name: string, totalDur: duration): m.Child { 127 const durPercent = (100 * Number(node.dur)) / Number(totalDur); 128 return m( 129 TreeNode, 130 { 131 left: name, 132 right: [ 133 m(DurationWidget, {dur: node.dur}), 134 ` (${durPercent.toFixed(2)}%)`, 135 ], 136 startsCollapsed: node.startsCollapsed, 137 }, 138 renderChildren(node, totalDur), 139 ); 140} 141 142interface BreakdownByThreadStateTreeNodeAttrs { 143 dur: duration; 144 data: BreakdownByThreadState; 145} 146 147// A tree node that displays a nested breakdown a time interval by thread state. 148export class BreakdownByThreadStateTreeNode 149 implements m.ClassComponent<BreakdownByThreadStateTreeNodeAttrs> 150{ 151 view({attrs}: m.Vnode<BreakdownByThreadStateTreeNodeAttrs>): m.Child[] { 152 return renderChildren(attrs.data.root, attrs.dur); 153 } 154} 155