Skip to content

Commit

Permalink
Upstream WebAssembly VM and Null VM from envoyproxy/envoy-wasm. (#8020)
Browse files Browse the repository at this point in the history
Description: Upstream from envoyproxy/envoy-wasm the WebAssembly VM support along with the Null VM support and tests. This is the first PR dealing with WebAssembly filter support in envoy.  See https://github.com/envoyproxy/envoy-wasm/blob/master/WASM.md and https://github.com/envoyproxy/envoy-wasm/blob/master/docs/root/api-v2/config/wasm/wasm.rst for details.
Risk Level: Medium
Testing: Unit tests.
Docs Changes: N/A
Release Notes: N/A
Part of #4272 

Signed-off-by: John Plevyak <jplevyak@gmail.com>
  • Loading branch information
jplevyak authored and lizan committed Sep 9, 2019
1 parent ced130a commit 3f63838
Show file tree
Hide file tree
Showing 15 changed files with 875 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,5 @@ extensions/filters/common/original_src @snowp @klarose
/*/extensions/filters/listener/http_inspector @crazyxy @PiotrSikora @lizan
# attribute context
/*/extensions/filters/common/expr @kyessenov @yangminzhu
# webassembly common extension
/*/extensions/common/wasm @jplevyak @PiotrSikora
3 changes: 2 additions & 1 deletion source/common/common/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ namespace Logger {
FUNCTION(thrift) \
FUNCTION(tracing) \
FUNCTION(upstream) \
FUNCTION(udp)
FUNCTION(udp) \
FUNCTION(wasm)

enum class Id {
ALL_LOGGER_IDS(GENERATE_ENUM)
Expand Down
36 changes: 36 additions & 0 deletions source/extensions/common/wasm/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
licenses(["notice"]) # Apache 2

load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "well_known_names",
hdrs = ["well_known_names.h"],
deps = [
"//source/common/singleton:const_singleton",
],
)

envoy_cc_library(
name = "wasm_vm_interface",
hdrs = ["wasm_vm.h"],
deps = [
":well_known_names",
"//source/common/common:minimal_logger_lib",
],
)

envoy_cc_library(
name = "wasm_vm_lib",
srcs = ["wasm_vm.cc"],
deps = [
":wasm_vm_interface",
"//source/common/common:assert_lib",
"//source/extensions/common/wasm/null:null_lib",
],
)
47 changes: 47 additions & 0 deletions source/extensions/common/wasm/null/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
licenses(["notice"]) # Apache 2

load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "null_vm_plugin_interface",
hdrs = ["null_vm_plugin.h"],
deps = [
"//source/extensions/common/wasm:wasm_vm_interface",
"//source/extensions/common/wasm:well_known_names",
],
)

envoy_cc_library(
name = "null_vm_lib",
srcs = ["null_vm.cc"],
hdrs = ["null_vm.h"],
deps = [
":null_vm_plugin_interface",
"//external:abseil_node_hash_map",
"//include/envoy/registry",
"//source/common/common:assert_lib",
"//source/extensions/common/wasm:wasm_vm_interface",
"//source/extensions/common/wasm:well_known_names",
],
)

envoy_cc_library(
name = "null_lib",
srcs = ["null.cc"],
hdrs = ["null.h"],
deps = [
":null_vm_lib",
":null_vm_plugin_interface",
"//external:abseil_node_hash_map",
"//include/envoy/registry",
"//source/common/common:assert_lib",
"//source/extensions/common/wasm:wasm_vm_interface",
"//source/extensions/common/wasm:well_known_names",
],
)
27 changes: 27 additions & 0 deletions source/extensions/common/wasm/null/null.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "extensions/common/wasm/null/null.h"

#include <memory>
#include <utility>
#include <vector>

#include "envoy/registry/registry.h"

#include "common/common/assert.h"

#include "extensions/common/wasm/null/null_vm.h"
#include "extensions/common/wasm/null/null_vm_plugin.h"
#include "extensions/common/wasm/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace Common {
namespace Wasm {
namespace Null {

WasmVmPtr createVm() { return std::make_unique<NullVm>(); }

} // namespace Null
} // namespace Wasm
} // namespace Common
} // namespace Extensions
} // namespace Envoy
20 changes: 20 additions & 0 deletions source/extensions/common/wasm/null/null.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <memory>

#include "extensions/common/wasm/null/null_vm_plugin.h"
#include "extensions/common/wasm/wasm_vm.h"

namespace Envoy {
namespace Extensions {
namespace Common {
namespace Wasm {
namespace Null {

WasmVmPtr createVm();

} // namespace Null
} // namespace Wasm
} // namespace Common
} // namespace Extensions
} // namespace Envoy
104 changes: 104 additions & 0 deletions source/extensions/common/wasm/null/null_vm.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "extensions/common/wasm/null/null_vm.h"

#include <memory>
#include <utility>
#include <vector>

#include "envoy/registry/registry.h"

#include "common/common/assert.h"

#include "extensions/common/wasm/null/null_vm_plugin.h"
#include "extensions/common/wasm/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace Common {
namespace Wasm {
namespace Null {

WasmVmPtr NullVm::clone() {
auto cloned_null_vm = std::make_unique<NullVm>(*this);
cloned_null_vm->load(plugin_name_, false /* unused */);
return cloned_null_vm;
}

