1 /* 2 * Copyright (c) 2016 Facebook, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <errno.h> 20 #include <sys/epoll.h> 21 #include <cstring> 22 #include <exception> 23 #include <map> 24 #include <memory> 25 #include <string> 26 #include <utility> 27 #include <vector> 28 29 #include "bcc_exception.h" 30 #include "bcc_syms.h" 31 #include "bpf_module.h" 32 #include "libbpf.h" 33 #include "perf_reader.h" 34 #include "table_desc.h" 35 36 namespace ebpf { 37 38 template <class KeyType, class ValueType> 39 class BPFTableBase { 40 public: 41 size_t capacity() { return desc.max_entries; } 42 43 StatusTuple string_to_key(const std::string& key_str, KeyType* key) { 44 return desc.key_sscanf(key_str.c_str(), key); 45 } 46 47 StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) { 48 return desc.leaf_sscanf(value_str.c_str(), value); 49 } 50 51 StatusTuple key_to_string(const KeyType* key, std::string& key_str) { 52 char buf[8 * desc.key_size]; 53 StatusTuple rc = desc.key_snprintf(buf, sizeof(buf), key); 54 if (!rc.code()) 55 key_str.assign(buf); 56 return rc; 57 } 58 59 StatusTuple leaf_to_string(const ValueType* value, std::string& value_str) { 60 char buf[8 * desc.leaf_size]; 61 StatusTuple rc = desc.leaf_snprintf(buf, sizeof(buf), value); 62 if (!rc.code()) 63 value_str.assign(buf); 64 return rc; 65 } 66 67 protected: 68 explicit BPFTableBase(const TableDesc& desc) : desc(desc) {} 69 70 bool lookup(void* key, void* value) { 71 return bpf_lookup_elem(desc.fd, key, value) >= 0; 72 } 73 74 bool first(void* key) { 75 return bpf_get_first_key(desc.fd, key, desc.key_size) >= 0; 76 } 77 78 bool next(void* key, void* next_key) { 79 return bpf_get_next_key(desc.fd, key, next_key) >= 0; 80 } 81 82 bool update(void* key, void* value) { 83 return bpf_update_elem(desc.fd, key, value, 0) >= 0; 84 } 85 86 bool remove(void* key) { return bpf_delete_elem(desc.fd, key) >= 0; } 87 88 const TableDesc& desc; 89 }; 90 91 class BPFTable : public BPFTableBase<void, void> { 92 public: 93 BPFTable(const TableDesc& desc); 94 95 StatusTuple get_value(const std::string& key_str, std::string& value); 96 StatusTuple get_value(const std::string& key_str, 97 std::vector<std::string>& value); 98 99 StatusTuple update_value(const std::string& key_str, 100 const std::string& value_str); 101 StatusTuple update_value(const std::string& key_str, 102 const std::vector<std::string>& value_str); 103 104 StatusTuple remove_value(const std::string& key_str); 105 106 StatusTuple clear_table_non_atomic(); 107 StatusTuple get_table_offline(std::vector<std::pair<std::string, std::string>> &res); 108 109 static size_t get_possible_cpu_count(); 110 }; 111 112 template <class ValueType> 113 void* get_value_addr(ValueType& t) { 114 return &t; 115 } 116 117 template <class ValueType> 118 void* get_value_addr(std::vector<ValueType>& t) { 119 return t.data(); 120 } 121 122 template <class ValueType> 123 class BPFArrayTable : public BPFTableBase<int, ValueType> { 124 public: 125 BPFArrayTable(const TableDesc& desc) : BPFTableBase<int, ValueType>(desc) { 126 if (desc.type != BPF_MAP_TYPE_ARRAY && 127 desc.type != BPF_MAP_TYPE_PERCPU_ARRAY) 128 throw std::invalid_argument("Table '" + desc.name + 129 "' is not an array table"); 130 } 131 132 virtual StatusTuple get_value(const int& index, ValueType& value) { 133 if (!this->lookup(const_cast<int*>(&index), get_value_addr(value))) 134 return StatusTuple(-1, "Error getting value: %s", std::strerror(errno)); 135 return StatusTuple(0); 136 } 137 138 virtual StatusTuple update_value(const int& index, const ValueType& value) { 139 if (!this->update(const_cast<int*>(&index), 140 get_value_addr(const_cast<ValueType&>(value)))) 141 return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); 142 return StatusTuple(0); 143 } 144 145 ValueType operator[](const int& key) { 146 ValueType value; 147 get_value(key, value); 148 return value; 149 } 150 151 std::vector<ValueType> get_table_offline() { 152 std::vector<ValueType> res(this->capacity()); 153 154 for (int i = 0; i < (int)this->capacity(); i++) { 155 get_value(i, res[i]); 156 } 157 158 return res; 159 } 160 }; 161 162 template <class ValueType> 163 class BPFPercpuArrayTable : public BPFArrayTable<std::vector<ValueType>> { 164 public: 165 BPFPercpuArrayTable(const TableDesc& desc) 166 : BPFArrayTable<std::vector<ValueType>>(desc), 167 ncpus(BPFTable::get_possible_cpu_count()) { 168 if (desc.type != BPF_MAP_TYPE_PERCPU_ARRAY) 169 throw std::invalid_argument("Table '" + desc.name + 170 "' is not a percpu array table"); 171 // leaf structures have to be aligned to 8 bytes as hardcoded in the linux 172 // kernel. 173 if (sizeof(ValueType) % 8) 174 throw std::invalid_argument("leaf must be aligned to 8 bytes"); 175 } 176 177 StatusTuple get_value(const int& index, std::vector<ValueType>& value) { 178 value.resize(ncpus); 179 return BPFArrayTable<std::vector<ValueType>>::get_value(index, value); 180 } 181 182 StatusTuple update_value(const int& index, 183 const std::vector<ValueType>& value) { 184 if (value.size() != ncpus) 185 return StatusTuple(-1, "bad value size"); 186 return BPFArrayTable<std::vector<ValueType>>::update_value(index, value); 187 } 188 189 private: 190 unsigned int ncpus; 191 }; 192 193 template <class KeyType, class ValueType> 194 class BPFHashTable : public BPFTableBase<KeyType, ValueType> { 195 public: 196 explicit BPFHashTable(const TableDesc& desc) 197 : BPFTableBase<KeyType, ValueType>(desc) { 198 if (desc.type != BPF_MAP_TYPE_HASH && 199 desc.type != BPF_MAP_TYPE_PERCPU_HASH && 200 desc.type != BPF_MAP_TYPE_LRU_HASH && 201 desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH) 202 throw std::invalid_argument("Table '" + desc.name + 203 "' is not a hash table"); 204 } 205 206 virtual StatusTuple get_value(const KeyType& key, ValueType& value) { 207 if (!this->lookup(const_cast<KeyType*>(&key), get_value_addr(value))) 208 return StatusTuple(-1, "Error getting value: %s", std::strerror(errno)); 209 return StatusTuple(0); 210 } 211 212 virtual StatusTuple update_value(const KeyType& key, const ValueType& value) { 213 if (!this->update(const_cast<KeyType*>(&key), 214 get_value_addr(const_cast<ValueType&>(value)))) 215 return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); 216 return StatusTuple(0); 217 } 218 219 virtual StatusTuple remove_value(const KeyType& key) { 220 if (!this->remove(const_cast<KeyType*>(&key))) 221 return StatusTuple(-1, "Error removing value: %s", std::strerror(errno)); 222 return StatusTuple(0); 223 } 224 225 ValueType operator[](const KeyType& key) { 226 ValueType value; 227 get_value(key, value); 228 return value; 229 } 230 231 std::vector<std::pair<KeyType, ValueType>> get_table_offline() { 232 std::vector<std::pair<KeyType, ValueType>> res; 233 KeyType cur; 234 ValueType value; 235 236 StatusTuple r(0); 237 238 if (!this->first(&cur)) 239 return res; 240 241 while (true) { 242 r = get_value(cur, value); 243 if (r.code() != 0) 244 break; 245 res.emplace_back(cur, value); 246 if (!this->next(&cur, &cur)) 247 break; 248 } 249 250 return res; 251 } 252 253 StatusTuple clear_table_non_atomic() { 254 KeyType cur; 255 while (this->first(&cur)) 256 TRY2(remove_value(cur)); 257 258 return StatusTuple(0); 259 } 260 }; 261 262 template <class KeyType, class ValueType> 263 class BPFPercpuHashTable 264 : public BPFHashTable<KeyType, std::vector<ValueType>> { 265 public: 266 explicit BPFPercpuHashTable(const TableDesc& desc) 267 : BPFHashTable<KeyType, std::vector<ValueType>>(desc), 268 ncpus(BPFTable::get_possible_cpu_count()) { 269 if (desc.type != BPF_MAP_TYPE_PERCPU_HASH && 270 desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH) 271 throw std::invalid_argument("Table '" + desc.name + 272 "' is not a percpu hash table"); 273 // leaf structures have to be aligned to 8 bytes as hardcoded in the linux 274 // kernel. 275 if (sizeof(ValueType) % 8) 276 throw std::invalid_argument("leaf must be aligned to 8 bytes"); 277 } 278 279 StatusTuple get_value(const KeyType& key, std::vector<ValueType>& value) { 280 value.resize(ncpus); 281 return BPFHashTable<KeyType, std::vector<ValueType>>::get_value(key, value); 282 } 283 284 StatusTuple update_value(const KeyType& key, 285 const std::vector<ValueType>& value) { 286 if (value.size() != ncpus) 287 return StatusTuple(-1, "bad value size"); 288 return BPFHashTable<KeyType, std::vector<ValueType>>::update_value(key, 289 value); 290 } 291 292 private: 293 unsigned int ncpus; 294 }; 295 296 // From src/cc/export/helpers.h 297 static const int BPF_MAX_STACK_DEPTH = 127; 298 struct stacktrace_t { 299 uintptr_t ip[BPF_MAX_STACK_DEPTH]; 300 }; 301 302 class BPFStackTable : public BPFTableBase<int, stacktrace_t> { 303 public: 304 BPFStackTable(const TableDesc& desc, bool use_debug_file, 305 bool check_debug_file_crc); 306 BPFStackTable(BPFStackTable&& that); 307 ~BPFStackTable(); 308 309 void clear_table_non_atomic(); 310 std::vector<uintptr_t> get_stack_addr(int stack_id); 311 std::vector<std::string> get_stack_symbol(int stack_id, int pid); 312 313 private: 314 bcc_symbol_option symbol_option_; 315 std::map<int, void*> pid_sym_; 316 }; 317 318 class BPFPerfBuffer : public BPFTableBase<int, int> { 319 public: 320 BPFPerfBuffer(const TableDesc& desc); 321 ~BPFPerfBuffer(); 322 323 StatusTuple open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, 324 void* cb_cookie, int page_cnt); 325 StatusTuple close_all_cpu(); 326 int poll(int timeout_ms); 327 328 private: 329 StatusTuple open_on_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb, 330 int cpu, void* cb_cookie, int page_cnt); 331 StatusTuple close_on_cpu(int cpu); 332 333 std::map<int, perf_reader*> cpu_readers_; 334 335 int epfd_; 336 std::unique_ptr<epoll_event[]> ep_events_; 337 }; 338 339 class BPFPerfEventArray : public BPFTableBase<int, int> { 340 public: 341 BPFPerfEventArray(const TableDesc& desc); 342 ~BPFPerfEventArray(); 343 344 StatusTuple open_all_cpu(uint32_t type, uint64_t config); 345 StatusTuple close_all_cpu(); 346 347 private: 348 StatusTuple open_on_cpu(int cpu, uint32_t type, uint64_t config); 349 StatusTuple close_on_cpu(int cpu); 350 351 std::map<int, int> cpu_fds_; 352 }; 353 354 class BPFProgTable : public BPFTableBase<int, int> { 355 public: 356 BPFProgTable(const TableDesc& desc); 357 358 StatusTuple update_value(const int& index, const int& prog_fd); 359 StatusTuple remove_value(const int& index); 360 }; 361 362 class BPFCgroupArray : public BPFTableBase<int, int> { 363 public: 364 BPFCgroupArray(const TableDesc& desc); 365 366 StatusTuple update_value(const int& index, const int& cgroup2_fd); 367 StatusTuple update_value(const int& index, const std::string& cgroup2_path); 368 StatusTuple remove_value(const int& index); 369 }; 370 371 class BPFDevmapTable : public BPFTableBase<int, int> { 372 public: 373 BPFDevmapTable(const TableDesc& desc); 374 375 StatusTuple update_value(const int& index, const int& value); 376 StatusTuple get_value(const int& index, int& value); 377 StatusTuple remove_value(const int& index); 378 379 }; 380 381 } // namespace ebpf 382