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:
capacity()41   size_t capacity() { return desc.max_entries; }
42 
string_to_key(const std::string & key_str,KeyType * key)43   StatusTuple string_to_key(const std::string& key_str, KeyType* key) {
44     return desc.key_sscanf(key_str.c_str(), key);
45   }
46 
string_to_leaf(const std::string & value_str,ValueType * value)47   StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) {
48     return desc.leaf_sscanf(value_str.c_str(), value);
49   }
50 
key_to_string(const KeyType * key,std::string & key_str)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 
leaf_to_string(const ValueType * value,std::string & value_str)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:
BPFTableBase(const TableDesc & desc)68   explicit BPFTableBase(const TableDesc& desc) : desc(desc) {}
69 
lookup(void * key,void * value)70   bool lookup(void* key, void* value) {
71     return bpf_lookup_elem(desc.fd, key, value) >= 0;
72   }
73 
first(void * key)74   bool first(void* key) {
75     return bpf_get_first_key(desc.fd, key, desc.key_size) >= 0;
76   }
77 
next(void * key,void * next_key)78   bool next(void* key, void* next_key) {
79     return bpf_get_next_key(desc.fd, key, next_key) >= 0;
80   }
81 
update(void * key,void * value)82   bool update(void* key, void* value) {
83     return bpf_update_elem(desc.fd, key, value, 0) >= 0;
84   }
85 
remove(void * key)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>
get_value_addr(ValueType & t)113 void* get_value_addr(ValueType& t) {
114   return &t;
115 }
116 
117 template <class ValueType>
get_value_addr(std::vector<ValueType> & t)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:
BPFArrayTable(const TableDesc & desc)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 
get_value(const int & index,ValueType & value)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 
update_value(const int & index,const ValueType & value)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 
get_table_offline()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:
BPFPercpuArrayTable(const TableDesc & desc)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 
get_value(const int & index,std::vector<ValueType> & value)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 
update_value(const int & index,const std::vector<ValueType> & value)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:
BPFHashTable(const TableDesc & desc)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 
get_value(const KeyType & key,ValueType & value)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 
update_value(const KeyType & key,const ValueType & value)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 
remove_value(const KeyType & key)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 
get_table_offline()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 
clear_table_non_atomic()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:
BPFPercpuHashTable(const TableDesc & desc)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 
get_value(const KeyType & key,std::vector<ValueType> & value)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 
update_value(const KeyType & key,const std::vector<ValueType> & value)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