[wasm][ukm] Add tests for Wasm events (reland)
Ensure that events are triggered when a module is decoded, compiled, instantiated and tiered-up. This is a reland of Ib5883a338c3756c6f3488fbdd7b6861ecc2ba218. R=clemensb@chromium.org TBR=adamk@chromium.org Bug: chromium:1092417 Change-Id: I803ae3db23a5f71f26e8ec118251eccdfc551353 Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel_ng Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_isolates_rel_ng Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2425056 Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#70113}
This commit is contained in:
parent
24fbcf8847
commit
b77c63c0a6
@ -16,7 +16,7 @@ struct WasmModuleDecoded {
|
||||
bool success = false;
|
||||
size_t module_size_in_bytes = 0;
|
||||
size_t function_count = 0;
|
||||
int64_t wall_clock_time_in_us = 0;
|
||||
int64_t wall_clock_time_in_us = -1;
|
||||
};
|
||||
|
||||
struct WasmModuleCompiled {
|
||||
@ -28,20 +28,20 @@ struct WasmModuleCompiled {
|
||||
bool success = false;
|
||||
size_t code_size_in_bytes = 0;
|
||||
size_t liftoff_bailout_count = 0;
|
||||
int64_t wall_clock_time_in_us = 0;
|
||||
int64_t wall_clock_time_in_us = -1;
|
||||
};
|
||||
|
||||
struct WasmModuleInstantiated {
|
||||
bool async = false;
|
||||
bool success = false;
|
||||
size_t imported_function_count = 0;
|
||||
int64_t wall_clock_time_in_us = 0;
|
||||
int64_t wall_clock_time_in_us = -1;
|
||||
};
|
||||
|
||||
struct WasmModuleTieredUp {
|
||||
bool lazy = false;
|
||||
size_t code_size_in_bytes = 0;
|
||||
int64_t wall_clock_time_in_us = 0;
|
||||
int64_t wall_clock_time_in_us = -1;
|
||||
};
|
||||
|
||||
struct WasmModulesPerIsolate {
|
||||
|
@ -1527,7 +1527,7 @@ class CompilationTimeCallback {
|
||||
false, // deserialized
|
||||
FLAG_wasm_lazy_compilation, // lazy
|
||||
true, // success
|
||||
native_module->generated_code_size(), // code_size_in_bytes
|
||||
native_module->liftoff_code_size(), // code_size_in_bytes
|
||||
native_module->liftoff_bailout_count(), // liftoff_bailout_count
|
||||
duration.InMicroseconds() // wall_clock_time_in_us
|
||||
};
|
||||
@ -1538,9 +1538,9 @@ class CompilationTimeCallback {
|
||||
histogram->AddSample(static_cast<int>(duration.InMicroseconds()));
|
||||
|
||||
v8::metrics::WasmModuleTieredUp event{
|
||||
FLAG_wasm_lazy_compilation, // lazy
|
||||
native_module->generated_code_size(), // code_size_in_bytes
|
||||
duration.InMicroseconds() // wall_clock_time_in_us
|
||||
FLAG_wasm_lazy_compilation, // lazy
|
||||
native_module->turbofan_code_size(), // code_size_in_bytes
|
||||
duration.InMicroseconds() // wall_clock_time_in_us
|
||||
};
|
||||
metrics_recorder_->DelayMainThreadEvent(event, context_id_);
|
||||
}
|
||||
@ -1552,7 +1552,7 @@ class CompilationTimeCallback {
|
||||
false, // deserialized
|
||||
FLAG_wasm_lazy_compilation, // lazy
|
||||
false, // success
|
||||
native_module->generated_code_size(), // code_size_in_bytes
|
||||
native_module->liftoff_code_size(), // code_size_in_bytes
|
||||
native_module->liftoff_bailout_count(), // liftoff_bailout_count
|
||||
duration.InMicroseconds() // wall_clock_time_in_us
|
||||
};
|
||||
@ -1792,6 +1792,7 @@ AsyncCompileJob::AsyncCompileJob(
|
||||
isolate->global_handles()->Create(context->native_context());
|
||||
DCHECK(native_context_->IsNativeContext());
|
||||
context_id_ = isolate->GetOrRegisterRecorderContextId(native_context_);
|
||||
metrics_event_.async = true;
|
||||
}
|
||||
|
||||
void AsyncCompileJob::Start() {
|
||||
@ -1958,7 +1959,7 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) {
|
||||
is_after_deserialization, // deserialized
|
||||
wasm_lazy_compilation_, // lazy
|
||||
!compilation_state->failed(), // success
|
||||
native_module_->generated_code_size(), // code_size_in_bytes
|
||||
native_module_->liftoff_code_size(), // code_size_in_bytes
|
||||
native_module_->liftoff_bailout_count(), // liftoff_bailout_count
|
||||
duration.InMicroseconds() // wall_clock_time_in_us
|
||||
};
|
||||
@ -2463,6 +2464,16 @@ void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(
|
||||
// of the AsyncCompileJob to DecodeFail.
|
||||
job_->background_task_manager_.CancelAndWait();
|
||||
|
||||
// Record event metrics.
|
||||
auto duration = base::TimeTicks::Now() - job_->start_time_;
|
||||
job_->metrics_event_.success = false;
|
||||
job_->metrics_event_.streamed = true;
|
||||
job_->metrics_event_.module_size_in_bytes = job_->wire_bytes_.length();
|
||||
job_->metrics_event_.function_count = num_functions_;
|
||||
job_->metrics_event_.wall_clock_time_in_us = duration.InMicroseconds();
|
||||
job_->isolate_->metrics_recorder()->DelayMainThreadEvent(job_->metrics_event_,
|
||||
job_->context_id_);
|
||||
|
||||
// Check if there is already a CompiledModule, in which case we have to clean
|
||||
// up the CompilationStateImpl as well.
|
||||
if (job_->native_module_) {
|
||||
@ -2674,6 +2685,16 @@ void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
|
||||
job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector());
|
||||
job_->bytes_copy_ = bytes.ReleaseData();
|
||||
|
||||
// Record event metrics.
|
||||
auto duration = base::TimeTicks::Now() - job_->start_time_;
|
||||
job_->metrics_event_.success = true;
|
||||
job_->metrics_event_.streamed = true;
|
||||
job_->metrics_event_.module_size_in_bytes = job_->wire_bytes_.length();
|
||||
job_->metrics_event_.function_count = num_functions_;
|
||||
job_->metrics_event_.wall_clock_time_in_us = duration.InMicroseconds();
|
||||
job_->isolate_->metrics_recorder()->DelayMainThreadEvent(job_->metrics_event_,
|
||||
job_->context_id_);
|
||||
|
||||
if (prefix_cache_hit_) {
|
||||
// Restart as an asynchronous, non-streaming compilation. Most likely
|
||||
// {PrepareAndStartCompile} will get the native module from the cache.
|
||||
|
@ -9,9 +9,9 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "include/v8-metrics.h"
|
||||
#include "src/base/optional.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/logging/metrics.h"
|
||||
#include "src/tasks/cancelable-task.h"
|
||||
#include "src/wasm/compilation-environment.h"
|
||||
#include "src/wasm/wasm-features.h"
|
||||
@ -213,6 +213,7 @@ class AsyncCompileJob {
|
||||
ModuleWireBytes wire_bytes_;
|
||||
Handle<NativeContext> native_context_;
|
||||
v8::metrics::Recorder::ContextId context_id_;
|
||||
v8::metrics::WasmModuleDecoded metrics_event_;
|
||||
const std::shared_ptr<CompilationResultResolver> resolver_;
|
||||
|
||||
Handle<WasmModuleObject> module_object_;
|
||||
|
@ -266,27 +266,6 @@ class WasmSectionIterator {
|
||||
}
|
||||
};
|
||||
|
||||
class AutoSubmitMetrics : public v8::metrics::WasmModuleDecoded {
|
||||
public:
|
||||
AutoSubmitMetrics(std::shared_ptr<metrics::Recorder> recorder,
|
||||
v8::metrics::Recorder::ContextId context_id)
|
||||
: recorder_(std::move(recorder)),
|
||||
context_id_(context_id),
|
||||
timed_scope_(this,
|
||||
&v8::metrics::WasmModuleDecoded::wall_clock_time_in_us) {}
|
||||
|
||||
~AutoSubmitMetrics() {
|
||||
timed_scope_.Stop();
|
||||
recorder_->DelayMainThreadEvent<v8::metrics::WasmModuleDecoded>(
|
||||
*this, context_id_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<metrics::Recorder> recorder_;
|
||||
v8::metrics::Recorder::ContextId context_id_;
|
||||
metrics::TimedScope<v8::metrics::WasmModuleDecoded> timed_scope_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// The main logic for decoding the bytes of a module.
|
||||
@ -301,13 +280,13 @@ class ModuleDecoderImpl : public Decoder {
|
||||
const byte* module_end, ModuleOrigin origin)
|
||||
: Decoder(module_start, module_end),
|
||||
enabled_features_(enabled),
|
||||
module_start_(module_start),
|
||||
module_end_(module_end),
|
||||
origin_(origin) {
|
||||
if (end_ < start_) {
|
||||
error(start_, "end is less than start");
|
||||
end_ = start_;
|
||||
}
|
||||
module_start_ = module_start;
|
||||
module_end_ = module_end;
|
||||
}
|
||||
|
||||
void onFirstError() override {
|
||||
@ -1225,13 +1204,6 @@ class ModuleDecoderImpl : public Decoder {
|
||||
CalculateGlobalOffsets(module_.get());
|
||||
}
|
||||
|
||||
if (metrics_) {
|
||||
metrics_->success = ok() && !intermediate_error_.has_error();
|
||||
metrics_->module_size_in_bytes = end() - start();
|
||||
metrics_->function_count = module_->num_declared_functions;
|
||||
metrics_.reset();
|
||||
}
|
||||
|
||||
ModuleResult result = toResult(std::move(module_));
|
||||
if (verify_functions && result.ok() && intermediate_error_.has_error()) {
|
||||
// Copy error message and location.
|
||||
@ -1275,13 +1247,6 @@ class ModuleDecoderImpl : public Decoder {
|
||||
if (FLAG_dump_wasm_module) DumpModule(orig_bytes);
|
||||
|
||||
if (decoder.failed()) {
|
||||
if (metrics_) {
|
||||
metrics_->success = false;
|
||||
metrics_->module_size_in_bytes = orig_bytes.length();
|
||||
metrics_->function_count = module_->num_declared_functions;
|
||||
metrics_.reset();
|
||||
}
|
||||
|
||||
return decoder.toResult<std::unique_ptr<WasmModule>>(nullptr);
|
||||
}
|
||||
|
||||
@ -1335,37 +1300,11 @@ class ModuleDecoderImpl : public Decoder {
|
||||
counters_ = counters;
|
||||
}
|
||||
|
||||
void EnableMetricsRecording(
|
||||
std::shared_ptr<metrics::Recorder> metrics_recorder,
|
||||
v8::metrics::Recorder::ContextId context_id,
|
||||
DecodingMethod decoding_method) {
|
||||
metrics_ = std::make_unique<AutoSubmitMetrics>(std::move(metrics_recorder),
|
||||
context_id);
|
||||
switch (decoding_method) {
|
||||
case DecodingMethod::kSync:
|
||||
break;
|
||||
case DecodingMethod::kAsync:
|
||||
metrics_->async = true;
|
||||
break;
|
||||
case DecodingMethod::kSyncStream:
|
||||
metrics_->streamed = true;
|
||||
break;
|
||||
case DecodingMethod::kAsyncStream:
|
||||
metrics_->async = true;
|
||||
metrics_->streamed = true;
|
||||
break;
|
||||
case DecodingMethod::kDeserialize:
|
||||
// TODO(ecmziegler): verify if we need to add a deserialized metric flag
|
||||
// in the next UKM update.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const WasmFeatures enabled_features_;
|
||||
std::shared_ptr<WasmModule> module_;
|
||||
const byte* module_start_;
|
||||
const byte* module_end_;
|
||||
const byte* module_start_ = nullptr;
|
||||
const byte* module_end_ = nullptr;
|
||||
Counters* counters_ = nullptr;
|
||||
// The type section is the first section in a module.
|
||||
uint8_t next_ordered_section_ = kFirstSectionInModule;
|
||||
@ -1386,7 +1325,6 @@ class ModuleDecoderImpl : public Decoder {
|
||||
// reporting once the whole type section is parsed.
|
||||
std::unordered_map<uint32_t, int> deferred_check_type_index_;
|
||||
ModuleOrigin origin_;
|
||||
std::unique_ptr<AutoSubmitMetrics> metrics_;
|
||||
|
||||
ValueType TypeOf(const WasmInitExpr& expr) {
|
||||
switch (expr.kind()) {
|
||||
@ -2240,9 +2178,28 @@ ModuleResult DecodeWasmModule(
|
||||
// Signatures are stored in zone memory, which have the same lifetime
|
||||
// as the {module}.
|
||||
ModuleDecoderImpl decoder(enabled, module_start, module_end, origin);
|
||||
decoder.EnableMetricsRecording(std::move(metrics_recorder), context_id,
|
||||
decoding_method);
|
||||
return decoder.DecodeModule(counters, allocator, verify_functions);
|
||||
v8::metrics::WasmModuleDecoded metrics_event;
|
||||
metrics::TimedScope<v8::metrics::WasmModuleDecoded> metrics_event_scope(
|
||||
&metrics_event, &v8::metrics::WasmModuleDecoded::wall_clock_time_in_us);
|
||||
ModuleResult result =
|
||||
decoder.DecodeModule(counters, allocator, verify_functions);
|
||||
|
||||
// Record event metrics.
|
||||
metrics_event_scope.Stop();
|
||||
metrics_event.success = decoder.ok() && result.ok();
|
||||
metrics_event.async = decoding_method == DecodingMethod::kAsync ||
|
||||
decoding_method == DecodingMethod::kAsyncStream;
|
||||
metrics_event.streamed = decoding_method == DecodingMethod::kSyncStream ||
|
||||
decoding_method == DecodingMethod::kAsyncStream;
|
||||
if (result.ok()) {
|
||||
metrics_event.function_count = result.value()->num_declared_functions;
|
||||
} else if (auto&& module = decoder.shared_module()) {
|
||||
metrics_event.function_count = module->num_declared_functions;
|
||||
}
|
||||
metrics_event.module_size_in_bytes = size;
|
||||
metrics_recorder->DelayMainThreadEvent(metrics_event, context_id);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ModuleDecoder::ModuleDecoder(const WasmFeatures& enabled)
|
||||
@ -2260,8 +2217,6 @@ void ModuleDecoder::StartDecoding(
|
||||
ModuleOrigin origin) {
|
||||
DCHECK_NULL(impl_);
|
||||
impl_.reset(new ModuleDecoderImpl(enabled_features_, origin));
|
||||
impl_->EnableMetricsRecording(std::move(metrics_recorder), context_id,
|
||||
DecodingMethod::kAsyncStream);
|
||||
impl_->StartDecoding(counters, allocator);
|
||||
}
|
||||
|
||||
|
@ -1020,6 +1020,7 @@ std::unique_ptr<WasmCode> NativeModule::AddCodeWithCodeSpace(
|
||||
Vector<uint8_t> dst_code_bytes, const JumpTablesRef& jump_tables) {
|
||||
Vector<byte> reloc_info{desc.buffer + desc.buffer_size - desc.reloc_size,
|
||||
static_cast<size_t>(desc.reloc_size)};
|
||||
UpdateCodeSize(desc.instr_size, tier, for_debugging);
|
||||
|
||||
// TODO(jgruber,v8:8758): Remove this translation. It exists only because
|
||||
// CodeDesc contains real offsets but WasmCode expects an offset of 0 to mean
|
||||
@ -1174,6 +1175,7 @@ WasmCode* NativeModule::AddDeserializedCode(
|
||||
// CodeSpaceWriteScope is provided by the caller.
|
||||
Vector<uint8_t> dst_code_bytes =
|
||||
code_allocator_.AllocateForCode(this, instructions.size());
|
||||
UpdateCodeSize(dst_code_bytes.size(), tier, kNoDebugging);
|
||||
memcpy(dst_code_bytes.begin(), instructions.begin(), instructions.size());
|
||||
|
||||
std::unique_ptr<WasmCode> code{new WasmCode{
|
||||
@ -1230,6 +1232,7 @@ WasmCode* NativeModule::CreateEmptyJumpTableInRegion(
|
||||
Vector<uint8_t> code_space = code_allocator_.AllocateForCodeInRegion(
|
||||
this, jump_table_size, region, allocator_lock);
|
||||
DCHECK(!code_space.empty());
|
||||
UpdateCodeSize(jump_table_size, ExecutionTier::kNone, kNoDebugging);
|
||||
CODE_SPACE_WRITE_SCOPE
|
||||
ZapCode(reinterpret_cast<Address>(code_space.begin()), code_space.size());
|
||||
std::unique_ptr<WasmCode> code{
|
||||
@ -1252,6 +1255,15 @@ WasmCode* NativeModule::CreateEmptyJumpTableInRegion(
|
||||
return PublishCode(std::move(code));
|
||||
}
|
||||
|
||||
void NativeModule::UpdateCodeSize(size_t size, ExecutionTier tier,
|
||||
ForDebugging for_debugging) {
|
||||
if (for_debugging != kNoDebugging) return;
|
||||
// Count jump tables (ExecutionTier::kNone) for both Liftoff and TurboFan as
|
||||
// this is shared code.
|
||||
if (tier != ExecutionTier::kTurbofan) liftoff_code_size_.fetch_add(size);
|
||||
if (tier != ExecutionTier::kLiftoff) turbofan_code_size_.fetch_add(size);
|
||||
}
|
||||
|
||||
void NativeModule::PatchJumpTablesLocked(uint32_t slot_index, Address target) {
|
||||
// The caller must hold the {allocation_mutex_}, thus we fail to lock it here.
|
||||
DCHECK(!allocation_mutex_.TryLock());
|
||||
|
@ -596,6 +596,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
|
||||
return code_allocator_.generated_code_size();
|
||||
}
|
||||
size_t liftoff_bailout_count() const { return liftoff_bailout_count_.load(); }
|
||||
size_t liftoff_code_size() const { return liftoff_code_size_.load(); }
|
||||
size_t turbofan_code_size() const { return turbofan_code_size_.load(); }
|
||||
WasmEngine* engine() const { return engine_; }
|
||||
|
||||
bool HasWireBytes() const {
|
||||
@ -697,6 +699,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
|
||||
int jump_table_size, base::AddressRegion,
|
||||
const WasmCodeAllocator::OptionalLock&);
|
||||
|
||||
void UpdateCodeSize(size_t, ExecutionTier, ForDebugging);
|
||||
|
||||
// Hold the {allocation_mutex_} when calling one of these methods.
|
||||
// {slot_index} is the index in the declared functions, i.e. function index
|
||||
// minus the number of imported functions.
|
||||
@ -788,6 +792,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
|
||||
UseTrapHandler use_trap_handler_ = kNoTrapHandler;
|
||||
bool lazy_compile_frozen_ = false;
|
||||
std::atomic<size_t> liftoff_bailout_count_{0};
|
||||
std::atomic<size_t> liftoff_code_size_{0};
|
||||
std::atomic<size_t> turbofan_code_size_{0};
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeModule);
|
||||
};
|
||||
|
@ -309,6 +309,7 @@ v8_source_set("cctest_sources") {
|
||||
"wasm/test-wasm-debug-evaluate.cc",
|
||||
"wasm/test-wasm-debug-evaluate.h",
|
||||
"wasm/test-wasm-import-wrapper-cache.cc",
|
||||
"wasm/test-wasm-metrics.cc",
|
||||
"wasm/test-wasm-serialization.cc",
|
||||
"wasm/test-wasm-shared-engine.cc",
|
||||
"wasm/test-wasm-stack.cc",
|
||||
|
@ -522,6 +522,7 @@
|
||||
'test-wasm-codegen/*': [SKIP],
|
||||
'test-wasm-debug-evaluate/*': [SKIP],
|
||||
'test-wasm-import-wrapper-cache/*': [SKIP],
|
||||
'test-wasm-metrics/*': [SKIP],
|
||||
'test-wasm-serialization/*': [SKIP],
|
||||
'test-wasm-shared-engine/*': [SKIP],
|
||||
'test-wasm-stack/*': [SKIP],
|
||||
|
330
test/cctest/wasm/test-wasm-metrics.cc
Normal file
330
test/cctest/wasm/test-wasm-metrics.cc
Normal file
@ -0,0 +1,330 @@
|
||||
// Copyright 2020 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 <memory>
|
||||
|
||||
#include "include/v8-metrics.h"
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/wasm/wasm-module-builder.h"
|
||||
#include "test/cctest/cctest.h"
|
||||
#include "test/common/wasm/flag-utils.h"
|
||||
#include "test/common/wasm/test-signatures.h"
|
||||
#include "test/common/wasm/wasm-macro-gen.h"
|
||||
#include "test/common/wasm/wasm-module-runner.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
namespace {
|
||||
|
||||
class MockPlatform final : public TestPlatform {
|
||||
public:
|
||||
MockPlatform() : task_runner_(std::make_shared<MockTaskRunner>()) {
|
||||
// Now that it's completely constructed, make this the current platform.
|
||||
i::V8::SetPlatformForTesting(this);
|
||||
}
|
||||
|
||||
~MockPlatform() override {
|
||||
for (auto* job_handle : job_handles_) job_handle->ResetPlatform();
|
||||
}
|
||||
|
||||
std::unique_ptr<v8::JobHandle> PostJob(
|
||||
v8::TaskPriority priority,
|
||||
std::unique_ptr<v8::JobTask> job_task) override {
|
||||
auto orig_job_handle = TestPlatform::PostJob(priority, std::move(job_task));
|
||||
auto job_handle =
|
||||
std::make_unique<MockJobHandle>(std::move(orig_job_handle), this);
|
||||
job_handles_.insert(job_handle.get());
|
||||
return job_handle;
|
||||
}
|
||||
|
||||
std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
|
||||
v8::Isolate* isolate) override {
|
||||
return task_runner_;
|
||||
}
|
||||
|
||||
void CallOnWorkerThread(std::unique_ptr<v8::Task> task) override {
|
||||
task_runner_->PostTask(std::move(task));
|
||||
}
|
||||
|
||||
bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; }
|
||||
|
||||
void ExecuteTasks() {
|
||||
for (auto* job_handle : job_handles_) {
|
||||
if (job_handle->IsRunning()) job_handle->Join();
|
||||
}
|
||||
task_runner_->ExecuteTasks();
|
||||
}
|
||||
|
||||
private:
|
||||
class MockTaskRunner final : public TaskRunner {
|
||||
public:
|
||||
void PostTask(std::unique_ptr<v8::Task> task) override {
|
||||
base::MutexGuard lock_scope(&tasks_lock_);
|
||||
tasks_.push(std::move(task));
|
||||
}
|
||||
|
||||
void PostDelayedTask(std::unique_ptr<Task> task,
|
||||
double delay_in_seconds) override {
|
||||
base::MutexGuard lock_scope(&tasks_lock_);
|
||||
tasks_.push(std::move(task));
|
||||
}
|
||||
|
||||
void PostIdleTask(std::unique_ptr<IdleTask> task) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool IdleTasksEnabled() override { return false; }
|
||||
|
||||
void ExecuteTasks() {
|
||||
std::queue<std::unique_ptr<v8::Task>> tasks;
|
||||
{
|
||||
base::MutexGuard lock_scope(&tasks_lock_);
|
||||
tasks.swap(tasks_);
|
||||
}
|
||||
while (!tasks.empty()) {
|
||||
std::unique_ptr<Task> task = std::move(tasks.front());
|
||||
tasks.pop();
|
||||
task->Run();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
base::Mutex tasks_lock_;
|
||||
// We do not execute tasks concurrently, so we only need one list of tasks.
|
||||
std::queue<std::unique_ptr<v8::Task>> tasks_;
|
||||
};
|
||||
|
||||
class MockJobHandle : public JobHandle {
|
||||
public:
|
||||
explicit MockJobHandle(std::unique_ptr<JobHandle> orig_handle,
|
||||
MockPlatform* platform)
|
||||
: orig_handle_(std::move(orig_handle)), platform_(platform) {}
|
||||
|
||||
~MockJobHandle() {
|
||||
if (platform_) platform_->job_handles_.erase(this);
|
||||
}
|
||||
|
||||
void ResetPlatform() { platform_ = nullptr; }
|
||||
|
||||
void NotifyConcurrencyIncrease() override {
|
||||
orig_handle_->NotifyConcurrencyIncrease();
|
||||
}
|
||||
void Join() override { orig_handle_->Join(); }
|
||||
void Cancel() override { orig_handle_->Cancel(); }
|
||||
bool IsRunning() override { return orig_handle_->IsRunning(); }
|
||||
bool IsCompleted() override { return orig_handle_->IsCompleted(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<JobHandle> orig_handle_;
|
||||
MockPlatform* platform_;
|
||||
};
|
||||
|
||||
std::shared_ptr<MockTaskRunner> task_runner_;
|
||||
std::unordered_set<MockJobHandle*> job_handles_;
|
||||
};
|
||||
|
||||
enum class CompilationStatus {
|
||||
kPending,
|
||||
kFinished,
|
||||
kFailed,
|
||||
};
|
||||
|
||||
class TestInstantiateResolver : public InstantiationResultResolver {
|
||||
public:
|
||||
TestInstantiateResolver(CompilationStatus* status, std::string* error_message)
|
||||
: status_(status), error_message_(error_message) {}
|
||||
|
||||
void OnInstantiationSucceeded(
|
||||
i::Handle<i::WasmInstanceObject> instance) override {
|
||||
*status_ = CompilationStatus::kFinished;
|
||||
}
|
||||
|
||||
void OnInstantiationFailed(i::Handle<i::Object> error_reason) override {
|
||||
*status_ = CompilationStatus::kFailed;
|
||||
Handle<String> str =
|
||||
Object::ToString(CcTest::i_isolate(), error_reason).ToHandleChecked();
|
||||
error_message_->assign(str->ToCString().get());
|
||||
}
|
||||
|
||||
private:
|
||||
CompilationStatus* const status_;
|
||||
std::string* const error_message_;
|
||||
};
|
||||
|
||||
class TestCompileResolver : public CompilationResultResolver {
|
||||
public:
|
||||
TestCompileResolver(CompilationStatus* status, std::string* error_message,
|
||||
Isolate* isolate,
|
||||
std::shared_ptr<NativeModule>* native_module)
|
||||
: status_(status),
|
||||
error_message_(error_message),
|
||||
isolate_(isolate),
|
||||
native_module_(native_module) {}
|
||||
|
||||
void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> module) override {
|
||||
if (!module.is_null()) {
|
||||
*native_module_ = module->shared_native_module();
|
||||
isolate_->wasm_engine()->AsyncInstantiate(
|
||||
isolate_,
|
||||
std::make_unique<TestInstantiateResolver>(status_, error_message_),
|
||||
module, MaybeHandle<JSReceiver>());
|
||||
}
|
||||
}
|
||||
|
||||
void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
|
||||
*status_ = CompilationStatus::kFailed;
|
||||
Handle<String> str =
|
||||
Object::ToString(CcTest::i_isolate(), error_reason).ToHandleChecked();
|
||||
error_message_->assign(str->ToCString().get());
|
||||
}
|
||||
|
||||
private:
|
||||
CompilationStatus* const status_;
|
||||
std::string* const error_message_;
|
||||
Isolate* isolate_;
|
||||
std::shared_ptr<NativeModule>* const native_module_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#define COMPILE_TEST(name) \
|
||||
void RunCompile_##name(); \
|
||||
TEST(Sync##name) { \
|
||||
i::FlagScope<bool> sync_scope(&i::FLAG_wasm_async_compilation, false); \
|
||||
RunCompile_##name(); \
|
||||
} \
|
||||
\
|
||||
TEST(Async##name) { RunCompile_##name(); } \
|
||||
\
|
||||
TEST(Streaming##name) { \
|
||||
i::FlagScope<bool> streaming_scope(&i::FLAG_wasm_test_streaming, true); \
|
||||
RunCompile_##name(); \
|
||||
} \
|
||||
void RunCompile_##name()
|
||||
|
||||
class MetricsRecorder : public v8::metrics::Recorder {
|
||||
public:
|
||||
std::vector<v8::metrics::WasmModuleDecoded> module_decoded_;
|
||||
std::vector<v8::metrics::WasmModuleCompiled> module_compiled_;
|
||||
std::vector<v8::metrics::WasmModuleInstantiated> module_instantiated_;
|
||||
std::vector<v8::metrics::WasmModuleTieredUp> module_tiered_up_;
|
||||
|
||||
void AddMainThreadEvent(const v8::metrics::WasmModuleDecoded& event,
|
||||
v8::metrics::Recorder::ContextId id) override {
|
||||
CHECK(!id.IsEmpty());
|
||||
module_decoded_.emplace_back(event);
|
||||
}
|
||||
void AddMainThreadEvent(const v8::metrics::WasmModuleCompiled& event,
|
||||
v8::metrics::Recorder::ContextId id) override {
|
||||
CHECK(!id.IsEmpty());
|
||||
module_compiled_.emplace_back(event);
|
||||
}
|
||||
void AddMainThreadEvent(const v8::metrics::WasmModuleInstantiated& event,
|
||||
v8::metrics::Recorder::ContextId id) override {
|
||||
CHECK(!id.IsEmpty());
|
||||
module_instantiated_.emplace_back(event);
|
||||
}
|
||||
void AddMainThreadEvent(const v8::metrics::WasmModuleTieredUp& event,
|
||||
v8::metrics::Recorder::ContextId id) override {
|
||||
CHECK(!id.IsEmpty());
|
||||
module_tiered_up_.emplace_back(event);
|
||||
}
|
||||
};
|
||||
|
||||
COMPILE_TEST(TestEventMetrics) {
|
||||
MockPlatform platform;
|
||||
Isolate* isolate = CcTest::InitIsolateOnce();
|
||||
CHECK_EQ(V8::GetCurrentPlatform(), &platform);
|
||||
HandleScope scope(isolate);
|
||||
testing::SetupIsolateForWasmModule(isolate);
|
||||
std::shared_ptr<MetricsRecorder> recorder =
|
||||
std::make_shared<MetricsRecorder>();
|
||||
CcTest::isolate()->SetMetricsRecorder(recorder);
|
||||
|
||||
TestSignatures sigs;
|
||||
v8::internal::AccountingAllocator allocator;
|
||||
Zone zone(&allocator, ZONE_NAME);
|
||||
|
||||
WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
|
||||
f->builder()->AddExport(CStrVector("main"), f);
|
||||
byte code[] = {WASM_I32V_2(0)};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
f->Emit(kExprEnd);
|
||||
ZoneBuffer buffer(&zone);
|
||||
builder->WriteTo(&buffer);
|
||||
|
||||
auto enabled_features = WasmFeatures::FromIsolate(isolate);
|
||||
CompilationStatus status = CompilationStatus::kPending;
|
||||
std::string error_message;
|
||||
std::shared_ptr<NativeModule> native_module;
|
||||
isolate->wasm_engine()->AsyncCompile(
|
||||
isolate, enabled_features,
|
||||
std::make_shared<TestCompileResolver>(&status, &error_message, isolate,
|
||||
&native_module),
|
||||
ModuleWireBytes(buffer.begin(), buffer.end()), true,
|
||||
"CompileAndInstantiateWasmModuleForTesting");
|
||||
|
||||
// Finish compilation tasks.
|
||||
while (status == CompilationStatus::kPending) {
|
||||
platform.ExecuteTasks();
|
||||
}
|
||||
platform.ExecuteTasks(); // Complete pending tasks beyond compilation.
|
||||
CHECK_EQ(CompilationStatus::kFinished, status);
|
||||
|
||||
CHECK_EQ(1, recorder->module_decoded_.size());
|
||||
CHECK(recorder->module_decoded_.back().success);
|
||||
CHECK_EQ(i::FLAG_wasm_async_compilation,
|
||||
recorder->module_decoded_.back().async);
|
||||
CHECK_EQ(i::FLAG_wasm_test_streaming,
|
||||
recorder->module_decoded_.back().streamed);
|
||||
CHECK_EQ(buffer.size(),
|
||||
recorder->module_decoded_.back().module_size_in_bytes);
|
||||
CHECK_EQ(1, recorder->module_decoded_.back().function_count);
|
||||
CHECK_LE(0, recorder->module_decoded_.back().wall_clock_time_in_us);
|
||||
|
||||
CHECK_EQ(1, recorder->module_compiled_.size());
|
||||
CHECK(recorder->module_compiled_.back().success);
|
||||
CHECK_EQ(i::FLAG_wasm_async_compilation,
|
||||
recorder->module_compiled_.back().async);
|
||||
CHECK_EQ(i::FLAG_wasm_test_streaming,
|
||||
recorder->module_compiled_.back().streamed);
|
||||
CHECK(!recorder->module_compiled_.back().cached);
|
||||
CHECK(!recorder->module_compiled_.back().deserialized);
|
||||
CHECK(!recorder->module_compiled_.back().lazy);
|
||||
CHECK_LT(0, recorder->module_compiled_.back().code_size_in_bytes);
|
||||
// We currently cannot ensure that no code is attributed to Liftoff after the
|
||||
// WasmModuleCompiled event has been emitted. We therefore only assume the
|
||||
// liftoff_code_size() to be an upper limit for the reported size.
|
||||
CHECK_GE(native_module->liftoff_code_size(),
|
||||
recorder->module_compiled_.back().code_size_in_bytes);
|
||||
CHECK_GE(native_module->generated_code_size(),
|
||||
recorder->module_compiled_.back().code_size_in_bytes);
|
||||
CHECK_EQ(0, recorder->module_compiled_.back().liftoff_bailout_count);
|
||||
CHECK_LE(0, recorder->module_compiled_.back().wall_clock_time_in_us);
|
||||
|
||||
CHECK_EQ(1, recorder->module_instantiated_.size());
|
||||
CHECK(recorder->module_instantiated_.back().success);
|
||||
// We currently don't support true async instantiation.
|
||||
CHECK(!recorder->module_instantiated_.back().async);
|
||||
CHECK_EQ(0, recorder->module_instantiated_.back().imported_function_count);
|
||||
CHECK_LE(0, recorder->module_instantiated_.back().wall_clock_time_in_us);
|
||||
|
||||
CHECK_EQ(1, recorder->module_tiered_up_.size());
|
||||
CHECK(!recorder->module_tiered_up_.back().lazy);
|
||||
CHECK_LT(0, recorder->module_tiered_up_.back().code_size_in_bytes);
|
||||
CHECK_EQ(native_module->turbofan_code_size(),
|
||||
recorder->module_tiered_up_.back().code_size_in_bytes);
|
||||
CHECK_GE(native_module->generated_code_size(),
|
||||
recorder->module_tiered_up_.back().code_size_in_bytes);
|
||||
CHECK_GE(native_module->committed_code_space(),
|
||||
recorder->module_tiered_up_.back().code_size_in_bytes);
|
||||
CHECK_LE(0, recorder->module_tiered_up_.back().wall_clock_time_in_us);
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
Loading…
Reference in New Issue
Block a user