diff options
Diffstat (limited to 'deps/v8/tools/system-analyzer/processor.mjs')
-rw-r--r-- | deps/v8/tools/system-analyzer/processor.mjs | 292 |
1 files changed, 191 insertions, 101 deletions
diff --git a/deps/v8/tools/system-analyzer/processor.mjs b/deps/v8/tools/system-analyzer/processor.mjs index 49448bb9da..9685e09ad6 100644 --- a/deps/v8/tools/system-analyzer/processor.mjs +++ b/deps/v8/tools/system-analyzer/processor.mjs @@ -5,7 +5,8 @@ import {LogReader, parseString, parseVarArgs} from '../logreader.mjs'; import {Profile} from '../profile.mjs'; -import {DeoptLogEntry} from './log/deopt.mjs'; +import {ApiLogEntry} from './log/api.mjs'; +import {CodeLogEntry, DeoptLogEntry} from './log/code.mjs'; import {IcLogEntry} from './log/ic.mjs'; import {Edge, MapLogEntry} from './log/map.mjs'; import {Timeline} from './timeline.mjs'; @@ -17,17 +18,28 @@ export class Processor extends LogReader { _mapTimeline = new Timeline(); _icTimeline = new Timeline(); _deoptTimeline = new Timeline(); + _codeTimeline = new Timeline(); + _apiTimeline = new Timeline(); _formatPCRegexp = /(.*):[0-9]+:[0-9]+$/; + _lastTimestamp = 0; + _lastCodeLogEntry; MAJOR_VERSION = 7; MINOR_VERSION = 6; constructor(logString) { super(); - this.propertyICParser = [ + const propertyICParser = [ parseInt, parseInt, parseInt, parseInt, parseString, parseString, parseString, parseString, parseString, parseString ]; this.dispatchTable_ = { __proto__: null, + 'v8-version': { + parsers: [ + parseInt, + parseInt, + ], + processor: this.processV8Version + }, 'code-creation': { parsers: [ parseString, parseInt, parseInt, parseInt, parseInt, parseString, @@ -42,20 +54,28 @@ export class Processor extends LogReader { ], processor: this.processCodeDeopt }, - 'v8-version': { + 'code-move': + {parsers: [parseInt, parseInt], processor: this.processCodeMove}, + 'code-delete': {parsers: [parseInt], processor: this.processCodeDelete}, + 'code-source-info': { + parsers: [ + parseInt, parseInt, parseInt, parseInt, parseString, parseString, + parseString + ], + processor: this.processCodeSourceInfo + }, + 'code-disassemble': { parsers: [ parseInt, - parseInt, + parseString, + parseString, ], - processor: this.processV8Version + processor: this.processCodeDisassemble }, 'script-source': { parsers: [parseInt, parseString, parseString], processor: this.processScriptSource }, - 'code-move': - {parsers: [parseInt, parseInt], processor: this.processCodeMove}, - 'code-delete': {parsers: [parseInt], processor: this.processCodeDelete}, 'sfi-move': {parsers: [parseInt, parseInt], processor: this.processFunctionMove}, 'map-create': @@ -72,33 +92,37 @@ export class Processor extends LogReader { processor: this.processMapDetails }, 'LoadGlobalIC': { - parsers: this.propertyICParser, + parsers: propertyICParser, processor: this.processPropertyIC.bind(this, 'LoadGlobalIC') }, 'StoreGlobalIC': { - parsers: this.propertyICParser, + parsers: propertyICParser, processor: this.processPropertyIC.bind(this, 'StoreGlobalIC') }, 'LoadIC': { - parsers: this.propertyICParser, + parsers: propertyICParser, processor: this.processPropertyIC.bind(this, 'LoadIC') }, 'StoreIC': { - parsers: this.propertyICParser, + parsers: propertyICParser, processor: this.processPropertyIC.bind(this, 'StoreIC') }, 'KeyedLoadIC': { - parsers: this.propertyICParser, + parsers: propertyICParser, processor: this.processPropertyIC.bind(this, 'KeyedLoadIC') }, 'KeyedStoreIC': { - parsers: this.propertyICParser, + parsers: propertyICParser, processor: this.processPropertyIC.bind(this, 'KeyedStoreIC') }, 'StoreInArrayLiteralIC': { - parsers: this.propertyICParser, + parsers: propertyICParser, processor: this.processPropertyIC.bind(this, 'StoreInArrayLiteralIC') }, + 'api': { + parsers: [parseString, parseVarArgs], + processor: this.processApiEvent + }, }; if (logString) this.processString(logString); } @@ -166,45 +190,56 @@ export class Processor extends LogReader { }); } - /** - * Parser for dynamic code optimization state. - */ - parseState(s) { - switch (s) { - case '': - return Profile.CodeState.COMPILED; - case '~': - return Profile.CodeState.OPTIMIZABLE; - case '*': - return Profile.CodeState.OPTIMIZED; + processV8Version(majorVersion, minorVersion) { + if ((majorVersion == this.MAJOR_VERSION && + minorVersion <= this.MINOR_VERSION) || + (majorVersion < this.MAJOR_VERSION)) { + window.alert( + `Unsupported version ${majorVersion}.${minorVersion}. \n` + + `Please use the matching tool for given the V8 version.`); } - throw new Error(`unknown code state: ${s}`); } processCodeCreation(type, kind, timestamp, start, size, name, maybe_func) { + this._lastTimestamp = timestamp; + let entry; + let stateName = ''; if (maybe_func.length) { const funcAddr = parseInt(maybe_func[0]); - const state = this.parseState(maybe_func[1]); - this._profile.addFuncCode( + stateName = maybe_func[1] ?? ''; + const state = Profile.parseState(maybe_func[1]); + entry = this._profile.addFuncCode( type, name, timestamp, start, size, funcAddr, state); } else { - this._profile.addCode(type, name, timestamp, start, size); + entry = this._profile.addCode(type, name, timestamp, start, size); } + this._lastCodeLogEntry = + new CodeLogEntry(type + stateName, timestamp, kind, entry); + this._codeTimeline.push(this._lastCodeLogEntry); } processCodeDeopt( timestamp, codeSize, instructionStart, inliningId, scriptOffset, deoptKind, deoptLocation, deoptReason) { - this._deoptTimeline.push(new DeoptLogEntry(deoptKind, timestamp)); - } - - processV8Version(majorVersion, minorVersion) { - if ((majorVersion == this.MAJOR_VERSION && - minorVersion <= this.MINOR_VERSION) || - (majorVersion < this.MAJOR_VERSION)) { - window.alert( - `Unsupported version ${majorVersion}.${minorVersion}. \n` + - `Please use the matching tool for given the V8 version.`); + this._lastTimestamp = timestamp; + const codeEntry = this._profile.findEntry(instructionStart); + const logEntry = new DeoptLogEntry( + deoptKind, timestamp, codeEntry, deoptReason, deoptLocation, + scriptOffset, instructionStart, codeSize, inliningId); + this._deoptTimeline.push(logEntry); + this.addSourcePosition(codeEntry, logEntry); + logEntry.functionSourcePosition = logEntry.sourcePosition; + // custom parse deopt location + if (deoptLocation !== '<unknown>') { + const colSeparator = deoptLocation.lastIndexOf(':'); + const rowSeparator = deoptLocation.lastIndexOf(':', colSeparator - 1); + const script = this.getScript(deoptLocation.substring(1, rowSeparator)); + const line = + parseInt(deoptLocation.substring(rowSeparator + 1, colSeparator)); + const column = parseInt( + deoptLocation.substring(colSeparator + 1, deoptLocation.length - 1)); + logEntry.sourcePosition = + script.addSourcePosition(line, column, logEntry); } } @@ -224,111 +259,138 @@ export class Processor extends LogReader { this._profile.moveFunc(from, to); } - formatName(entry) { - if (!entry) return '<unknown>'; - let name = entry.func.getName(); - let re = /(.*):[0-9]+:[0-9]+$/; - let array = re.exec(name); - if (!array) return name; - return entry.getState() + array[1]; + processCodeSourceInfo( + start, scriptId, startPos, endPos, sourcePositions, inliningPositions, + inlinedFunctions) { + this._profile.addSourcePositions( + start, scriptId, startPos, endPos, sourcePositions, inliningPositions, + inlinedFunctions); + let profileEntry = this._profile.findEntry(start); + if (profileEntry !== this._lastCodeLogEntry._entry) return; + this.addSourcePosition(profileEntry, this._lastCodeLogEntry); + this._lastCodeLogEntry = undefined; + } + + addSourcePosition(profileEntry, logEntry) { + let script = this.getProfileEntryScript(profileEntry); + const parts = profileEntry.getRawName().split(':'); + if (parts.length < 3) return; + const line = parseInt(parts[parts.length - 2]); + const column = parseInt(parts[parts.length - 1]); + logEntry.sourcePosition = script.addSourcePosition(line, column, logEntry); + } + + processCodeDisassemble(start, kind, disassemble) { + this._profile.addDisassemble(start, kind, disassemble); } processPropertyIC( - type, pc, time, line, column, old_state, new_state, map, key, modifier, + type, pc, time, line, column, old_state, new_state, mapId, key, modifier, slow_reason) { - let fnName = this.functionName(pc); - let parts = fnName.split(' '); - let fileName = parts[parts.length - 1]; - let script = this.getScript(fileName); + this._lastTimestamp = time; + const profileEntry = this._profile.findEntry(pc); + const fnName = this.formatProfileEntry(profileEntry); + const script = this.getProfileEntryScript(profileEntry); + const map = this.getOrCreateMapEntry(mapId, time); // TODO: Use SourcePosition here directly let entry = new IcLogEntry( type, fnName, time, line, column, key, old_state, new_state, map, - slow_reason, script, modifier); + slow_reason, modifier); if (script) { entry.sourcePosition = script.addSourcePosition(line, column, entry); } this._icTimeline.push(entry); } - functionName(pc) { - let entry = this._profile.findEntry(pc); - return this.formatName(entry); - } - formatPC(pc, line, column) { - let entry = this._profile.findEntry(pc); - if (!entry) return '<unknown>' - if (entry.type === 'Builtin') { - return entry.name; - } - let name = entry.func.getName(); - let array = this._formatPCRegexp.exec(name); - if (array === null) { - entry = name; - } else { - entry = entry.getState() + array[1]; - } - return entry + ':' + line + ':' + column; + formatProfileEntry(profileEntry, line, column) { + if (!profileEntry) return '<unknown>'; + if (profileEntry.type === 'Builtin') return profileEntry.name; + const name = profileEntry.func.getName(); + const array = this._formatPCRegexp.exec(name); + const formatted = + (array === null) ? name : profileEntry.getState() + array[1]; + if (line === undefined || column === undefined) return formatted; + return `${formatted}:${line}:${column}`; } - processFileName(filePositionLine) { - if (!filePositionLine.includes(' ')) return; - // Try to handle urls with file positions: https://foo.bar.com/:17:330" - filePositionLine = filePositionLine.split(' '); - let parts = filePositionLine[1].split(':'); - if (parts[0].length <= 5) return parts[0] + ':' + parts[1]; - return parts[1]; + getProfileEntryScript(profileEntry) { + if (!profileEntry) return undefined; + if (profileEntry.type === 'Builtin') return undefined; + const script = profileEntry.source?.script; + if (script !== undefined) return script; + // Slow path, try to get the script from the url: + const fnName = this.formatProfileEntry(profileEntry); + let parts = fnName.split(' '); + let fileName = parts[parts.length - 1]; + return this.getScript(fileName); } processMap(type, time, from, to, pc, line, column, reason, name) { - let time_ = parseInt(time); + this._lastTimestamp = time; + const time_ = parseInt(time); if (type === 'Deprecate') return this.deprecateMap(type, time_, from); - let from_ = this.getExistingMapEntry(from, time_); - let to_ = this.getExistingMapEntry(to, time_); + // Skip normalized maps that were cached so we don't introduce multiple + // edges with the same source and target map. + if (type === 'NormalizeCached') return; + const from_ = this.getOrCreateMapEntry(from, time_); + const to_ = this.getOrCreateMapEntry(to, time_); + if (type === 'Normalize') { + // Fix a bug where we log "Normalize" transitions for maps created from + // the NormalizedMapCache. + if (to_.parent()?.id === from || to_.time < from_.time || to_.depth > 0) { + console.log(`Skipping transition to cached normalized map`); + return; + } + } // TODO: use SourcePosition directly. let edge = new Edge(type, name, reason, time, from_, to_); - to_.filePosition = this.formatPC(pc, line, column); - let fileName = this.processFileName(to_.filePosition); - // TODO: avoid undefined source positions. - if (fileName !== undefined) { - to_.script = this.getScript(fileName); + const profileEntry = this._profile.findEntry(pc) + to_.entry = profileEntry; + let script = this.getProfileEntryScript(profileEntry); + if (script) { + to_.sourcePosition = script.addSourcePosition(line, column, to_) } - if (to_.script) { - to_.sourcePosition = to_.script.addSourcePosition(line, column, to_) + if (to_.parent() !== undefined && to_.parent() === from_) { + // Fix bug where we double log transitions. + console.warn('Fixing up double transition'); + to_.edge.updateFrom(edge); + } else { + edge.finishSetup(); } - edge.finishSetup(); } deprecateMap(type, time, id) { - this.getExistingMapEntry(id, time).deprecate(); + this._lastTimestamp = time; + this.getOrCreateMapEntry(id, time).deprecate(); } processMapCreate(time, id) { // map-create events might override existing maps if the addresses get // recycled. Hence we do not check for existing maps. - let map = this.createMapEntry(id, time); + this._lastTimestamp = time; + this.createMapEntry(id, time); } processMapDetails(time, id, string) { // TODO(cbruni): fix initial map logging. - let map = this.getExistingMapEntry(id, time); + const map = this.getOrCreateMapEntry(id, time); map.description = string; } createMapEntry(id, time) { - let map = new MapLogEntry(id, time); + this._lastTimestamp = time; + const map = new MapLogEntry(id, time); this._mapTimeline.push(map); return map; } - getExistingMapEntry(id, time) { + getOrCreateMapEntry(id, time) { if (id === '0x000000000000') return undefined; - let map = MapLogEntry.get(id, time); - if (map === undefined) { - console.error(`No map details provided: id=${id}`); - // Manually patch in a map to continue running. - return this.createMapEntry(id, time); - }; - return map; + const map = MapLogEntry.get(id, time); + if (map !== undefined) return map; + console.warn(`No map details provided: id=${id}`); + // Manually patch in a map to continue running. + return this.createMapEntry(id, time); } getScript(url) { @@ -340,6 +402,22 @@ export class Processor extends LogReader { return script; } + processApiEvent(type, varArgs) { + let name, arg1; + if (varArgs.length == 0) { + const index = type.indexOf(':'); + if (index > 0) { + name = type; + type = type.substr(0, index); + } + } else { + name = varArgs[0]; + arg1 = varArgs[1]; + } + this._apiTimeline.push( + new ApiLogEntry(type, this._lastTimestamp, name, arg1)); + } + get icTimeline() { return this._icTimeline; } @@ -352,7 +430,19 @@ export class Processor extends LogReader { return this._deoptTimeline; } + get codeTimeline() { + return this._codeTimeline; + } + + get apiTimeline() { + return this._apiTimeline; + } + get scripts() { return this._profile.scripts_.filter(script => script !== undefined); } + + get profile() { + return this._profile; + } } |