Commit b77c63c0 authored by Emanuel Ziegler's avatar Emanuel Ziegler Committed by Commit Bot

[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: 's avatarClemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70113}
parent 24fbcf88
...@@ -16,7 +16,7 @@ struct WasmModuleDecoded { ...@@ -16,7 +16,7 @@ struct WasmModuleDecoded {
bool success = false; bool success = false;
size_t module_size_in_bytes = 0; size_t module_size_in_bytes = 0;
size_t function_count = 0; size_t function_count = 0;
int64_t wall_clock_time_in_us = 0; int64_t wall_clock_time_in_us = -1;
}; };
struct WasmModuleCompiled { struct WasmModuleCompiled {
...@@ -28,20 +28,20 @@ struct WasmModuleCompiled { ...@@ -28,20 +28,20 @@ struct WasmModuleCompiled {
bool success = false; bool success = false;
size_t code_size_in_bytes = 0; size_t code_size_in_bytes = 0;
size_t liftoff_bailout_count = 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 { struct WasmModuleInstantiated {
bool async = false; bool async = false;
bool success = false; bool success = false;
size_t imported_function_count = 0; size_t imported_function_count = 0;
int64_t wall_clock_time_in_us = 0; int64_t wall_clock_time_in_us = -1;
}; };
struct WasmModuleTieredUp { struct WasmModuleTieredUp {
bool lazy = false; bool lazy = false;
size_t code_size_in_bytes = 0; 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 { struct WasmModulesPerIsolate {
......
...@@ -1527,7 +1527,7 @@ class CompilationTimeCallback { ...@@ -1527,7 +1527,7 @@ class CompilationTimeCallback {
false, // deserialized false, // deserialized
FLAG_wasm_lazy_compilation, // lazy FLAG_wasm_lazy_compilation, // lazy
true, // success 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 native_module->liftoff_bailout_count(), // liftoff_bailout_count
duration.InMicroseconds() // wall_clock_time_in_us duration.InMicroseconds() // wall_clock_time_in_us
}; };
...@@ -1539,7 +1539,7 @@ class CompilationTimeCallback { ...@@ -1539,7 +1539,7 @@ class CompilationTimeCallback {
v8::metrics::WasmModuleTieredUp event{ v8::metrics::WasmModuleTieredUp event{
FLAG_wasm_lazy_compilation, // lazy FLAG_wasm_lazy_compilation, // lazy
native_module->generated_code_size(), // code_size_in_bytes native_module->turbofan_code_size(), // code_size_in_bytes
duration.InMicroseconds() // wall_clock_time_in_us duration.InMicroseconds() // wall_clock_time_in_us
}; };
metrics_recorder_->DelayMainThreadEvent(event, context_id_); metrics_recorder_->DelayMainThreadEvent(event, context_id_);
...@@ -1552,7 +1552,7 @@ class CompilationTimeCallback { ...@@ -1552,7 +1552,7 @@ class CompilationTimeCallback {
false, // deserialized false, // deserialized
FLAG_wasm_lazy_compilation, // lazy FLAG_wasm_lazy_compilation, // lazy
false, // success 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 native_module->liftoff_bailout_count(), // liftoff_bailout_count
duration.InMicroseconds() // wall_clock_time_in_us duration.InMicroseconds() // wall_clock_time_in_us
}; };
...@@ -1792,6 +1792,7 @@ AsyncCompileJob::AsyncCompileJob( ...@@ -1792,6 +1792,7 @@ AsyncCompileJob::AsyncCompileJob(
isolate->global_handles()->Create(context->native_context()); isolate->global_handles()->Create(context->native_context());
DCHECK(native_context_->IsNativeContext()); DCHECK(native_context_->IsNativeContext());
context_id_ = isolate->GetOrRegisterRecorderContextId(native_context_); context_id_ = isolate->GetOrRegisterRecorderContextId(native_context_);
metrics_event_.async = true;
} }
void AsyncCompileJob::Start() { void AsyncCompileJob::Start() {
...@@ -1958,7 +1959,7 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) { ...@@ -1958,7 +1959,7 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) {
is_after_deserialization, // deserialized is_after_deserialization, // deserialized
wasm_lazy_compilation_, // lazy wasm_lazy_compilation_, // lazy
!compilation_state->failed(), // success !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 native_module_->liftoff_bailout_count(), // liftoff_bailout_count
duration.InMicroseconds() // wall_clock_time_in_us duration.InMicroseconds() // wall_clock_time_in_us
}; };
...@@ -2463,6 +2464,16 @@ void AsyncStreamingProcessor::FinishAsyncCompileJobWithError( ...@@ -2463,6 +2464,16 @@ void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(
// of the AsyncCompileJob to DecodeFail. // of the AsyncCompileJob to DecodeFail.
job_->background_task_manager_.CancelAndWait(); 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 // Check if there is already a CompiledModule, in which case we have to clean
// up the CompilationStateImpl as well. // up the CompilationStateImpl as well.
if (job_->native_module_) { if (job_->native_module_) {
...@@ -2674,6 +2685,16 @@ void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) { ...@@ -2674,6 +2685,16 @@ void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector()); job_->wire_bytes_ = ModuleWireBytes(bytes.as_vector());
job_->bytes_copy_ = bytes.ReleaseData(); 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_) { if (prefix_cache_hit_) {
// Restart as an asynchronous, non-streaming compilation. Most likely // Restart as an asynchronous, non-streaming compilation. Most likely
// {PrepareAndStartCompile} will get the native module from the cache. // {PrepareAndStartCompile} will get the native module from the cache.
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include "include/v8-metrics.h"
#include "src/base/optional.h" #include "src/base/optional.h"
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/logging/metrics.h"
#include "src/tasks/cancelable-task.h" #include "src/tasks/cancelable-task.h"
#include "src/wasm/compilation-environment.h" #include "src/wasm/compilation-environment.h"
#include "src/wasm/wasm-features.h" #include "src/wasm/wasm-features.h"
...@@ -213,6 +213,7 @@ class AsyncCompileJob { ...@@ -213,6 +213,7 @@ class AsyncCompileJob {
ModuleWireBytes wire_bytes_; ModuleWireBytes wire_bytes_;
Handle<NativeContext> native_context_; Handle<NativeContext> native_context_;
v8::metrics::Recorder::ContextId context_id_; v8::metrics::Recorder::ContextId context_id_;
v8::metrics::WasmModuleDecoded metrics_event_;
const std::shared_ptr<CompilationResultResolver> resolver_; const std::shared_ptr<CompilationResultResolver> resolver_;
Handle<WasmModuleObject> module_object_; Handle<WasmModuleObject> module_object_;
......
...@@ -266,27 +266,6 @@ class WasmSectionIterator { ...@@ -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 } // namespace
// The main logic for decoding the bytes of a module. // The main logic for decoding the bytes of a module.
...@@ -301,13 +280,13 @@ class ModuleDecoderImpl : public Decoder { ...@@ -301,13 +280,13 @@ class ModuleDecoderImpl : public Decoder {
const byte* module_end, ModuleOrigin origin) const byte* module_end, ModuleOrigin origin)
: Decoder(module_start, module_end), : Decoder(module_start, module_end),
enabled_features_(enabled), enabled_features_(enabled),
module_start_(module_start),
module_end_(module_end),
origin_(origin) { origin_(origin) {
if (end_ < start_) { if (end_ < start_) {
error(start_, "end is less than start"); error(start_, "end is less than start");
end_ = start_; end_ = start_;
} }
module_start_ = module_start;
module_end_ = module_end;
} }
void onFirstError() override { void onFirstError() override {
...@@ -1225,13 +1204,6 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1225,13 +1204,6 @@ class ModuleDecoderImpl : public Decoder {
CalculateGlobalOffsets(module_.get()); 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_)); ModuleResult result = toResult(std::move(module_));
if (verify_functions && result.ok() && intermediate_error_.has_error()) { if (verify_functions && result.ok() && intermediate_error_.has_error()) {
// Copy error message and location. // Copy error message and location.
...@@ -1275,13 +1247,6 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1275,13 +1247,6 @@ class ModuleDecoderImpl : public Decoder {
if (FLAG_dump_wasm_module) DumpModule(orig_bytes); if (FLAG_dump_wasm_module) DumpModule(orig_bytes);
if (decoder.failed()) { 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); return decoder.toResult<std::unique_ptr<WasmModule>>(nullptr);
} }
...@@ -1335,37 +1300,11 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1335,37 +1300,11 @@ class ModuleDecoderImpl : public Decoder {
counters_ = counters; 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: private:
const WasmFeatures enabled_features_; const WasmFeatures enabled_features_;
std::shared_ptr<WasmModule> module_; std::shared_ptr<WasmModule> module_;
const byte* module_start_; const byte* module_start_ = nullptr;
const byte* module_end_; const byte* module_end_ = nullptr;
Counters* counters_ = nullptr; Counters* counters_ = nullptr;
// The type section is the first section in a module. // The type section is the first section in a module.
uint8_t next_ordered_section_ = kFirstSectionInModule; uint8_t next_ordered_section_ = kFirstSectionInModule;
...@@ -1386,7 +1325,6 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1386,7 +1325,6 @@ class ModuleDecoderImpl : public Decoder {
// reporting once the whole type section is parsed. // reporting once the whole type section is parsed.
std::unordered_map<uint32_t, int> deferred_check_type_index_; std::unordered_map<uint32_t, int> deferred_check_type_index_;
ModuleOrigin origin_; ModuleOrigin origin_;
std::unique_ptr<AutoSubmitMetrics> metrics_;
ValueType TypeOf(const WasmInitExpr& expr) { ValueType TypeOf(const WasmInitExpr& expr) {
switch (expr.kind()) { switch (expr.kind()) {
...@@ -2240,9 +2178,28 @@ ModuleResult DecodeWasmModule( ...@@ -2240,9 +2178,28 @@ ModuleResult DecodeWasmModule(
// Signatures are stored in zone memory, which have the same lifetime // Signatures are stored in zone memory, which have the same lifetime
// as the {module}. // as the {module}.
ModuleDecoderImpl decoder(enabled, module_start, module_end, origin); ModuleDecoderImpl decoder(enabled, module_start, module_end, origin);
decoder.EnableMetricsRecording(std::move(metrics_recorder), context_id, v8::metrics::WasmModuleDecoded metrics_event;
decoding_method); metrics::TimedScope<v8::metrics::WasmModuleDecoded> metrics_event_scope(
return decoder.DecodeModule(counters, allocator, verify_functions); &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) ModuleDecoder::ModuleDecoder(const WasmFeatures& enabled)
...@@ -2260,8 +2217,6 @@ void ModuleDecoder::StartDecoding( ...@@ -2260,8 +2217,6 @@ void ModuleDecoder::StartDecoding(
ModuleOrigin origin) { ModuleOrigin origin) {
DCHECK_NULL(impl_); DCHECK_NULL(impl_);
impl_.reset(new ModuleDecoderImpl(enabled_features_, origin)); impl_.reset(new ModuleDecoderImpl(enabled_features_, origin));
impl_->EnableMetricsRecording(std::move(metrics_recorder), context_id,
DecodingMethod::kAsyncStream);
impl_->StartDecoding(counters, allocator); impl_->StartDecoding(counters, allocator);
} }
......
...@@ -1020,6 +1020,7 @@ std::unique_ptr<WasmCode> NativeModule::AddCodeWithCodeSpace( ...@@ -1020,6 +1020,7 @@ std::unique_ptr<WasmCode> NativeModule::AddCodeWithCodeSpace(
Vector<uint8_t> dst_code_bytes, const JumpTablesRef& jump_tables) { Vector<uint8_t> dst_code_bytes, const JumpTablesRef& jump_tables) {
Vector<byte> reloc_info{desc.buffer + desc.buffer_size - desc.reloc_size, Vector<byte> reloc_info{desc.buffer + desc.buffer_size - desc.reloc_size,
static_cast<size_t>(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 // TODO(jgruber,v8:8758): Remove this translation. It exists only because
// CodeDesc contains real offsets but WasmCode expects an offset of 0 to mean // CodeDesc contains real offsets but WasmCode expects an offset of 0 to mean
...@@ -1174,6 +1175,7 @@ WasmCode* NativeModule::AddDeserializedCode( ...@@ -1174,6 +1175,7 @@ WasmCode* NativeModule::AddDeserializedCode(
// CodeSpaceWriteScope is provided by the caller. // CodeSpaceWriteScope is provided by the caller.
Vector<uint8_t> dst_code_bytes = Vector<uint8_t> dst_code_bytes =
code_allocator_.AllocateForCode(this, instructions.size()); code_allocator_.AllocateForCode(this, instructions.size());
UpdateCodeSize(dst_code_bytes.size(), tier, kNoDebugging);
memcpy(dst_code_bytes.begin(), instructions.begin(), instructions.size()); memcpy(dst_code_bytes.begin(), instructions.begin(), instructions.size());
std::unique_ptr<WasmCode> code{new WasmCode{ std::unique_ptr<WasmCode> code{new WasmCode{
...@@ -1230,6 +1232,7 @@ WasmCode* NativeModule::CreateEmptyJumpTableInRegion( ...@@ -1230,6 +1232,7 @@ WasmCode* NativeModule::CreateEmptyJumpTableInRegion(
Vector<uint8_t> code_space = code_allocator_.AllocateForCodeInRegion( Vector<uint8_t> code_space = code_allocator_.AllocateForCodeInRegion(
this, jump_table_size, region, allocator_lock); this, jump_table_size, region, allocator_lock);
DCHECK(!code_space.empty()); DCHECK(!code_space.empty());
UpdateCodeSize(jump_table_size, ExecutionTier::kNone, kNoDebugging);
CODE_SPACE_WRITE_SCOPE CODE_SPACE_WRITE_SCOPE
ZapCode(reinterpret_cast<Address>(code_space.begin()), code_space.size()); ZapCode(reinterpret_cast<Address>(code_space.begin()), code_space.size());
std::unique_ptr<WasmCode> code{ std::unique_ptr<WasmCode> code{
...@@ -1252,6 +1255,15 @@ WasmCode* NativeModule::CreateEmptyJumpTableInRegion( ...@@ -1252,6 +1255,15 @@ WasmCode* NativeModule::CreateEmptyJumpTableInRegion(
return PublishCode(std::move(code)); 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) { void NativeModule::PatchJumpTablesLocked(uint32_t slot_index, Address target) {
// The caller must hold the {allocation_mutex_}, thus we fail to lock it here. // The caller must hold the {allocation_mutex_}, thus we fail to lock it here.
DCHECK(!allocation_mutex_.TryLock()); DCHECK(!allocation_mutex_.TryLock());
......
...@@ -596,6 +596,8 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -596,6 +596,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
return code_allocator_.generated_code_size(); return code_allocator_.generated_code_size();
} }
size_t liftoff_bailout_count() const { return liftoff_bailout_count_.load(); } 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_; } WasmEngine* engine() const { return engine_; }
bool HasWireBytes() const { bool HasWireBytes() const {
...@@ -697,6 +699,8 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -697,6 +699,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
int jump_table_size, base::AddressRegion, int jump_table_size, base::AddressRegion,
const WasmCodeAllocator::OptionalLock&); const WasmCodeAllocator::OptionalLock&);
void UpdateCodeSize(size_t, ExecutionTier, ForDebugging);
// Hold the {allocation_mutex_} when calling one of these methods. // Hold the {allocation_mutex_} when calling one of these methods.
// {slot_index} is the index in the declared functions, i.e. function index // {slot_index} is the index in the declared functions, i.e. function index
// minus the number of imported functions. // minus the number of imported functions.
...@@ -788,6 +792,8 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -788,6 +792,8 @@ class V8_EXPORT_PRIVATE NativeModule final {
UseTrapHandler use_trap_handler_ = kNoTrapHandler; UseTrapHandler use_trap_handler_ = kNoTrapHandler;
bool lazy_compile_frozen_ = false; bool lazy_compile_frozen_ = false;
std::atomic<size_t> liftoff_bailout_count_{0}; 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); DISALLOW_COPY_AND_ASSIGN(NativeModule);
}; };
......
...@@ -309,6 +309,7 @@ v8_source_set("cctest_sources") { ...@@ -309,6 +309,7 @@ v8_source_set("cctest_sources") {
"wasm/test-wasm-debug-evaluate.cc", "wasm/test-wasm-debug-evaluate.cc",
"wasm/test-wasm-debug-evaluate.h", "wasm/test-wasm-debug-evaluate.h",
"wasm/test-wasm-import-wrapper-cache.cc", "wasm/test-wasm-import-wrapper-cache.cc",
"wasm/test-wasm-metrics.cc",
"wasm/test-wasm-serialization.cc", "wasm/test-wasm-serialization.cc",
"wasm/test-wasm-shared-engine.cc", "wasm/test-wasm-shared-engine.cc",
"wasm/test-wasm-stack.cc", "wasm/test-wasm-stack.cc",
......
...@@ -522,6 +522,7 @@ ...@@ -522,6 +522,7 @@
'test-wasm-codegen/*': [SKIP], 'test-wasm-codegen/*': [SKIP],
'test-wasm-debug-evaluate/*': [SKIP], 'test-wasm-debug-evaluate/*': [SKIP],
'test-wasm-import-wrapper-cache/*': [SKIP], 'test-wasm-import-wrapper-cache/*': [SKIP],
'test-wasm-metrics/*': [SKIP],
'test-wasm-serialization/*': [SKIP], 'test-wasm-serialization/*': [SKIP],
'test-wasm-shared-engine/*': [SKIP], 'test-wasm-shared-engine/*': [SKIP],
'test-wasm-stack/*': [SKIP], 'test-wasm-stack/*': [SKIP],
......
// 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment