[ukm][wasm] Add event WasmModuleDecoded

Add an event for recording metrics related to decoding Wasm modules.

R=clemensb@chromium.org

Bug: chromium:1092417
Change-Id: Id60560d8eb8c14edb5b863857b18c1c82f48e7e7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2351672
Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69425}
This commit is contained in:
Emanuel Ziegler 2020-08-13 17:04:08 +02:00 committed by Commit Bot
parent 5488d2dbd8
commit 4c153339e5
10 changed files with 148 additions and 40 deletions

View File

@ -1550,10 +1550,12 @@ AsyncCompileJob::AsyncCompileJob(
native_context_ = native_context_ =
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_);
} }
void AsyncCompileJob::Start() { void AsyncCompileJob::Start() {
DoAsync<DecodeModule>(isolate_->counters()); // -- DoAsync<DecodeModule>(isolate_->counters(),
isolate_->metrics_recorder()); // --
} }
void AsyncCompileJob::Abort() { void AsyncCompileJob::Abort() {
@ -1959,7 +1961,9 @@ void AsyncCompileJob::NextStep(Args&&... args) {
//========================================================================== //==========================================================================
class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep { class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
public: public:
explicit DecodeModule(Counters* counters) : counters_(counters) {} explicit DecodeModule(Counters* counters,
std::shared_ptr<metrics::Recorder> metrics_recorder)
: counters_(counters), metrics_recorder_(std::move(metrics_recorder)) {}
void RunInBackground(AsyncCompileJob* job) override { void RunInBackground(AsyncCompileJob* job) override {
ModuleResult result; ModuleResult result;
@ -1971,10 +1975,10 @@ class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"), TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
"wasm.DecodeModule"); "wasm.DecodeModule");
auto enabled_features = job->enabled_features_; auto enabled_features = job->enabled_features_;
result = DecodeWasmModule(enabled_features, job->wire_bytes_.start(), result = DecodeWasmModule(
job->wire_bytes_.end(), false, kWasmOrigin, enabled_features, job->wire_bytes_.start(), job->wire_bytes_.end(),
counters_, false, kWasmOrigin, counters_, metrics_recorder_, job->context_id(),
job->isolate()->wasm_engine()->allocator()); DecodingMethod::kAsync, job->isolate()->wasm_engine()->allocator());
// Validate lazy functions here if requested. // Validate lazy functions here if requested.
if (!FLAG_wasm_lazy_validation && result.ok()) { if (!FLAG_wasm_lazy_validation && result.ok()) {
@ -2026,6 +2030,7 @@ class AsyncCompileJob::DecodeModule : public AsyncCompileJob::CompileStep {
private: private:
Counters* const counters_; Counters* const counters_;
std::shared_ptr<metrics::Recorder> metrics_recorder_;
}; };
//========================================================================== //==========================================================================
@ -2216,8 +2221,9 @@ void AsyncStreamingProcessor::FinishAsyncCompileJobWithError(
bool AsyncStreamingProcessor::ProcessModuleHeader(Vector<const uint8_t> bytes, bool AsyncStreamingProcessor::ProcessModuleHeader(Vector<const uint8_t> bytes,
uint32_t offset) { uint32_t offset) {
TRACE_STREAMING("Process module header...\n"); TRACE_STREAMING("Process module header...\n");
decoder_.StartDecoding(job_->isolate()->counters(), decoder_.StartDecoding(
job_->isolate()->wasm_engine()->allocator()); job_->isolate()->counters(), job_->isolate()->metrics_recorder(),
job_->context_id(), job_->isolate()->wasm_engine()->allocator());
decoder_.DecodeModuleHeader(bytes, offset); decoder_.DecodeModuleHeader(bytes, offset);
if (!decoder_.ok()) { if (!decoder_.ok()) {
FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error()); FinishAsyncCompileJobWithError(decoder_.FinishDecoding(false).error());

View File

@ -9,6 +9,7 @@
#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/tasks/cancelable-task.h" #include "src/tasks/cancelable-task.h"
@ -126,7 +127,8 @@ class AsyncCompileJob {
Isolate* isolate() const { return isolate_; } Isolate* isolate() const { return isolate_; }
Handle<Context> context() const { return native_context_; } Handle<NativeContext> context() const { return native_context_; }
v8::metrics::Recorder::ContextId context_id() const { return context_id_; }
private: private:
class CompileTask; class CompileTask;
@ -209,7 +211,8 @@ class AsyncCompileJob {
// Reference to the wire bytes (held in {bytes_copy_} or as part of // Reference to the wire bytes (held in {bytes_copy_} or as part of
// {native_module_}). // {native_module_}).
ModuleWireBytes wire_bytes_; ModuleWireBytes wire_bytes_;
Handle<Context> native_context_; Handle<NativeContext> native_context_;
v8::metrics::Recorder::ContextId context_id_;
const std::shared_ptr<CompilationResultResolver> resolver_; const std::shared_ptr<CompilationResultResolver> resolver_;
Handle<WasmModuleObject> module_object_; Handle<WasmModuleObject> module_object_;

View File

@ -9,6 +9,7 @@
#include "src/flags/flags.h" #include "src/flags/flags.h"
#include "src/init/v8.h" #include "src/init/v8.h"
#include "src/logging/counters.h" #include "src/logging/counters.h"
#include "src/logging/metrics.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "src/utils/ostreams.h" #include "src/utils/ostreams.h"
#include "src/wasm/decoder.h" #include "src/wasm/decoder.h"
@ -265,6 +266,27 @@ 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.
@ -1189,6 +1211,14 @@ class ModuleDecoderImpl : public Decoder {
if (ok() && CheckMismatchedCounts()) { if (ok() && CheckMismatchedCounts()) {
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.
@ -1232,6 +1262,13 @@ 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);
} }
@ -1285,6 +1322,32 @@ 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_;
@ -1310,6 +1373,7 @@ 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()) {
@ -2121,11 +2185,12 @@ class ModuleDecoderImpl : public Decoder {
} }
}; };
ModuleResult DecodeWasmModule(const WasmFeatures& enabled, ModuleResult DecodeWasmModule(
const byte* module_start, const byte* module_end, const WasmFeatures& enabled, const byte* module_start,
bool verify_functions, ModuleOrigin origin, const byte* module_end, bool verify_functions, ModuleOrigin origin,
Counters* counters, Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder,
AccountingAllocator* allocator) { v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method,
AccountingAllocator* allocator) {
size_t size = module_end - module_start; size_t size = module_end - module_start;
CHECK_LE(module_start, module_end); CHECK_LE(module_start, module_end);
size_t max_size = max_module_size(); size_t max_size = max_module_size();
@ -2140,6 +2205,8 @@ ModuleResult DecodeWasmModule(const WasmFeatures& enabled,
// 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,
decoding_method);
return decoder.DecodeModule(counters, allocator, verify_functions); return decoder.DecodeModule(counters, allocator, verify_functions);
} }
@ -2152,11 +2219,14 @@ const std::shared_ptr<WasmModule>& ModuleDecoder::shared_module() const {
return impl_->shared_module(); return impl_->shared_module();
} }
void ModuleDecoder::StartDecoding(Counters* counters, void ModuleDecoder::StartDecoding(
AccountingAllocator* allocator, Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder,
ModuleOrigin origin) { v8::metrics::Recorder::ContextId context_id, AccountingAllocator* allocator,
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);
} }

View File

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/logging/metrics.h"
#include "src/wasm/function-body-decoder.h" #include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-constants.h" #include "src/wasm/wasm-constants.h"
#include "src/wasm/wasm-features.h" #include "src/wasm/wasm-features.h"
@ -125,11 +126,21 @@ class LocalNames {
std::vector<LocalNamesPerFunction> functions_; std::vector<LocalNamesPerFunction> functions_;
}; };
enum class DecodingMethod {
kSync,
kAsync,
kSyncStream,
kAsyncStream,
kDeserialize
};
// Decodes the bytes of a wasm module between {module_start} and {module_end}. // Decodes the bytes of a wasm module between {module_start} and {module_end}.
V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule( V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(
const WasmFeatures& enabled, const byte* module_start, const WasmFeatures& enabled, const byte* module_start,
const byte* module_end, bool verify_functions, ModuleOrigin origin, const byte* module_end, bool verify_functions, ModuleOrigin origin,
Counters* counters, AccountingAllocator* allocator); Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder,
v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method,
AccountingAllocator* allocator);
// Exposed for testing. Decodes a single function signature, allocating it // Exposed for testing. Decodes a single function signature, allocating it
// in the given zone. Returns {nullptr} upon failure. // in the given zone. Returns {nullptr} upon failure.
@ -188,7 +199,10 @@ class ModuleDecoder {
explicit ModuleDecoder(const WasmFeatures& enabled); explicit ModuleDecoder(const WasmFeatures& enabled);
~ModuleDecoder(); ~ModuleDecoder();
void StartDecoding(Counters* counters, AccountingAllocator* allocator, void StartDecoding(Counters* counters,
std::shared_ptr<metrics::Recorder> metrics_recorder,
v8::metrics::Recorder::ContextId context_id,
AccountingAllocator* allocator,
ModuleOrigin origin = ModuleOrigin::kWasmOrigin); ModuleOrigin origin = ModuleOrigin::kWasmOrigin);
void DecodeModuleHeader(Vector<const uint8_t> bytes, uint32_t offset); void DecodeModuleHeader(Vector<const uint8_t> bytes, uint32_t offset);

View File

@ -443,9 +443,11 @@ bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
TRACE_EVENT0("v8.wasm", "wasm.SyncValidate"); TRACE_EVENT0("v8.wasm", "wasm.SyncValidate");
// TODO(titzer): remove dependency on the isolate. // TODO(titzer): remove dependency on the isolate.
if (bytes.start() == nullptr || bytes.length() == 0) return false; if (bytes.start() == nullptr || bytes.length() == 0) return false;
ModuleResult result = ModuleResult result = DecodeWasmModule(
DecodeWasmModule(enabled, bytes.start(), bytes.end(), true, kWasmOrigin, enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
isolate->counters(), allocator()); isolate->counters(), isolate->metrics_recorder(),
isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
DecodingMethod::kSync, allocator());
return result.ok(); return result.ok();
} }
@ -457,9 +459,11 @@ MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
ModuleOrigin origin = language_mode == LanguageMode::kSloppy ModuleOrigin origin = language_mode == LanguageMode::kSloppy
? kAsmJsSloppyOrigin ? kAsmJsSloppyOrigin
: kAsmJsStrictOrigin; : kAsmJsStrictOrigin;
ModuleResult result = ModuleResult result = DecodeWasmModule(
DecodeWasmModule(WasmFeatures::ForAsmjs(), bytes.start(), bytes.end(), WasmFeatures::ForAsmjs(), bytes.start(), bytes.end(), false, origin,
false, origin, isolate->counters(), allocator()); isolate->counters(), isolate->metrics_recorder(),
isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
DecodingMethod::kSync, allocator());
if (result.failed()) { if (result.failed()) {
// This happens once in a while when we have missed some limit check // This happens once in a while when we have missed some limit check
// in the asm parser. Output an error message to help diagnose, but crash. // in the asm parser. Output an error message to help diagnose, but crash.
@ -498,9 +502,11 @@ MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower, Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
const ModuleWireBytes& bytes) { const ModuleWireBytes& bytes) {
TRACE_EVENT0("v8.wasm", "wasm.SyncCompile"); TRACE_EVENT0("v8.wasm", "wasm.SyncCompile");
ModuleResult result = ModuleResult result = DecodeWasmModule(
DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin, enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
isolate->counters(), allocator()); isolate->counters(), isolate->metrics_recorder(),
isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
DecodingMethod::kSync, allocator());
if (result.failed()) { if (result.failed()) {
thrower->CompileFailed(result.error()); thrower->CompileFailed(result.error());
return {}; return {};

View File

@ -618,7 +618,9 @@ MaybeHandle<WasmModuleObject> DeserializeNativeModule(
WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate); WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate);
ModuleResult decode_result = DecodeWasmModule( ModuleResult decode_result = DecodeWasmModule(
enabled_features, wire_bytes.start(), wire_bytes.end(), false, enabled_features, wire_bytes.start(), wire_bytes.end(), false,
i::wasm::kWasmOrigin, isolate->counters(), wasm_engine->allocator()); i::wasm::kWasmOrigin, isolate->counters(), isolate->metrics_recorder(),
isolate->GetOrRegisterRecorderContextId(isolate->native_context()),
DecodingMethod::kDeserialize, wasm_engine->allocator());
if (decode_result.failed()) return {}; if (decode_result.failed()) return {};
std::shared_ptr<WasmModule> module = std::move(decode_result.value()); std::shared_ptr<WasmModule> module = std::move(decode_result.value());
CHECK_NOT_NULL(module); CHECK_NOT_NULL(module);

View File

@ -309,10 +309,11 @@ STREAM_TEST(TestAllBytesArriveAOTCompilerFinishesFirst) {
size_t GetFunctionOffset(i::Isolate* isolate, const uint8_t* buffer, size_t GetFunctionOffset(i::Isolate* isolate, const uint8_t* buffer,
size_t size, size_t index) { size_t size, size_t index) {
ModuleResult result = ModuleResult result = DecodeWasmModule(
DecodeWasmModule(WasmFeatures::All(), buffer, buffer + size, false, WasmFeatures::All(), buffer, buffer + size, false,
ModuleOrigin::kWasmOrigin, isolate->counters(), ModuleOrigin::kWasmOrigin, isolate->counters(),
isolate->wasm_engine()->allocator()); isolate->metrics_recorder(), v8::metrics::Recorder::ContextId::Empty(),
DecodingMethod::kSyncStream, isolate->wasm_engine()->allocator());
CHECK(result.ok()); CHECK(result.ok());
const WasmFunction* func = &result.value()->functions[index]; const WasmFunction* func = &result.value()->functions[index];
return func->code.offset(); return func->code.offset();

View File

@ -160,7 +160,8 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
ModuleResult module_res = DecodeWasmModule( ModuleResult module_res = DecodeWasmModule(
enabled_features, wire_bytes.start(), wire_bytes.end(), kVerifyFunctions, enabled_features, wire_bytes.start(), wire_bytes.end(), kVerifyFunctions,
ModuleOrigin::kWasmOrigin, isolate->counters(), ModuleOrigin::kWasmOrigin, isolate->counters(),
isolate->wasm_engine()->allocator()); isolate->metrics_recorder(), v8::metrics::Recorder::ContextId::Empty(),
DecodingMethod::kSync, isolate->wasm_engine()->allocator());
CHECK(module_res.ok()); CHECK(module_res.ok());
WasmModule* module = module_res.value().get(); WasmModule* module = module_res.value().get();
CHECK_NOT_NULL(module); CHECK_NOT_NULL(module);

View File

@ -206,15 +206,19 @@ class WasmModuleVerifyTest : public TestWithIsolateAndZone {
} }
ModuleResult result = DecodeWasmModule( ModuleResult result = DecodeWasmModule(
enabled_features_, temp, temp + total, false, kWasmOrigin, enabled_features_, temp, temp + total, false, kWasmOrigin,
isolate()->counters(), isolate()->wasm_engine()->allocator()); isolate()->counters(), isolate()->metrics_recorder(),
v8::metrics::Recorder::ContextId::Empty(), DecodingMethod::kSync,
isolate()->wasm_engine()->allocator());
delete[] temp; delete[] temp;
return result; return result;
} }
ModuleResult DecodeModuleNoHeader(const byte* module_start, ModuleResult DecodeModuleNoHeader(const byte* module_start,
const byte* module_end) { const byte* module_end) {
return DecodeWasmModule(enabled_features_, module_start, module_end, false, return DecodeWasmModule(
kWasmOrigin, isolate()->counters(), enabled_features_, module_start, module_end, false, kWasmOrigin,
isolate()->wasm_engine()->allocator()); isolate()->counters(), isolate()->metrics_recorder(),
v8::metrics::Recorder::ContextId::Empty(), DecodingMethod::kSync,
isolate()->wasm_engine()->allocator());
} }
}; };

View File

@ -60,7 +60,8 @@ TEST_F(WasmCapiTest, Traps) {
ModuleResult result = DecodeWasmModule( ModuleResult result = DecodeWasmModule(
WasmFeatures::All(), wire_bytes()->begin(), wire_bytes()->end(), false, WasmFeatures::All(), wire_bytes()->begin(), wire_bytes()->end(), false,
ModuleOrigin::kWasmOrigin, isolate->counters(), ModuleOrigin::kWasmOrigin, isolate->counters(),
isolate->wasm_engine()->allocator()); isolate->metrics_recorder(), v8::metrics::Recorder::ContextId::Empty(),
DecodingMethod::kSync, isolate->wasm_engine()->allocator());
ASSERT_TRUE(result.ok()); ASSERT_TRUE(result.ok());
const WasmFunction* func1 = &result.value()->functions[1]; const WasmFunction* func1 = &result.value()->functions[1];
const WasmFunction* func2 = &result.value()->functions[2]; const WasmFunction* func2 = &result.value()->functions[2];