Home | History | Annotate | Download | only in api
      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