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 { procedurePool } from '../../../database/Procedure';
19import { info } from '../../../../log/Log';
20import { TableNoData } from '../TableNoData';
21import '../TableNoData';
22import { LitProgressBar } from '../../../../base-ui/progress-bar/LitProgressBar';
23import '../../../../base-ui/progress-bar/LitProgressBar';
24import { LitChartColumn } from '../../../../base-ui/chart/column/LitChartColumn';
25import '../../../../base-ui/chart/column/LitChartColumn';
26import { Utils } from '../../trace/base/Utils';
27
28@element('top10-longest-runtime-process')
29export class Top10LongestRunTimeProcess extends BaseElement {
30  traceChange: boolean = false;
31  private processRunTimeTbl: LitTable | null | undefined;
32  private threadRunTimeTbl: LitTable | null | undefined;
33  private processRunTimeProgress: LitProgressBar | null | undefined;
34  private nodataPro: TableNoData | null | undefined;
35  private processRunTimeData: Array<Top10RunTimeData> = [];
36  private threadRunTimeData: Array<Top10RunTimeData> = [];
37  private processSwitchCountChart: LitChartColumn | null | undefined;
38  private threadSwitchCountChart: LitChartColumn | null | undefined;
39  private nodataThr: TableNoData | null | undefined;
40  private display_pro: HTMLDivElement | null | undefined;
41  private display_thr: HTMLDivElement | null | undefined;
42  private processId: number | undefined;
43  private display_flag: boolean = true;
44  private back: HTMLDivElement | null | undefined;
45  private processMap: Map<number, string> = new Map();
46  private threadMap: Map<number, string> = new Map();
47
48  /**
49   * 初始化操作,若trace发生改变,将所有变量设置为默认值并重新请求数据。若trace未改变,跳出初始化
50   */
51  init(): void {
52    if (!this.traceChange) {
53      if (this.processRunTimeTbl!.recycleDataSource.length > 0) {
54        this.processRunTimeTbl?.reMeauseHeight();
55      }
56      return;
57    }
58    this.traceChange = false;
59    this.processRunTimeProgress!.loading = true;
60    this.display_flag = true;
61    this.display_pro!.style.display = 'block';
62    this.display_thr!.style.display = 'none';
63    this.processMap = Utils.getInstance().getProcessMap();
64    this.threadMap = Utils.getInstance().getThreadMap();
65    this.queryLogicWorker(
66      'scheduling-Process Top10RunTime',
67      'query Process Top10 Run Time Analysis Time:',
68      this.callBack.bind(this)
69    );
70  }
71
72  /**
73   * 清除已存储数据
74   */
75  clearData(): void {
76    this.traceChange = true;
77    this.processSwitchCountChart!.dataSource = [];
78    this.processRunTimeTbl!.recycleDataSource = [];
79    this.threadSwitchCountChart!.dataSource = [];
80    this.threadRunTimeTbl!.recycleDataSource = [];
81    this.processRunTimeData = [];
82    this.threadRunTimeData = [];
83    this.processMap = new Map();
84    this.threadMap = new Map();
85  }
86
87  /**
88   * 提交worker线程,进行数据库查询
89   * @param option 操作的key值,用于找到并执行对应方法
90   * @param log 日志打印内容
91   * @param handler 结果回调函数
92   * @param pid 需要查询某一进程下线程数据的进程id
93   */
94  queryLogicWorker(option: string, log: string, handler: (res: Array<Top10RunTimeData>) => void, pid?: number): void {
95    let processThreadCountTime = new Date().getTime();
96    procedurePool.submitWithName('logic0', option, { pid: pid }, undefined, handler);
97    let durTime = new Date().getTime() - processThreadCountTime;
98    info(log, durTime);
99  }
100
101  /**
102   * 提交worker线程,进行数据库查询
103   * @param option 操作的key值,用于找到并执行对应方法
104   * @param log 日志打印内容
105   * @param handler 结果回调函数
106   * @param pid 需要查询某一进程下线程数据的进程id
107   */
108  organizationData(arr: Array<Top10RunTimeData>): Array<Top10RunTimeData> {
109    let result: Array<Top10RunTimeData> = [];
110    for (let i = 0; i < arr.length; i++) {
111      const pStr: string | null = this.processMap.get(arr[i].pid!)!;
112      const tStr: string | null = this.threadMap.get(arr[i].tid!)!;
113      result.push({
114        no: i + 1,
115        pid: arr[i].pid || this.processId,
116        pName: pStr === null ? 'Process ' : pStr,
117        dur: arr[i].dur,
118        tid: arr[i].tid,
119        tName: tStr === null ? 'Thread ' : tStr
120      });
121    }
122    return result;
123  }
124
125  /**
126   * 提交线程后,结果返回后的回调函数
127   * @param res 数据库查询结果
128   */
129  callBack(res: Array<Top10RunTimeData>): void {
130    let result: Array<Top10RunTimeData> = this.organizationData(res);
131    if (this.display_flag === true) {
132      this.processCallback(result);
133    } else {
134      this.threadCallback(result);
135    }
136    this.processRunTimeProgress!.loading = false;
137  }
138
139  /**
140   * 大函数块拆解分为两部分,此部分为Top10进程数据
141   * @param result 需要显示在表格中的数据
142   */
143  processCallback(result: Array<Top10RunTimeData>): void {
144    this.nodataPro!.noData = result === undefined || result.length === 0;
145    this.processRunTimeTbl!.recycleDataSource = result;
146    this.processRunTimeTbl!.reMeauseHeight();
147    this.processRunTimeData = result;
148    this.processSwitchCountChart!.config = {
149      data: result,
150      appendPadding: 10,
151      xField: 'pid',
152      yField: 'dur',
153      seriesField: 'size',
154      color: (a): string => {
155        return '#0a59f7';
156      },
157      hoverHandler: (no): void => {
158        let data: unknown = result.find((it) => it.no === no);
159        if (data) {
160          // @ts-ignore
161          data.isHover = true;
162          this.processRunTimeTbl!.setCurrentHover(data);
163        } else {
164          this.processRunTimeTbl!.mouseOut();
165        }
166      },
167      tip: (obj): string => {
168        return `
169          <div>
170            <div>Process_Id:${
171          // @ts-ignore
172          obj[0].obj.pid}</div> 
173            <div>Process_Name:${
174          // @ts-ignore
175          obj[0].obj.pName}</div> 
176            <div>Run_Time:${
177          // @ts-ignore
178          obj[0].obj.dur}</div> 
179          </div>
180        `;
181      },
182      label: null,
183    };
184  }
185
186  /**
187   * 大函数块拆解分为两部分,此部分为Top10线程数据
188   * @param result 需要显示在表格中的数据
189   */
190  threadCallback(result: Array<Top10RunTimeData>): void {
191    this.nodataThr!.noData = result === undefined || result.length === 0;
192    this.threadRunTimeTbl!.recycleDataSource = result;
193    this.threadRunTimeTbl!.reMeauseHeight();
194    this.threadRunTimeData = result;
195    this.threadSwitchCountChart!.config = {
196      data: result,
197      appendPadding: 10,
198      xField: 'tid',
199      yField: 'dur',
200      seriesField: 'size',
201      color: (a): string => {
202        return '#0a59f7';
203      },
204      hoverHandler: (no): void => {
205        let data: unknown = result.find((it) => it.no === no);
206        if (data) {
207          // @ts-ignore
208          data.isHover = true;
209          this.threadRunTimeTbl!.setCurrentHover(data);
210        } else {
211          this.threadRunTimeTbl!.mouseOut();
212        }
213      },
214      tip: (obj): string => {
215        return `
216          <div>
217            <div>Process_Id:${
218          // @ts-ignore
219          obj[0].obj.pid}</div> 
220            <div>Thread_Id:${
221          // @ts-ignore
222          obj[0].obj.tid}</div> 
223            <div>Thread_Name:${
224          // @ts-ignore
225          obj[0].obj.tName}</div> 
226            <div>Run_Time:${
227          // @ts-ignore
228          obj[0].obj.dur}</div> 
229          </div>
230        `;
231      },
232      label: null,
233    };
234  }
235
236  /**
237   * 元素初始化,将html节点与内部变量进行绑定
238   */
239  initElements(): void {
240    this.processRunTimeProgress = this.shadowRoot!.querySelector<LitProgressBar>('#loading');
241    this.nodataPro = this.shadowRoot!.querySelector<TableNoData>('#nodata_Pro');
242    this.processRunTimeTbl = this.shadowRoot!.querySelector<LitTable>('#tb-process-run-time');
243    this.processSwitchCountChart = this.shadowRoot!.querySelector<LitChartColumn>('#chart_pro');
244    this.nodataThr = this.shadowRoot!.querySelector<TableNoData>('#nodata_Thr');
245    this.threadRunTimeTbl = this.shadowRoot!.querySelector<LitTable>('#tb-thread-run-time');
246    this.threadSwitchCountChart = this.shadowRoot!.querySelector<LitChartColumn>('#chart_thr');
247    this.display_pro = this.shadowRoot!.querySelector<HTMLDivElement>('#display_pro');
248    this.display_thr = this.shadowRoot!.querySelector<HTMLDivElement>('#display_thr');
249    this.back = this.shadowRoot!.querySelector<HTMLDivElement>('#back');
250    this.clickEventListener();
251    this.hoverEventListener();
252  }
253
254  /**
255   * 点击监听事件函数块
256   */
257  clickEventListener(): void {
258    // @ts-ignore
259    this.processRunTimeTbl!.addEventListener('row-click', (evt: CustomEvent) => {
260      this.display_flag = false;
261      let data = evt.detail.data;
262      this.processId = data.pid;
263      this.display_thr!.style.display = 'block';
264      this.display_pro!.style.display = 'none';
265      this.queryLogicWorker(
266        'scheduling-Process Top10RunTime',
267        'query Thread Top10 Run Time Analysis Time:',
268        this.callBack.bind(this),
269        data.pid
270      );
271    });
272    this.processRunTimeTbl!.addEventListener('column-click', (evt) => {
273      // @ts-ignore
274      this.sortByColumn(evt.detail, this.processRunTimeData);
275      this.processRunTimeTbl!.recycleDataSource = this.processRunTimeData;
276    });
277    this.threadRunTimeTbl!.addEventListener('column-click', (evt) => {
278      // @ts-ignore
279      this.sortByColumn(evt.detail, this.threadRunTimeData);
280      this.threadRunTimeTbl!.recycleDataSource = this.threadRunTimeData;
281    });
282    this.processRunTimeTbl!.addEventListener('contextmenu', (ev) => { 
283      ev.preventDefault();
284    });
285    this.threadRunTimeTbl!.addEventListener('contextmenu', (ev) => {
286      ev.preventDefault();
287    });
288    this.back?.addEventListener('click', (event) => {
289      this.display_flag = true;
290      this.display_pro!.style.display = 'block';
291      this.display_thr!.style.display = 'none';
292    });
293  }
294
295  /**
296   * 移入事件监听函数块
297   */
298  hoverEventListener(): void {
299    // @ts-ignore
300    this.processRunTimeTbl!.addEventListener('row-hover', (evt: CustomEvent) => {
301      if (evt.detail.data) {
302        let data = evt.detail.data;
303        data.isHover = true;
304        if (evt.detail.callBack) {
305          evt.detail.callBack(true);
306        }
307        this.processSwitchCountChart?.showHoverColumn(data.no);
308      }
309    });
310    // @ts-ignore
311    this.threadRunTimeTbl!.addEventListener('row-hover', (evt: CustomEvent) => {
312      if (evt.detail.data) {
313        let data = evt.detail.data;
314        data.isHover = true;
315        if (evt.detail.callBack) {
316          evt.detail.callBack(true);
317        }
318        this.threadSwitchCountChart?.showHoverColumn(data.no);
319      }
320    });
321  }
322
323  /**
324   * 表格数据排序
325   * @param detail 点击的列名,以及排序状态0 1 2分别代表不排序、升序排序、降序排序
326   * @param data 表格中需要排序的数据
327   */
328  sortByColumn(detail: unknown, data: Array<Top10RunTimeData>): void {
329    // @ts-ignore
330    function compare(processThreadCountProperty, sort, type) {
331      return function (a: unknown, b: unknown) {
332        if (type === 'number') {
333          // @ts-ignore
334          return sort === 2
335          // @ts-ignore
336            ? parseFloat(b[processThreadCountProperty]) -
337            // @ts-ignore
338            parseFloat(a[processThreadCountProperty])
339            // @ts-ignore
340            : parseFloat(a[processThreadCountProperty]) -
341            // @ts-ignore
342            parseFloat(b[processThreadCountProperty]);
343        } else {
344          if (sort === 2) {
345            // @ts-ignore
346            return b[processThreadCountProperty]
347              .toString()
348              // @ts-ignore
349              .localeCompare(a[processThreadCountProperty].toString());
350          } else {
351            // @ts-ignore
352            return a[processThreadCountProperty]
353              .toString()
354              // @ts-ignore
355              .localeCompare(b[processThreadCountProperty].toString());
356          }
357        }
358      };
359    }
360    // @ts-ignore
361    if (detail.key === 'pName' || detail.key === 'tName') {
362      data.sort(
363        // @ts-ignore
364        compare(detail.key, detail.sort, 'string')
365      );
366    } else {
367      data.sort(
368        // @ts-ignore
369        compare(detail.key, detail.sort, 'number')
370      );
371    }
372  }
373
374  /**
375   * 用于将元素节点挂载,大函数块拆分为样式、节点
376   * @returns 返回字符串形式的元素节点
377   */
378  initHtml(): string {
379    return this.initStyleHtml() + this.initTagHtml();
380  }
381
382  /**
383   * 样式html代码块
384   * @returns 返回样式代码块字符串
385   */
386  initStyleHtml(): string {
387    return `
388      <style>
389        .content_grid{
390            display: grid;
391            padding: 15px;
392            grid-column-gap: 15px;
393            grid-row-gap: 15px;
394            grid-template-columns: 1fr 1fr;
395            background-color: var(--dark-background,#FFFFFF);
396        }
397        .chart_div{
398            display: flex;
399            flex-direction: column;
400            background-color: var(--dark-background,#FFFFFF);
401            align-items: center;
402            height: 370px;
403            padding-left: 5px;
404            padding-right: 5px;
405            border-radius: 5px
406        }
407        :host {
408            width: 100%;
409            height: 100%;
410            background: var(--dark-background5,#F6F6F6);
411        }
412        .tb_run_time{
413            overflow: auto;
414            background-color: var(--dark-background,#FFFFFF);
415            border-radius: 5px;
416            border: solid 1px var(--dark-border1,#e0e0e0);
417            display: flex;
418        }
419        .bg{
420            background-color: var(--dark-background,#FFFFFF);
421            padding-left: 10px;
422        }
423        .labels{
424            display: flex;
425            flex-direction: row;
426            align-items: center;
427            justify-content: center;
428            font-size: 9pt;
429            padding-right: 15px;
430        }
431      </style>
432    `;
433  }
434
435  /**
436   * 节点html代码块
437   * @returns 返回节点代码块字符串
438   */
439  initTagHtml(): string {
440    return `
441      <lit-progress-bar id="loading" style="height: 1px;width: 100%"></lit-progress-bar>
442      <div id='display_pro'>
443        <table-no-data id="nodata_Pro" contentHeight="500px">
444          <div class="root">
445            <div style="width:100%;height: 45px;"></div>
446            <div class="content_grid" id="total">
447              <div class="chart_div">
448                <div style="line-height: 40px;height: 40px;width: 100%;text-align: center;">Top10运行超长进程</div>
449                <lit-chart-column id="chart_pro" style="width:100%;height:300px"></lit-chart-column>
450              </div>
451              <div class="tb_run_time" >
452                <lit-table id='tb-process-run-time' hideDownload style='height: auto'>
453                  <lit-table-column width='1fr' title='NO' data-index='no' key='no' align='flex-start' order></lit-table-column>
454                  <lit-table-column width='1fr' title='Process_Id' data-index='pid' key='pid' align='flex-start' order></lit-table-column>
455                  <lit-table-column width='1fr' title='Process_Name' data-index='pName' key='pName' align='flex-start' order></lit-table-column>
456                  <lit-table-column width='1fr' title='Run_Time(ns)' data-index='dur' key='dur' align='flex-start' order></lit-table-column>        
457                </lit-table>
458              </div>
459            </div>
460          </div>
461        </table-no-data>
462      </div>
463      <div id='display_thr' style='display: none'>
464        <table-no-data id="nodata_Thr" contentHeight="500px">
465          <div class="root">
466            <div class="bg" style="display: flex;flex-direction: row;">
467              <div id="back" style="height: 45px;display: flex;flex-direction: row;align-items: center;cursor: pointer" title="Back Previous Level">
468                <span style="width: 10px"></span>Previous Level<span style="width: 10px"></span><lit-icon name="vertical-align-top" size="20"></lit-icon>
469              </div>
470            </div>
471            <div class="content_grid" id="total">
472              <div class="chart_div">
473                <div style="line-height: 40px;height: 40px;width: 100%;text-align: center;">Top10运行超长线程</div>
474                <lit-chart-column id="chart_thr" style="width:100%;height:300px"></lit-chart-column>
475              </div>
476              <div class="tb_run_time" >
477                <lit-table id='tb-thread-run-time' hideDownload style='height: auto'>
478                  <lit-table-column width='1fr' title='NO' data-index='no' key='no' align='flex-start' order></lit-table-column>
479                  <lit-table-column width='1fr' title='Process_Id' data-index='pid' key='pid' align='flex-start' order></lit-table-column>
480                  <lit-table-column width='1fr' title='Thread_Id' data-index='tid' key='tid' align='flex-start' order></lit-table-column>
481                  <lit-table-column width='1fr' title='Thread_Name' data-index='tName' key='tName' align='flex-start' order></lit-table-column>
482                  <lit-table-column width='1fr' title='Run_Time(ns)' data-index='dur' key='dur' align='flex-start' order></lit-table-column>        
483                </lit-table>
484              </div>
485            </div>
486          </div>
487        </table-no-data>
488      </div>
489    `;
490  }
491}
492
493interface Top10RunTimeData {
494  no?: number,
495  pid?: number,
496  tid?: number,
497  pName?: string,
498  tName?: string,
499  switchCount?: number,
500  dur?: number
501}