[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:
Emanuel Ziegler 2020-09-24 10:14:38 +02:00 committed by Commit Bot
parent 24fbcf8847
commit b77c63c0a6
9 changed files with 409 additions and 82 deletions

View File

@ -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 {

View File

@ -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.

View File

@ -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_;

View File

@ -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);
}

View File

@ -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());

View File

@ -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);
};

View File

@ -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",

View File

@ -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],

View 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