Commit 1ca9a770 authored by Camillo Bruni's avatar Camillo Bruni Committed by V8 LUCI CQ

[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/+/3245931Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77567}
parent 54f90462
......@@ -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;
......
......@@ -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;
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment