Commit e3b4ffa9 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools] Improve function event logging and parse processor

- Log script sources with --log-function-events
- Don't show confusing duration in graphs
- Introduce separate compilation category
- Log script details after deserialization
- Log parse times for eval scripts
- Display deserialized scripts and functions

Change-Id: I58f3bf8efe3955632322f958716c36ad38761fd0
Bug: chromium:757467, chromium:850038
Reviewed-on: https://chromium-review.googlesource.com/1128082Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54465}
parent 1d4a1172
...@@ -970,7 +970,7 @@ BackgroundCompileTask::BackgroundCompileTask(ScriptStreamingData* source, ...@@ -970,7 +970,7 @@ BackgroundCompileTask::BackgroundCompileTask(ScriptStreamingData* source,
// Prepare the data for the internalization phase and compilation phase, which // Prepare the data for the internalization phase and compilation phase, which
// will happen in the main thread after parsing. // will happen in the main thread after parsing.
ParseInfo* info = new ParseInfo(isolate); ParseInfo* info = new ParseInfo(isolate);
LOG(isolate, ScriptEvent(Logger::ScriptEventType::kBackgroundCompile, LOG(isolate, ScriptEvent(Logger::ScriptEventType::kStreamingCompile,
info->script_id())); info->script_id()));
if (V8_UNLIKELY(FLAG_runtime_stats)) { if (V8_UNLIKELY(FLAG_runtime_stats)) {
info->set_runtime_call_stats(new (info->zone()) RuntimeCallStats()); info->set_runtime_call_stats(new (info->zone()) RuntimeCallStats());
......
...@@ -1549,25 +1549,18 @@ void Logger::FunctionEvent(const char* reason, int script_id, double time_delta, ...@@ -1549,25 +1549,18 @@ void Logger::FunctionEvent(const char* reason, int script_id, double time_delta,
msg.WriteToLogFile(); msg.WriteToLogFile();
} }
namespace { void Logger::CompilationCacheEvent(const char* action, const char* cache_type,
void AppendCompilationCacheMessage(Log::MessageBuilder& msg,
SharedFunctionInfo* sfi) { SharedFunctionInfo* sfi) {
if (!log_->IsEnabled() || !FLAG_log_function_events) return;
Log::MessageBuilder msg(log_);
int script_id = -1; int script_id = -1;
if (sfi->script()->IsScript()) { if (sfi->script()->IsScript()) {
script_id = Script::cast(sfi->script())->id(); script_id = Script::cast(sfi->script())->id();
} }
msg << script_id << Logger::kNext << sfi->StartPosition() << Logger::kNext
<< sfi->EndPosition();
}
} // namespace
void Logger::CompilationCacheEvent(const char* action, const char* cache_type,
SharedFunctionInfo* sfi) {
if (!log_->IsEnabled() || !FLAG_log_function_events) return;
Log::MessageBuilder msg(log_);
msg << "compilation-cache" << Logger::kNext << action << Logger::kNext msg << "compilation-cache" << Logger::kNext << action << Logger::kNext
<< cache_type << Logger::kNext; << cache_type << Logger::kNext << script_id << Logger::kNext
AppendCompilationCacheMessage(msg, sfi); << sfi->StartPosition() << Logger::kNext << sfi->EndPosition()
<< Logger::kNext << timer_.Elapsed().InMicroseconds();
msg.WriteToLogFile(); msg.WriteToLogFile();
} }
...@@ -1588,24 +1581,31 @@ void Logger::ScriptEvent(ScriptEventType type, int script_id) { ...@@ -1588,24 +1581,31 @@ void Logger::ScriptEvent(ScriptEventType type, int script_id) {
case ScriptEventType::kBackgroundCompile: case ScriptEventType::kBackgroundCompile:
msg << "background-compile"; msg << "background-compile";
break; break;
case ScriptEventType::kStreamingCompile:
msg << "streaming-compile";
break;
} }
msg << Logger::kNext << script_id; msg << Logger::kNext << script_id << Logger::kNext
<< timer_.Elapsed().InMicroseconds();
msg.WriteToLogFile(); msg.WriteToLogFile();
} }
void Logger::ScriptDetails(Script* script) { void Logger::ScriptDetails(Script* script) {
if (!log_->IsEnabled() || !FLAG_log_function_events) return; if (!log_->IsEnabled() || !FLAG_log_function_events) return;
Log::MessageBuilder msg(log_); {
msg << "script-details" << Logger::kNext << script->id() << Logger::kNext; Log::MessageBuilder msg(log_);
if (script->name()->IsString()) { msg << "script-details" << Logger::kNext << script->id() << Logger::kNext;
msg << String::cast(script->name()); if (script->name()->IsString()) {
} msg << String::cast(script->name());
msg << Logger::kNext << script->line_offset() << Logger::kNext }
<< script->column_offset() << Logger::kNext; msg << Logger::kNext << script->line_offset() << Logger::kNext
if (script->source_mapping_url()->IsString()) { << script->column_offset() << Logger::kNext;
msg << String::cast(script->source_mapping_url()); if (script->source_mapping_url()->IsString()) {
msg << String::cast(script->source_mapping_url());
}
msg.WriteToLogFile();
} }
msg.WriteToLogFile(); EnsureLogScriptSource(script);
} }
bool Logger::EnsureLogScriptSource(Script* script) { bool Logger::EnsureLogScriptSource(Script* script) {
......
...@@ -128,7 +128,8 @@ class Logger : public CodeEventListener { ...@@ -128,7 +128,8 @@ class Logger : public CodeEventListener {
kReserveId, kReserveId,
kCreate, kCreate,
kDeserialize, kDeserialize,
kBackgroundCompile kBackgroundCompile,
kStreamingCompile
}; };
// The separator is used to write an unescaped "," into the log. // The separator is used to write an unescaped "," into the log.
......
...@@ -276,7 +276,7 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize( ...@@ -276,7 +276,7 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
Isolate* isolate, ScriptData* cached_data, Handle<String> source, Isolate* isolate, ScriptData* cached_data, Handle<String> source,
ScriptOriginOptions origin_options) { ScriptOriginOptions origin_options) {
base::ElapsedTimer timer; base::ElapsedTimer timer;
if (FLAG_profile_deserialization) timer.Start(); if (FLAG_profile_deserialization || FLAG_log_function_events) timer.Start();
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -311,15 +311,24 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize( ...@@ -311,15 +311,24 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms); PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
} }
if (isolate->logger()->is_listening_to_code_events() || bool log_code_creation = isolate->logger()->is_listening_to_code_events() ||
isolate->is_profiling()) { isolate->is_profiling();
if (log_code_creation || FLAG_log_function_events) {
String* name = ReadOnlyRoots(isolate).empty_string(); String* name = ReadOnlyRoots(isolate).empty_string();
if (result->script()->IsScript()) { if (result->script()->IsScript()) {
Script* script = Script::cast(result->script()); Script* script = Script::cast(result->script());
if (script->name()->IsString()) name = String::cast(script->name()); if (script->name()->IsString()) name = String::cast(script->name());
if (FLAG_log_function_events) {
LOG(isolate, FunctionEvent("deserialize", script->id(),
timer.Elapsed().InMillisecondsF(),
result->StartPosition(),
result->EndPosition(), name));
}
}
if (log_code_creation) {
PROFILE(isolate, CodeCreateEvent(CodeEventListener::SCRIPT_TAG,
result->abstract_code(), *result, name));
} }
PROFILE(isolate, CodeCreateEvent(CodeEventListener::SCRIPT_TAG,
result->abstract_code(), *result, name));
} }
if (isolate->NeedsSourcePositionsForProfiling()) { if (isolate->NeedsSourcePositionsForProfiling()) {
......
...@@ -172,6 +172,7 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj, ...@@ -172,6 +172,7 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
} else if (obj->IsScript()) { } else if (obj->IsScript()) {
LOG(isolate_, ScriptEvent(Logger::ScriptEventType::kDeserialize, LOG(isolate_, ScriptEvent(Logger::ScriptEventType::kDeserialize,
Script::cast(obj)->id())); Script::cast(obj)->id()));
LOG(isolate_, ScriptDetails(Script::cast(obj)));
} }
if (obj->IsAllocationSite()) { if (obj->IsAllocationSite()) {
......
...@@ -83,6 +83,7 @@ void ObjectDeserializer::CommitPostProcessedObjects() { ...@@ -83,6 +83,7 @@ void ObjectDeserializer::CommitPostProcessedObjects() {
script->set_id(isolate()->heap()->NextScriptId()); script->set_id(isolate()->heap()->NextScriptId());
LOG(isolate(), LOG(isolate(),
ScriptEvent(Logger::ScriptEventType::kDeserialize, script->id())); ScriptEvent(Logger::ScriptEventType::kDeserialize, script->id()));
LOG(isolate(), ScriptDetails(*script));
// Add script to list. // Add script to list.
Handle<Object> list = Handle<Object> list =
FixedArrayOfWeakCells::Add(isolate(), factory->script_list(), script); FixedArrayOfWeakCells::Add(isolate(), factory->script_list(), script);
......
...@@ -66,34 +66,38 @@ code is governed by a BSD-style license that can be found in the LICENSE file. ...@@ -66,34 +66,38 @@ code is governed by a BSD-style license that can be found in the LICENSE file.
} }
.script-size { .script-size {
display: inline-block; display: inline-flex;
background-color: #505050; background-color: #505050;
border-radius: 3px; border-radius: 3px;
padding: 3px; padding: 3px;
margin: 2px; margin: 2px;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
min-width: 2em;
text-decoration: none; text-decoration: none;
color: white; color: white;
} }
.script-size.eval { .script-size.eval {
background-color: #ee6300fc; background-color: #ee6300fc;
} }
.script-size.background { .script-size.streaming {
background-color: #008aff; background-color: #008aff;
} }
.script-size.deserialized {
background-color: #1fad00fc;
}
.script-id { .script-details {
display: inline; padding-right: 5px;
padding-right: 2px; margin-right: 4px;
margin-right: 2px;
border-right: 1px grey dotted;
} }
.script-url { /* all but the last need a border */
display: inline; .script-details:nth-last-child(n+2) {
text-overflow: ellipis; border-right: 1px white solid;
}
.script-details.id {
min-width: 2em;
text-align: right;
} }
</style> </style>
<script src="./splaytree.js" type="text/javascript"></script> <script src="./splaytree.js" type="text/javascript"></script>
...@@ -115,7 +119,6 @@ function $(query) { ...@@ -115,7 +119,6 @@ function $(query) {
return document.querySelector(query); return document.querySelector(query);
} }
function loadFile() { function loadFile() {
let files = $('#uploadInput').files; let files = $('#uploadInput').files;
...@@ -202,11 +205,10 @@ function renderParseResults(parseProcessor) { ...@@ -202,11 +205,10 @@ function renderParseResults(parseProcessor) {
appendGraph(target.script, target, start, end); appendGraph(target.script, target, start, end);
observer.unobserve(entry.target); observer.unobserve(entry.target);
}); });
}, {}); }, {rootMargin: '400px'});
document.querySelectorAll('.script').forEach(div => io.observe(div)); document.querySelectorAll('.script').forEach(div => io.observe(div));
} }
const kTimeFactor = 10; const kTimeFactor = 10;
const kHeight = 20; const kHeight = 20;
const kFunktionTopOffset = 50; const kFunktionTopOffset = 50;
...@@ -219,11 +221,11 @@ function renderScript(result, script, start, end) { ...@@ -219,11 +221,11 @@ function renderScript(result, script, start, end) {
scriptDiv.script = script; scriptDiv.script = script;
let scriptTitle = h3(); let scriptTitle = h3();
if (script.file) scriptTitle.appendChild(a(script.file, script.file)); let anchor = a("", 'Script #' + script.id);
let anchor = a("", ' id=' + script.id);
anchor.name = "script"+script.id anchor.name = "script"+script.id
scriptTitle.appendChild(anchor); scriptTitle.appendChild(anchor);
scriptDiv.appendChild(scriptTitle); scriptDiv.appendChild(scriptTitle);
if (script.file) scriptTitle.appendChild(a(script.file, script.file));
let summary = createNode('pre', 'script-details'); let summary = createNode('pre', 'script-details');
summary.appendChild(text(script.summary)); summary.appendChild(text(script.summary));
scriptDiv.appendChild(summary); scriptDiv.appendChild(summary);
...@@ -235,23 +237,28 @@ function renderScriptSizes(parseProcessor) { ...@@ -235,23 +237,28 @@ function renderScriptSizes(parseProcessor) {
parseProcessor.scripts.forEach( parseProcessor.scripts.forEach(
script => { script => {
let scriptDiv = a('#script'+script.id, '', 'script-size'); let scriptDiv = a('#script'+script.id, '', 'script-size');
let scriptId = div('script-id'); let scriptId = div('script-details');
scriptId.classList.add('id');
scriptId.innerText = script.id; scriptId.innerText = script.id;
scriptDiv.appendChild(scriptId); scriptDiv.appendChild(scriptId);
let scriptUrl = div('script-url'); let scriptSize = div('script-details');
scriptSize.innerText = BYTES(script.bytesTotal);
scriptDiv.appendChild(scriptSize);
let scriptUrl = div('script-details');
if (script.isEval) { if (script.isEval) {
scriptUrl.innerText = "eval"; scriptUrl.innerText = "eval";
scriptDiv.classList.add('eval'); scriptDiv.classList.add('eval');
} else { } else {
scriptUrl.innerText = script.file.split("/").pop(); scriptUrl.innerText = script.file.split("/").pop();
} }
if (script.isBackgroundCompiled ) { if (script.isStreamingCompiled ) {
scriptDiv.classList.add('background'); scriptDiv.classList.add('streaming');
} else if (script.deserializationTimestamp > 0) {
scriptDiv.classList.add('deserialized');
} }
scriptDiv.appendChild(scriptUrl); scriptDiv.appendChild(scriptUrl);
scriptDiv.style.width = script.bytesTotal * 0.001; scriptDiv.style.width = script.bytesTotal * 0.001;
scriptsDiv.appendChild(scriptDiv); scriptsDiv.appendChild(scriptDiv);
}); });
} }
...@@ -259,16 +266,24 @@ const kMaxTime = 120 * kSecondsToMillis; ...@@ -259,16 +266,24 @@ const kMaxTime = 120 * kSecondsToMillis;
// Resolution of the graphs // Resolution of the graphs
const kTimeIncrement = 1; const kTimeIncrement = 1;
const kSelectionTimespan = 2; const kSelectionTimespan = 2;
// TODO(cbruni): support compilation cache hit.
const series = [ const series = [
['firstParseEvent', 'Any Parse Event'], ['firstParseEvent', 'Any Parse', 'area'],
['execution', 'First Execution'], ['execution', '1st Exec', 'area'],
['firstCompileEvent', 'Any Compile', 'area'],
['compile', 'Eager Compile'],
['lazyCompile', 'Lazy Compile'],
['parse', 'Parsing'], ['parse', 'Parsing'],
['preparse', 'Preparsing'], ['preparse', 'Preparse'],
['resolution', 'Preparsing with Var. Resolution'], ['resolution', 'Preparse with Var. Resolution'],
['lazyCompile', 'Lazy Compilation'], ['deserialization', 'Deserialization'],
['compile', 'Eager Compilation'], ['optimization', 'Optimize'],
]; ];
const metricNames = series.map(each => each[0]); const metricNames = series.map(each => each[0]);
// Display cumulative values (useuful for bytes).
const kCumulative = true;
// Include durations in the graphs.
const kUseDuration = false;
function appendGraph(script, parentNode, start, end) { function appendGraph(script, parentNode, start, end) {
...@@ -279,28 +294,32 @@ function appendGraph(script, parentNode, start, end) { ...@@ -279,28 +294,32 @@ function appendGraph(script, parentNode, start, end) {
let data = new google.visualization.DataTable(); let data = new google.visualization.DataTable();
data.addColumn('number', 'Duration'); data.addColumn('number', 'Duration');
// The series are interleave bytes processed, time spent and thus have two // The series are interleave bytes processed, time spent and thus have two
// different vAxes. // different vAxes.
let seriesOptions = []; let seriesOptions = [];
let colors = ['#4D4D4D', '#5DA5DA', '#FAA43A', '#60BD68', '#F17CB0', let colors = ['#4D4D4D', '#fff700', '#5DA5DA', '#FAA43A', '#60BD68',
'#B2912F', '#B276B2', '#DECF3F', '#F15854']; '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#F15854'];
series.forEach(each => { series.forEach(([metric, description, type]) => {
let description = each[1];
let color = colors.shift(); let color = colors.shift();
// Add the bytes column. // Add the bytes column.
data.addColumn('number', description + ' Bytes'); data.addColumn('number', description);
seriesOptions.push({targetAxisIndex: 0, color: color}); let options = {targetAxisIndex: 0, color: color};
if (type == 'area') options.type = 'area';
seriesOptions.push(options)
// Add the time column. // Add the time column.
data.addColumn('number', description + ' Duration'); if (kUseDuration) {
seriesOptions.push({targetAxisIndex: 1, color: color, lineDashStyle: [3, 2]}); data.addColumn('number', description + ' Duration');
seriesOptions.push(
{targetAxisIndex: 1, color: color, lineDashStyle: [3, 2]});
}
}); });
// The first entry contains the total.
seriesOptions[0].type = 'area';
const maxTime = Math.min(kMaxTime, end); const maxTime = Math.min(kMaxTime, end);
console.time('metrics'); console.time('metrics');
let metricValues = let metricValues =
script.getAccumulatedTimeMetrics(metricNames , 0, maxTime, kTimeIncrement); script.getAccumulatedTimeMetrics(metricNames , 0, maxTime, kTimeIncrement,
kCumulative, kUseDuration);
console.timeEnd('metrics'); console.timeEnd('metrics');
// Make sure that the series added to the graph matches the returned values.
console.assert(metricValues[0].length == seriesOptions.length + 1); console.assert(metricValues[0].length == seriesOptions.length + 1);
data.addRows(metricValues); data.addRows(metricValues);
...@@ -318,7 +337,7 @@ function appendGraph(script, parentNode, start, end) { ...@@ -318,7 +337,7 @@ function appendGraph(script, parentNode, start, end) {
}, },
height: 400, height: 400,
width: 1000, width: 1000,
chartArea: {left: '5%', top: '15%', width: "85%", height: "75%"}, chartArea: {left: 70, top: 0, right: 160, height: "90%"},
// The first series should be a area chart (total bytes touched), // The first series should be a area chart (total bytes touched),
series: seriesOptions, series: seriesOptions,
// everthing else is a line. // everthing else is a line.
...@@ -332,28 +351,29 @@ function appendGraph(script, parentNode, start, end) { ...@@ -332,28 +351,29 @@ function appendGraph(script, parentNode, start, end) {
google.visualization.events.addListener(chart, 'select', google.visualization.events.addListener(chart, 'select',
() => selectGraphPointHandler(chart, data, script, parentNode)); () => selectGraphPointHandler(chart, data, script, parentNode));
chart.draw(data, options); chart.draw(data, options);
// Add event listeners // Add event listeners
console.timeEnd(timerLabel); console.timeEnd(timerLabel);
} }
function selectGraphPointHandler(chart, data, script, parentNode) { function selectGraphPointHandler(chart, data, script, parentNode) {
let selection = chart.getSelection(); let selection = chart.getSelection();
if (selection.length <= 0) return; if (selection.length <= 0) return;
// Display a list of funktions with events at the given time. // Display a list of funktions with events at the given time.
let {row, column} = selection[0]; let {row, column} = selection[0];
if (row === null|| column === null) return; if (row === null|| column === null) return;
let name = series[((column-1)/2) | 0][0]; const kEntrySize = kUseDuration ? 2 : 1;
let [metric, description] = series[((column-1)/ kEntrySize) | 0];
let time = data.getValue(row, 0); let time = data.getValue(row, 0);
let funktions = script.getFunktionsAtTime( let funktions = script.getFunktionsAtTime(
time * kSecondsToMillis, kSelectionTimespan, name); time * kSecondsToMillis, kSelectionTimespan, metric);
let oldList = parentNode.querySelector('.funktion-list'); let oldList = parentNode.querySelector('.funktion-list');
parentNode.replaceChild(createFunktionList(name, time, funktions), oldList); parentNode.replaceChild(
createFunktionList(metric, description, time, funktions), oldList);
} }
function createFunktionList(metric, time, funktions) { function createFunktionList(metric, description, time, funktions) {
let container = createNode('div', 'funktion-list'); let container = createNode('div', 'funktion-list');
container.appendChild(h3('Changes of ' + metric + ' at ' + container.appendChild(h3('Changes of "' + description + '" at ' +
time + 's: ' + funktions.length)); time + 's: ' + funktions.length));
let listNode = createNode('ul'); let listNode = createNode('ul');
funktions.forEach(funktion => { funktions.forEach(funktion => {
......
This diff is collapsed.
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