1/*
2 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development 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
16const { XTools } = require('../engine/XTools');
17const { X2DFast } = require('../engine/graphics/X2DFast');
18const { NapiLog } = require('./NapiLog');
19
20class LogParser {
21  constructor(result) {
22    const regexVT100 = /\x1B\[[0-9;]*[A-Za-z]/g; // 匹配VT100字符的正则表达式
23    result = result.replace(regexVT100, '');
24    result = result.replace(/\t/g, '    ');
25    result = result.replace(/\r/g, '');
26    this.logLines_ = result.split('\n');
27    this.stat_ = 0;
28    this.lineNum_ = 0;
29    this.initBlock_ = {
30      filePoint: null,
31      funcPoint: null,
32      blockType: -1,
33    };
34    this.procBlock_ = {
35      blockStat: -1,
36      blockCollect: -1,
37      oneBlock: -1,
38    };
39    this.procNormal_ = null;
40    this.output_ = {};
41  }
42
43  parsing() {
44    if (this.lineNum_ >= this.logLines_.length) {
45      return false;
46    }
47
48    for (let i = 0; i < 300 && this.lineNum_ < this.logLines_.length; i++) {
49      this.parseLine(this.logLines_[this.lineNum_]);
50      this.lineNum_++;
51    }
52
53    XTools.PROC_TO = this.lineNum_ * 20 / this.logLines_.length;
54    return true;
55  }
56  isBlock(l) {
57    for (let bt of XTools.CONFIG.BlockTypes) {
58      if (l.indexOf(bt) >= 0) {
59        this.stat_ = this.initBlock_.blockType;
60        this.procBlock_.blockStat = 0;
61        this.procBlock_.blockCollect = {
62          type: 'block:' + bt,
63          func: this.initBlock_.funcPoint,
64          file: this.initBlock_.filePoint,
65          irList: [],
66          startLine: l,
67        };
68        this.procBlock_.oneBlock = null;
69        return true;
70      }
71    }
72    return false;
73  }
74  splitLast(s) {
75    let i = s.lastIndexOf('@');
76
77    return [s.substring(0, i), s.substring(i + 1)];
78  }
79  isStart(l) {
80    //========= After bytecode2circuit lowering [func_main_0@484@arkcompiler/ets_runtime/sd_test/ttabs.abc] ========
81    const regexStart = /=+ *After ([a-zA-Z0-9_ ]+) \[([#a-zA-Z0-9_@/.-]+)\] *=+/g;
82    //========= After inlining [OthreMath@test@arkcompiler/ets_runtime/sd_test/test.abc] Caller
83    //method [func_main_0@641@arkcompiler/ets_runtime/sd_test/test.abc]====================[0m
84    const regexStart2 = /=+ *After ([a-zA-Z0-9_ ]+) \[([a-zA-Z0-9_@/.-]+)\] *Caller method \[([#a-zA-Z0-9_@/.]+)\] *=+/g;
85
86    if (l[11] !== '=') {
87      return;
88    }
89    let ret = regexStart.exec(l);
90    if (ret) {
91      let tt = this.splitLast(ret[2]);
92      this.procNormal_ = {
93        type: ret[1], //优化类型
94        func: tt[0], //函数名
95        file: tt[1], //文件名
96        irList: [],
97        startLine: l,
98      };
99      this.stat_ = 1;
100      [this.initBlock_.funcPoint, this.initBlock_.filePoint] = [tt[0], tt[tt.length - 1]];
101      this.initBlock_.blockType = 10;
102    }
103    else {
104      ret = regexStart2.exec(l);
105      if (ret) {
106        let tt = this.splitLast(ret[2]);
107        let tt2 = this.splitLast(ret[3]);
108        NapiLog.logInfo(tt[0], 'Caller method(' + this.lineNum_ + '行)', ret[3]);
109        this.procNormal_ = {
110          type: ret[1] + ' ' + tt[0], //优化类型
111          func: tt2[0], //函数名
112          file: tt2[1], //文件名
113          irList: [],
114          startLine: l,
115        };
116        this.stat_ = 1;
117        [this.initBlock_.funcPoint, this.initBlock_.filePoint] = [tt2[0], tt2[tt2.length - 1]];
118        this.initBlock_.blockType = 10;
119      }
120      else {
121        if (l.search('After') > 0) {
122          alert(l);
123        }
124      }
125    }
126  }
127  collectNormal(l) {
128    let idx = l.search('{"id":');
129    if (idx >= 0) {
130      let idx2 = l.lastIndexOf('}');
131      let str = l.substring(idx, idx2 + 1);
132
133      let ir = JSON.parse(str);
134      { //根据XTools.CONFIG.MTypeField切割MType
135        let cutPos = [];
136        for (let field of XTools.CONFIG.MTypeField) {
137          let idx = ir.MType.indexOf(', ' + field);
138          if (idx >= 0) {
139            cutPos.push(idx);
140          }
141        }
142        cutPos.push(ir.MType.length);
143        cutPos.sort((a, b) => { return parseInt(a) - parseInt(b) });
144        if (cutPos[0] === 0) {
145          cutPos.shift();
146        }
147        let p = 0;
148        let cutResult = [];
149        for (let i of cutPos) {
150          let tmp = ir.MType.substring(p, i);
151          if (tmp.startsWith(', ')) {
152            tmp = tmp.substring(2);
153          }
154          if (tmp.endsWith(', ')) {
155            tmp = tmp.substring(0, tmp.length - 2);
156          }
157          cutResult.push(tmp);
158          p = i;
159        }
160        cutResult.push('inNum=[' + ir.in[0].length + ',' + ir.in[1].length + ',' + ir.in[2].length + ',' + ir.in[3].length + ',' + ir.in[4].length + ']');
161        cutResult.push('outNum=' + ir.out.length);
162        if ('comment' in ir) {
163          cutResult.push('//' + ir.comment);
164        }
165        ir.maxDetailWidth = 0;
166        for (let detail of cutResult) {
167          let w = X2DFast.gi().getTextWidth(detail, 14);
168          if (ir.maxDetailWidth < w) {
169            ir.maxDetailWidth = w;
170          }
171        }
172        ir.detailList = cutResult;
173      }
174      this.procNormal_.irList.push(ir);
175    }
176    else {
177      //= End typeHitRate: 0.500000 =
178      let regexEnd = /=+ End[a-zA-Z:.0-9 ]* =+/g;
179      let tt = regexEnd.exec(l);
180      if (tt) { //收集结束,入大表l.search('== End ==') > 0
181        if (this.procNormal_.irList.length > 0) {
182          if (!(this.procNormal_.file in this.output_)) {
183            this.output_[this.procNormal_.file] = {};
184          }
185          if (!(this.procNormal_.func in this.output_[this.procNormal_.file])) {
186            this.output_[this.procNormal_.file][this.procNormal_.func] = [];
187          }
188          this.output_[this.procNormal_.file][this.procNormal_.func].push(this.procNormal_);
189        }
190        else {
191          NapiLog.logError('After和End之间没有指令(' + this.lineNum_ + '行)');
192        }
193        this.stat_ = 0;
194      }
195      else {
196        NapiLog.logError('After和End之间解析失败(' + (this.lineNum_ + 1) + ')行');
197        this.stat_ = 0;
198      }
199    }
200  }
201  parseLine(l) {
202    switch (this.stat_) {
203      case 0: //搜索起始
204        if (this.SearchBegin(l) || this.isBlock(l)) {
205          return;
206        }
207        this.isStart(l);
208        break;
209      case 1: //收集ir表
210        this.collectNormal(l);
211        break;
212      case 10: //收集block一
213        if (this.CollectBlock(l)) {
214          this.stat_ = 0;
215          this.lineNum_ -= 1;
216        }
217        break;
218      case 20: //收集block二
219        if (this.CollectBlock2(l)) {
220          this.stat_ = 0;
221          this.lineNum_ -= 1;
222        }
223        break;
224    }
225  }
226
227  static Load(fn, cb) {
228    const xhr = new XMLHttpRequest();
229    xhr.open('GET', fn);
230    xhr.setRequestHeader('Cache-Control', 'no-cache');
231    xhr.onreadystatechange = () => {
232      if (xhr.readyState === XMLHttpRequest.DONE) {
233        if (xhr.status === 200) {
234          XTools.PORC_TO = 10;
235          cb(fn, xhr.responseText);
236        }
237      }
238    };
239    xhr.send();
240  }
241  NumberStringToArray(ss) {
242    let outs = ss.split(',');
243    let ret = [];
244    for (let s of outs) {
245      let ttt = parseInt(s);
246      if (!isNaN(ttt)) {
247        ret.push(ttt);
248      }
249    }
250    return ret;
251  }
252  SearchBegin(l) {
253    let ret;
254    let ib = this.initBlock_;
255
256    if (l.startsWith('[compiler] aot method')) {
257      //[compiler] aot method [func_main_0@b.abc] log:
258      const regexFuncName = /^\[compiler\] aot method \[([#a-zA-Z0-9_@/.]+)\] (recordName \[[a-zA-Z0-9_]*\] )*log:/g;
259      ret = regexFuncName.exec(l);
260      if (ret) {
261        [ib.funcPoint, ib.filePoint] = this.splitLast(ret[1]);
262        ib.blockType = 10;
263        return true;
264      }
265    }
266    if (l.startsWith('[compiler] ==================== Before state split')) {
267      const regexFuncName2 = /^\[compiler\] =+ Before state split linearizer \[([#a-zA-Z0-9_@/.]+)\] *=*/g;
268      ret = regexFuncName2.exec(l);
269      if (ret) {
270        [ib.funcPoint, ib.filePoint] = this.splitLast(ret[1]);
271        ib.blockType = 20;
272        return true;
273      }
274    }
275    if (l.startsWith('[compiler] ==================== After graph lineari')) {
276      const regexFuncName3 = /^\[compiler\] =+ After graph linearizer \[([#a-zA-Z0-9_@/.]+)\] *=*/g;
277      ret = regexFuncName3.exec(l);
278      if (ret) {
279        [ib.funcPoint, ib.filePoint] = this.splitLast(ret[1]);
280        ib.blockType = 20;
281        return true;
282      }
283    }
284    return false;
285  }
286  CollectBlock(l) {
287    const regexBlock = [
288      /^\[compiler\] B([0-9]+):                               ;preds=([0-9, ]*)$/g, //[compiler] B0:                               ;preds=
289      /^\[compiler\]  *Succes:([0-9, ]*)$/g, //[compiler] 	Succes:
290      /^\[compiler\]  *Bytecode\[\] = *(Empty)*$/g, //[compiler] 	Bytecode[] = Empty
291      /^\[compiler\]  *Trys:([0-9, ]*)$/g, //[compiler] 	Trys:
292    ];
293    let ret;
294    let pb = this.procBlock_;
295    if (pb.blockStat === 0) {
296      ret = regexBlock[0].exec(l);
297      if (ret) {
298        pb.oneBlock = {
299          id: ret[1],
300          op: 'B' + ret[1],
301          detailList: [],
302          maxDetailWidth: 0,
303        };
304        pb.oneBlock.in = [[], [], [], [], this.NumberStringToArray(ret[2])];
305        return false;
306      }
307      if (!pb.oneBlock) { //完成了一个block的解析
308        if (!(pb.blockCollect.file in this.output_)) {
309          this.output_[pb.blockCollect.file] = {};
310        }
311        if (!(pb.blockCollect.func in this.output_[pb.blockCollect.file])) {
312          this.output_[pb.blockCollect.file][pb.blockCollect.func] = [];
313        }
314        this.output_[pb.blockCollect.file][pb.blockCollect.func].push(pb.blockCollect);
315        return true;
316      }
317      ret = regexBlock[1].exec(l);
318      if (ret) {
319        pb.oneBlock.out = this.NumberStringToArray(ret[1]);
320        return false;
321      }
322      ret = regexBlock[2].exec(l);
323      if (ret) {
324        pb.blockStat = 1;
325        return false;
326      }
327    }
328    else if (pb.blockStat === 1) { //开始记录bytecode,直到空行,结束这个block
329      if (/^\[compiler\] *$/g.test(l)) { //遇到空行,完成block
330        if (pb.oneBlock.maxDetailWidth === 0) {
331          pb.oneBlock.maxDetailWidth = X2DFast.gi().getTextWidth('Empty', 14);
332          pb.oneBlock.detailList.push('Empty');
333        }
334        pb.blockCollect.irList.push(pb.oneBlock);
335        pb.oneBlock = null;
336        pb.blockStat = 0;
337      }
338      else {
339        let s = l.substring(11);
340        while (s.startsWith(' ')) {
341          s = s.substring(1);
342        }
343        let w = X2DFast.gi().getTextWidth(s, 14);
344        if (pb.oneBlock.maxDetailWidth < w) {
345          pb.oneBlock.maxDetailWidth = w;
346        }
347        pb.oneBlock.detailList.push(s);
348      }
349    }
350    return false;
351  }
352  CollectBlock2(l) {
353    const regexBlock = [
354      /^\[compiler\] B([0-9]+):/g, //[compiler] B0:                               ;preds=
355      /^\[compiler\]  *Preds:([0-9, ]*)$/g,
356      /^\[compiler\]  *Succes:([0-9, ]*)$/g, //[compiler] 	Succes:
357      /^\[compiler\]  *Bytecode\[\] = *(Empty)*$/g, //[compiler] 	Bytecode[] = Empty
358      /^\[compiler\]  *Trys:([0-9, ]*)$/g, //[compiler] 	Trys:
359    ];
360    let pb = this.procBlock_;
361    let ret;
362    switch (pb.blockStat) {
363      case 0:
364        ret = regexBlock[0].exec(l);
365        if (ret) {
366          pb.oneBlock = {
367            id: ret[1],
368            op: 'B' + ret[1],
369            detailList: [],
370            maxDetailWidth: 0,
371          };
372          pb.blockStat = 1;
373          return false;
374        }
375        if (!pb.oneBlock) { //完成了一个block的解析
376          if (!(pb.blockCollect.file in this.output_)) {
377            this.output_[pb.blockCollect.file] = {};
378          }
379          if (!(pb.blockCollect.func in this.output_[pb.blockCollect.file])) {
380            this.output_[pb.blockCollect.file][pb.blockCollect.func] = [];
381          }
382          this.output_[pb.blockCollect.file][pb.blockCollect.func].push(pb.blockCollect);
383          return true;
384        }
385        break;
386      case 1:
387        ret = regexBlock[1].exec(l);
388        if (ret) {
389          pb.oneBlock.in = [[], [], [], [], this.NumberStringToArray(ret[1])];
390          pb.blockStat = 2;
391          return false;
392        }
393        break;
394      case 2:
395        ret = regexBlock[2].exec(l);
396        if (ret) {
397          pb.oneBlock.out = this.NumberStringToArray(ret[1]);
398          pb.blockStat = 3;
399          return false;
400        }
401        break;
402      case 3: //开始记录bytecode,直到空行,结束这个block
403        if (/^\[compiler\] *$/g.test(l)) { //遇到空行,完成block
404          if (pb.oneBlock.maxDetailWidth === 0) {
405            pb.oneBlock.maxDetailWidth = X2DFast.gi().getTextWidth('Empty', 14);
406            pb.oneBlock.detailList.push('Empty');
407          }
408          pb.blockCollect.irList.push(pb.oneBlock);
409          pb.oneBlock = null;
410          pb.blockStat = 0;
411        }
412        else {
413          let s = l.substring(11);
414          while (s.startsWith(' ')) {
415            s = s.substring(1);
416          }
417          let w = X2DFast.gi().getTextWidth(s, 14);
418          if (pb.oneBlock.maxDetailWidth < w) {
419            pb.oneBlock.maxDetailWidth = w;
420          }
421          pb.oneBlock.detailList.push(s);
422        }
423        return false;
424      default:
425        return false;
426    }
427    return false;
428  }
429}
430
431module.exports = {
432  LogParser
433};
434