[Interpreter] Add Ignition statistics JavaScript extension.

This commit introduces IgnitionStatisticsExtension, which provides
methods for accessing Ignition statistics and counters from JavaScript.

The extension is registered when FLAG_ignition and
FLAG_trace_ignition_dispatches are both enabled.

For the moment, the only exposed function is
getIgnitionDispatchCounters(), which allows to retrieve Ignition
dispatch counters as a JavaScript object.

BUG=v8:4899
LOG=N

Review URL: https://codereview.chromium.org/1899133004

Cr-Commit-Position: refs/heads/master@{#35816}
This commit is contained in:
ssanfilippo 2016-04-27 04:10:41 -07:00 committed by Commit bot
parent 612985b298
commit 905becd13b
12 changed files with 221 additions and 49 deletions

View File

@ -1065,6 +1065,8 @@ source_set("v8_base") {
"src/extensions/free-buffer-extension.h",
"src/extensions/gc-extension.cc",
"src/extensions/gc-extension.h",
"src/extensions/ignition-statistics-extension.cc",
"src/extensions/ignition-statistics-extension.h",
"src/extensions/statistics-extension.cc",
"src/extensions/statistics-extension.h",
"src/extensions/trigger-failure-extension.cc",

View File

@ -1075,7 +1075,7 @@ ExternalReference ExternalReference::interpreter_dispatch_table_address(
ExternalReference ExternalReference::interpreter_dispatch_counters(
Isolate* isolate) {
return ExternalReference(
isolate->interpreter()->bytecode_dispatch_count_table());
isolate->interpreter()->bytecode_dispatch_counters_table());
}
ExternalReference::ExternalReference(StatsCounter* counter)

View File

@ -10,6 +10,7 @@
#include "src/extensions/externalize-string-extension.h"
#include "src/extensions/free-buffer-extension.h"
#include "src/extensions/gc-extension.h"
#include "src/extensions/ignition-statistics-extension.h"
#include "src/extensions/statistics-extension.h"
#include "src/extensions/trigger-failure-extension.h"
#include "src/heap/heap.h"
@ -73,7 +74,7 @@ v8::Extension* Bootstrapper::gc_extension_ = NULL;
v8::Extension* Bootstrapper::externalize_string_extension_ = NULL;
v8::Extension* Bootstrapper::statistics_extension_ = NULL;
v8::Extension* Bootstrapper::trigger_failure_extension_ = NULL;
v8::Extension* Bootstrapper::ignition_statistics_extension_ = NULL;
void Bootstrapper::InitializeOncePerProcess() {
free_buffer_extension_ = new FreeBufferExtension;
@ -86,6 +87,8 @@ void Bootstrapper::InitializeOncePerProcess() {
v8::RegisterExtension(statistics_extension_);
trigger_failure_extension_ = new TriggerFailureExtension;
v8::RegisterExtension(trigger_failure_extension_);
ignition_statistics_extension_ = new IgnitionStatisticsExtension;
v8::RegisterExtension(ignition_statistics_extension_);
}
@ -100,6 +103,8 @@ void Bootstrapper::TearDownExtensions() {
statistics_extension_ = NULL;
delete trigger_failure_extension_;
trigger_failure_extension_ = NULL;
delete ignition_statistics_extension_;
ignition_statistics_extension_ = NULL;
}
@ -3209,17 +3214,20 @@ bool Genesis::InstallExtensions(Handle<Context> native_context,
Isolate* isolate = native_context->GetIsolate();
ExtensionStates extension_states; // All extensions have state UNVISITED.
return InstallAutoExtensions(isolate, &extension_states) &&
(!FLAG_expose_free_buffer ||
InstallExtension(isolate, "v8/free-buffer", &extension_states)) &&
(!FLAG_expose_gc ||
InstallExtension(isolate, "v8/gc", &extension_states)) &&
(!FLAG_expose_externalize_string ||
InstallExtension(isolate, "v8/externalize", &extension_states)) &&
(!FLAG_track_gc_object_stats ||
InstallExtension(isolate, "v8/statistics", &extension_states)) &&
(!FLAG_expose_trigger_failure ||
InstallExtension(isolate, "v8/trigger-failure", &extension_states)) &&
InstallRequestedExtensions(isolate, extensions, &extension_states);
(!FLAG_expose_free_buffer ||
InstallExtension(isolate, "v8/free-buffer", &extension_states)) &&
(!FLAG_expose_gc ||
InstallExtension(isolate, "v8/gc", &extension_states)) &&
(!FLAG_expose_externalize_string ||
InstallExtension(isolate, "v8/externalize", &extension_states)) &&
(!FLAG_track_gc_object_stats ||
InstallExtension(isolate, "v8/statistics", &extension_states)) &&
(!FLAG_expose_trigger_failure ||
InstallExtension(isolate, "v8/trigger-failure", &extension_states)) &&
(!(FLAG_ignition && FLAG_trace_ignition_dispatches) ||
InstallExtension(isolate, "v8/ignition-statistics",
&extension_states)) &&
InstallRequestedExtensions(isolate, extensions, &extension_states);
}

View File

@ -136,6 +136,7 @@ class Bootstrapper final {
static v8::Extension* externalize_string_extension_;
static v8::Extension* statistics_extension_;
static v8::Extension* trigger_failure_extension_;
static v8::Extension* ignition_statistics_extension_;
DISALLOW_COPY_AND_ASSIGN(Bootstrapper);
};

View File

@ -19,6 +19,7 @@
#ifndef V8_SHARED
#include <algorithm>
#include <fstream>
#include <vector>
#endif // !V8_SHARED
@ -1276,6 +1277,21 @@ struct CounterAndKey {
inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) {
return strcmp(lhs.key, rhs.key) < 0;
}
void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
->interpreter()
->GetDispatchCountersObject();
std::ofstream dispatch_counters_stream(
i::FLAG_trace_ignition_dispatches_output_file);
dispatch_counters_stream << *String::Utf8Value(
JSON::Stringify(context, dispatch_counters).ToLocalChecked());
}
#endif // !V8_SHARED
@ -1314,12 +1330,6 @@ void Shell::OnExit(v8::Isolate* isolate) {
delete [] counters;
}
if (i::FLAG_trace_ignition_dispatches) {
reinterpret_cast<i::Isolate*>(isolate)
->interpreter()
->WriteDispatchCounters();
}
delete counters_file_;
delete counter_map_;
#endif // !V8_SHARED
@ -2484,6 +2494,12 @@ int Shell::Main(int argc, char* argv[]) {
RunShell(isolate);
}
#ifndef V8_SHARED
if (i::FLAG_ignition && i::FLAG_trace_ignition_dispatches) {
WriteIgnitionDispatchCountersFile(isolate);
}
#endif
// Shut down contexts and collect garbage.
evaluation_context_.Reset();
#ifndef V8_SHARED

View File

@ -461,6 +461,7 @@ class Shell : public i::AllStatic {
static i::List<Worker*> workers_;
static i::List<SharedArrayBuffer::Contents> externalized_shared_contents_;
static void WriteIgnitionDispatchCountersFile(v8::Isolate* isolate);
static Counter* GetCounter(const char* name, bool is_histogram);
static Local<String> Stringify(Isolate* isolate, Local<Value> value);
#endif // !V8_SHARED

View File

@ -0,0 +1,36 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/extensions/ignition-statistics-extension.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate.h"
namespace v8 {
namespace internal {
v8::Local<v8::FunctionTemplate>
IgnitionStatisticsExtension::GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Local<v8::String> name) {
DCHECK_EQ(strcmp(*v8::String::Utf8Value(name), "getIgnitionDispatchCounters"),
0);
return v8::FunctionTemplate::New(
isolate, IgnitionStatisticsExtension::GetIgnitionDispatchCounters);
}
const char* const IgnitionStatisticsExtension::kSource =
"native function getIgnitionDispatchCounters();";
void IgnitionStatisticsExtension::GetIgnitionDispatchCounters(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DCHECK_EQ(args.Length(), 0);
DCHECK(FLAG_trace_ignition_dispatches);
args.GetReturnValue().Set(reinterpret_cast<Isolate*>(args.GetIsolate())
->interpreter()
->GetDispatchCountersObject());
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,31 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_EXTENSIONS_IGNITION_STATISTICS_EXTENSION_H_
#define V8_EXTENSIONS_IGNITION_STATISTICS_EXTENSION_H_
#include "include/v8.h"
namespace v8 {
namespace internal {
class IgnitionStatisticsExtension : public v8::Extension {
public:
IgnitionStatisticsExtension()
: v8::Extension("v8/ignition-statistics", kSource) {}
v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Local<v8::String> name) override;
static void GetIgnitionDispatchCounters(
const v8::FunctionCallbackInfo<v8::Value>& args);
private:
static const char* const kSource;
};
} // namespace internal
} // namespace v8
#endif // V8_EXTENSIONS_IGNITION_STATISTICS_EXTENSION_H_

View File

@ -38,9 +38,9 @@ void Interpreter::Initialize() {
if (FLAG_trace_ignition_dispatches) {
static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
bytecode_dispatch_count_table_.Reset(
bytecode_dispatch_counters_table_.Reset(
new uintptr_t[kBytecodeCount * kBytecodeCount]);
memset(bytecode_dispatch_count_table_.get(), 0,
memset(bytecode_dispatch_counters_table_.get(), 0,
sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
}
@ -201,9 +201,18 @@ const char* Interpreter::LookupNameOfBytecodeHandler(Code* code) {
return nullptr;
}
void Interpreter::WriteDispatchCounters() {
std::ofstream stream(FLAG_trace_ignition_dispatches_output_file);
static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
uintptr_t Interpreter::GetDispatchCounter(Bytecode from, Bytecode to) const {
int from_index = Bytecodes::ToByte(from);
int to_index = Bytecodes::ToByte(to);
return bytecode_dispatch_counters_table_[from_index * kNumberOfBytecodes +
to_index];
}
Local<v8::Object> Interpreter::GetDispatchCountersObject() {
v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(isolate_);
Local<v8::Context> context = isolate->GetCurrentContext();
Local<v8::Object> counters_map = v8::Object::New(isolate);
// Output is a JSON-encoded object of objects.
//
@ -216,35 +225,36 @@ void Interpreter::WriteDispatchCounters() {
// object is always present, even if the value is empty because all counters
// for that source are zero.
stream << '{';
for (int from_index = 0; from_index < kBytecodeCount; ++from_index) {
if (from_index > 0) stream << ",\n ";
for (int from_index = 0; from_index < kNumberOfBytecodes; ++from_index) {
Bytecode from_bytecode = Bytecodes::FromByte(from_index);
stream << "\"" << Bytecodes::ToString(from_bytecode) << "\": {";
Local<v8::Object> counters_row = v8::Object::New(isolate);
for (int to_index = 0; to_index < kNumberOfBytecodes; ++to_index) {
Bytecode to_bytecode = Bytecodes::FromByte(to_index);
uintptr_t counter = GetDispatchCounter(from_bytecode, to_bytecode);
bool emitted_first = false;
for (int to_index = 0; to_index < kBytecodeCount; ++to_index) {
uintptr_t counter =
bytecode_dispatch_count_table_[from_index * kBytecodeCount +
to_index];
if (counter > 0) {
if (emitted_first) {
stream << ", ";
} else {
emitted_first = true;
}
Bytecode to_bytecode = Bytecodes::FromByte(to_index);
stream << '"' << Bytecodes::ToString(to_bytecode) << "\": " << counter;
std::string to_name = Bytecodes::ToString(to_bytecode);
Local<v8::String> to_name_object =
v8::String::NewFromUtf8(isolate, to_name.c_str(),
NewStringType::kNormal)
.ToLocalChecked();
Local<v8::Number> counter_object = v8::Number::New(isolate, counter);
CHECK(counters_row->Set(context, to_name_object, counter_object)
.IsJust());
}
}
stream << "}";
std::string from_name = Bytecodes::ToString(from_bytecode);
Local<v8::String> from_name_object =
v8::String::NewFromUtf8(isolate, from_name.c_str(),
NewStringType::kNormal)
.ToLocalChecked();
CHECK(counters_map->Set(context, from_name_object, counters_row).IsJust());
}
stream << '}';
return counters_map;
}
// LdaZero

View File

@ -53,14 +53,14 @@ class Interpreter {
void TraceCodegen(Handle<Code> code);
const char* LookupNameOfBytecodeHandler(Code* code);
void WriteDispatchCounters();
Local<v8::Object> GetDispatchCountersObject();
Address dispatch_table_address() {
return reinterpret_cast<Address>(&dispatch_table_[0]);
}
uintptr_t* bytecode_dispatch_count_table() {
return bytecode_dispatch_count_table_.get();
Address bytecode_dispatch_counters_table() {
return reinterpret_cast<Address>(bytecode_dispatch_counters_table_.get());
}
private:
@ -143,6 +143,8 @@ class Interpreter {
void DoStoreLookupSlot(LanguageMode language_mode,
InterpreterAssembler* assembler);
uintptr_t GetDispatchCounter(Bytecode from, Bytecode to) const;
// Get dispatch table index of bytecode.
static size_t GetDispatchTableIndex(Bytecode bytecode,
OperandScale operand_scale);
@ -151,10 +153,11 @@ class Interpreter {
static const int kNumberOfWideVariants = 3;
static const int kDispatchTableSize = kNumberOfWideVariants * (kMaxUInt8 + 1);
static const int kNumberOfBytecodes = static_cast<int>(Bytecode::kLast) + 1;
Isolate* isolate_;
Address dispatch_table_[kDispatchTableSize];
v8::base::SmartArrayPointer<uintptr_t> bytecode_dispatch_count_table_;
v8::base::SmartArrayPointer<uintptr_t> bytecode_dispatch_counters_table_;
DISALLOW_COPY_AND_ASSIGN(Interpreter);
};

View File

@ -775,6 +775,8 @@
'extensions/free-buffer-extension.h',
'extensions/gc-extension.cc',
'extensions/gc-extension.h',
'extensions/ignition-statistics-extension.cc',
'extensions/ignition-statistics-extension.h',
'extensions/statistics-extension.cc',
'extensions/statistics-extension.h',
'extensions/trigger-failure-extension.cc',

View File

@ -0,0 +1,62 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --ignition --trace-ignition-dispatches
assertEquals(typeof getIgnitionDispatchCounters, "function");
var old_dispatch_counters = getIgnitionDispatchCounters();
// Check that old_dispatch_counters is a non-empty object of objects, such that
// the value of each property in the inner objects is a number.
assertEquals(typeof old_dispatch_counters, "object");
assertTrue(Object.getOwnPropertyNames(old_dispatch_counters).length > 0);
for (var source_bytecode in old_dispatch_counters) {
var counters_row = old_dispatch_counters[source_bytecode];
assertEquals(typeof counters_row, "object");
for (var counter in counters_row) {
assertEquals(typeof counters_row[counter], "number");
}
}
// Do something
function f(x) { return x*x; }
f(42);
var new_dispatch_counters = getIgnitionDispatchCounters();
var old_source_bytecodes = Object.getOwnPropertyNames(old_dispatch_counters);
var new_source_bytecodes = Object.getOwnPropertyNames(new_dispatch_counters);
var common_source_bytecodes = new_source_bytecodes.filter(function (name) {
return old_source_bytecodes.indexOf(name) > -1;
});
// Check that the keys on the outer objects are the same
assertEquals(common_source_bytecodes, old_source_bytecodes);
assertEquals(common_source_bytecodes, new_source_bytecodes);
common_source_bytecodes.forEach(function (source_bytecode) {
var new_counters_row = new_dispatch_counters[source_bytecode];
var old_counters_row = old_dispatch_counters[source_bytecode];
var old_destination_bytecodes = Object.getOwnPropertyNames(old_counters_row);
var new_destination_bytecodes = Object.getOwnPropertyNames(new_counters_row);
// Check that all the keys in old_ are in new_ too
old_destination_bytecodes.forEach(function (name) {
assertTrue(new_destination_bytecodes.indexOf(name) > -1);
});
// Check that for each source-destination pair, the counter has either
// appeared (was undefined before calling f()), is unchanged, or incremented.
new_destination_bytecodes.forEach(function (destination_bytecode) {
var new_counter = new_counters_row[destination_bytecode];
var old_counter = old_counters_row[destination_bytecode];
assertTrue(typeof new_counter === "number");
if (typeof old_counter === "number") {
assertTrue(new_counter >= old_counter);
}
});
});