• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
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 */
15
16import { BaseElement, element } from '../../../../../base-ui/BaseElement';
17import { LitTable } from '../../../../../base-ui/table/lit-table';
18import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection';
19import '../../../StackBar';
20import { log } from '../../../../../log/Log';
21import { getThreadUsageProbablyTime } from '../../../../database/logic-worker/ProcedureLogicWorkerCommon';
22import { Utils } from '../../base/Utils';
23import { CpuStruct } from '../../../../database/ui-worker/cpu/ProcedureWorkerCPU';
24import { resizeObserver } from '../SheetUtils';
25import { getTabRunningPersent, getTabThreadStatesCpu } from '../../../../database/sql/ProcessThread.sql';
26
27@element('tabpane-thread-usage')
28export class TabPaneThreadUsage extends BaseElement {
29  private threadUsageTbl: LitTable | null | undefined;
30  private range: HTMLLabelElement | null | undefined;
31  private threadUsageSource: Array<SelectionData> = [];
32  private cpuCount = 0;
33  private currentSelectionParam: SelectionParam | undefined;
34  private pubColumns = `
35            <lit-table-column width="200px" title="Process" data-index="process" key="process"
36            align="flex-start" order>
37            </lit-table-column>
38            <lit-table-column width="100px" title="PID" data-index="pid" key="pid"
39            align="flex-start" order >
40            </lit-table-column>
41            <lit-table-column width="200px" title="Thread" data-index="thread" key="thread"
42            align="flex-start" order >
43            </lit-table-column>
44            <lit-table-column width="100px" title="TID" data-index="tid" key="tid"
45            align="flex-start" order >
46            </lit-table-column>
47            <lit-table-column width="160px" title="Wall duration(μs)" data-index="wallDurationTimeStr"
48            key="wallDurationTimeStr"  align="flex-start" order >
49            </lit-table-column>
50    `;
51
52  set data(threadUsageParam: SelectionParam | unknown) {
53    if (this.currentSelectionParam === threadUsageParam) {
54      return;
55    }
56    // @ts-ignore
57    this.currentSelectionParam = threadUsageParam;
58    // @ts-ignore
59    let traceId = threadUsageParam.traceId;
60    if (this.cpuCount !== Utils.getInstance().getCpuCount(traceId)) {
61      this.cpuCount = Utils.getInstance().getCpuCount(traceId); // @ts-ignore
62      this.threadUsageTbl!.innerHTML = this.getTableColumns(traceId);
63    }
64    //@ts-ignore
65    this.threadUsageTbl?.shadowRoot?.querySelector('.table')?.style?.height = `${
66      this.parentElement!.clientHeight - 45
67    }px`;
68    // 框选区域内running的时间
69    // @ts-ignore
70    getTabRunningPersent(threadUsageParam.threadIds, threadUsageParam.leftNs, threadUsageParam.rightNs).then(
71      (result) => {
72        // 数组套对象
73        // 开始的时间leftStartNs
74        // @ts-ignore
75        let leftStartNs = threadUsageParam.leftNs + threadUsageParam.recordStartNs;
76        // 结束的时间rightEndNs
77        // @ts-ignore
78        let rightEndNs = threadUsageParam.rightNs + threadUsageParam.recordStartNs;
79        let sum = rightEndNs - leftStartNs;
80        this.range!.textContent = `Selected range: ${(sum / 1000000.0).toFixed(5)} ms`;
81      }
82    );
83    this.threadUsageTbl!.loading = true;
84    // @ts-ignore
85    getTabThreadStatesCpu(threadUsageParam.threadIds, threadUsageParam.leftNs, threadUsageParam.rightNs).then(
86      (result) => {
87        this.threadUsageTbl!.loading = false;
88        this.threadStatesCpuDataHandler(result, threadUsageParam);
89      }
90    );
91  }
92
93  private threadStatesCpuDataHandler(result: unknown[], threadUsageParam: SelectionParam | unknown): void {
94    if (result !== null && result.length > 0) {
95      log(`getTabThreadStates result size : ${result.length}`);
96      // @ts-ignore
97      let filterArr = result.filter((it) => threadUsageParam.processIds.includes(it.pid));
98      let totalDurtion = 0;
99      filterArr.forEach((item) => {
100        // @ts-ignore
101        totalDurtion = totalDurtion + item.wallDuration;
102      });
103      let map: Map<number, unknown> = new Map<number, unknown>();
104      for (let resultEl of filterArr) {
105        // @ts-ignore
106        if (threadUsageParam.processIds.includes(resultEl.pid)) {
107          // @ts-ignore
108          if (map.has(resultEl.tid)) {
109            // @ts-ignore
110            map.get(resultEl.tid)[`cpu${resultEl.cpu}`] = resultEl.wallDuration || 0;
111            // @ts-ignore
112            map.get(resultEl.tid)[`cpu${resultEl.cpu}TimeStr`] = getThreadUsageProbablyTime(resultEl.wallDuration || 0);
113            // @ts-ignore
114            map.get(resultEl.tid).wallDuration = map.get(resultEl.tid).wallDuration + (resultEl.wallDuration || 0);
115            // @ts-ignore
116            map.get(resultEl.tid).wallDurationTimeStr = getThreadUsageProbablyTime(map.get(resultEl.tid).wallDuration);
117          } else {
118            // @ts-ignore
119            let process = Utils.getInstance().getProcessMap(threadUsageParam.traceId).get(resultEl.pid);
120            // @ts-ignore
121            let thread = Utils.getInstance().getThreadMap(threadUsageParam.traceId).get(resultEl.tid);
122            let threadStatesStruct: unknown = {
123              // @ts-ignore
124              tid: resultEl.tid,
125              // @ts-ignore
126              pid: resultEl.pid,
127              thread: thread || 'null',
128              process: process || 'null',
129              // @ts-ignore
130              wallDuration: resultEl.wallDuration || 0,
131              // @ts-ignore
132              wallDurationTimeStr: getThreadUsageProbablyTime(resultEl.wallDuration || 0),
133            };
134            for (let i = 0; i < this.cpuCount; i++) {
135              // @ts-ignore
136              threadStatesStruct[`cpu${i}`] = 0;
137              // @ts-ignore
138              threadStatesStruct[`cpu${i}TimeStr`] = '0';
139              // @ts-ignore
140              threadStatesStruct[`cpu${i}Ratio`] = '0';
141            }
142            // @ts-ignore
143            threadStatesStruct[`cpu${resultEl.cpu}`] = resultEl.wallDuration || 0;
144            // @ts-ignore
145            threadStatesStruct[`cpu${resultEl.cpu}TimeStr`] = getThreadUsageProbablyTime(resultEl.wallDuration || 0);
146            // @ts-ignore
147            map.set(resultEl.tid, threadStatesStruct);
148          }
149        }
150      }
151      map.forEach((val) => {
152        for (let i = 0; i < this.cpuCount; i++) {
153          // @ts-ignore
154          val[`cpu${i}Ratio`] = ((100.0 * val[`cpu${i}`]) / val.wallDuration).toFixed(2);
155        }
156      });
157      // @ts-ignore
158      this.threadUsageSource = Array.from(map.values());
159      this.threadUsageTbl!.recycleDataSource = this.threadUsageSource;
160    } else {
161      this.threadUsageSource = [];
162      this.threadUsageTbl!.recycleDataSource = [];
163    }
164  }
165
166  getTableColumns(traceId?: string | null): string {
167    let threadUsageHtml = `${this.pubColumns}`;
168    let cpuCount = Utils.getInstance().getCpuCount(traceId);
169    for (let index = 0; index < cpuCount; index++) {
170      threadUsageHtml = `${threadUsageHtml}
171            <lit-table-column width="100px" title="cpu${index}(μs)" data-index="cpu${index}TimeStr"
172            key="cpu${index}TimeStr"  align="flex-start" order>
173            </lit-table-column>
174            <lit-table-column width="100px" title="%" data-index="cpu${index}Ratio"
175            key="cpu${index}Ratio"  align="flex-start" order>
176            </lit-table-column>
177            `;
178    }
179    return threadUsageHtml;
180  }
181
182  initElements(): void {
183    this.threadUsageTbl = this.shadowRoot?.querySelector<LitTable>('#tb-thread-states');
184    this.range = this.shadowRoot?.querySelector('#thread-usage-time-range');
185    this.threadUsageTbl!.addEventListener('column-click', (evt: unknown) => {
186      // @ts-ignore
187      this.sortByColumn(evt.detail);
188    });
189  }
190
191  connectedCallback(): void {
192    super.connectedCallback();
193    resizeObserver(this.parentElement!, this.threadUsageTbl!);
194  }
195
196  initHtml(): string {
197    return `
198        <style>
199        .thread-usage-table{
200          flex-direction: row;
201          margin-bottom: 5px;
202        }
203        :host{
204            display: flex;
205            flex-direction: column;
206            padding: 10px 10px;
207        }
208        </style>
209        <div class="thread-usage-table" style="display: flex;height: 20px;align-items: center;
210        flex-direction: row;margin-bottom: 5px">
211            <stack-bar id="thread-usage-stack-bar" style="flex: 1"></stack-bar>
212            <label id="thread-usage-time-range"  style="width: auto;text-align: end;
213            font-size: 10pt;">Selected range:0.0 ms</label>
214        </div>
215        <div style="overflow: auto">
216            <lit-table id="tb-thread-states" style="height: auto"></lit-table>
217        </div>
218        `;
219  }
220
221  sortByColumn(treadUsageDetail: unknown): void {
222    function compare(property: unknown, treadUsageSort: unknown, type: unknown) {
223      return function (threadUsageLeftData: SelectionData | unknown, threadUsageRightData: SelectionData | unknown) {
224        // @ts-ignore
225        if (threadUsageLeftData.process === ' ' || threadUsageRightData.process === ' ') {
226          return 0;
227        }
228        if (type === 'number') {
229          return treadUsageSort === 2
230            ? // @ts-ignore
231              parseFloat(threadUsageRightData[property]) - parseFloat(threadUsageLeftData[property])
232            : // @ts-ignore
233              parseFloat(threadUsageLeftData[property]) - parseFloat(threadUsageRightData[property]);
234        } else {
235          // @ts-ignore
236          if (threadUsageRightData[property] > threadUsageLeftData[property]) {
237            return treadUsageSort === 2 ? 1 : -1;
238            // @ts-ignore
239          } else if (threadUsageRightData[property] === threadUsageLeftData[property]) {
240            return 0;
241          } else {
242            return treadUsageSort === 2 ? -1 : 1;
243          }
244        }
245      };
246    }
247
248    if (
249      // @ts-ignore
250      treadUsageDetail.key === 'process' ||
251      // @ts-ignore
252      treadUsageDetail.key === 'thread' ||
253      // @ts-ignore
254      (treadUsageDetail.key as string).includes('Ratio')
255    ) {
256      // @ts-ignore
257      this.threadUsageSource.sort(compare(treadUsageDetail.key, treadUsageDetail.sort, 'string'));
258    } else {
259      this.threadUsageSource.sort(
260        // @ts-ignore
261        compare((treadUsageDetail.key as string).replace('TimeStr', ''), treadUsageDetail.sort, 'number')
262      );
263    }
264    this.threadUsageTbl!.recycleDataSource = this.threadUsageSource;
265  }
266}
267
268export function judgement(result: Array<unknown>, leftStart: unknown, rightEnd: unknown): number {
269  let sum = 0;
270  if (result !== null && result.length > 0) {
271    log(`getTabRunningTime result size : ${result.length}`);
272    let rightEndNs = rightEnd;
273    let leftStartNs = leftStart;
274    // 尾部running的结束时间
275    // @ts-ignore
276    let RunningEnds = result[result.length - 1].dur - (rightEndNs - result[result.length - 1].ts) + rightEndNs;
277    // 如果截取了开头和结尾的长度
278    // @ts-ignore
279    let beigin = result[0].dur - (leftStartNs - result[0].ts);
280    // @ts-ignore
281    let end = rightEndNs - result[result.length - 1].ts;
282    // 用来存储数据的新数组
283    let arr = [];
284    // 如果开头和结尾都截取了
285    // @ts-ignore
286    if (leftStartNs > result[0].ts && rightEndNs < RunningEnds) {
287      // 首尾的running长度
288      let beginAndEnd = beigin + end;
289
290      // 截取的除了开头和结尾的数据
291      arr = result.slice(1, result.length - 1);
292      let res = arr.reduce((total, item) => {
293        // @ts-ignore
294        return total + item.dur;
295      }, 0);
296      // @ts-ignore
297      sum = beginAndEnd + res;
298      // @ts-ignore
299    } else if (leftStartNs > result[0].ts) {
300      // 如果只是截取了开头
301      arr = result.slice(1);
302      let res = arr.reduce((total, item) => {
303        // @ts-ignore
304        return total + item.dur;
305      }, 0);
306      // @ts-ignore
307      sum = beigin + res;
308      // @ts-ignore
309    } else if (rightEndNs < RunningEnds) {
310      // 如果只是截取了结尾
311      arr = result.slice(0, result.length - 1);
312      let res = arr.reduce((total, item) => {
313        // @ts-ignore
314        return total + item.dur;
315      }, 0);
316      // @ts-ignore
317      sum = end + res;
318    } else {
319      // 如果都没截取
320      for (let i of result) {
321        // @ts-ignore
322        sum += i.dur;
323      }
324    }
325  }
326  return sum;
327}
328