| /* |
| * Copyright (c) 2016 Facebook, Inc. |
| * |
| * 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. |
| */ |
| |
| #pragma once |
| |
| #include <errno.h> |
| #include <sys/epoll.h> |
| #include <cstring> |
| #include <exception> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "bcc_exception.h" |
| #include "bcc_syms.h" |
| #include "bpf_module.h" |
| #include "libbpf.h" |
| #include "perf_reader.h" |
| #include "table_desc.h" |
| |
| namespace ebpf { |
| |
| template <class KeyType, class ValueType> |
| class BPFTableBase { |
| public: |
| size_t capacity() { return desc.max_entries; } |
| |
| StatusTuple string_to_key(const std::string& key_str, KeyType* key) { |
| return desc.key_sscanf(key_str.c_str(), key); |
| } |
| |
| StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) { |
| return desc.leaf_sscanf(value_str.c_str(), value); |
| } |
| |
| StatusTuple key_to_string(const KeyType* key, std::string& key_str) { |
| char buf[8 * desc.key_size]; |
| StatusTuple rc = desc.key_snprintf(buf, sizeof(buf), key); |
| if (!rc.code()) |
| key_str.assign(buf); |
| return rc; |
| } |
| |
| StatusTuple leaf_to_string(const ValueType* value, std::string& value_str) { |
| char buf[8 * desc.leaf_size]; |
| StatusTuple rc = desc.leaf_snprintf(buf, sizeof(buf), value); |
| if (!rc.code()) |
| value_str.assign(buf); |
| return rc; |
| } |
| |
| protected: |
| explicit BPFTableBase(const TableDesc& desc) : desc(desc) {} |
| |
| bool lookup(void* key, void* value) { |
| return bpf_lookup_elem(desc.fd, key, value) >= 0; |
| } |
| |
| bool first(void* key) { |
| return bpf_get_first_key(desc.fd, key, desc.key_size) >= 0; |
| } |
| |
| bool next(void* key, void* next_key) { |
| return bpf_get_next_key(desc.fd, key, next_key) >= 0; |
| } |
| |
| bool update(void* key, void* value) { |
| return bpf_update_elem(desc.fd, key, value, 0) >= 0; |
| } |
| |
| bool remove(void* key) { return bpf_delete_elem(desc.fd, key) >= 0; } |
| |
| const TableDesc& desc; |
| }; |
| |
| class BPFTable : public BPFTableBase<void, void> { |
| public: |
| BPFTable(const TableDesc& desc); |
| |
| StatusTuple get_value(const std::string& key_str, std::string& value); |
| StatusTuple get_value(const std::string& key_str, |
| std::vector<std::string>& value); |
| |
| StatusTuple update_value(const std::string& key_str, |
| const std::string& value_str); |
| StatusTuple update_value(const std::string& key_str, |
| const std::vector<std::string>& value_str); |
| |
| StatusTuple remove_value(const std::string& key_str); |
| |
| StatusTuple clear_table_non_atomic(); |
| StatusTuple get_table_offline(std::vector<std::pair<std::string, std::string>> &res); |
| |
| static size_t get_possible_cpu_count(); |
| }; |
| |
| template <class ValueType> |
| void* get_value_addr(ValueType& t) { |
| return &t; |
| } |
| |
| template <class ValueType> |
| void* get_value_addr(std::vector<ValueType>& t) { |
| return t.data(); |
| } |
| |
| template <class ValueType> |
| class BPFArrayTable : public BPFTableBase<int, ValueType> { |
| public: |
| BPFArrayTable(const TableDesc& desc) : BPFTableBase<int, ValueType>(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"); |
| } |
| |
| virtual StatusTuple get_value(const int& index, ValueType& value) { |
| if (!this->lookup(const_cast<int*>(&index), get_value_addr(value))) |
| return StatusTuple(-1, "Error getting value: %s", std::strerror(errno)); |
| return StatusTuple(0); |
| } |
| |
| virtual StatusTuple update_value(const int& index, const ValueType& value) { |
| if (!this->update(const_cast<int*>(&index), |
| get_value_addr(const_cast<ValueType&>(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<ValueType> get_table_offline() { |
| std::vector<ValueType> res(this->capacity()); |
| |
| for (int i = 0; i < (int)this->capacity(); i++) { |
| get_value(i, res[i]); |
| } |
| |
| return res; |
| } |
| }; |
| |
| template <class ValueType> |
| class BPFPercpuArrayTable : public BPFArrayTable<std::vector<ValueType>> { |
| public: |
| BPFPercpuArrayTable(const TableDesc& desc) |
| : BPFArrayTable<std::vector<ValueType>>(desc), |
| ncpus(BPFTable::get_possible_cpu_count()) { |
| if (desc.type != BPF_MAP_TYPE_PERCPU_ARRAY) |
| throw std::invalid_argument("Table '" + desc.name + |
| "' is not a percpu array table"); |
| // leaf structures have to be aligned to 8 bytes as hardcoded in the linux |
| // kernel. |
| if (sizeof(ValueType) % 8) |
| throw std::invalid_argument("leaf must be aligned to 8 bytes"); |
| } |
| |
| StatusTuple get_value(const int& index, std::vector<ValueType>& value) { |
| value.resize(ncpus); |
| return BPFArrayTable<std::vector<ValueType>>::get_value(index, value); |
| } |
| |
| StatusTuple update_value(const int& index, |
| const std::vector<ValueType>& value) { |
| if (value.size() != ncpus) |
| return StatusTuple(-1, "bad value size"); |
| return BPFArrayTable<std::vector<ValueType>>::update_value(index, value); |
| } |
| |
| private: |
| unsigned int ncpus; |
| }; |
| |
| template <class KeyType, class ValueType> |
| class BPFHashTable : public BPFTableBase<KeyType, ValueType> { |
| public: |
| explicit BPFHashTable(const TableDesc& desc) |
| : BPFTableBase<KeyType, ValueType>(desc) { |
| if (desc.type != BPF_MAP_TYPE_HASH && |
| desc.type != BPF_MAP_TYPE_PERCPU_HASH && |
| desc.type != BPF_MAP_TYPE_LRU_HASH && |
| desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH) |
| throw std::invalid_argument("Table '" + desc.name + |
| "' is not a hash table"); |
| } |
| |
| virtual StatusTuple get_value(const KeyType& key, ValueType& value) { |
| if (!this->lookup(const_cast<KeyType*>(&key), get_value_addr(value))) |
| return StatusTuple(-1, "Error getting value: %s", std::strerror(errno)); |
| return StatusTuple(0); |
| } |
| |
| virtual StatusTuple update_value(const KeyType& key, const ValueType& value) { |
| if (!this->update(const_cast<KeyType*>(&key), |
| get_value_addr(const_cast<ValueType&>(value)))) |
| return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); |
| return StatusTuple(0); |
| } |
| |
| virtual StatusTuple remove_value(const KeyType& key) { |
| if (!this->remove(const_cast<KeyType*>(&key))) |
| return StatusTuple(-1, "Error removing value: %s", std::strerror(errno)); |
| return StatusTuple(0); |
| } |
| |
| ValueType operator[](const KeyType& key) { |
| ValueType value; |
| get_value(key, value); |
| return value; |
| } |
| |
| std::vector<std::pair<KeyType, ValueType>> get_table_offline() { |
| std::vector<std::pair<KeyType, ValueType>> res; |
| KeyType cur; |
| ValueType value; |
| |
| StatusTuple r(0); |
| |
| if (!this->first(&cur)) |
| return res; |
| |
| while (true) { |
| r = get_value(cur, value); |
| if (r.code() != 0) |
| break; |
| res.emplace_back(cur, value); |
| if (!this->next(&cur, &cur)) |
| break; |
| } |
| |
| return res; |
| } |
| |
| StatusTuple clear_table_non_atomic() { |
| KeyType cur; |
| while (this->first(&cur)) |
| TRY2(remove_value(cur)); |
| |
| return StatusTuple(0); |
| } |
| }; |
| |
| template <class KeyType, class ValueType> |
| class BPFPercpuHashTable |
| : public BPFHashTable<KeyType, std::vector<ValueType>> { |
| public: |
| explicit BPFPercpuHashTable(const TableDesc& desc) |
| : BPFHashTable<KeyType, std::vector<ValueType>>(desc), |
| ncpus(BPFTable::get_possible_cpu_count()) { |
| if (desc.type != BPF_MAP_TYPE_PERCPU_HASH && |
| desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH) |
| throw std::invalid_argument("Table '" + desc.name + |
| "' is not a percpu hash table"); |
| // leaf structures have to be aligned to 8 bytes as hardcoded in the linux |
| // kernel. |
| if (sizeof(ValueType) % 8) |
| throw std::invalid_argument("leaf must be aligned to 8 bytes"); |
| } |
| |
| StatusTuple get_value(const KeyType& key, std::vector<ValueType>& value) { |
| value.resize(ncpus); |
| return BPFHashTable<KeyType, std::vector<ValueType>>::get_value(key, value); |
| } |
| |
| StatusTuple update_value(const KeyType& key, |
| const std::vector<ValueType>& value) { |
| if (value.size() != ncpus) |
| return StatusTuple(-1, "bad value size"); |
| return BPFHashTable<KeyType, std::vector<ValueType>>::update_value(key, |
| value); |
| } |
| |
| private: |
| unsigned int ncpus; |
| }; |
| |
| // From src/cc/export/helpers.h |
| static const int BPF_MAX_STACK_DEPTH = 127; |
| struct stacktrace_t { |
| uintptr_t ip[BPF_MAX_STACK_DEPTH]; |
| }; |
| |
| class BPFStackTable : public BPFTableBase<int, stacktrace_t> { |
| public: |
| BPFStackTable(const TableDesc& desc, bool use_debug_file, |
| bool check_debug_file_crc); |
| BPFStackTable(BPFStackTable&& that); |
| ~BPFStackTable(); |
| |
| void clear_table_non_atomic(); |
| std::vector<uintptr_t> get_stack_addr(int stack_id); |
| std::vector<std::string> get_stack_symbol(int stack_id, int pid); |
| |
| private: |
| bcc_symbol_option symbol_option_; |
| std::map<int, void*> pid_sym_; |
| }; |
| |
| class BPFPerfBuffer : public BPFTableBase<int, int> { |
| public: |
| BPFPerfBuffer(const TableDesc& desc); |
| ~BPFPerfBuffer(); |
| |
| StatusTuple open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, |
| void* cb_cookie, int page_cnt); |
| StatusTuple close_all_cpu(); |
| int poll(int timeout_ms); |
| |
| private: |
| StatusTuple open_on_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, |
| int cpu, void* cb_cookie, int page_cnt); |
| StatusTuple close_on_cpu(int cpu); |
| |
| std::map<int, perf_reader*> cpu_readers_; |
| |
| int epfd_; |
| std::unique_ptr<epoll_event[]> ep_events_; |
| }; |
| |
| class BPFPerfEventArray : public BPFTableBase<int, int> { |
| public: |
| BPFPerfEventArray(const TableDesc& desc); |
| ~BPFPerfEventArray(); |
| |
| StatusTuple open_all_cpu(uint32_t type, uint64_t config); |
| StatusTuple close_all_cpu(); |
| |
| private: |
| StatusTuple open_on_cpu(int cpu, uint32_t type, uint64_t config); |
| StatusTuple close_on_cpu(int cpu); |
| |
| std::map<int, int> cpu_fds_; |
| }; |
| |
| class BPFProgTable : public BPFTableBase<int, int> { |
| public: |
| BPFProgTable(const TableDesc& desc); |
| |
| StatusTuple update_value(const int& index, const int& prog_fd); |
| StatusTuple remove_value(const int& index); |
| }; |
| |
| class BPFCgroupArray : public BPFTableBase<int, int> { |
| public: |
| BPFCgroupArray(const TableDesc& desc); |
| |
| StatusTuple update_value(const int& index, const int& cgroup2_fd); |
| StatusTuple update_value(const int& index, const std::string& cgroup2_path); |
| StatusTuple remove_value(const int& index); |
| }; |
| |
| class BPFDevmapTable : public BPFTableBase<int, int> { |
| public: |
| BPFDevmapTable(const TableDesc& desc); |
| |
| StatusTuple update_value(const int& index, const int& value); |
| StatusTuple get_value(const int& index, int& value); |
| StatusTuple remove_value(const int& index); |
| |
| }; |
| |
| } // namespace ebpf |