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