v8/test/cctest/test-ignition-statistics-extension.cc
Seth Brenith 82b673bcbc Fix counting ignition dispatches
The flag --trace-ignition-dispatches has been broken for a long time,
since it was not designed to work with bytecode handlers that are
generated ahead of time by mksnapshot. This splits the existing
--trace-ignition-dispatches logic into two separate parts:

1. A gn argument which instructs mksnapshot to include dispatch counting
   in the bytecode handlers, and ensures that the Interpreter allocates
   the array of counters, and
2. A runtime flag which enables the ignition-statistics extension which
   implements the JS-accessible function getIgnitionDispatchCounters().

Change-Id: I89323425697f5641451f67b9ddcc0303b8ca209f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2937564
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#74992}
2021-06-07 18:47:24 +00:00

130 lines
4.9 KiB
C++

// Copyright 2021 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/interpreter/bytecodes.h"
#include "src/interpreter/interpreter.h"
#include "test/cctest/test-api.h"
namespace v8 {
namespace internal {
class IgnitionStatisticsTester {
public:
explicit IgnitionStatisticsTester(Isolate* isolate) : isolate_(isolate) {
// In case the build specified v8_enable_ignition_dispatch_counting, the
// interpreter already has a dispatch counters table and the bytecode
// handlers will update it. To avoid crashes, we keep that array alive here.
// This file doesn't test the results in the real array since there is no
// automated testing on configurations with
// v8_enable_ignition_dispatch_counting.
original_bytecode_dispatch_counters_table_ =
std::move(isolate->interpreter()->bytecode_dispatch_counters_table_);
// This sets up the counters array, but does not rewrite the bytecode
// handlers to update it.
isolate->interpreter()->InitDispatchCounters();
}
void SetDispatchCounter(interpreter::Bytecode from, interpreter::Bytecode to,
uintptr_t value) const {
int from_index = interpreter::Bytecodes::ToByte(from);
int to_index = interpreter::Bytecodes::ToByte(to);
isolate_->interpreter()->bytecode_dispatch_counters_table_
[from_index * interpreter::Bytecodes::kBytecodeCount + to_index] =
value;
CHECK_EQ(isolate_->interpreter()->GetDispatchCounter(from, to), value);
}
private:
Isolate* isolate_;
std::unique_ptr<uintptr_t[]> original_bytecode_dispatch_counters_table_;
};
TEST(IgnitionStatisticsExtension) {
FLAG_expose_ignition_statistics = true;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
IgnitionStatisticsTester tester(CcTest::i_isolate());
Local<Value> typeof_result =
CompileRun("typeof getIgnitionDispatchCounters === 'function'");
CHECK(typeof_result->BooleanValue(isolate));
// Get the list of all bytecode names into a JavaScript array.
#define BYTECODE_NAME_WITH_COMMA(Name, ...) "'" #Name "', "
const char* kBytecodeNames =
"var bytecodeNames = [" BYTECODE_LIST(BYTECODE_NAME_WITH_COMMA) "];";
#undef BYTECODE_NAME_WITH_COMMA
CompileRun(kBytecodeNames);
// Check that the dispatch counters object is a non-empty object of objects
// where each property name is a bytecode name, in order, and each inner
// object is empty.
const char* kEmptyTest = R"(
var emptyCounters = getIgnitionDispatchCounters();
function isEmptyDispatchCounters(counters) {
if (typeof counters !== "object") return false;
var i = 0;
for (var sourceBytecode in counters) {
if (sourceBytecode !== bytecodeNames[i]) return false;
var countersRow = counters[sourceBytecode];
if (typeof countersRow !== "object") return false;
for (var counter in countersRow) {
return false;
}
++i;
}
return true;
}
isEmptyDispatchCounters(emptyCounters);)";
Local<Value> empty_result = CompileRun(kEmptyTest);
CHECK(empty_result->BooleanValue(isolate));
// Simulate running some code, which would update the counters.
tester.SetDispatchCounter(interpreter::Bytecode::kLdar,
interpreter::Bytecode::kStar, 3);
tester.SetDispatchCounter(interpreter::Bytecode::kLdar,
interpreter::Bytecode::kLdar, 4);
tester.SetDispatchCounter(interpreter::Bytecode::kMov,
interpreter::Bytecode::kLdar, 5);
// Check that the dispatch counters object is a non-empty object of objects
// where each property name is a bytecode name, in order, and the inner
// objects reflect the new state.
const char* kNonEmptyTest = R"(
var nonEmptyCounters = getIgnitionDispatchCounters();
function isUpdatedDispatchCounters(counters) {
if (typeof counters !== "object") return false;
var i = 0;
for (var sourceBytecode in counters) {
if (sourceBytecode !== bytecodeNames[i]) return false;
var countersRow = counters[sourceBytecode];
if (typeof countersRow !== "object") return false;
switch (sourceBytecode) {
case "Ldar":
if (JSON.stringify(countersRow) !== '{"Ldar":4,"Star":3}')
return false;
break;
case "Mov":
if (JSON.stringify(countersRow) !== '{"Ldar":5}')
return false;
break;
default:
for (var counter in countersRow) {
return false;
}
}
++i;
}
return true;
}
isUpdatedDispatchCounters(nonEmptyCounters);)";
Local<Value> non_empty_result = CompileRun(kNonEmptyTest);
CHECK(non_empty_result->BooleanValue(isolate));
}
} // namespace internal
} // namespace v8