/* * 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 #include #include #include #include #include #include #include #include #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 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 { 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& 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& value_str); StatusTuple remove_value(const std::string& key_str); StatusTuple clear_table_non_atomic(); StatusTuple get_table_offline(std::vector> &res); static size_t get_possible_cpu_count(); }; template void* get_value_addr(ValueType& t) { return &t; } template void* get_value_addr(std::vector& t) { return t.data(); } template class BPFArrayTable : public 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"); } virtual StatusTuple get_value(const int& index, ValueType& value) { if (!this->lookup(const_cast(&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(&index), get_value_addr(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 BPFPercpuArrayTable : public BPFArrayTable> { public: BPFPercpuArrayTable(const TableDesc& desc) : BPFArrayTable>(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& value) { value.resize(ncpus); return BPFArrayTable>::get_value(index, value); } StatusTuple update_value(const int& index, const std::vector& value) { if (value.size() != ncpus) return StatusTuple(-1, "bad value size"); return BPFArrayTable>::update_value(index, value); } private: unsigned int ncpus; }; template class BPFHashTable : public BPFTableBase { public: explicit BPFHashTable(const TableDesc& desc) : BPFTableBase(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(&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(&key), get_value_addr(const_cast(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(&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> get_table_offline() { std::vector> 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 BPFPercpuHashTable : public BPFHashTable> { public: explicit BPFPercpuHashTable(const TableDesc& desc) : BPFHashTable>(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& value) { value.resize(ncpus); return BPFHashTable>::get_value(key, value); } StatusTuple update_value(const KeyType& key, const std::vector& value) { if (value.size() != ncpus) return StatusTuple(-1, "bad value size"); return BPFHashTable>::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 { 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 get_stack_addr(int stack_id); std::vector get_stack_symbol(int stack_id, int pid); private: bcc_symbol_option symbol_option_; std::map pid_sym_; }; class BPFPerfBuffer : public BPFTableBase { 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 cpu_readers_; int epfd_; std::unique_ptr ep_events_; }; class BPFPerfEventArray : public BPFTableBase { 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 cpu_fds_; }; class BPFProgTable : public BPFTableBase { 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 { 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 { 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