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 { LitChartPie } from '../../../../base-ui/chart/pie/LitChartPie';
25import '../../../../base-ui/chart/pie/LitChartPie';
26import { Utils } from '../../trace/base/Utils';
27
28@element('top10-process-switch-count')
29export class Top10ProcessSwitchCount extends BaseElement {
30  traceChange: boolean = false;
31  private processSwitchCountTbl: LitTable | null | undefined;
32  private threadSwitchCountTbl: LitTable | null | undefined;
33  private nodataPro: TableNoData | null | undefined;
34  private nodataThr: TableNoData | null | undefined;
35  private processSwitchCountData: Array<Top10ProcSwiCount> = [];
36  private threadSwitchCountData: Array<Top10ProcSwiCount> = [];
37  private threadSwitchCountPie: LitChartPie | null | undefined;
38  private processSwitchCountPie: LitChartPie | null | undefined;
39  private display_pro: HTMLDivElement | null | undefined;
40  private display_thr: HTMLDivElement | null | undefined;
41  private processSwitchCountProgress: LitProgressBar | 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.processSwitchCountTbl!.recycleDataSource.length > 0) {
54        this.processSwitchCountTbl?.reMeauseHeight();
55      }
56      return;
57    }
58    this.traceChange = false;
59    this.processSwitchCountProgress!.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 Top10Swicount',
67      'query Process Top10 Switch Count Analysis Time:',
68      this.callBack.bind(this)
69    );
70  }
71
72  /**
73   * 清除已存储数据
74   */
75  clearData(): void {
76    this.traceChange = true;
77    this.processSwitchCountPie!.dataSource = [];
78    this.processSwitchCountTbl!.recycleDataSource = [];
79    this.threadSwitchCountPie!.dataSource = [];
80    this.threadSwitchCountTbl!.recycleDataSource = [];
81    this.processSwitchCountData = [];
82    this.threadSwitchCountData = [];
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<Top10ProcSwiCount>) => 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   * 抽取公共方法,提取数据,用于展示到表格中
103   * @param arr 数据库查询结果
104   * @returns 整理好的数据,包含进程名,线程名等相关信息
105   */
106  organizationData(arr: Array<Top10ProcSwiCount>): Array<Top10ProcSwiCount> {
107    let result: Array<Top10ProcSwiCount> = [];
108    for (let i = 0; i < arr.length; i++) {
109      const pStr: string | null = this.processMap.get(arr[i].pid!)!;
110      const tStr: string | null = this.threadMap.get(arr[i].tid!)!;
111      result.push({
112        NO: i + 1,
113        pid: arr[i].pid || this.processId,
114        pName: pStr === null ? 'Process ' : pStr,
115        switchCount: arr[i].occurrences,
116        tid: arr[i].tid,
117        tName: tStr === null ? 'Thread ' : tStr
118      });
119    }
120    return result;
121  }
122
123  /**
124   * 提交线程后,结果返回后的回调函数
125   * @param res 数据库查询结果
126   */
127  callBack(res: Array<Top10ProcSwiCount>): void {
128    let result: Array<Top10ProcSwiCount> = this.organizationData(res);
129    // 判断当前显示的是进程组还是线程组
130    if (this.display_flag === true) {
131      this.processCallback(result);
132    } else {
133      this.threadCallback(result);
134    }
135    this.processSwitchCountProgress!.loading = false;
136  }
137
138  /**
139   * 大函数块拆解分为两部分,此部分为Top10进程数据
140   * @param result 需要显示在表格中的数据
141   */
142  processCallback(result: Array<Top10ProcSwiCount>): void {
143    this.nodataPro!.noData = result === undefined || result.length === 0;
144    this.processSwitchCountData = result;
145    this.processSwitchCountPie!.config = {
146      appendPadding: 10,
147      data: result,
148      angleField: 'switchCount',
149      colorField: 'pid',
150      radius: 0.8,
151      label: {
152        type: 'outer',
153      },
154      hoverHandler: (data): void => {
155        if (data) {
156          this.processSwitchCountTbl!.setCurrentHover(data);
157        } else {
158          this.processSwitchCountTbl!.mouseOut();
159        }
160      },
161      tip: (obj): string => {
162        return `
163          <div>
164            <div>Process_Id:${
165          // @ts-ignore
166          obj.obj.pid}</div> 
167            <div>Process_Name:${
168          // @ts-ignore
169          obj.obj.pName}</div> 
170            <div>Switch Count:${
171          // @ts-ignore
172          obj.obj.switchCount}</div> 
173          </div>
174        `;
175      },
176      interactions: [
177        {
178          type: 'element-active',
179        },
180      ],
181    };
182    this.processSwitchCountTbl!.recycleDataSource = result;
183    this.processSwitchCountTbl!.reMeauseHeight();
184  }
185
186  /**
187   * 大函数块拆解分为两部分,此部分为Top10线程数据
188   * @param result 需要显示在表格中的数据
189   */
190  threadCallback(result: Array<Top10ProcSwiCount>): void {
191    this.nodataThr!.noData = result === undefined || result.length === 0;
192    this.threadSwitchCountTbl!.recycleDataSource = result;
193    this.threadSwitchCountTbl!.reMeauseHeight();
194    this.threadSwitchCountData = result;
195    this.threadSwitchCountPie!.config = {
196      appendPadding: 10,
197      data: result,
198      angleField: 'switchCount',
199      colorField: 'tid',
200      radius: 0.8,
201      label: {
202        type: 'outer',
203      },
204      hoverHandler: (data): void => {
205        if (data) {
206          this.threadSwitchCountTbl!.setCurrentHover(data);
207        } else {
208          this.threadSwitchCountTbl!.mouseOut();
209        }
210      },
211      tip: (obj): string => {
212        return `
213          <div>
214            <div>Thread_Id:${
215          // @ts-ignore
216          obj.obj.tid}</div> 
217            <div>Thread_Name:${
218          // @ts-ignore
219          obj.obj.tName}</div> 
220            <div>Switch Count:${
221          // @ts-ignore
222          obj.obj.switchCount}</div> 
223            <div>Process_Id:${
224          // @ts-ignore
225          obj.obj.pid}</div> 
226          </div>
227        `;
228      },
229      interactions: [
230        {
231          type: 'element-active',
232        },
233      ],
234    };
235  }
236
237  /**
238   * 元素初始化,将html节点与内部变量进行绑定
239   */
240  initElements(): void {
241    this.processSwitchCountProgress = this.shadowRoot!.querySelector<LitProgressBar>('#loading');
242    this.processSwitchCountTbl = this.shadowRoot!.querySelector<LitTable>('#tb-process-switch-count');
243    this.threadSwitchCountTbl = this.shadowRoot!.querySelector<LitTable>('#tb-thread-switch-count');
244    this.processSwitchCountPie = this.shadowRoot!.querySelector<LitChartPie>('#pie_pro');
245    this.threadSwitchCountPie = this.shadowRoot!.querySelector<LitChartPie>('#pie_thr');
246    this.nodataPro = this.shadowRoot!.querySelector<TableNoData>('#nodata_pro');
247    this.nodataThr = this.shadowRoot!.querySelector<TableNoData>('#nodata_thr');
248    this.display_pro = this.shadowRoot!.querySelector<HTMLDivElement>('#display_pro');
249    this.display_thr = this.shadowRoot!.querySelector<HTMLDivElement>('#display_thr');
250    this.back = this.shadowRoot!.querySelector<HTMLDivElement>('#back');
251    this.clickEventListener();
252    this.hoverEventListener();
253    this.addEventListener('contextmenu', (event) => {
254      event.preventDefault(); // 阻止默认的上下文菜单弹框
255    });
256  }
257
258  /**
259   * 点击监听事件函数块
260   */
261  clickEventListener(): void {
262    // @ts-ignore
263    this.processSwitchCountTbl!.addEventListener('row-click', (evt: CustomEvent) => {
264      this.display_flag = false;
265      let data = evt.detail.data;
266      this.processId = data.pid;
267      this.display_thr!.style.display = 'block';
268      this.display_pro!.style.display = 'none';
269      this.queryLogicWorker(
270        'scheduling-Process Top10Swicount',
271        'query Process Top10 Switch Count Analysis Time:',
272        this.callBack.bind(this),
273        data.pid
274      );
275    });
276    this.processSwitchCountTbl!.addEventListener('column-click', (evt) => {
277      // @ts-ignore
278      this.sortByColumn(evt.detail, this.processSwitchCountData);
279      this.processSwitchCountTbl!.recycleDataSource = this.processSwitchCountData;
280    });
281    this.threadSwitchCountTbl!.addEventListener('column-click', (evt) => {
282      // @ts-ignore
283      this.sortByColumn(evt.detail, this.threadSwitchCountData);
284      this.threadSwitchCountTbl!.recycleDataSource = this.threadSwitchCountData;
285    });
286    
287    this.back!.addEventListener('click', (event) => {
288      this.display_flag = true;
289      this.display_pro!.style.display = 'block';
290      this.display_thr!.style.display = 'none';
291    });
292
293  }
294
295  /**
296   * 移入事件监听函数块
297   */
298  hoverEventListener(): void {
299    // @ts-ignore
300    this.processSwitchCountTbl!.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      }
308      this.processSwitchCountPie?.showHover();
309    });
310    // @ts-ignore
311    this.threadSwitchCountTbl!.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      }
319      this.threadSwitchCountPie?.showHover();
320    });
321    this.addEventListener('mouseenter', () => {
322      if (this.processSwitchCountTbl!.recycleDataSource.length > 0) {
323        this.processSwitchCountTbl?.reMeauseHeight();
324      }
325    });
326  }
327
328  /**
329   * 表格数据排序
330   * @param detail 点击的列名,以及排序状态0 1 2分别代表不排序、升序排序、降序排序
331   * @param data 表格中需要排序的数据
332   */
333  sortByColumn(detail: { key: string, sort: number }, data: Array<Top10ProcSwiCount>): void {
334    // @ts-ignore
335    function compare(processThreadCountProperty, sort, type) {
336      return function (a: unknown, b: unknown) {
337        if (type === 'number') {
338          // @ts-ignore
339          return sort === 2
340            // @ts-ignore
341            ? parseFloat(b[processThreadCountProperty]) -
342            // @ts-ignore
343            parseFloat(a[processThreadCountProperty])
344            // @ts-ignore
345            : parseFloat(a[processThreadCountProperty]) -
346            // @ts-ignore
347            parseFloat(b[processThreadCountProperty]);
348        } else {
349          if (sort === 2) {
350            // @ts-ignore
351            return b[processThreadCountProperty]
352              .toString()
353              // @ts-ignore
354              .localeCompare(a[processThreadCountProperty].toString());
355          } else {
356            // @ts-ignore
357            return a[processThreadCountProperty]
358              .toString()
359              // @ts-ignore
360              .localeCompare(b[processThreadCountProperty].toString());
361          }
362        }
363      };
364    }
365    if (detail.key === 'pName' || detail.key === 'tName') {
366      data.sort(
367        compare(detail.key, detail.sort, 'string')
368      );
369    } else {
370      data.sort(
371        compare(detail.key, detail.sort, 'number')
372      );
373    }
374  }
375
376  /**
377   * 用于将元素节点挂载,大函数块拆分为样式、节点
378   * @returns 返回字符串形式的元素节点
379   */
380  initHtml(): string {
381    return this.initStyleHtml() + this.initTagHtml();
382  }
383
384  /**
385   * 样式html代码块
386   * @returns 返回样式代码块字符串
387   */
388  initStyleHtml(): string {
389    return `
390      <style>
391        :host {
392            width: 100%;
393            height: 100%;
394            background-color: var(--dark-background5,#F6F6F6);
395        }
396        .pie-chart{
397            display: flex;
398            box-sizing: border-box;
399            width: 500px;
400            height: 500px;
401        }
402        .tb_switch_count{
403            flex: 1;
404            overflow: auto ;
405            border-radius: 5px;
406            border: solid 1px var(--dark-border1,#e0e0e0);
407            margin: 15px;
408            padding: 5px 15px
409        }
410        .switchcount-root{
411            width: 100%;
412            height: 100%;
413            box-sizing: border-box;
414            display: flex;
415            flex-direction: row;
416        }
417        .bg{
418            background-color: var(--dark-background,#FFFFFF);
419            padding-left: 10px;
420        }
421      </style>
422    `;
423  }
424
425  /**
426   * 节点html代码块
427   * @returns 返回节点代码块字符串
428   */
429  initTagHtml(): string {
430    return `
431      <lit-progress-bar id='loading' style='height: 1px;width: 100%' loading></lit-progress-bar>
432      <div id='display_pro'>
433        <table-no-data id='nodata_pro' contentHeight='500px'>
434          <div class="root">
435            <div style="width:100%;height: 45px;"></div>
436            <div class='switchcount-root'>
437              <div style='display: flex;flex-direction: column;align-items: center'>
438                <div>Statistics By Process's Switch Count</div>
439                <lit-chart-pie id='pie_pro' class='pie-chart'></lit-chart-pie>
440              </div>
441              <div class='tb_switch_count'>
442                <lit-table id='tb-process-switch-count' hideDownload style='height: auto'>
443                  <lit-table-column width='1fr' title='NO' data-index='NO' key='NO' align='flex-start' order></lit-table-column>
444                  <lit-table-column width='1fr' title='Process_Id' data-index='pid' key='pid' align='flex-start' order></lit-table-column>
445                  <lit-table-column width='1fr' title='Process_Name' data-index='pName' key='pName' align='flex-start' order></lit-table-column>
446                  <lit-table-column width='1fr' title='Switch Count' data-index='switchCount' key='switchCount' align='flex-start' order></lit-table-column>        
447                </lit-table>
448              </div>
449            </div>
450          </div>
451        </table-no-data>
452      </div>
453      <div id='display_thr' style='display: none'>
454        <table-no-data id='nodata_thr' contentHeight='500px'>
455          <div class="root">
456            <div class="bg" style="display: flex;flex-direction: row;">
457              <div id="back" style="height: 45px;display: flex;flex-direction: row;align-items: center;cursor: pointer" title="Back Previous Level">
458                <span style="width: 10px"></span>Previous Level<span style="width: 10px"></span><lit-icon name="vertical-align-top" size="20"></lit-icon>
459              </div>
460            </div>
461            <div class='switchcount-root'>
462              <div style='display: flex;flex-direction: column;align-items: center'>
463                <div>Statistics By Thread's Switch Count</div>
464                <lit-chart-pie id='pie_thr' class='pie-chart'></lit-chart-pie>
465              </div>
466              <div class='tb_switch_count'>
467                <lit-table id='tb-thread-switch-count' hideDownload style='height: auto'>
468                  <lit-table-column width='1fr' title='NO' data-index='NO' key='NO' align='flex-start' order></lit-table-column>
469                  <lit-table-column width='1fr' title='Process_Id' data-index='pid' key='pid' align='flex-start' order></lit-table-column>
470                  <lit-table-column width='1fr' title='Thread_Id' data-index='tid' key='tid' align='flex-start' order></lit-table-column>
471                  <lit-table-column width='1fr' title='Thread_Name' data-index='tName' key='tName' align='flex-start' order></lit-table-column>
472                  <lit-table-column width='1fr' title='Switch Count' data-index='switchCount' key='switchCount' align='flex-start' order></lit-table-column>        
473                </lit-table>
474              </div>
475            </div>
476          </div>
477        </table-no-data>
478      </div>
479    `;
480  }
481}
482
483interface Top10ProcSwiCount {
484  NO?: number,
485  pid?: number,
486  tid?: number,
487  pName?: string,
488  tName?: string,
489  switchCount?: number,
490  occurrences?: number
491}