• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2019 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 {searchEq, searchRange, searchSegment} from '../../base/binary_search';
16import {assertTrue} from '../../base/logging';
17import {Actions} from '../../common/actions';
18import {colorForThread} from '../../common/colorizer';
19import {checkerboardExcept} from '../../frontend/checkerboard';
20import {globals} from '../../frontend/globals';
21import {NewTrackArgs, Track} from '../../frontend/track';
22import {trackRegistry} from '../../frontend/track_registry';
23
24import {
25  Config,
26  Data,
27  PROCESS_SCHEDULING_TRACK_KIND,
28} from './common';
29
30const MARGIN_TOP = 5;
31const RECT_HEIGHT = 30;
32const TRACK_HEIGHT = MARGIN_TOP * 2 + RECT_HEIGHT;
33
34class ProcessSchedulingTrack extends Track<Config, Data> {
35  static readonly kind = PROCESS_SCHEDULING_TRACK_KIND;
36  static create(args: NewTrackArgs): ProcessSchedulingTrack {
37    return new ProcessSchedulingTrack(args);
38  }
39
40  private mousePos?: {x: number, y: number};
41  private utidHoveredInThisTrack = -1;
42
43  constructor(args: NewTrackArgs) {
44    super(args);
45  }
46
47  getHeight(): number {
48    return TRACK_HEIGHT;
49  }
50
51  renderCanvas(ctx: CanvasRenderingContext2D): void {
52    // TODO: fonts and colors should come from the CSS and not hardcoded here.
53    const {timeScale, visibleWindowTime} = globals.frontendLocalState;
54    const data = this.data();
55
56    if (data === undefined) return;  // Can't possibly draw anything.
57
58    // If the cached trace slices don't fully cover the visible time range,
59    // show a gray rectangle with a "Loading..." label.
60    checkerboardExcept(
61        ctx,
62        this.getHeight(),
63        timeScale.timeToPx(visibleWindowTime.start),
64        timeScale.timeToPx(visibleWindowTime.end),
65        timeScale.timeToPx(data.start),
66        timeScale.timeToPx(data.end));
67
68    assertTrue(data.starts.length === data.ends.length);
69    assertTrue(data.starts.length === data.utids.length);
70
71    const rawStartIdx =
72        data.ends.findIndex(end => end >= visibleWindowTime.start);
73    const startIdx = rawStartIdx === -1 ? data.starts.length : rawStartIdx;
74
75    const [, rawEndIdx] = searchSegment(data.starts, visibleWindowTime.end);
76    const endIdx = rawEndIdx === -1 ? data.starts.length : rawEndIdx;
77
78    const cpuTrackHeight = Math.floor(RECT_HEIGHT / data.maxCpu);
79
80    for (let i = startIdx; i < endIdx; i++) {
81      const tStart = data.starts[i];
82      const tEnd = data.ends[i];
83      const utid = data.utids[i];
84      const cpu = data.cpus[i];
85
86      const rectStart = timeScale.timeToPx(tStart);
87      const rectEnd = timeScale.timeToPx(tEnd);
88      const rectWidth = rectEnd - rectStart;
89      if (rectWidth < 0.3) continue;
90
91      const threadInfo = globals.threads.get(utid);
92      const pid = (threadInfo ? threadInfo.pid : -1) || -1;
93
94      const isHovering = globals.state.hoveredUtid !== -1;
95      const isThreadHovered = globals.state.hoveredUtid === utid;
96      const isProcessHovered = globals.state.hoveredPid === pid;
97      const color = colorForThread(threadInfo);
98      if (isHovering && !isThreadHovered) {
99        if (!isProcessHovered) {
100          color.l = 90;
101          color.s = 0;
102        } else {
103          color.l = Math.min(color.l + 30, 80);
104          color.s -= 20;
105        }
106      } else {
107        color.l = Math.min(color.l + 10, 60);
108        color.s -= 20;
109      }
110      ctx.fillStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
111      const y = MARGIN_TOP + cpuTrackHeight * cpu + cpu;
112      ctx.fillRect(rectStart, y, rectEnd - rectStart, cpuTrackHeight);
113    }
114
115    const hoveredThread = globals.threads.get(this.utidHoveredInThisTrack);
116    if (hoveredThread !== undefined && this.mousePos !== undefined) {
117      const tidText = `T: ${hoveredThread.threadName} [${hoveredThread.tid}]`;
118      if (hoveredThread.pid) {
119        const pidText = `P: ${hoveredThread.procName} [${hoveredThread.pid}]`;
120        this.drawTrackHoverTooltip(ctx, this.mousePos, pidText, tidText);
121      } else {
122        this.drawTrackHoverTooltip(ctx, this.mousePos, tidText);
123      }
124    }
125  }
126
127  onMouseMove(pos: {x: number, y: number}) {
128    const data = this.data();
129    this.mousePos = pos;
130    if (data === undefined) return;
131    if (pos.y < MARGIN_TOP || pos.y > MARGIN_TOP + RECT_HEIGHT) {
132      this.utidHoveredInThisTrack = -1;
133      globals.dispatch(Actions.setHoveredUtidAndPid({utid: -1, pid: -1}));
134      return;
135    }
136
137    const cpuTrackHeight = Math.floor(RECT_HEIGHT / data.maxCpu);
138    const cpu = Math.floor((pos.y - MARGIN_TOP) / (cpuTrackHeight + 1));
139    const {timeScale} = globals.frontendLocalState;
140    const t = timeScale.pxToTime(pos.x);
141
142    const [i, j] = searchRange(data.starts, t, searchEq(data.cpus, cpu));
143    if (i === j || i >= data.starts.length || t > data.ends[i]) {
144      this.utidHoveredInThisTrack = -1;
145      globals.dispatch(Actions.setHoveredUtidAndPid({utid: -1, pid: -1}));
146      return;
147    }
148
149    const utid = data.utids[i];
150    this.utidHoveredInThisTrack = utid;
151    const threadInfo = globals.threads.get(utid);
152    const pid = threadInfo ? (threadInfo.pid ? threadInfo.pid : -1) : -1;
153    globals.dispatch(Actions.setHoveredUtidAndPid({utid, pid}));
154  }
155
156  onMouseOut() {
157    this.utidHoveredInThisTrack = -1;
158    globals.dispatch(Actions.setHoveredUtidAndPid({utid: -1, pid: -1}));
159    this.mousePos = undefined;
160  }
161}
162
163trackRegistry.register(ProcessSchedulingTrack);
164