From e5b912a91ebbb1eb5e042538de98d317496ef376 Mon Sep 17 00:00:00 2001 From: John Plevyak Date: Tue, 19 Nov 2019 22:25:05 +0000 Subject: [PATCH 1/2] Update to the newest code from envoyu-wasm. - make v8 cloneable - add VM stats and debug logging. - make v8 less build environmentally sensitive/hermetic. Signed-off-by: John Plevyak --- bazel/external/wee8.genrule_cmd | 6 ++- source/extensions/common/wasm/BUILD | 8 +++ source/extensions/common/wasm/null/BUILD | 1 + source/extensions/common/wasm/null/null.cc | 2 +- source/extensions/common/wasm/null/null.h | 2 +- source/extensions/common/wasm/null/null_vm.cc | 2 + source/extensions/common/wasm/null/null_vm.h | 14 +++-- source/extensions/common/wasm/v8/BUILD | 1 + source/extensions/common/wasm/v8/v8.cc | 34 ++++++++++--- source/extensions/common/wasm/v8/v8.h | 2 +- source/extensions/common/wasm/wasm_vm.cc | 7 +-- source/extensions/common/wasm/wasm_vm.h | 16 +++--- test/extensions/common/wasm/wasm_vm_test.cc | 51 +++++++++++-------- 13 files changed, 103 insertions(+), 43 deletions(-) diff --git a/bazel/external/wee8.genrule_cmd b/bazel/external/wee8.genrule_cmd index 742527cabfd38..bc4a6ede05340 100644 --- a/bazel/external/wee8.genrule_cmd +++ b/bazel/external/wee8.genrule_cmd @@ -71,7 +71,11 @@ WEE8_BUILD_ARGS+=" v8_use_external_startup_data=false" WEE8_BUILD_ARGS+=" v8_enable_shared_ro_heap=false" # Build wee8. -third_party/depot_tools/gn gen out/wee8 --args="$$WEE8_BUILD_ARGS" +if [[ `uname` == "Darwin" ]]; then + buildtools/mac/gn gen out/wee8 --args="$$WEE8_BUILD_ARGS" +else + buildtools/linux64/gn gen out/wee8 --args="$$WEE8_BUILD_ARGS" +fi third_party/depot_tools/ninja -C out/wee8 wee8 # Move compiled library to the expected destinations. diff --git a/source/extensions/common/wasm/BUILD b/source/extensions/common/wasm/BUILD index dbc0f83910be8..9333c679421f6 100644 --- a/source/extensions/common/wasm/BUILD +++ b/source/extensions/common/wasm/BUILD @@ -25,6 +25,14 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "wasm_vm_base", + hdrs = ["wasm_vm_base.h"], + deps = [ + "//source/common/stats:stats_lib", + ], +) + envoy_cc_library( name = "wasm_vm_lib", srcs = ["wasm_vm.cc"], diff --git a/source/extensions/common/wasm/null/BUILD b/source/extensions/common/wasm/null/BUILD index eed8e62d2e49b..6f93952bc9cfc 100644 --- a/source/extensions/common/wasm/null/BUILD +++ b/source/extensions/common/wasm/null/BUILD @@ -26,6 +26,7 @@ envoy_cc_library( "//external:abseil_node_hash_map", "//include/envoy/registry", "//source/common/common:assert_lib", + "//source/extensions/common/wasm:wasm_vm_base", "//source/extensions/common/wasm:wasm_vm_interface", "//source/extensions/common/wasm:well_known_names", ], diff --git a/source/extensions/common/wasm/null/null.cc b/source/extensions/common/wasm/null/null.cc index 185dde60780ab..1a2fc46924331 100644 --- a/source/extensions/common/wasm/null/null.cc +++ b/source/extensions/common/wasm/null/null.cc @@ -18,7 +18,7 @@ namespace Common { namespace Wasm { namespace Null { -WasmVmPtr createVm() { return std::make_unique(); } +WasmVmPtr createVm(Stats::ScopeSharedPtr scope) { return std::make_unique(scope); } } // namespace Null } // namespace Wasm diff --git a/source/extensions/common/wasm/null/null.h b/source/extensions/common/wasm/null/null.h index 7d88fb356923e..1fab2784f04bc 100644 --- a/source/extensions/common/wasm/null/null.h +++ b/source/extensions/common/wasm/null/null.h @@ -11,7 +11,7 @@ namespace Common { namespace Wasm { namespace Null { -WasmVmPtr createVm(); +WasmVmPtr createVm(Stats::ScopeSharedPtr scope); } // namespace Null } // namespace Wasm diff --git a/source/extensions/common/wasm/null/null_vm.cc b/source/extensions/common/wasm/null/null_vm.cc index d2cb16b994f16..075c07ef4ced3 100644 --- a/source/extensions/common/wasm/null/null_vm.cc +++ b/source/extensions/common/wasm/null/null_vm.cc @@ -17,6 +17,8 @@ namespace Common { namespace Wasm { namespace Null { +VmGlobalStats global_stats_; + WasmVmPtr NullVm::clone() { auto cloned_null_vm = std::make_unique(*this); cloned_null_vm->load(plugin_name_, false /* unused */); diff --git a/source/extensions/common/wasm/null/null_vm.h b/source/extensions/common/wasm/null/null_vm.h index d23e332bcb77d..52e70cc69aab6 100644 --- a/source/extensions/common/wasm/null/null_vm.h +++ b/source/extensions/common/wasm/null/null_vm.h @@ -9,6 +9,7 @@ #include "common/common/assert.h" #include "extensions/common/wasm/null/null_vm_plugin.h" +#include "extensions/common/wasm/wasm_vm_base.h" #include "extensions/common/wasm/well_known_names.h" namespace Envoy { @@ -17,16 +18,21 @@ namespace Common { namespace Wasm { namespace Null { +extern VmGlobalStats global_stats_; + // The NullVm wraps a C++ WASM plugin which has been compiled with the WASM API // and linked directly into the Envoy process. This is useful for development // in that it permits the debugger to set breakpoints in both Envoy and the plugin. -struct NullVm : public WasmVm { - NullVm() = default; - NullVm(const NullVm& other) : plugin_name_(other.plugin_name_) {} +struct NullVm : public WasmVmBase { + NullVm(Stats::ScopeSharedPtr scope) + : WasmVmBase(scope, &global_stats_, WasmRuntimeNames::get().Null) {} + NullVm(const NullVm& other) + : WasmVmBase(other.scope_, &global_stats_, WasmRuntimeNames::get().Null), + plugin_name_(other.plugin_name_) {} // WasmVm absl::string_view runtime() override { return WasmRuntimeNames::get().Null; } - bool cloneable() override { return true; }; + Cloneable cloneable() override { return Cloneable::InstantiatedModule; }; WasmVmPtr clone() override; bool load(const std::string& code, bool allow_precompiled) override; void link(absl::string_view debug_name) override; diff --git a/source/extensions/common/wasm/v8/BUILD b/source/extensions/common/wasm/v8/BUILD index fb5a473026402..04d0954d1b2cd 100644 --- a/source/extensions/common/wasm/v8/BUILD +++ b/source/extensions/common/wasm/v8/BUILD @@ -17,6 +17,7 @@ envoy_cc_library( ], deps = [ "//source/common/common:assert_lib", + "//source/extensions/common/wasm:wasm_vm_base", "//source/extensions/common/wasm:wasm_vm_interface", "//source/extensions/common/wasm:well_known_names", ], diff --git a/source/extensions/common/wasm/v8/v8.cc b/source/extensions/common/wasm/v8/v8.cc index 44e4a46f1989d..073e32bec4af6 100644 --- a/source/extensions/common/wasm/v8/v8.cc +++ b/source/extensions/common/wasm/v8/v8.cc @@ -6,6 +6,7 @@ #include "common/common/assert.h" +#include "extensions/common/wasm/wasm_vm_base.h" #include "extensions/common/wasm/well_known_names.h" #include "absl/container/flat_hash_map.h" @@ -18,6 +19,8 @@ namespace Common { namespace Wasm { namespace V8 { +VmGlobalStats global_stats_; + wasm::Engine* engine() { static const auto engine = wasm::Engine::make(); return engine.get(); @@ -33,9 +36,9 @@ struct FuncData { using FuncDataPtr = std::unique_ptr; -class V8 : public WasmVm { +class V8 : public WasmVmBase { public: - V8() = default; + V8(Stats::ScopeSharedPtr scope) : WasmVmBase(scope, &global_stats_, WasmRuntimeNames::get().V8) {} // Extensions::Common::Wasm::WasmVm absl::string_view runtime() override { return WasmRuntimeNames::get().V8; } @@ -44,9 +47,8 @@ class V8 : public WasmVm { absl::string_view getCustomSection(absl::string_view name) override; void link(absl::string_view debug_name) override; - // V8 is currently not cloneable. - bool cloneable() override { return false; } - WasmVmPtr clone() override { return nullptr; } + Cloneable cloneable() override { return Cloneable::CompiledBytecode; } + WasmVmPtr clone() override; uint64_t getMemorySize() override; absl::optional getMemory(uint64_t pointer, uint64_t size) override; @@ -89,6 +91,7 @@ class V8 : public WasmVm { wasm::vec source_ = wasm::vec::invalid(); wasm::own store_; wasm::own module_; + wasm::own> shared_module_; wasm::own instance_; wasm::own memory_; wasm::own table_; @@ -247,9 +250,28 @@ bool V8::load(const std::string& code, bool /* allow_precompiled */) { ::memcpy(source_.get(), code.data(), code.size()); module_ = wasm::Module::make(store_.get(), source_); + if (module_) { + shared_module_ = module_->share(); + RELEASE_ASSERT(shared_module_ != nullptr, ""); + } + return module_ != nullptr; } +WasmVmPtr V8::clone() { + ENVOY_LOG(trace, "clone()"); + ASSERT(shared_module_ != nullptr); + + auto clone = std::make_unique(scope_); + clone->store_ = wasm::Store::make(engine()); + RELEASE_ASSERT(clone->store_ != nullptr, ""); + + clone->module_ = wasm::Module::obtain(clone->store_.get(), shared_module_.get()); + RELEASE_ASSERT(clone->module_ != nullptr, ""); + + return clone; +} + absl::string_view V8::getCustomSection(absl::string_view name) { ENVOY_LOG(trace, "getCustomSection(\"{}\")", name); ASSERT(source_.get() != nullptr); @@ -562,7 +584,7 @@ void V8::getModuleFunctionImpl(absl::string_view function_name, }; } -WasmVmPtr createVm() { return std::make_unique(); } +WasmVmPtr createVm(Stats::ScopeSharedPtr scope) { return std::make_unique(scope); } } // namespace V8 } // namespace Wasm diff --git a/source/extensions/common/wasm/v8/v8.h b/source/extensions/common/wasm/v8/v8.h index 3650f190a73c8..1cee4424e8152 100644 --- a/source/extensions/common/wasm/v8/v8.h +++ b/source/extensions/common/wasm/v8/v8.h @@ -10,7 +10,7 @@ namespace Common { namespace Wasm { namespace V8 { -WasmVmPtr createVm(); +WasmVmPtr createVm(Stats::ScopeSharedPtr scope); } // namespace V8 } // namespace Wasm diff --git a/source/extensions/common/wasm/wasm_vm.cc b/source/extensions/common/wasm/wasm_vm.cc index 34f2745331440..3f281b149fc37 100644 --- a/source/extensions/common/wasm/wasm_vm.cc +++ b/source/extensions/common/wasm/wasm_vm.cc @@ -3,6 +3,7 @@ #include #include "extensions/common/wasm/null/null.h" + #include "extensions/common/wasm/v8/v8.h" #include "extensions/common/wasm/well_known_names.h" @@ -14,13 +15,13 @@ namespace Wasm { thread_local Envoy::Extensions::Common::Wasm::Context* current_context_ = nullptr; thread_local uint32_t effective_context_id_ = 0; -WasmVmPtr createWasmVm(absl::string_view runtime) { +WasmVmPtr createWasmVm(absl::string_view runtime, Stats::ScopeSharedPtr scope) { if (runtime.empty()) { throw WasmVmException("Failed to create WASM VM with unspecified runtime."); } else if (runtime == WasmRuntimeNames::get().Null) { - return Null::createVm(); + return Null::createVm(scope); } else if (runtime == WasmRuntimeNames::get().V8) { - return V8::createVm(); + return V8::createVm(scope); } else { throw WasmVmException(fmt::format( "Failed to create WASM VM using {} runtime. Envoy was compiled without support for it.", diff --git a/source/extensions/common/wasm/wasm_vm.h b/source/extensions/common/wasm/wasm_vm.h index 7cf58ed561550..45e5c08d0a81b 100644 --- a/source/extensions/common/wasm/wasm_vm.h +++ b/source/extensions/common/wasm/wasm_vm.h @@ -3,6 +3,7 @@ #include #include "envoy/common/exception.h" +#include "envoy/stats/scope.h" #include "common/common/logger.h" @@ -98,8 +99,7 @@ template using WasmCallWord = std::function) _f(WasmCallVoid<1>) _f(WasmCallVoid<2>) _f(WasmCallVoid<3>) \ - _f(WasmCallVoid<4>) _f(WasmCallVoid<5>) _f(WasmCallVoid<8>) _f(WasmCallWord<0>) \ - _f(WasmCallWord<1>) _f(WasmCallWord<2>) _f(WasmCallWord<3>) + _f(WasmCallVoid<5>) _f(WasmCallWord<1>) _f(WasmCallWord<2>) _f(WasmCallWord<3>) // Calls out of the WASM VM. // 1st arg is always a pointer to raw_context (void*). @@ -111,6 +111,7 @@ template using WasmCallbackWord = WasmFuncType* // Extended with W = Word // Z = void, j = uint32_t, l = int64_t, m = uint64_t using WasmCallback_WWl = Word (*)(void*, Word, int64_t); +using WasmCallback_WWlWW = Word (*)(void*, Word, int64_t, Word, Word); using WasmCallback_WWm = Word (*)(void*, Word, uint64_t); using WasmCallback_dd = double (*)(void*, double); @@ -119,8 +120,11 @@ using WasmCallback_dd = double (*)(void*, double); _f(WasmCallbackVoid<4>) _f(WasmCallbackWord<0>) _f(WasmCallbackWord<1>) \ _f(WasmCallbackWord<2>) _f(WasmCallbackWord<3>) _f(WasmCallbackWord<4>) \ _f(WasmCallbackWord<5>) _f(WasmCallbackWord<6>) _f(WasmCallbackWord<7>) \ - _f(WasmCallbackWord<8>) _f(WasmCallbackWord<9>) _f(WasmCallback_WWl) \ - _f(WasmCallback_WWm) _f(WasmCallback_dd) + _f(WasmCallbackWord<8>) _f(WasmCallbackWord<9>) _f(WasmCallbackWord<10>) \ + _f(WasmCallback_WWl) _f(WasmCallback_WWlWW) _f(WasmCallback_WWm) \ + _f(WasmCallback_dd) + +enum class Cloneable { NotCloneable, CompiledBytecode, InstantiatedModule }; // Wasm VM instance. Provides the low level WASM interface. class WasmVm : public Logger::Loggable { @@ -143,7 +147,7 @@ class WasmVm : public Logger::Loggable { * VM from scratch for each worker. * @return true if the VM is cloneable. */ - virtual bool cloneable() PURE; + virtual Cloneable cloneable() PURE; /** * Make a worker/thread-specific copy if supported by the underlying VM system (see cloneable() @@ -287,7 +291,7 @@ struct SaveRestoreContext { }; // Create a new low-level WASM VM using runtime of the given type (e.g. "envoy.wasm.runtime.wavm"). -WasmVmPtr createWasmVm(absl::string_view runtime); +WasmVmPtr createWasmVm(absl::string_view runtime, Stats::ScopeSharedPtr scope); } // namespace Wasm } // namespace Common diff --git a/test/extensions/common/wasm/wasm_vm_test.cc b/test/extensions/common/wasm/wasm_vm_test.cc index 1584406c89097..87085a97475ef 100644 --- a/test/extensions/common/wasm/wasm_vm_test.cc +++ b/test/extensions/common/wasm/wasm_vm_test.cc @@ -1,5 +1,7 @@ #include "envoy/registry/registry.h" +#include "common/stats/isolated_store_impl.h" + #include "extensions/common/wasm/null/null_vm_plugin.h" #include "extensions/common/wasm/wasm_vm.h" @@ -35,7 +37,7 @@ class PluginFactory : public Null::NullVmPluginFactory { }; TestNullVmPlugin* test_null_vm_plugin_ = nullptr; -REGISTER_FACTORY(PluginFactory, Null::NullVmPluginFactory); +Envoy::Registry::RegisterFactory register_; std::unique_ptr PluginFactory::create() const { auto result = std::make_unique(); @@ -43,29 +45,38 @@ std::unique_ptr PluginFactory::create() const { return result; } -TEST(BadVmTest, NoRuntime) { - EXPECT_THROW_WITH_MESSAGE(createWasmVm(""), WasmVmException, +class BaseVmTest : public testing::Test { +public: + BaseVmTest() : scope_(Stats::ScopeSharedPtr(stats_store.createScope("wasm."))) {} + +protected: + Stats::IsolatedStoreImpl stats_store; + Stats::ScopeSharedPtr scope_; +}; + +TEST_F(BaseVmTest, NoRuntime) { + EXPECT_THROW_WITH_MESSAGE(createWasmVm("", scope_), WasmVmException, "Failed to create WASM VM with unspecified runtime."); } -TEST(BadVmTest, BadRuntime) { - EXPECT_THROW_WITH_MESSAGE(createWasmVm("envoy.wasm.runtime.invalid"), WasmVmException, +TEST_F(BaseVmTest, BadRuntime) { + EXPECT_THROW_WITH_MESSAGE(createWasmVm("envoy.wasm.runtime.invalid", scope_), WasmVmException, "Failed to create WASM VM using envoy.wasm.runtime.invalid runtime. " "Envoy was compiled without support for it."); } -TEST(NullVmTest, NullVmStartup) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.null"); +TEST_F(BaseVmTest, NullVmStartup) { + auto wasm_vm = createWasmVm("envoy.wasm.runtime.null", scope_); EXPECT_TRUE(wasm_vm != nullptr); EXPECT_TRUE(wasm_vm->runtime() == "envoy.wasm.runtime.null"); - EXPECT_TRUE(wasm_vm->cloneable()); + EXPECT_TRUE(wasm_vm->cloneable() == Cloneable::InstantiatedModule); auto wasm_vm_clone = wasm_vm->clone(); EXPECT_TRUE(wasm_vm_clone != nullptr); EXPECT_TRUE(wasm_vm->getCustomSection("user").empty()); } -TEST(NullVmTest, NullVmMemory) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.null"); +TEST_F(BaseVmTest, NullVmMemory) { + auto wasm_vm = createWasmVm("envoy.wasm.runtime.null", scope_); EXPECT_EQ(wasm_vm->getMemorySize(), std::numeric_limits::max()); std::string d = "data"; auto m = wasm_vm->getMemory(reinterpret_cast(d.data()), d.size()).value(); @@ -114,26 +125,23 @@ Word bad_pong2(void*, Word) { return 2; } // pong() with wrong argument type. double bad_pong3(void*, double) { return 3; } -class WasmVmTest : public testing::Test { +class WasmVmTest : public BaseVmTest { public: void SetUp() override { g_host_functions = new MockHostFunctions(); } void TearDown() override { delete g_host_functions; } }; TEST_F(WasmVmTest, V8BadCode) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); ASSERT_TRUE(wasm_vm != nullptr); EXPECT_FALSE(wasm_vm->load("bad code", false)); } TEST_F(WasmVmTest, V8Code) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); ASSERT_TRUE(wasm_vm != nullptr); - EXPECT_TRUE(wasm_vm->runtime() == "envoy.wasm.runtime.v8"); - EXPECT_FALSE(wasm_vm->cloneable()); - EXPECT_TRUE(wasm_vm->clone() == nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/common/wasm/test_data/test_rust.wasm")); @@ -141,10 +149,13 @@ TEST_F(WasmVmTest, V8Code) { EXPECT_THAT(wasm_vm->getCustomSection("producers"), HasSubstr("rustc")); EXPECT_TRUE(wasm_vm->getCustomSection("emscripten_metadata").empty()); + + EXPECT_TRUE(wasm_vm->cloneable() == Cloneable::CompiledBytecode); + EXPECT_TRUE(wasm_vm->clone() != nullptr); } TEST_F(WasmVmTest, V8BadHostFunctions) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); ASSERT_TRUE(wasm_vm != nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -172,7 +183,7 @@ TEST_F(WasmVmTest, V8BadHostFunctions) { } TEST_F(WasmVmTest, V8BadModuleFunctions) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); ASSERT_TRUE(wasm_vm != nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -200,7 +211,7 @@ TEST_F(WasmVmTest, V8BadModuleFunctions) { } TEST_F(WasmVmTest, V8FunctionCalls) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); ASSERT_TRUE(wasm_vm != nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -238,7 +249,7 @@ TEST_F(WasmVmTest, V8FunctionCalls) { } TEST_F(WasmVmTest, V8Memory) { - auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8"); + auto wasm_vm = createWasmVm("envoy.wasm.runtime.v8", scope_); ASSERT_TRUE(wasm_vm != nullptr); auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( From 0dd69993df98b0ee1955142a2242ace19e45a3dc Mon Sep 17 00:00:00 2001 From: John Plevyak Date: Wed, 20 Nov 2019 23:29:33 +0000 Subject: [PATCH 2/2] Address comments. Add missing file. Signed-off-by: John Plevyak --- source/extensions/common/wasm/null/null_vm.cc | 3 +- source/extensions/common/wasm/null/null_vm.h | 7 ++- source/extensions/common/wasm/v8/v8.cc | 6 +- source/extensions/common/wasm/wasm_vm.cc | 1 - source/extensions/common/wasm/wasm_vm_base.h | 63 +++++++++++++++++++ 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 source/extensions/common/wasm/wasm_vm_base.h diff --git a/source/extensions/common/wasm/null/null_vm.cc b/source/extensions/common/wasm/null/null_vm.cc index 075c07ef4ced3..c23d6d7bd0c48 100644 --- a/source/extensions/common/wasm/null/null_vm.cc +++ b/source/extensions/common/wasm/null/null_vm.cc @@ -7,6 +7,7 @@ #include "envoy/registry/registry.h" #include "common/common/assert.h" +#include "common/singleton/threadsafe_singleton.h" #include "extensions/common/wasm/null/null_vm_plugin.h" #include "extensions/common/wasm/well_known_names.h" @@ -17,7 +18,7 @@ namespace Common { namespace Wasm { namespace Null { -VmGlobalStats global_stats_; +ThreadSafeSingleton global_stats_; WasmVmPtr NullVm::clone() { auto cloned_null_vm = std::make_unique(*this); diff --git a/source/extensions/common/wasm/null/null_vm.h b/source/extensions/common/wasm/null/null_vm.h index 52e70cc69aab6..64f0e666978e1 100644 --- a/source/extensions/common/wasm/null/null_vm.h +++ b/source/extensions/common/wasm/null/null_vm.h @@ -7,6 +7,7 @@ #include "envoy/registry/registry.h" #include "common/common/assert.h" +#include "common/singleton/threadsafe_singleton.h" #include "extensions/common/wasm/null/null_vm_plugin.h" #include "extensions/common/wasm/wasm_vm_base.h" @@ -18,16 +19,16 @@ namespace Common { namespace Wasm { namespace Null { -extern VmGlobalStats global_stats_; +extern ThreadSafeSingleton global_stats_; // The NullVm wraps a C++ WASM plugin which has been compiled with the WASM API // and linked directly into the Envoy process. This is useful for development // in that it permits the debugger to set breakpoints in both Envoy and the plugin. struct NullVm : public WasmVmBase { NullVm(Stats::ScopeSharedPtr scope) - : WasmVmBase(scope, &global_stats_, WasmRuntimeNames::get().Null) {} + : WasmVmBase(scope, &global_stats_.get(), WasmRuntimeNames::get().Null) {} NullVm(const NullVm& other) - : WasmVmBase(other.scope_, &global_stats_, WasmRuntimeNames::get().Null), + : WasmVmBase(other.scope_, &global_stats_.get(), WasmRuntimeNames::get().Null), plugin_name_(other.plugin_name_) {} // WasmVm diff --git a/source/extensions/common/wasm/v8/v8.cc b/source/extensions/common/wasm/v8/v8.cc index 073e32bec4af6..e7fbea3620d90 100644 --- a/source/extensions/common/wasm/v8/v8.cc +++ b/source/extensions/common/wasm/v8/v8.cc @@ -5,6 +5,7 @@ #include #include "common/common/assert.h" +#include "common/singleton/threadsafe_singleton.h" #include "extensions/common/wasm/wasm_vm_base.h" #include "extensions/common/wasm/well_known_names.h" @@ -19,7 +20,7 @@ namespace Common { namespace Wasm { namespace V8 { -VmGlobalStats global_stats_; +ThreadSafeSingleton global_stats_; wasm::Engine* engine() { static const auto engine = wasm::Engine::make(); @@ -38,7 +39,8 @@ using FuncDataPtr = std::unique_ptr; class V8 : public WasmVmBase { public: - V8(Stats::ScopeSharedPtr scope) : WasmVmBase(scope, &global_stats_, WasmRuntimeNames::get().V8) {} + V8(Stats::ScopeSharedPtr scope) + : WasmVmBase(scope, &global_stats_.get(), WasmRuntimeNames::get().V8) {} // Extensions::Common::Wasm::WasmVm absl::string_view runtime() override { return WasmRuntimeNames::get().V8; } diff --git a/source/extensions/common/wasm/wasm_vm.cc b/source/extensions/common/wasm/wasm_vm.cc index 3f281b149fc37..88ee11c60bc0d 100644 --- a/source/extensions/common/wasm/wasm_vm.cc +++ b/source/extensions/common/wasm/wasm_vm.cc @@ -3,7 +3,6 @@ #include #include "extensions/common/wasm/null/null.h" - #include "extensions/common/wasm/v8/v8.h" #include "extensions/common/wasm/well_known_names.h" diff --git a/source/extensions/common/wasm/wasm_vm_base.h b/source/extensions/common/wasm/wasm_vm_base.h new file mode 100644 index 0000000000000..4ad0950ffa72c --- /dev/null +++ b/source/extensions/common/wasm/wasm_vm_base.h @@ -0,0 +1,63 @@ +#pragma once + +#include "envoy/stats/scope.h" +#include "envoy/stats/stats.h" +#include "envoy/stats/stats_macros.h" + +#include "extensions/common/wasm/wasm_vm.h" + +#include "absl/strings/str_cat.h" + +namespace Envoy { +namespace Extensions { +namespace Common { +namespace Wasm { + +/** + * Wasm host stats. + */ +#define ALL_VM_STATS(COUNTER, GAUGE) \ + COUNTER(created) \ + COUNTER(cloned) \ + GAUGE(active, NeverImport) + +struct VmStats { + ALL_VM_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT) +}; + +struct VmGlobalStats { + std::atomic active_; +}; + +// Wasm VM base instance. Provides common behavior (e.g. Stats). +class WasmVmBase : public WasmVm { +public: + WasmVmBase(Stats::ScopeSharedPtr scope, VmGlobalStats* global_stats_ptr, + absl::string_view runtime) + : scope_(scope), global_stats_ptr_(global_stats_ptr), + stats_(VmStats{ + ALL_VM_STATS(POOL_COUNTER_PREFIX(*scope_, absl::StrCat("wasm_vm.", runtime, ".")), + POOL_GAUGE_PREFIX(*scope_, absl::StrCat("wasm_vm.", runtime, ".")))}), + runtime_(std::string(runtime)) { + global_stats_ptr_->active_++; + stats_.created_.inc(); + stats_.active_.set(global_stats_ptr_->active_); + ENVOY_LOG(debug, "WasmVm created {} now active", runtime_, global_stats_ptr_->active_); + } + virtual ~WasmVmBase() { + global_stats_ptr_->active_--; + stats_.active_.set(global_stats_ptr_->active_); + ENVOY_LOG(debug, "~WasmVm {} {} remaining active", runtime_, global_stats_ptr_->active_); + } + +protected: + Stats::ScopeSharedPtr scope_; + VmGlobalStats* global_stats_ptr_; + VmStats stats_; + std::string runtime_; +}; + +} // namespace Wasm +} // namespace Common +} // namespace Extensions +} // namespace Envoy