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, RedrawTreeForm } from '../../../../../base-ui/table/lit-table';
18import { SelectionParam } from '../../../../bean/BoxSelection';
19import { resizeObserver } from '../SheetUtils';
20import { Utils } from '../../base/Utils';
21import { Priority } from '../../../../bean/StateProcessThread';
22import { queryArgsById, queryThreadStateArgsById } from '../../../../database/sql/ProcessThread.sql';
23import { FlagsConfig } from '../../../SpFlags';
24import { sliceSPTSender } from '../../../../database/data-trafic/SliceSender';
25
26@element('tabpane-sched-priority')
27export class TabPaneSchedPriority extends BaseElement {
28  private priorityTbl: LitTable | null | undefined;
29  private range: HTMLLabelElement | null | undefined;
30  private selectionParam: SelectionParam | null | undefined;
31  private strValueMap: Map<number, string> = new Map<number, string>();
32
33  set data(sptValue: SelectionParam) {
34    if (sptValue === this.selectionParam) {
35      return;
36    }
37    this.selectionParam = sptValue;
38    if (this.priorityTbl) {
39      // @ts-ignore
40      this.priorityTbl.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - 45}px`;
41    }
42    this.range!.textContent = `Selected range: ${parseFloat(
43      ((sptValue.rightNs - sptValue.leftNs) / 1000000.0).toFixed(5)
44    )} ms`;
45    this.queryDataByDB(sptValue);
46  }
47
48  public initElements(): void {
49    this.priorityTbl = this.shadowRoot?.querySelector<LitTable>('#priority-tbl');
50    this.range = this.shadowRoot?.querySelector('#priority-time-range');
51  }
52
53  public connectedCallback(): void {
54    super.connectedCallback();
55    resizeObserver(this.parentElement!, this.priorityTbl!);
56  }
57
58  private async queryDataByDB(sptParam: SelectionParam | unknown): Promise<void> {
59    this.priorityTbl!.loading = true;
60    const resultData: Array<Priority> = [];
61    await this.fetchAndProcessData();
62
63    const filterList = ['0', '0x0']; //next_info第2字段不为0 || next_info第3字段不为0
64    // 通过priority与next_info结合判断优先级等级
65    function setPriority(item: Priority, strArg: string[]): void {
66      let flagsItem = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY);
67      let flagsItemJson = JSON.parse(flagsItem!);
68      let hmKernel = flagsItemJson.HMKernel;
69      if (hmKernel === 'Enabled') {
70        if (item.priority >= 0 && item.priority <= 40) {
71          item.priorityType = 'CFS';
72        } else {
73          item.priorityType = 'RT';
74        }
75      } else {
76        if (item.priority >= 0 && item.priority <= 88) {
77          item.priorityType = 'RT';
78        } else if (item.priority >= 89 && item.priority <= 99) {
79          item.priorityType = 'VIP2.0';
80        } else if (
81          item.priority >= 100 &&
82          strArg.length > 1 &&
83          (!filterList.includes(strArg[1]) || !filterList.includes(strArg[2]))
84        ) {
85          item.priorityType = 'STATIC_VIP';
86        } else {
87          item.priorityType = 'CFS';
88        }
89      }
90    }
91    // thread_state表中runnable数据的Map
92    const runnableMap = new Map<string, Priority>();
93    // @ts-ignore
94    sliceSPTSender(sptParam.leftNs, sptParam.rightNs, [], 'spt-getCpuPriorityByTime', sptParam.traceId).then(res => {
95      for (const item of res) {
96        //@ts-ignore
97        if (['R', 'R+'].includes(item.state)) {
98          //@ts-ignore
99          runnableMap.set(`${item.id}_${item.startTime + item.dur}`, item);
100        }
101        // @ts-ignore
102        if (item.cpu === null || !sptParam.cpus.includes(item.cpu)) {
103          continue;
104        }
105        this.fetchData(item, setPriority, resultData, runnableMap);
106      }
107      this.getDataByPriority(resultData);
108    });
109  }
110
111  private fetchData(
112    item: any,
113    setPriority: (item: Priority, strArg: string[]) => void,
114    resultData: Array<Priority>,
115    runnableMap: Map<string, Priority>
116  ): void {
117    let strArg: string[] = [];
118    const args = this.strValueMap.get(item.argSetId);
119    if (args) {
120      strArg = args!.split(',');
121    }
122    const slice = Utils.getInstance().getSchedSliceMap(Utils.currentSelectTrace).get(`${item.id}-${item.startTime}`);
123    if (slice) {
124      const runningPriority = new Priority();
125      runningPriority.priority = slice.priority;
126      runningPriority.state = 'Running';
127      runningPriority.dur = item.dur;
128      setPriority(runningPriority, strArg);
129      resultData.push(runningPriority);
130
131      const runnableItem = runnableMap.get(`${item.id}_${item.startTime}`);
132      if (runnableItem) {
133        const runnablePriority = new Priority();
134        runnablePriority.priority = slice.priority;
135        runnablePriority.state = 'Runnable';
136        runnablePriority.dur = runnableItem.dur;
137        setPriority(runnablePriority, strArg);
138        resultData.push(runnablePriority);
139      }
140    }
141  }
142
143  private async fetchAndProcessData(): Promise<void> {
144    if (this.strValueMap.size === 0) {
145      let res = await queryArgsById('next_info', this.selectionParam?.traceId || undefined);
146      await queryThreadStateArgsById(res[0].id, this.selectionParam?.traceId || undefined).
147        then((value): void => {
148          for (const item of value) {
149            this.strValueMap.set(item.argset, item.strValue);
150          }
151        });
152    }
153  }
154
155  private getDataByPriority(source: Array<Priority>): void {
156    const priorityMap: Map<string, Priority> = new Map<string, Priority>();
157    const stateMap: Map<string, Priority> = new Map<string, Priority>();
158    this.prepareMaps(source, priorityMap, stateMap);
159
160    const priorityArr: Array<Priority> = [];
161    for (const key of priorityMap.keys()) {
162      const ptsValues = priorityMap.get(key);
163      ptsValues!.children = [];
164      for (const itemKey of stateMap.keys()) {
165        if (itemKey.startsWith(`${key}_`)) {
166          const sp = stateMap.get(itemKey);
167          ptsValues!.children.push(sp!);
168        }
169      }
170      priorityArr.push(ptsValues!);
171    }
172    this.priorityTbl!.loading = false;
173    this.priorityTbl!.recycleDataSource = priorityArr;
174    this.theadClick(priorityArr);
175  }
176
177  private prepareMaps(
178    source: Array<Priority>,
179    priorityMap: Map<string, Priority>,
180    stateMap: Map<string, Priority>
181  ): void {
182    source.map((priorityItem): void => {
183      if (priorityMap.has(`${priorityItem.priorityType}`)) {
184        const priorityMapObj = priorityMap.get(`${priorityItem.priorityType}`);
185        priorityMapObj!.count++;
186        priorityMapObj!.wallDuration += priorityItem.dur;
187        priorityMapObj!.avgDuration = (priorityMapObj!.wallDuration / priorityMapObj!.count).toFixed(2);
188        if (priorityItem.dur > priorityMapObj!.maxDuration) {
189          priorityMapObj!.maxDuration = priorityItem.dur;
190        }
191        if (priorityItem.dur < priorityMapObj!.minDuration) {
192          priorityMapObj!.minDuration = priorityItem.dur;
193        }
194      } else {
195        const stateMapObj = new Priority();
196        stateMapObj.title = priorityItem.priorityType;
197        stateMapObj.minDuration = priorityItem.dur;
198        stateMapObj.maxDuration = priorityItem.dur;
199        stateMapObj.count = 1;
200        stateMapObj.avgDuration = `${priorityItem.dur}`;
201        stateMapObj.wallDuration = priorityItem.dur;
202        priorityMap.set(`${priorityItem.priorityType}`, stateMapObj);
203      }
204      if (stateMap.has(`${priorityItem.priorityType}_${priorityItem.state}`)) {
205        const ptsPtMapObj = stateMap.get(`${priorityItem.priorityType}_${priorityItem.state}`);
206        ptsPtMapObj!.count++;
207        ptsPtMapObj!.wallDuration += priorityItem.dur;
208        ptsPtMapObj!.avgDuration = (ptsPtMapObj!.wallDuration / ptsPtMapObj!.count).toFixed(2);
209        if (priorityItem.dur > ptsPtMapObj!.maxDuration) {
210          ptsPtMapObj!.maxDuration = priorityItem.dur;
211        }
212        if (priorityItem.dur < ptsPtMapObj!.minDuration) {
213          ptsPtMapObj!.minDuration = priorityItem.dur;
214        }
215      } else {
216        const ptsPtMapObj = new Priority();
217        ptsPtMapObj.title = priorityItem.state;
218        ptsPtMapObj.minDuration = priorityItem.dur;
219        ptsPtMapObj.maxDuration = priorityItem.dur;
220        ptsPtMapObj.count = 1;
221        ptsPtMapObj.avgDuration = `${priorityItem.dur}`;
222        ptsPtMapObj.wallDuration = priorityItem.dur;
223        stateMap.set(`${priorityItem.priorityType}_${priorityItem.state}`, ptsPtMapObj);
224      }
225    });
226  }
227
228  private theadClick(data: Array<Priority>): void {
229    let labels = this.priorityTbl?.shadowRoot?.querySelector('.th > .td')!.querySelectorAll('label');
230    if (labels) {
231      for (let i = 0; i < labels.length; i++) {
232        let label = labels[i].innerHTML;
233        labels[i].addEventListener('click', (): void => {
234          if (label.includes('Priority') && i === 0) {
235            this.priorityTbl!.setStatus(data, false);
236            this.priorityTbl!.recycleDs = this.priorityTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract);
237          } else if (label.includes('State') && i === 1) {
238            this.priorityTbl!.setStatus(data, true);
239            this.priorityTbl!.recycleDs = this.priorityTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand);
240          }
241        });
242      }
243    }
244  }
245
246  public initHtml(): string {
247    return `
248        <style>
249        :host{
250            display: flex;
251            flex-direction: column;
252            padding: 10px 10px;
253        }
254        </style>
255        <label id="priority-time-range" style="width: 100%;height: 20px;text-align: end;font-size: 10pt;margin-bottom: 5px">Selected range:0.0 ms</label>
256        <lit-table id="priority-tbl" style="height: auto" tree>
257            <lit-table-column width="27%" data-index="title" key="title" align="flex-start" title="Priority/State" retract>
258            </lit-table-column>
259            <lit-table-column width="1fr" data-index="count" key="count" align="flex-start" title="Count">
260            </lit-table-column>
261            <lit-table-column width="1fr" data-index="wallDuration" key="wallDuration" align="flex-start" title="Duration(ns)">
262            </lit-table-column>
263            <lit-table-column width="1fr" data-index="minDuration" key="minDuration" align="flex-start" title="Min Duration(ns)">
264            </lit-table-column>
265            <lit-table-column width="1fr" data-index="avgDuration" key="avgDuration" align="flex-start" title="Avg Duration(ns)">
266            </lit-table-column>
267            <lit-table-column width="1fr" data-index="maxDuration" key="maxDuration" align="flex-start" title="Max Duration(ns)">
268            </lit-table-column>
269        </lit-table>
270        `;
271  }
272}
273