[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:
parent
612985b298
commit
905becd13b
2
BUILD.gn
2
BUILD.gn
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
28
src/d8.cc
28
src/d8.cc
@ -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
|
||||
|
1
src/d8.h
1
src/d8.h
@ -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
|
||||
|
36
src/extensions/ignition-statistics-extension.cc
Normal file
36
src/extensions/ignition-statistics-extension.cc
Normal 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
|
31
src/extensions/ignition-statistics-extension.h
Normal file
31
src/extensions/ignition-statistics-extension.h
Normal 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_
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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',
|
||||
|
62
test/mjsunit/ignition/ignition-statistics-extension.js
Normal file
62
test/mjsunit/ignition/ignition-statistics-extension.js
Normal 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);
|
||||
}
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user