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