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 { convertJSON, LogicHandler } from './ProcedureLogicWorkerCommon';
17
18interface SPT {
19  title: string;
20  count: number;
21  wallDuration: number;
22  minDuration: number;
23  maxDuration: number;
24  avgDuration: string;
25  children: Array<SPT>;
26  state: string;
27  pid: number;
28  tid: number;
29}
30
31export class ProcedureLogicWorkerSPT extends LogicHandler {
32  threadSlice: Array<ThreadSlice> = [];
33  currentEventId: string = '';
34
35  clearAll(): void {
36    this.threadSlice.length = 0;
37  }
38
39  handle(data: unknown): void {
40    //@ts-ignore
41    this.currentEventId = data.id;
42    //@ts-ignore
43    if (data && data.type) {
44      //@ts-ignore
45      switch (data.type) {
46        case 'spt-init':
47          this.sptInit(data);
48          break;
49        case 'spt-getPTS':
50          //@ts-ignore
51          this.sptGetPTS(data.params);
52          break;
53        case 'spt-getSPT':
54          //@ts-ignore
55          this.sptGetSPT(data.params);
56          break;
57        case 'spt-getCpuPriority':
58          this.sptGetCpuPriority();
59          break;
60        case 'spt-getCpuPriorityByTime':
61          //@ts-ignore
62          this.sptGetCpuPriorityByTime(data.params);
63          break;
64      }
65    }
66  }
67  private sptInit(data: unknown): void {
68    //@ts-ignore
69    if (data.params.list) {
70      //@ts-ignore
71      this.threadSlice = convertJSON(data.params.list);
72      self.postMessage({
73        id: this.currentEventId,
74        action: 'spt-init',
75        results: [],
76      });
77    } else {
78      this.getThreadState();
79    }
80  }
81
82  private sptGetPTS(params: { leftNs: number; rightNs: number; cpus: Array<number> }): void {
83    self.postMessage({
84      id: this.currentEventId,
85      action: 'spt-getPTS',
86      results: this.getPTSData(params.leftNs, params.rightNs, params.cpus),
87    });
88  }
89  private sptGetSPT(params: { leftNs: number; rightNs: number; cpus: Array<number> }): void {
90    self.postMessage({
91      id: this.currentEventId,
92      action: 'spt-getSPT',
93      results: this.getSPTData(params.leftNs, params.rightNs, params.cpus),
94    });
95  }
96  private sptGetCpuPriority(): void {
97    self.postMessage({
98      id: this.currentEventId,
99      action: 'spt-getCpuPriority',
100      results: this.threadSlice,
101    });
102  }
103  private sptGetCpuPriorityByTime(params: { leftNs: number; rightNs: number; cpus: Array<number> }): void {
104    const result = this.threadSlice.filter((item: ThreadSlice) => {
105      return !(item.endTs! < params.leftNs || item.startTs! > params.rightNs);
106    });
107    self.postMessage({
108      id: this.currentEventId,
109      action: 'spt-getCpuPriorityByTime',
110      results: result,
111    });
112  }
113  queryData(queryName: string, sql: string, args: unknown): void {
114    self.postMessage({
115      id: this.currentEventId,
116      type: queryName,
117      isQuery: true,
118      args: args,
119      sql: sql,
120    });
121  }
122
123  getThreadState(): void {
124    this.queryData(
125      'spt-init',
126      `
127    select
128       state,
129       dur,
130       (ts - start_ts) as startTs,
131       (ts - start_ts + dur) as endTs,
132       cpu,
133       tid,
134       itid as itId,
135       arg_setid as argSetID,
136       pid
137from thread_state,trace_range where dur > 0 and (ts - start_ts) >= 0;
138`,
139      {}
140    );
141  }
142
143  private getPTSData(ptsLeftNs: number, ptsRightNs: number, cpus: Array<number>): unknown[] {
144    let ptsFilter = this.threadSlice.filter(
145      (it) =>
146        Math.max(ptsLeftNs, it.startTs!) < Math.min(ptsRightNs, it.startTs! + it.dur!) &&
147        (it.cpu === null || it.cpu === undefined || cpus.includes(it.cpu))
148    );
149    let group: unknown = {};
150    ptsFilter.forEach((slice) => {
151      let title = `S-${slice.state}`;
152      let item = this.setStateData(slice, title) as SPT;
153      //@ts-ignore
154      if (group[`${slice.pid}`]) {
155        //@ts-ignore
156        let process = group[`${slice.pid}`] as SPT;
157        process.count += 1;
158        process.wallDuration += slice.dur!;
159        process.minDuration = Math.min(process.minDuration, slice.dur!);
160        process.maxDuration = Math.max(process.maxDuration, slice.dur!);
161        process.avgDuration = (process.wallDuration / process.count).toFixed(2);
162        let thread = process.children.find((child: SPT) => child.title === `T-${slice.tid}`);
163        if (thread) {
164          thread.count += 1;
165          thread.wallDuration += slice.dur!;
166          thread.minDuration = Math.min(thread.minDuration, slice.dur!);
167          thread.maxDuration = Math.max(thread.maxDuration, slice.dur!);
168          thread.avgDuration = (thread.wallDuration / thread.count).toFixed(2);
169          let state = thread.children.find((child: SPT) => child.title === `S-${slice.state}`);
170          if (state) {
171            state.count += 1;
172            state.wallDuration += slice.dur!;
173            state.minDuration = Math.min(state.minDuration, slice.dur!);
174            state.maxDuration = Math.max(state.maxDuration, slice.dur!);
175            state.avgDuration = (state.wallDuration / state.count).toFixed(2);
176          } else {
177            thread.children.push(item);
178          }
179        } else {
180          let processChild = this.setThreadData(slice, item) as SPT;
181          process.children.push(processChild);
182        }
183      } else {
184        //@ts-ignore
185        group[`${slice.pid}`] = this.setProcessData(slice, item);
186      }
187    });
188    //@ts-ignore
189    return Object.values(group);
190  }
191  private setStateData(slice: ThreadSlice, title: string): unknown {
192    return {
193      title: title,
194      count: 1,
195      state: slice.state,
196      tid: slice.tid,
197      pid: slice.pid,
198      minDuration: slice.dur || 0,
199      maxDuration: slice.dur || 0,
200      wallDuration: slice.dur || 0,
201      avgDuration: `${slice.dur}`,
202    };
203  }
204  private setProcessData(slice: ThreadSlice, item: SPT): unknown {
205    return {
206      title: `P-${slice.pid}`,
207      count: 1,
208      pid: slice.pid,
209      minDuration: slice.dur || 0,
210      maxDuration: slice.dur || 0,
211      wallDuration: slice.dur || 0,
212      avgDuration: `${slice.dur}`,
213      children: [
214        {
215          title: `T-${slice.tid}`,
216          count: 1,
217          pid: slice.pid,
218          tid: slice.tid,
219          minDuration: slice.dur || 0,
220          maxDuration: slice.dur || 0,
221          wallDuration: slice.dur || 0,
222          avgDuration: `${slice.dur}`,
223          children: [item],
224        },
225      ],
226    };
227  }
228  private setThreadData(slice: ThreadSlice, item: SPT): unknown {
229    return {
230      title: `T-${slice.tid}`,
231      count: 1,
232      tid: slice.tid,
233      pid: slice.pid,
234      minDuration: slice.dur || 0,
235      maxDuration: slice.dur || 0,
236      wallDuration: slice.dur || 0,
237      avgDuration: `${slice.dur}`,
238      children: [item],
239    };
240  }
241  private getSPTData(sptLeftNs: number, sptRightNs: number, cpus: Array<number>): unknown {
242    let sptFilter = this.threadSlice.filter(
243      (it) =>
244        Math.max(sptLeftNs, it.startTs!) < Math.min(sptRightNs, it.startTs! + it.dur!) &&
245        (it.cpu === null || it.cpu === undefined || cpus.includes(it.cpu))
246    );
247    let group: unknown = {};
248    sptFilter.forEach((slice) => {
249      let item = {
250        title: `T-${slice.tid}`,
251        count: 1,
252        state: slice.state,
253        pid: slice.pid,
254        tid: slice.tid,
255        minDuration: slice.dur || 0,
256        maxDuration: slice.dur || 0,
257        wallDuration: slice.dur || 0,
258        avgDuration: `${slice.dur}`,
259      } as SPT;
260      //@ts-ignore
261      if (group[`${slice.state}`]) {
262        this.setSPTData(group, slice, item);
263      } else {
264        //@ts-ignore
265        group[`${slice.state}`] = {
266          title: `S-${slice.state}`,
267          count: 1,
268          state: slice.state,
269          minDuration: slice.dur || 0,
270          maxDuration: slice.dur || 0,
271          wallDuration: slice.dur || 0,
272          avgDuration: `${slice.dur}`,
273          children: [
274            {
275              title: `P-${slice.pid}`,
276              count: 1,
277              state: slice.state,
278              pid: slice.pid,
279              minDuration: slice.dur || 0,
280              maxDuration: slice.dur || 0,
281              wallDuration: slice.dur || 0,
282              avgDuration: `${slice.dur}`,
283              children: [item],
284            },
285          ],
286        };
287      }
288    });
289    //@ts-ignore
290    return Object.values(group);
291  }
292  private setSPTData(group: unknown, slice: ThreadSlice, item: SPT): void {
293    //@ts-ignore
294    let state = group[`${slice.state}`];
295    state.count += 1;
296    state.wallDuration += slice.dur;
297    state.minDuration = Math.min(state.minDuration, slice.dur!);
298    state.maxDuration = Math.max(state.maxDuration, slice.dur!);
299    state.avgDuration = (state.wallDuration / state.count).toFixed(2);
300    let process = state.children.find((child: SPT) => child.title === `P-${slice.pid}`);
301    if (process) {
302      process.count += 1;
303      process.wallDuration += slice.dur;
304      process.minDuration = Math.min(process.minDuration, slice.dur!);
305      process.maxDuration = Math.max(process.maxDuration, slice.dur!);
306      process.avgDuration = (process.wallDuration / process.count).toFixed(2);
307      let thread = process.children.find((child: SPT) => child.title === `T-${slice.tid}`);
308      if (thread) {
309        thread.count += 1;
310        thread.wallDuration += slice.dur;
311        thread.minDuration = Math.min(thread.minDuration, slice.dur!);
312        thread.maxDuration = Math.max(thread.maxDuration, slice.dur!);
313        thread.avgDuration = (thread.wallDuration / thread.count).toFixed(2);
314      } else {
315        process.children.push(item);
316      }
317    } else {
318      state.children.push({
319        title: `P-${slice.pid}`,
320        count: 1,
321        state: slice.state,
322        pid: slice.pid,
323        minDuration: slice.dur || 0,
324        maxDuration: slice.dur || 0,
325        wallDuration: slice.dur || 0,
326        avgDuration: `${slice.dur}`,
327        children: [item],
328      });
329    }
330  }
331}
332
333export class ThreadSlice {
334  state?: string;
335  dur?: number;
336  startTs?: number;
337  endTs?: number;
338  cpu?: number | null;
339  tid?: number;
340  pid?: number;
341  itId?: number;
342  priorityType?: string;
343  end_state?: string;
344  priority?: number;
345  argSetID?: number;
346}
347