// "Load" the plugin by obtaining a pointer to it from the factory.
bool NullVm::load(const std::string& name, bool /* allow_precompiled */) {
auto factory = Registry::FactoryRegistry<NullVmPluginFactory>::getFactory(name);
if (!factory) {
return false;
}
plugin_name_ = name;
plugin_ = factory->create();
return true;
}

void NullVm::link(absl::string_view /* name */, bool /* needs_emscripten */) {}

void NullVm::makeModule(absl::string_view /* name */) {
// NullVm does not advertise code as emscripten so this will not get called.
NOT_REACHED_GCOVR_EXCL_LINE;
}

void NullVm::start(Common::Wasm::Context* context) {
SaveRestoreContext saved_context(context);
plugin_->start();
}

uint64_t NullVm::getMemorySize() { return std::numeric_limits<uint64_t>::max(); }

// NulVm pointers are just native pointers.
absl::optional<absl::string_view> NullVm::getMemory(uint64_t pointer, uint64_t size) {
if (pointer == 0 && size != 0) {
return absl::nullopt;
}
return absl::string_view(reinterpret_cast<char*>(pointer), static_cast<size_t>(size));
}

bool NullVm::getMemoryOffset(void* host_pointer, uint64_t* vm_pointer) {
*vm_pointer = reinterpret_cast<uint64_t>(host_pointer);
return true;
}

bool NullVm::setMemory(uint64_t pointer, uint64_t size, const void* data) {
if ((pointer == 0 || data == nullptr)) {
if (size != 0) {
return false;
} else {
return true;
}
}
auto p = reinterpret_cast<char*>(pointer);
memcpy(p, data, size);
return true;
}

bool NullVm::setWord(uint64_t pointer, Word data) {
if (pointer == 0) {
return false;
}
auto p = reinterpret_cast<char*>(pointer);
memcpy(p, &data.u64_, sizeof(data.u64_));
return true;
}

bool NullVm::getWord(uint64_t pointer, Word* data) {
if (pointer == 0) {
return false;
}
auto p = reinterpret_cast<char*>(pointer);
memcpy(&data->u64_, p, sizeof(data->u64_));
return true;
}

absl::string_view NullVm::getUserSection(absl::string_view /* name */) {
// Return nothing: there is no WASM file.
return {};
}

} // namespace Null
} // namespace Wasm
} // namespace Common
} // namespace Extensions
} // namespace Envoy
75 changes: 75 additions & 0 deletions source/extensions/common/wasm/null/null_vm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#pragma once

#include <memory>
#include <utility>
#include <vector>

#include "envoy/registry/registry.h"

#include "common/common/assert.h"

