diff --git a/src/cc/BPF.h b/src/cc/BPF.h index dc6e0a651ae3..d5e57c9cd539 100644 --- a/src/cc/BPF.h +++ b/src/cc/BPF.h @@ -90,6 +90,14 @@ class BPF { int group_fd = -1); StatusTuple detach_perf_event(uint32_t ev_type, uint32_t ev_config); + template + BPFArrayTable get_array_table(const std::string& name) { + TableStorage::iterator it; + if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) + return BPFArrayTable(it->second); + return BPFArrayTable({}); + } + template BPFHashTable get_hash_table(const std::string& name) { TableStorage::iterator it; diff --git a/src/cc/BPFTable.h b/src/cc/BPFTable.h index e746fb570aae..6a9e32d42af0 100644 --- a/src/cc/BPFTable.h +++ b/src/cc/BPFTable.h @@ -67,6 +67,45 @@ class BPFTableBase { size_t capacity_; }; +template +class BPFArrayTable : protected BPFTableBase { +public: + BPFArrayTable(const TableDesc& desc) + : BPFTableBase(desc) { + if (desc.type != BPF_MAP_TYPE_ARRAY && + desc.type != BPF_MAP_TYPE_PERCPU_ARRAY) + throw std::invalid_argument("Table '" + desc.name + "' is not an array table"); + } + + StatusTuple get_value(const int& index, ValueType& value) { + if (!this->lookup(const_cast(&index), &value)) + return StatusTuple(-1, "Error getting value: %s", std::strerror(errno)); + return StatusTuple(0); + } + + StatusTuple update_value(const int& index, const ValueType& value) { + if (!this->update(const_cast(&index), const_cast(&value))) + return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); + return StatusTuple(0); + } + + ValueType operator[](const int& key) { + ValueType value; + get_value(key, value); + return value; + } + + std::vector get_table_offline() { + std::vector res(this->capacity()); + + for (int i = 0; i < (int) this->capacity(); i++) { + get_value(i, res[i]); + } + + return res; + } +}; + template class BPFHashTable : protected BPFTableBase { public: diff --git a/tests/cc/CMakeLists.txt b/tests/cc/CMakeLists.txt index c5052c37ebe2..03e177540303 100644 --- a/tests/cc/CMakeLists.txt +++ b/tests/cc/CMakeLists.txt @@ -11,6 +11,7 @@ add_test(NAME c_test_static COMMAND ${TEST_WRAPPER} c_test_static sudo ${CMAKE_C add_executable(test_libbcc test_libbcc.cc test_c_api.cc + test_array_table.cc test_hash_table.cc test_usdt_args.cc test_usdt_probes.cc) diff --git a/tests/cc/test_array_table.cc b/tests/cc/test_array_table.cc new file mode 100644 index 000000000000..0b8695b9512a --- /dev/null +++ b/tests/cc/test_array_table.cc @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017 Politecnico di Torino + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BPF.h" + +#include "catch.hpp" + +#include +#include + +TEST_CASE("test array table", "[array_table]") { + const std::string BPF_PROGRAM = R"( + BPF_TABLE("hash", int, int, myhash, 128); + BPF_TABLE("array", int, int, myarray, 128); + )"; + + ebpf::BPF bpf; + ebpf::StatusTuple res(0); + res = bpf.init(BPF_PROGRAM); + REQUIRE(res.code() == 0); + + ebpf::BPFArrayTable t = bpf.get_array_table("myarray"); + + SECTION("bad table type") { + // try to get table of wrong type + auto f1 = [&](){ + bpf.get_array_table("myhash"); + }; + + REQUIRE_THROWS(f1()); + } + + SECTION("standard methods") { + int i, v1, v2; + i = 1; + v1 = 42; + // update element + res = t.update_value(i, v1); + REQUIRE(res.code() == 0); + res = t.get_value(i, v2); + REQUIRE(res.code() == 0); + REQUIRE(v2 == 42); + + // update another element + i = 2; + v1 = 69; + res = t.update_value(i, v1); + REQUIRE(res.code() == 0); + res = t.get_value(i, v2); + REQUIRE(res.code() == 0); + REQUIRE(v2 == 69); + + // get non existing element + i = 1024; + res = t.get_value(i, v2); + REQUIRE(res.code() != 0); + } + + SECTION("full table") { + // random number generator + std::mt19937 rng; + rng.seed(std::random_device()()); + std::uniform_int_distribution dist; + + std::vector localtable(128); + + for(int i = 0; i < 128; i++) { + int v = dist(rng); + + res = t.update_value(i, v); + REQUIRE(res.code() == 0); + + // save it in the local table to compare later on + localtable[i] = v; + } + + std::vector offlinetable = t.get_table_offline(); + REQUIRE(localtable == offlinetable); + } +}