[tools][system-analyzer] Add FeedbackVector support
Log FeedbackVectors for optimised code and show them in the code-panel. Drive-by-fixes: - Fix off-by-one in SourcePositionIteration, making sure we always show the last element - Ensure we process all SourcePositions in SourcePositionIteration - Fix first load error in script-panel - Allow expanding all text with SHIFT-click Bug: v8:10644 Change-Id: Ic40a36ea82f0dfa2386c3196f27ca6978cf23643 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3245931 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Commit-Queue: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/main@{#77567}
This commit is contained in:
parent
54f90462ec
commit
1ca9a77095
@ -209,8 +209,9 @@ struct ScopedTimer {
|
||||
// static
|
||||
void Compiler::LogFunctionCompilation(Isolate* isolate,
|
||||
CodeEventListener::LogEventsAndTags tag,
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
Handle<Script> script,
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
Handle<FeedbackVector> vector,
|
||||
Handle<AbstractCode> abstract_code,
|
||||
CodeKind kind, double time_taken_ms) {
|
||||
DCHECK(!abstract_code.is_null());
|
||||
@ -235,6 +236,9 @@ void Compiler::LogFunctionCompilation(Isolate* isolate,
|
||||
Logger::ToNativeByScript(tag, *script);
|
||||
PROFILE(isolate, CodeCreateEvent(log_tag, abstract_code, shared, script_name,
|
||||
line_num, column_num));
|
||||
if (!vector.is_null()) {
|
||||
LOG(isolate, FeedbackVectorEvent(*vector, *abstract_code));
|
||||
}
|
||||
if (!FLAG_log_function_events) return;
|
||||
|
||||
std::string name;
|
||||
@ -363,9 +367,9 @@ void RecordUnoptimizedFunctionCompilation(
|
||||
time_taken_to_finalize.InMillisecondsF();
|
||||
|
||||
Handle<Script> script(Script::cast(shared->script()), isolate);
|
||||
Compiler::LogFunctionCompilation(isolate, tag, shared, script, abstract_code,
|
||||
CodeKind::INTERPRETED_FUNCTION,
|
||||
time_taken_ms);
|
||||
Compiler::LogFunctionCompilation(
|
||||
isolate, tag, script, shared, Handle<FeedbackVector>(), abstract_code,
|
||||
CodeKind::INTERPRETED_FUNCTION, time_taken_ms);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -499,9 +503,11 @@ void OptimizedCompilationJob::RecordFunctionCompilation(
|
||||
|
||||
Handle<Script> script(
|
||||
Script::cast(compilation_info()->shared_info()->script()), isolate);
|
||||
Handle<FeedbackVector> feedback_vector(
|
||||
compilation_info()->closure()->feedback_vector(), isolate);
|
||||
Compiler::LogFunctionCompilation(
|
||||
isolate, tag, compilation_info()->shared_info(), script, abstract_code,
|
||||
compilation_info()->code_kind(), time_taken_ms);
|
||||
isolate, tag, script, compilation_info()->shared_info(), feedback_vector,
|
||||
abstract_code, compilation_info()->code_kind(), time_taken_ms);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -2019,9 +2025,10 @@ bool Compiler::CompileSharedWithBaseline(Isolate* isolate,
|
||||
|
||||
if (shared->script().IsScript()) {
|
||||
Compiler::LogFunctionCompilation(
|
||||
isolate, CodeEventListener::FUNCTION_TAG, shared,
|
||||
handle(Script::cast(shared->script()), isolate),
|
||||
Handle<AbstractCode>::cast(code), CodeKind::BASELINE, time_taken_ms);
|
||||
isolate, CodeEventListener::FUNCTION_TAG,
|
||||
handle(Script::cast(shared->script()), isolate), shared,
|
||||
Handle<FeedbackVector>(), Handle<AbstractCode>::cast(code),
|
||||
CodeKind::BASELINE, time_taken_ms);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -88,8 +88,9 @@ class V8_EXPORT_PRIVATE Compiler : public AllStatic {
|
||||
|
||||
static void LogFunctionCompilation(Isolate* isolate,
|
||||
CodeEventListener::LogEventsAndTags tag,
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
Handle<Script> script,
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
Handle<FeedbackVector> feedback_vector,
|
||||
Handle<AbstractCode> abstract_code,
|
||||
CodeKind kind, double time_taken_ms);
|
||||
// Collect source positions for a function that has already been compiled to
|
||||
|
@ -1385,6 +1385,30 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, Handle<AbstractCode> code,
|
||||
LogCodeDisassemble(code);
|
||||
}
|
||||
|
||||
void Logger::FeedbackVectorEvent(FeedbackVector vector, AbstractCode code) {
|
||||
DisallowGarbageCollection no_gc;
|
||||
if (!FLAG_log_code) return;
|
||||
MSG_BUILDER();
|
||||
msg << "feedback-vector" << kNext << Time();
|
||||
msg << kNext << reinterpret_cast<void*>(vector.address()) << kNext
|
||||
<< vector.length();
|
||||
msg << kNext << reinterpret_cast<void*>(code.InstructionStart());
|
||||
msg << kNext << vector.optimization_marker();
|
||||
msg << kNext << vector.optimization_tier();
|
||||
msg << kNext << vector.invocation_count();
|
||||
msg << kNext << vector.profiler_ticks() << kNext;
|
||||
|
||||
#ifdef OBJECT_PRINT
|
||||
std::ostringstream buffer;
|
||||
vector.FeedbackVectorPrint(buffer);
|
||||
std::string contents = buffer.str();
|
||||
msg.AppendString(contents.c_str(), contents.length());
|
||||
#else
|
||||
msg << "object-printing-disabled";
|
||||
#endif
|
||||
msg.WriteToLogFile();
|
||||
}
|
||||
|
||||
// Functions
|
||||
// Although, it is possible to extract source and line from
|
||||
// the SharedFunctionInfo object, we left it to caller
|
||||
|
@ -233,6 +233,7 @@ class Logger : public CodeEventListener {
|
||||
void CodeDependencyChangeEvent(Handle<Code> code,
|
||||
Handle<SharedFunctionInfo> sfi,
|
||||
const char* reason) override;
|
||||
void FeedbackVectorEvent(FeedbackVector vector, AbstractCode code);
|
||||
void WeakCodeClearEvent() override {}
|
||||
|
||||
void ProcessDeoptEvent(Handle<Code> code, SourcePosition position,
|
||||
|
@ -54,6 +54,7 @@ export class CodeLogEntry extends LogEntry {
|
||||
this._kind = kind;
|
||||
this._kindName = kindName;
|
||||
this._entry = entry;
|
||||
this._feedbackVector = undefined;
|
||||
entry.logEntry = this;
|
||||
}
|
||||
|
||||
@ -94,6 +95,17 @@ export class CodeLogEntry extends LogEntry {
|
||||
return entries.map(each => each.logEntry);
|
||||
}
|
||||
|
||||
get feedbackVector() {
|
||||
return this._feedbackVector;
|
||||
}
|
||||
|
||||
setFeedbackVector(fbv) {
|
||||
if (this._feedbackVector) {
|
||||
throw new Error('Double setting FeedbackVector');
|
||||
}
|
||||
this._feedbackVector = fbv;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Code(${this.type})`;
|
||||
}
|
||||
@ -107,7 +119,61 @@ export class CodeLogEntry extends LogEntry {
|
||||
static get propertyNames() {
|
||||
return [
|
||||
'functionName', 'sourcePosition', 'kindName', 'size', 'type', 'kind',
|
||||
'script', 'source', 'code', 'variants'
|
||||
'script', 'source', 'code', 'feedbackVector', 'variants'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class FeedbackVectorEntry extends LogEntry {
|
||||
constructor(
|
||||
timestamp, codeEntry, fbvAddress, length, optimizationMarker,
|
||||
optimizationTier, invocationCount, profilerTicks, string) {
|
||||
super('FeedbackVector', timestamp);
|
||||
this._length = length;
|
||||
this._code = codeEntry;
|
||||
this._string = string;
|
||||
this._optimizationMarker = optimizationMarker;
|
||||
this._optimizationTier = optimizationTier;
|
||||
this._invocationCount = invocationCount;
|
||||
this._profilerTicks = profilerTicks;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `FeedbackVector(l=${this.length})`
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
get code() {
|
||||
return this._code;
|
||||
}
|
||||
|
||||
get string() {
|
||||
return this._string;
|
||||
}
|
||||
|
||||
get optimizationMarker() {
|
||||
return this._optimizationMarker;
|
||||
}
|
||||
|
||||
get optimizationTier() {
|
||||
return this._optimizationTier;
|
||||
}
|
||||
|
||||
get invocationCount() {
|
||||
return this._invocationCount;
|
||||
}
|
||||
|
||||
get profilerTicks() {
|
||||
return this._profilerTicks;
|
||||
}
|
||||
|
||||
static get propertyNames() {
|
||||
return [
|
||||
'length', 'length', 'code', 'optimizationMarker', 'optimizationTier',
|
||||
'invocationCount', 'profilerTicks', 'string'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import {Profile} from '../profile.mjs';
|
||||
import {RemoteLinuxCppEntriesProvider, RemoteMacOSCppEntriesProvider} from '../tickprocessor.mjs'
|
||||
|
||||
import {ApiLogEntry} from './log/api.mjs';
|
||||
import {CodeLogEntry, DeoptLogEntry, SharedLibLogEntry} from './log/code.mjs';
|
||||
import {CodeLogEntry, DeoptLogEntry, FeedbackVectorEntry, SharedLibLogEntry} from './log/code.mjs';
|
||||
import {IcLogEntry} from './log/ic.mjs';
|
||||
import {Edge, MapLogEntry} from './log/map.mjs';
|
||||
import {TickLogEntry} from './log/tick.mjs';
|
||||
@ -115,6 +115,13 @@ export class Processor extends LogReader {
|
||||
],
|
||||
processor: this.processCodeDisassemble
|
||||
},
|
||||
'feedback-vector': {
|
||||
parsers: [
|
||||
parseInt, parseString, parseInt, parseInt, parseString, parseString,
|
||||
parseInt, parseInt, parseString
|
||||
],
|
||||
processor: this.processFeedbackVector
|
||||
},
|
||||
'script-source': {
|
||||
parsers: [parseInt, parseString, parseString],
|
||||
processor: this.processScriptSource
|
||||
@ -328,6 +335,21 @@ export class Processor extends LogReader {
|
||||
logEntry.sourcePosition = script.addSourcePosition(line, column, logEntry);
|
||||
}
|
||||
|
||||
processFeedbackVector(
|
||||
timestamp, fbv_address, fbv_length, instructionStart, optimization_marker,
|
||||
optimization_tier, invocation_count, profiler_ticks, fbv_string) {
|
||||
const codeEntry = this._profile.findEntry(instructionStart);
|
||||
if (!codeEntry) {
|
||||
console.warn('Didn\'t find code for FBV', {fbv, instructionStart});
|
||||
return;
|
||||
}
|
||||
const fbv = new FeedbackVectorEntry(
|
||||
timestamp, codeEntry.logEntry, fbv_address, fbv_length,
|
||||
optimization_marker, optimization_tier, invocation_count,
|
||||
profiler_ticks, fbv_string);
|
||||
codeEntry.logEntry.setFeedbackVector(fbv);
|
||||
}
|
||||
|
||||
processScriptSource(scriptId, url, source) {
|
||||
this._profile.addScriptSource(scriptId, url, source);
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ found in the LICENSE file. -->
|
||||
<div class="panelBody">
|
||||
<h3>Properties</h3>
|
||||
<property-link-table id="properties"></property-link-table>
|
||||
<h3>FeedbackVector</h3>
|
||||
<property-link-table id="feedbackVector"></property-link-table>
|
||||
<h3>Disassembly</h3>
|
||||
<pre id="disassembly"></pre>
|
||||
<h3>Source Code</h3>
|
||||
|
@ -19,8 +19,10 @@ DOM.defineCustomElement('view/code-panel',
|
||||
|
||||
constructor() {
|
||||
super(templateText);
|
||||
this._propertiesNode = this.$('#properties');
|
||||
this._codeSelectNode = this.$('#codeSelect');
|
||||
this._disassemblyNode = this.$('#disassembly');
|
||||
this._feedbackVectorNode = this.$('#feedbackVector');
|
||||
this._sourceNode = this.$('#sourceCode');
|
||||
this._registerSelector = new RegisterSelector(this._disassemblyNode);
|
||||
|
||||
@ -42,8 +44,10 @@ DOM.defineCustomElement('view/code-panel',
|
||||
|
||||
set entry(entry) {
|
||||
this._entry = entry;
|
||||
if (entry !== undefined) {
|
||||
this.$('#properties').propertyDict = {
|
||||
if (!entry) {
|
||||
this._propertiesNode.propertyDict = {};
|
||||
} else {
|
||||
this._propertiesNode.propertyDict = {
|
||||
'__this__': entry,
|
||||
functionName: entry.functionName,
|
||||
size: formatBytes(entry.size),
|
||||
@ -54,19 +58,29 @@ DOM.defineCustomElement('view/code-panel',
|
||||
kind: entry.kindName,
|
||||
variants: entry.variants.length > 1 ? entry.variants : undefined,
|
||||
};
|
||||
} else {
|
||||
this.$('#properties').propertyDict = {};
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
_update() {
|
||||
this._updateSelect();
|
||||
this._formatDisassembly();
|
||||
this._updateDisassembly();
|
||||
this._updateFeedbackVector();
|
||||
this._sourceNode.innerText = this._entry?.source ?? '';
|
||||
}
|
||||
|
||||
_formatDisassembly() {
|
||||
_updateFeedbackVector() {
|
||||
if (!this._entry?.feedbackVector) {
|
||||
this._feedbackVectorNode.propertyDict = {};
|
||||
} else {
|
||||
const dict = this._entry.feedbackVector.toolTipDict;
|
||||
delete dict.title;
|
||||
delete dict.code;
|
||||
this._feedbackVectorNode.propertyDict = dict;
|
||||
}
|
||||
}
|
||||
|
||||
_updateDisassembly() {
|
||||
if (!this._entry?.code) {
|
||||
this._disassemblyNode.innerText = '';
|
||||
return;
|
||||
@ -152,4 +166,4 @@ class RegisterSelector {
|
||||
node.classList.add('selected');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -364,16 +364,17 @@ export class ExpandableText {
|
||||
button.innerText = '...';
|
||||
button.onclick = (e) => {
|
||||
e.stopImmediatePropagation();
|
||||
this.expand()
|
||||
this.expand(e.shiftKey);
|
||||
};
|
||||
button.title = 'Expand text. Use SHIFT-click to show all.'
|
||||
return button;
|
||||
}
|
||||
|
||||
expand() {
|
||||
expand(showAll = false) {
|
||||
DOM.removeAllChildren(this._node);
|
||||
this._start = this._start + this._delta;
|
||||
this._end = this._end - this._delta;
|
||||
if (this._start >= this._end) {
|
||||
if (this._start >= this._end || showAll) {
|
||||
this._node.innerText = this._string;
|
||||
this._button.onclick = undefined;
|
||||
return;
|
||||
@ -466,4 +467,4 @@ export function gradientStopsFromGroups(
|
||||
return stops;
|
||||
}
|
||||
|
||||
export * from '../helper.mjs';
|
||||
export * from '../helper.mjs';
|
||||
|
@ -26,6 +26,10 @@ DOM.defineCustomElement(
|
||||
|
||||
set propertyDict(propertyDict) {
|
||||
if (this._propertyDict === propertyDict) return;
|
||||
if (typeof propertyDict !== 'object') {
|
||||
throw new Error(
|
||||
`Invalid property dict, expected object: ${propertyDict}`);
|
||||
}
|
||||
this._propertyDict = propertyDict;
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ DOM.defineCustomElement('view/script-panel',
|
||||
(templateText) =>
|
||||
class SourcePanel extends CollapsableElement {
|
||||
_selectedSourcePositions = [];
|
||||
_sourcePositionsToMarkNodesPromise = Promise.resolve([]);
|
||||
_sourcePositionsToMarkNodesPromise = defer();
|
||||
_scripts = [];
|
||||
_script;
|
||||
|
||||
@ -229,7 +229,7 @@ class SourcePositionIterator {
|
||||
}
|
||||
|
||||
_done() {
|
||||
return this._index + 1 >= this._entries.length;
|
||||
return this._index >= this._entries.length;
|
||||
}
|
||||
|
||||
_next() {
|
||||
@ -295,6 +295,10 @@ class LineBuilder {
|
||||
scriptNode.appendChild(
|
||||
this._createLineNode(sourcePositionsIterator, lineIndex, line));
|
||||
}
|
||||
if (this._script.sourcePositions.length !=
|
||||
this._sourcePositionToMarkers.size) {
|
||||
console.error('Not all SourcePositions were processed.');
|
||||
}
|
||||
return scriptNode;
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ class CppEntriesProvider {
|
||||
} catch (e) {
|
||||
if (!response || response.status == 404) {
|
||||
// Assume that the local symbol server is not reachable.
|
||||
console.error("Disabling remote symbol loading:", e);
|
||||
console.warn("Disabling remote symbol loading:", e);
|
||||
this._isEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user