• 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 { SpSystemTrace } from '../SpSystemTrace';
17import { TraceRow } from '../trace/base/TraceRow';
18import { renders } from '../../database/ui-worker/ProcedureWorker';
19import { SampleStruct, SampleRender } from '../../database/ui-worker/ProcedureWorkerBpftrace';
20import { queryStartTime } from '../../database/sql/SqlLite.sql';
21import { SpStatisticsHttpUtil } from '../../../statistics/util/SpStatisticsHttpUtil';
22
23export class SpBpftraceChart {
24  private trace: SpSystemTrace;
25
26  constructor(trace: SpSystemTrace) {
27    this.trace = trace;
28  }
29
30  async init(file: File | null) {
31    if (!file) {
32      let startTime = await queryStartTime();
33      //@ts-ignore
34      let folder = await this.initSample(startTime[0].start_ts, file);
35      this.trace.rowsEL?.appendChild(folder);
36    } else {
37      let folder = await this.initSample(-1, file);
38      this.trace.rowsEL?.appendChild(folder);
39    }
40  }
41
42  async initSample(start_ts: number, file: any): Promise<TraceRow<SampleStruct>> {
43    let traceRow = TraceRow.skeleton<SampleStruct>();
44    traceRow.rowId = 'bpftrace';
45    traceRow.index = 0;
46    traceRow.rowType = TraceRow.ROW_TYPE_SAMPLE;
47    traceRow.rowParentId = '';
48    traceRow.folder = false;
49    traceRow.style.height = '40px';
50    traceRow.name = 'bpftrace';
51    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
52    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
53    //添加上传按钮
54    traceRow.addRowSampleUpload();
55    this.addTraceRowEventListener(traceRow, start_ts);
56    //单独上传
57    if (file) {
58      this.getJsonData(file).then((res: any) => {
59        const propertyData = res.data;
60        const treeNodes = res.relation.children || [res.relation.RS.children[0]];
61        const uniqueProperty = this.removeDuplicates(propertyData);
62        const flattenTreeArray = this.getFlattenTreeData(treeNodes);
63        const height = (Math.max(...flattenTreeArray.map((obj: any) => obj.depth)) + 1) * 20;
64        const sampleProperty = this.setRelationDataProperty(flattenTreeArray, uniqueProperty);
65        const startTS = flattenTreeArray[0].property[0].begin;
66        traceRow.supplier = () =>
67          new Promise((resolve): void => {
68            resolve(sampleProperty);
69          });
70        traceRow.onThreadHandler = (useCache) => {
71          let context: CanvasRenderingContext2D;
72          if (traceRow.currentContext) {
73            context = traceRow.currentContext;
74          } else {
75            context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
76          }
77          traceRow.canvasSave(context);
78          (renders.sample as SampleRender).renderMainThread(
79            {
80              context: context,
81              useCache: useCache,
82              type: 'bpftrace',
83              start_ts: startTS,
84              uniqueProperty: uniqueProperty,
85              flattenTreeArray: flattenTreeArray,
86            },
87            traceRow
88          );
89          traceRow.canvasRestore(context);
90        };
91        traceRow.style.height = `${height}px`;
92      });
93    } else {
94      traceRow.supplier = () =>
95        new Promise((resolve): void => {
96          resolve([]);
97        });
98      traceRow.onThreadHandler = (useCache) => {
99        let context: CanvasRenderingContext2D;
100        if (traceRow.currentContext) {
101          context = traceRow.currentContext;
102        } else {
103          context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
104        }
105        traceRow.canvasSave(context);
106        (renders.sample as SampleRender).renderMainThread(
107          {
108            context: context,
109            useCache: useCache,
110            type: 'bpftrace',
111            start_ts: 0,
112            uniqueProperty: [],
113            flattenTreeArray: [],
114          },
115          traceRow
116        );
117        traceRow.canvasRestore(context);
118      };
119    }
120    return traceRow;
121  }
122
123  /**
124   * 监听文件上传事件
125   * @param row
126   * @param start_ts
127   */
128  addTraceRowEventListener(row: TraceRow<any>, start_ts: number) {
129    row.uploadEl?.addEventListener('sample-file-change', (e: any) => {
130      this.getJsonData(e).then((res: any) => {
131        this.resetChartData(row);
132        const propertyData = res.data;
133        const treeNodes = res.relation.children || [res.relation.RS.children[0]];
134        const uniqueProperty = this.removeDuplicates(propertyData);
135        const flattenTreeArray = this.getFlattenTreeData(treeNodes);
136        const height = (Math.max(...flattenTreeArray.map((obj: any) => obj.depth)) + 1) * 20;
137        const sampleProperty = this.setRelationDataProperty(flattenTreeArray, uniqueProperty);
138        const startTS = start_ts > 0 ? start_ts : flattenTreeArray[0].property[0].begin;
139        row.supplier = () =>
140          new Promise((resolve): void => {
141            resolve(sampleProperty);
142          });
143        row.onThreadHandler = (useCache) => {
144          let context: CanvasRenderingContext2D;
145          if (row.currentContext) {
146            context = row.currentContext;
147          } else {
148            context = row.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
149          }
150          row.canvasSave(context);
151          (renders.sample as SampleRender).renderMainThread(
152            {
153              context: context,
154              useCache: useCache,
155              type: 'bpftrace',
156              start_ts: startTS,
157              uniqueProperty: uniqueProperty,
158              flattenTreeArray: flattenTreeArray,
159            },
160            row
161          );
162          row.canvasRestore(context);
163        };
164        row.style.height = `${height}px`;
165      });
166    });
167  }
168
169  /**
170   * 清空缓存
171   * @param row
172   */
173  resetChartData(row: TraceRow<any>) {
174    row.dataList = [];
175    row.dataList2 = [];
176    row.dataListCache = [];
177    row.isComplete = false;
178  }
179
180  /**
181   * 获取上传的文件内容 转为json格式
182   * @param file
183   * @returns
184   */
185  getJsonData(file: any): Promise<any> {
186    return new Promise((resolve, reject) => {
187      let reader = new FileReader();
188      reader.readAsText(file.detail || file);
189      reader.onloadend = (e: any) => {
190        const fileContent = e.target?.result;
191        try {
192          resolve(JSON.parse(fileContent));
193          document.dispatchEvent(new CustomEvent('file-correct'));
194          SpStatisticsHttpUtil.addOrdinaryVisitAction({
195            event: 'bpftrace',
196            action: 'bpftrace',
197          });
198        } catch (error) {
199          document.dispatchEvent(new CustomEvent('file-error'));
200        }
201      };
202    });
203  }
204
205  /**
206   * 树结构扁平化
207   * @param treeData
208   * @param depth
209   * @param parentName
210   * @returns
211   */
212  getFlattenTreeData(treeData: Array<any>, depth: number = 0, parentName: string = ''): Array<any> {
213    let result: Array<object> = [];
214    treeData.forEach((node) => {
215      const name: string = node['function_name'];
216      const newNode: any = {};
217      if (name.indexOf('unknown') > -1) {
218        newNode['children'] = this.getUnknownAllChildrenNames(node);
219      }
220      newNode['detail'] = node['detail'];
221      newNode['depth'] = depth;
222      newNode['name'] = name;
223      newNode['parentName'] = parentName;
224      newNode['property'] = [];
225      result.push(newNode);
226      if (node.children) {
227        result = result.concat(this.getFlattenTreeData(node.children, depth + 1, node.function_name));
228      }
229    });
230    return result;
231  }
232
233  /**
234   * 查找重复项
235   * @param propertyData
236   * @returns
237   */
238  removeDuplicates(propertyData: Array<any>): Array<any> {
239    const result: Array<any> = [];
240    propertyData.forEach((propertyGroup) => {
241      const groups: Array<any> = [];
242      propertyGroup.forEach((property: any) => {
243        const duplicateObj = groups.find((group) => group.func_name === property.func_name);
244        if (duplicateObj) {
245          duplicateObj['begin'] = Math.min(duplicateObj['begin'], property['begin']);
246          duplicateObj['end'] = Math.max(duplicateObj['end'], property['end']);
247        } else {
248          groups.push(property);
249        }
250      });
251      result.push(groups);
252    });
253    return result;
254  }
255
256  /**
257   * 关系树赋值
258   * @param relationData
259   * @param propertyData
260   */
261  setRelationDataProperty(relationData: Array<any>, propertyData: Array<any>): Array<any> {
262    const sampleProperty = relationData;
263    //数组每一项进行比对
264    propertyData.forEach((propertyGroup) => {
265      propertyGroup.forEach((property: any) => {
266        const relation = sampleProperty.find((relation) => relation.name === property.func_name);
267        //property属性存储每帧数据
268        relation?.property.push({
269          name: property['func_name'],
270          detail: relation['detail'],
271          end: property['end'],
272          begin: property['begin'],
273          depth: relation['depth'],
274          instructions: property['instructions'],
275          cycles: property.cycles,
276        });
277      });
278    });
279
280    //获取所有名字为unknown的数据
281    const unknownRelation = sampleProperty.filter((relation) => relation.name.indexOf('unknown') > -1);
282    //二维数组 用于存放unknown下所有子节点的数据
283    let twoDimensionalArray: Array<any> = [];
284    let result: Array<any> = [];
285    unknownRelation.forEach((unknownItem) => {
286      result = [];
287      twoDimensionalArray = [];
288      const children = unknownItem['children'];
289      //先获取到unknwon节点下每个子节点的property
290      Object.keys(children).forEach((key) => {
291        unknownItem.children[key] = sampleProperty.find((relation) => relation.name === key).property;
292      });
293      //将每个子节点的property加到二维数组中
294      Object.values(children).forEach((value: any) => {
295        if (value.length > 0) {
296          twoDimensionalArray.push(value);
297        }
298      });
299      if (twoDimensionalArray.length > 0) {
300        //取每列的最大值和最小值
301        for (let i = 0; i < twoDimensionalArray[0].length; i++) {
302          const data = {
303            name: unknownItem['name'],
304            detail: unknownItem['detail'],
305            begin: twoDimensionalArray[0][i].begin,
306            end: 0,
307            depth: unknownItem.depth,
308          };
309          for (let j = 0; j < twoDimensionalArray.length; j++) {
310            data.end = Math.max(twoDimensionalArray[j][i].end, data.end);
311            data.begin = Math.min(twoDimensionalArray[j][i].begin, data.begin);
312          }
313          result.push(data);
314        }
315        unknownItem.property = result;
316      }
317    });
318    return sampleProperty;
319  }
320
321  /**
322   * 获取unknown节点下所有孩子节点的名称
323   * @param node
324   * @param names
325   */
326  getUnknownAllChildrenNames(node: any, names: any = {}): object {
327    if (node['children']) {
328      node['children'].forEach((child: any) => {
329        if (child['function_name'].indexOf('unknown') < 0) {
330          names[child.function_name] = [];
331        } else {
332          this.getUnknownAllChildrenNames(child, names);
333        }
334      });
335    }
336    return names;
337  }
338}
339