• 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 size 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 {Actions} from '../common/actions';
18import {translateState} from '../common/thread_state';
19import {Anchor} from '../widgets/anchor';
20import {DetailsShell} from '../widgets/details_shell';
21import {GridLayout} from '../widgets/grid_layout';
22import {Section} from '../widgets/section';
23import {SqlRef} from '../widgets/sql_ref';
24import {Tree, TreeNode} from '../widgets/tree';
25
26import {globals, SliceDetails, ThreadDesc} from './globals';
27import {scrollToTrackAndTs} from './scroll_helper';
28import {SlicePanel} from './slice_panel';
29import {DurationWidget} from './widgets/duration';
30import {Timestamp} from './widgets/timestamp';
31import {THREAD_STATE_TRACK_KIND} from '../core/track_kinds';
32
33const MIN_NORMAL_SCHED_PRIORITY = 100;
34
35export class SliceDetailsPanel extends SlicePanel {
36  view() {
37    const sliceInfo = globals.sliceDetails;
38    if (sliceInfo.utid === undefined) return;
39    const threadInfo = globals.threads.get(sliceInfo.utid);
40
41    return m(
42      DetailsShell,
43      {
44        title: 'CPU Sched Slice',
45        description: this.renderDescription(sliceInfo),
46      },
47      m(
48        GridLayout,
49        this.renderDetails(sliceInfo, threadInfo),
50        this.renderSchedLatencyInfo(sliceInfo),
51      ),
52    );
53  }
54
55  private renderDescription(sliceInfo: SliceDetails) {
56    const threadInfo = globals.threads.get(sliceInfo.wakerUtid!);
57    if (!threadInfo) {
58      return null;
59    }
60    return `${threadInfo.procName} [${threadInfo.pid}]`;
61  }
62
63  private renderSchedLatencyInfo(sliceInfo: SliceDetails): m.Children {
64    if (!this.hasSchedLatencyInfo(sliceInfo)) {
65      return null;
66    }
67    return m(
68      Section,
69      {title: 'Scheduling Latency'},
70      m(
71        '.slice-details-latency-panel',
72        m('img.slice-details-image', {
73          src: `${globals.root}assets/scheduling_latency.png`,
74        }),
75        this.renderWakeupText(sliceInfo),
76        this.renderDisplayLatencyText(sliceInfo),
77      ),
78    );
79  }
80
81  private renderWakeupText(sliceInfo: SliceDetails): m.Children {
82    if (sliceInfo.wakerUtid === undefined) {
83      return null;
84    }
85    const threadInfo = globals.threads.get(sliceInfo.wakerUtid!);
86    if (!threadInfo) {
87      return null;
88    }
89    return m(
90      '.slice-details-wakeup-text',
91      m(
92        '',
93        `Wakeup @ `,
94        m(Timestamp, {ts: sliceInfo.wakeupTs!}),
95        ` on CPU ${sliceInfo.wakerCpu} by`,
96      ),
97      m('', `P: ${threadInfo.procName} [${threadInfo.pid}]`),
98      m('', `T: ${threadInfo.threadName} [${threadInfo.tid}]`),
99    );
100  }
101
102  private renderDisplayLatencyText(sliceInfo: SliceDetails): m.Children {
103    if (sliceInfo.ts === undefined || sliceInfo.wakeupTs === undefined) {
104      return null;
105    }
106
107    const latency = sliceInfo.ts - sliceInfo.wakeupTs;
108    return m(
109      '.slice-details-latency-text',
110      m('', `Scheduling latency: `, m(DurationWidget, {dur: latency})),
111      m(
112        '.text-detail',
113        `This is the interval from when the task became eligible to run
114        (e.g. because of notifying a wait queue it was suspended on) to
115        when it started running.`,
116      ),
117    );
118  }
119
120  private hasSchedLatencyInfo({wakeupTs, wakerUtid}: SliceDetails): boolean {
121    return wakeupTs !== undefined && wakerUtid !== undefined;
122  }
123
124  private renderThreadDuration(sliceInfo: SliceDetails) {
125    if (sliceInfo.threadDur !== undefined && sliceInfo.threadTs !== undefined) {
126      return m(TreeNode, {
127        icon: 'timer',
128        left: 'Thread Duration',
129        right: m(DurationWidget, {dur: sliceInfo.threadDur}),
130      });
131    } else {
132      return null;
133    }
134  }
135
136  private renderPriorityText(priority?: number) {
137    if (priority === undefined) {
138      return undefined;
139    }
140    return priority < MIN_NORMAL_SCHED_PRIORITY
141      ? `${priority} (real-time)`
142      : `${priority}`;
143  }
144
145  private renderDetails(
146    sliceInfo: SliceDetails,
147    threadInfo?: ThreadDesc,
148  ): m.Children {
149    if (
150      !threadInfo ||
151      sliceInfo.ts === undefined ||
152      sliceInfo.dur === undefined
153    ) {
154      return null;
155    } else {
156      const extras: m.Children = [];
157
158      for (const [key, value] of this.getProcessThreadDetails(sliceInfo)) {
159        if (value !== undefined) {
160          extras.push(m(TreeNode, {left: key, right: value}));
161        }
162      }
163
164      const treeNodes = [
165        m(TreeNode, {
166          left: 'Process',
167          right: `${threadInfo.procName} [${threadInfo.pid}]`,
168        }),
169        m(TreeNode, {
170          left: 'Thread',
171          right: m(
172            Anchor,
173            {
174              icon: 'call_made',
175              onclick: () => {
176                this.goToThread();
177              },
178            },
179            `${threadInfo.threadName} [${threadInfo.tid}]`,
180          ),
181        }),
182        m(TreeNode, {
183          left: 'Cmdline',
184          right: threadInfo.cmdline,
185        }),
186        m(TreeNode, {
187          left: 'Start time',
188          right: m(Timestamp, {ts: sliceInfo.ts}),
189        }),
190        m(TreeNode, {
191          left: 'Duration',
192          right: m(DurationWidget, {dur: sliceInfo.dur}),
193        }),
194        this.renderThreadDuration(sliceInfo),
195        m(TreeNode, {
196          left: 'Priority',
197          right: this.renderPriorityText(sliceInfo.priority),
198        }),
199        m(TreeNode, {
200          left: 'End State',
201          right: translateState(sliceInfo.endState),
202        }),
203        m(TreeNode, {
204          left: 'SQL ID',
205          right: m(SqlRef, {table: 'sched', id: sliceInfo.id}),
206        }),
207        ...extras,
208      ];
209
210      return m(Section, {title: 'Details'}, m(Tree, treeNodes));
211    }
212  }
213
214  goToThread() {
215    const sliceInfo = globals.sliceDetails;
216    if (sliceInfo.utid === undefined) return;
217    const threadInfo = globals.threads.get(sliceInfo.utid);
218
219    if (
220      sliceInfo.id === undefined ||
221      sliceInfo.ts === undefined ||
222      sliceInfo.dur === undefined ||
223      sliceInfo.cpu === undefined ||
224      threadInfo === undefined
225    ) {
226      return;
227    }
228
229    let trackKey: string | number | undefined;
230    for (const track of Object.values(globals.state.tracks)) {
231      const trackDesc = globals.trackManager.resolveTrackInfo(track.uri);
232      // TODO(stevegolton): Handle v2.
233      if (
234        trackDesc &&
235        trackDesc.kind === THREAD_STATE_TRACK_KIND &&
236        trackDesc.utid === threadInfo.utid
237      ) {
238        trackKey = track.key;
239      }
240    }
241
242    // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
243    if (trackKey && sliceInfo.threadStateId) {
244      globals.makeSelection(
245        Actions.selectThreadState({
246          id: sliceInfo.threadStateId,
247          trackKey: trackKey.toString(),
248        }),
249      );
250
251      scrollToTrackAndTs(trackKey, sliceInfo.ts, true);
252    }
253  }
254
255  renderCanvas() {}
256}
257