From 905becd13b8696e126255decf130fdb9e1d9aa30 Mon Sep 17 00:00:00 2001 From: ssanfilippo Date: Wed, 27 Apr 2016 04:10:41 -0700 Subject: [PATCH] [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} --- BUILD.gn | 2 + src/assembler.cc | 2 +- src/bootstrapper.cc | 32 ++++++---- src/bootstrapper.h | 1 + src/d8.cc | 28 +++++++-- src/d8.h | 1 + .../ignition-statistics-extension.cc | 36 +++++++++++ .../ignition-statistics-extension.h | 31 ++++++++++ src/interpreter/interpreter.cc | 62 +++++++++++-------- src/interpreter/interpreter.h | 11 ++-- src/v8.gyp | 2 + .../ignition/ignition-statistics-extension.js | 62 +++++++++++++++++++ 12 files changed, 221 insertions(+), 49 deletions(-) create mode 100644 src/extensions/ignition-statistics-extension.cc create mode 100644 src/extensions/ignition-statistics-extension.h create mode 100644 test/mjsunit/ignition/ignition-statistics-extension.js diff --git a/BUILD.gn b/BUILD.gn index 46afb06e08..fc96fb6048 100644 --- a/BUILD.gn +++ b/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", diff --git a/src/assembler.cc b/src/assembler.cc index de3832b33d..412785df11 100644 --- a/src/assembler.cc +++ b/src/assembler.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) diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index bad8c46617..37639ece53 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -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 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); } diff --git a/src/bootstrapper.h b/src/bootstrapper.h index d1bf201139..5563eea043 100644 --- a/src/bootstrapper.h +++ b/src/bootstrapper.h @@ -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); }; diff --git a/src/d8.cc b/src/d8.cc index 169bc26393..287c6a0f7a 100644 --- a/src/d8.cc +++ b/src/d8.cc @@ -19,6 +19,7 @@ #ifndef V8_SHARED #include +#include #include #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::New(isolate); + Context::Scope context_scope(context); + + Local dispatch_counters = reinterpret_cast(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(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 diff --git a/src/d8.h b/src/d8.h index 321d9c1770..e51e8ee13f 100644 --- a/src/d8.h +++ b/src/d8.h @@ -461,6 +461,7 @@ class Shell : public i::AllStatic { static i::List workers_; static i::List externalized_shared_contents_; + static void WriteIgnitionDispatchCountersFile(v8::Isolate* isolate); static Counter* GetCounter(const char* name, bool is_histogram); static Local Stringify(Isolate* isolate, Local value); #endif // !V8_SHARED diff --git a/src/extensions/ignition-statistics-extension.cc b/src/extensions/ignition-statistics-extension.cc new file mode 100644 index 0000000000..b22c599cb4 --- /dev/null +++ b/src/extensions/ignition-statistics-extension.cc @@ -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 +IgnitionStatisticsExtension::GetNativeFunctionTemplate( + v8::Isolate* isolate, v8::Local 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& args) { + DCHECK_EQ(args.Length(), 0); + DCHECK(FLAG_trace_ignition_dispatches); + args.GetReturnValue().Set(reinterpret_cast(args.GetIsolate()) + ->interpreter() + ->GetDispatchCountersObject()); +} + +} // namespace internal +} // namespace v8 diff --git a/src/extensions/ignition-statistics-extension.h b/src/extensions/ignition-statistics-extension.h new file mode 100644 index 0000000000..fee55f6128 --- /dev/null +++ b/src/extensions/ignition-statistics-extension.h @@ -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 GetNativeFunctionTemplate( + v8::Isolate* isolate, v8::Local name) override; + + static void GetIgnitionDispatchCounters( + const v8::FunctionCallbackInfo& args); + + private: + static const char* const kSource; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_EXTENSIONS_IGNITION_STATISTICS_EXTENSION_H_ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 835f80a1b7..b515c7bb97 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -38,9 +38,9 @@ void Interpreter::Initialize() { if (FLAG_trace_ignition_dispatches) { static const int kBytecodeCount = static_cast(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(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 Interpreter::GetDispatchCountersObject() { + v8::Isolate* isolate = reinterpret_cast(isolate_); + Local context = isolate->GetCurrentContext(); + + Local 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 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 to_name_object = + v8::String::NewFromUtf8(isolate, to_name.c_str(), + NewStringType::kNormal) + .ToLocalChecked(); + Local 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 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 diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index fe9fcdea7b..26efd16287 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -53,14 +53,14 @@ class Interpreter { void TraceCodegen(Handle code); const char* LookupNameOfBytecodeHandler(Code* code); - void WriteDispatchCounters(); + Local GetDispatchCountersObject(); Address dispatch_table_address() { return reinterpret_cast
(&dispatch_table_[0]); } - uintptr_t* bytecode_dispatch_count_table() { - return bytecode_dispatch_count_table_.get(); + Address bytecode_dispatch_counters_table() { + return reinterpret_cast
(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(Bytecode::kLast) + 1; Isolate* isolate_; Address dispatch_table_[kDispatchTableSize]; - v8::base::SmartArrayPointer bytecode_dispatch_count_table_; + v8::base::SmartArrayPointer bytecode_dispatch_counters_table_; DISALLOW_COPY_AND_ASSIGN(Interpreter); }; diff --git a/src/v8.gyp b/src/v8.gyp index 76796f032e..810ad979f9 100644 --- a/src/v8.gyp +++ b/src/v8.gyp @@ -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', diff --git a/test/mjsunit/ignition/ignition-statistics-extension.js b/test/mjsunit/ignition/ignition-statistics-extension.js new file mode 100644 index 0000000000..43d05c94a3 --- /dev/null +++ b/test/mjsunit/ignition/ignition-statistics-extension.js @@ -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); + } + }); +});