#include "extensions/common/wasm/null/null_vm_plugin.h"
#include "extensions/common/wasm/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace Common {
namespace Wasm {
namespace Null {

// 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_) {}

// WasmVm
absl::string_view vm() override { return WasmVmNames::get().Null; }
bool cloneable() override { return true; };
WasmVmPtr clone() override;
bool load(const std::string& code, bool allow_precompiled) override;
void link(absl::string_view debug_name, bool needs_emscripten) override;
void setMemoryLayout(uint64_t, uint64_t, uint64_t) override {}
void start(Common::Wasm::Context* context) override;
uint64_t getMemorySize() override;
absl::optional<absl::string_view> getMemory(uint64_t pointer, uint64_t size) override;
bool getMemoryOffset(void* host_pointer, uint64_t* vm_pointer) override;
bool setMemory(uint64_t pointer, uint64_t size, const void* data) override;
bool setWord(uint64_t pointer, Word data) override;
bool getWord(uint64_t pointer, Word* data) override;
void makeModule(absl::string_view name) override;
absl::string_view getUserSection(absl::string_view name) override;

#define _FORWARD_GET_FUNCTION(_T) \
void getFunction(absl::string_view function_name, _T* f) override { \
plugin_->getFunction(function_name, f); \
}
FOR_ALL_WASM_VM_EXPORTS(_FORWARD_GET_FUNCTION)
#undef _FORWARD_GET_FUNCTION

// These are not needed for NullVm which invokes the handlers directly.
#define _REGISTER_CALLBACK(_T) \
void registerCallback(absl::string_view, absl::string_view, _T, \
typename ConvertFunctionTypeWordToUint32<_T>::type) override{};
FOR_ALL_WASM_VM_IMPORTS(_REGISTER_CALLBACK)
#undef _REGISTER_CALLBACK

// NullVm does not advertise code as emscripten so this will not get called.
std::unique_ptr<Global<double>> makeGlobal(absl::string_view, absl::string_view,
double) override {
NOT_REACHED_GCOVR_EXCL_LINE;
};
std::unique_ptr<Global<Word>> makeGlobal(absl::string_view, absl::string_view, Word) override {
NOT_REACHED_GCOVR_EXCL_LINE;
};

std::string plugin_name_;
std::unique_ptr<NullVmPlugin> plugin_;
};

} // namespace Null
} // namespace Wasm
} // namespace Common
} // namespace Extensions
} // namespace Envoy
50 changes: 50 additions & 0 deletions source/extensions/common/wasm/null/null_vm_plugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once

#include "extensions/common/wasm/wasm_vm.h"

namespace Envoy {
namespace Extensions {
namespace Common {
namespace Wasm {
namespace Null {

// A wrapper for the natively compiled NullVm plugin which implements the WASM ABI.
class NullVmPlugin {
public:
NullVmPlugin() = default;
virtual ~NullVmPlugin() = default;

// NB: These are defined rather than declared PURE because gmock uses __LINE__ internally for
// uniqueness, making it impossible to use FOR_ALL_WASM_VM_EXPORTS with MOCK_METHOD2.
#define _DEFINE_GET_FUNCTION(_T) \
virtual void getFunction(absl::string_view, _T* f) { *f = nullptr; }
FOR_ALL_WASM_VM_EXPORTS(_DEFINE_GET_FUNCTION)
#undef _DEFIN_GET_FUNCTIONE

virtual void start() PURE;
};

/**
* Pseudo-WASM plugins using the NullVM should implement this factory and register via
* Registry::registerFactory or the convenience class RegisterFactory.
*/
class NullVmPluginFactory {
public:
virtual ~NullVmPluginFactory() = default;

/**
* Name of the plugin.
*/
virtual const std::string name() const PURE;

/**
* Create an instance of the plugin.
*/
virtual std::unique_ptr<NullVmPlugin> create() const PURE;
};

} // namespace Null
} // namespace Wasm
} // namespace Common
} // namespace Extensions
} // namespace Envoy
Loading

0 comments on commit 3f63838

Please sign in to comment.