From 5edc48850c1f66a8aed0b8c4bbf06b80ea7be734 Mon Sep 17 00:00:00 2001 From: Pete Stevenson Date: Wed, 9 Aug 2023 17:00:14 -0700 Subject: [PATCH] [record & replay] Create virtual base classes for wrapped maps & arrays. (#1662) Summary: In preparation for introducing record & replay, we create a virtual base classes for wrapped BCC maps & arrays. In a future PR, we will introduce two new implementations, one for recording and one for replaying. Relevant Issues: https://github.com/pixie-io/pixie/issues/1163 Type of change: /kind feature Test Plan: fully covered by existing tests. --------- Signed-off-by: Pete Stevenson --- src/stirling/bpf_tools/bcc_wrapper.cc | 5 + src/stirling/bpf_tools/bcc_wrapper.h | 140 ++++++++++++++++++-------- 2 files changed, 103 insertions(+), 42 deletions(-) diff --git a/src/stirling/bpf_tools/bcc_wrapper.cc b/src/stirling/bpf_tools/bcc_wrapper.cc index a8c84b102de..551df60f7cc 100644 --- a/src/stirling/bpf_tools/bcc_wrapper.cc +++ b/src/stirling/bpf_tools/bcc_wrapper.cc @@ -451,6 +451,11 @@ void BCCWrapperImpl::Close() { std::unique_ptr CreateBCC() { return std::make_unique(); } +std::unique_ptr WrappedBCCStackTable::Create(bpf_tools::BCCWrapper* bcc, + const std::string& name) { + return std::make_unique(bcc, name); +} + } // namespace bpf_tools } // namespace stirling } // namespace px diff --git a/src/stirling/bpf_tools/bcc_wrapper.h b/src/stirling/bpf_tools/bcc_wrapper.h index 42d845fe32e..83cdcced786 100644 --- a/src/stirling/bpf_tools/bcc_wrapper.h +++ b/src/stirling/bpf_tools/bcc_wrapper.h @@ -332,17 +332,23 @@ class BCCWrapperImpl : public BCCWrapper { std::unique_ptr CreateBCC(); +// Wrapped maps & arrays. template class WrappedBCCArrayTable { public: - using U = ebpf::BPFArrayTable; + virtual ~WrappedBCCArrayTable() {} + static std::unique_ptr Create(BCCWrapper* bcc, const std::string& name); - static std::unique_ptr Create(bpf_tools::BCCWrapper* bcc, - const std::string& name) { - return std::unique_ptr(new WrappedBCCArrayTable(bcc, name)); - } + virtual StatusOr GetValue(const uint32_t idx) = 0; + virtual Status SetValue(const uint32_t idx, const T& value) = 0; +}; + +template +class WrappedBCCArrayTableImpl : public WrappedBCCArrayTable { + public: + using U = ebpf::BPFArrayTable; - StatusOr GetValue(const uint32_t idx) { + StatusOr GetValue(const uint32_t idx) override { T value; ebpf::StatusTuple s = underlying_->get_value(idx, value); if (!s.ok()) { @@ -351,7 +357,7 @@ class WrappedBCCArrayTable { return value; } - Status SetValue(const uint32_t idx, const T& value) { + Status SetValue(const uint32_t idx, const T& value) override { ebpf::StatusTuple s = underlying_->update_value(idx, value); if (!s.ok()) { return error::Internal(absl::Substitute(err_msg_, "set", name_, idx, s.msg())); @@ -359,31 +365,39 @@ class WrappedBCCArrayTable { return Status::OK(); } - private: - WrappedBCCArrayTable(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) { + WrappedBCCArrayTableImpl(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) { underlying_ = std::make_unique(bcc->BPF().get_array_table(name_)); } + private: const std::string name_; char const* const err_msg_ = "BPF failed to $0 value for array table: $1, index: $2. $3."; std::unique_ptr underlying_; }; +template +class WrappedBCCMap { + public: + static std::unique_ptr Create(bpf_tools::BCCWrapper* bcc, const std::string& name); + virtual ~WrappedBCCMap() {} + + virtual size_t capacity() const = 0; + virtual StatusOr GetValue(const K& key) const = 0; + virtual Status SetValue(const K& key, const V& value) = 0; + virtual Status RemoveValue(const K& key) = 0; + virtual std::vector> GetTableOffline(const bool clear_table = false) = 0; +}; + // Template parameter kUserSpaceManaged enables the "shadow keys" optimization. // Set to true iff the map is modified/updated from user space only. template -class WrappedBCCMap { +class WrappedBCCMapImpl : public WrappedBCCMap { public: using U = ebpf::BPFHashTable; - static std::unique_ptr Create(bpf_tools::BCCWrapper* bcc, - const std::string& name) { - return std::unique_ptr(new WrappedBCCMap(bcc, name)); - } - - size_t capacity() const { return underlying_->capacity(); } + size_t capacity() const override { return underlying_->capacity(); } - StatusOr GetValue(const K& key) const { + StatusOr GetValue(const K& key) const override { V value; ebpf::StatusTuple s = underlying_->get_value(key, value); if (!s.ok()) { @@ -392,7 +406,7 @@ class WrappedBCCMap { return value; } - Status SetValue(const K& key, const V& value) { + Status SetValue(const K& key, const V& value) override { ebpf::StatusTuple s = underlying_->update_value(key, value); if (!s.ok()) { return error::Internal(absl::Substitute(err_msg_, "set", name_, s.msg())); @@ -403,7 +417,7 @@ class WrappedBCCMap { return Status::OK(); } - Status RemoveValue(const K& key) { + Status RemoveValue(const K& key) override { if constexpr (kUserSpaceManaged) { if (!shadow_keys_.contains(key)) { return Status::OK(); @@ -420,7 +434,7 @@ class WrappedBCCMap { return Status::OK(); } - std::vector> GetTableOffline(const bool clear_table = false) { + std::vector> GetTableOffline(const bool clear_table = false) override { if constexpr (!kUserSpaceManaged) { return underlying_->get_table_offline(clear_table); } @@ -443,11 +457,11 @@ class WrappedBCCMap { return r; } - private: - WrappedBCCMap(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) { + WrappedBCCMapImpl(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) { underlying_ = std::make_unique(bcc->BPF().get_hash_table(name_)); } + private: const std::string name_; char const* const err_msg_ = "BPF failed to $0 value for map: $1. $2."; std::unique_ptr underlying_; @@ -457,14 +471,19 @@ class WrappedBCCMap { template class WrappedBCCPerCPUArrayTable { public: - using U = ebpf::BPFPercpuArrayTable; - static std::unique_ptr Create(bpf_tools::BCCWrapper* bcc, - const std::string& name) { - return std::unique_ptr(new WrappedBCCPerCPUArrayTable(bcc, name)); - } + const std::string& name); + virtual ~WrappedBCCPerCPUArrayTable() {} + + virtual Status SetValues(const int idx, const T& value) = 0; +}; - Status SetValues(const int idx, const T& value) { +template +class WrappedBCCPerCPUArrayTableImpl : public WrappedBCCPerCPUArrayTable { + public: + using U = ebpf::BPFPercpuArrayTable; + + Status SetValues(const int idx, const T& value) override { std::vector values(bpf_tools::BCCWrapper::kCPUCount, value); ebpf::StatusTuple s = underlying_->update_value(idx, values); if (!s.ok()) { @@ -474,45 +493,82 @@ class WrappedBCCPerCPUArrayTable { return Status::OK(); } - private: - WrappedBCCPerCPUArrayTable(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) { + WrappedBCCPerCPUArrayTableImpl(bpf_tools::BCCWrapper* bcc, const std::string& name) + : name_(name) { underlying_ = std::make_unique(bcc->BPF().get_percpu_array_table(name_)); } + private: const std::string name_; std::unique_ptr underlying_; }; class WrappedBCCStackTable { public: - using U = ebpf::BPFStackTable; - static std::unique_ptr Create(bpf_tools::BCCWrapper* bcc, - const std::string& name) { - return std::unique_ptr(new WrappedBCCStackTable(bcc, name)); - } + const std::string& name); + virtual ~WrappedBCCStackTable() {} - std::vector GetStackAddr(const int stack_id, const bool clear_stack_id) { + virtual std::vector GetStackAddr(const int stack_id, const bool clear_stack_id) = 0; + virtual std::string GetAddrSymbol(const uintptr_t addr, const int pid) = 0; + virtual void ClearStackID(const int stack_id) = 0; +}; + +class WrappedBCCStackTableImpl : public WrappedBCCStackTable { + public: + using U = ebpf::BPFStackTable; + + std::vector GetStackAddr(const int stack_id, const bool clear_stack_id) override { return underlying_->get_stack_addr(stack_id, clear_stack_id); } - std::string GetAddrSymbol(const uintptr_t addr, const int pid) { + std::string GetAddrSymbol(const uintptr_t addr, const int pid) override { return underlying_->get_addr_symbol(addr, pid); } - void ClearStackID(const int stack_id) { underlying_->clear_stack_id(stack_id); } - - U* RawPtr() { return underlying_.get(); } + void ClearStackID(const int stack_id) override { underlying_->clear_stack_id(stack_id); } - private: - WrappedBCCStackTable(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) { + WrappedBCCStackTableImpl(bpf_tools::BCCWrapper* bcc, const std::string& name) : name_(name) { underlying_ = std::make_unique(bcc->BPF().get_stack_table(name_)); } + private: const std::string name_; std::unique_ptr underlying_; }; +// Creators fns for wrapped maps & arrays: +template +std::unique_ptr CreateBCCWrappedMapOrArray(BCCWrapper* bcc, const std::string& name) { + // The decision logic for "normal" vs. "recording" vs. "replaying" impl. will be inserted + // here in a future PR. + return std::make_unique(bcc, name); +} + +template +std::unique_ptr> WrappedBCCArrayTable::Create(BCCWrapper* bcc, + const std::string& name) { + using BaseT = WrappedBCCArrayTable; + using ImplT = WrappedBCCArrayTableImpl; + return CreateBCCWrappedMapOrArray(bcc, name); +} + +template +std::unique_ptr> WrappedBCCMap::Create(BCCWrapper* bcc, + const std::string& name) { + using BaseT = WrappedBCCMap; + using ImplT = WrappedBCCMapImpl; + return CreateBCCWrappedMapOrArray(bcc, name); +} + +template +std::unique_ptr> WrappedBCCPerCPUArrayTable::Create( + BCCWrapper* bcc, const std::string& name) { + using BaseT = WrappedBCCPerCPUArrayTable; + using ImplT = WrappedBCCPerCPUArrayTableImpl; + return CreateBCCWrappedMapOrArray(bcc, name); +} + } // namespace bpf_tools } // namespace stirling } // namespace px