• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "linux/bpf.h"
36 
37 namespace ebpf {
38 
39 template<class ValueType>
40 class BPFQueueStackTableBase {
41  public:
capacity()42   size_t capacity() const { return desc.max_entries; }
43 
string_to_leaf(const std::string & value_str,ValueType * value)44   StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) {
45     return desc.leaf_sscanf(value_str.c_str(), value);
46   }
47 
leaf_to_string(const ValueType * value,std::string & value_str)48   StatusTuple leaf_to_string(const ValueType* value, std::string& value_str) {
49     char buf[8 * desc.leaf_size];
50     StatusTuple rc = desc.leaf_snprintf(buf, sizeof(buf), value);
51     if (rc.ok())
52       value_str.assign(buf);
53     return rc;
54   }
55 
get_fd()56   int get_fd() { return desc.fd; }
57 
58  protected:
BPFQueueStackTableBase(const TableDesc & desc)59   explicit BPFQueueStackTableBase(const TableDesc& desc) : desc(desc) {}
60 
pop(void * value)61   bool pop(void *value) {
62     return bpf_lookup_and_delete(desc.fd, nullptr, value) >= 0;
63   }
64   // Flags are extremely useful, since they completely changes extraction behaviour
65   // (eg. if flag BPF_EXIST, then if the queue/stack is full remove the oldest one)
push(void * value,unsigned long long int flags)66   bool push(void *value, unsigned long long int flags) {
67     return bpf_update_elem(desc.fd, nullptr, value, flags) >= 0;
68   }
69 
peek(void * value)70   bool peek(void *value) {
71     return bpf_lookup_elem(desc.fd, nullptr, value) >= 0;
72   }
73 
74   const TableDesc& desc;
75 };
76 
77 template <class KeyType, class ValueType>
78 class BPFTableBase {
79  public:
capacity()80   size_t capacity() { return desc.max_entries; }
81 
string_to_key(const std::string & key_str,KeyType * key)82   StatusTuple string_to_key(const std::string& key_str, KeyType* key) {
83     return desc.key_sscanf(key_str.c_str(), key);
84   }
85 
string_to_leaf(const std::string & value_str,ValueType * value)86   StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) {
87     return desc.leaf_sscanf(value_str.c_str(), value);
88   }
89 
key_to_string(const KeyType * key,std::string & key_str)90   StatusTuple key_to_string(const KeyType* key, std::string& key_str) {
91     char buf[8 * desc.key_size];
92     StatusTuple rc = desc.key_snprintf(buf, sizeof(buf), key);
93     if (rc.ok())
94       key_str.assign(buf);
95     return rc;
96   }
97 
leaf_to_string(const ValueType * value,std::string & value_str)98   StatusTuple leaf_to_string(const ValueType* value, std::string& value_str) {
99     char buf[8 * desc.leaf_size];
100     StatusTuple rc = desc.leaf_snprintf(buf, sizeof(buf), value);
101     if (rc.ok())
102       value_str.assign(buf);
103     return rc;
104   }
105 
get_fd()106   int get_fd() {
107     return desc.fd;
108   }
109 
110  protected:
BPFTableBase(const TableDesc & desc)111   explicit BPFTableBase(const TableDesc& desc) : desc(desc) {}
112 
lookup(void * key,void * value)113   bool lookup(void* key, void* value) {
114     return bpf_lookup_elem(desc.fd, key, value) >= 0;
115   }
116 
first(void * key)117   bool first(void* key) {
118     return bpf_get_first_key(desc.fd, key, desc.key_size) >= 0;
119   }
120 
next(void * key,void * next_key)121   bool next(void* key, void* next_key) {
122     return bpf_get_next_key(desc.fd, key, next_key) >= 0;
123   }
124 
update(void * key,void * value)125   bool update(void* key, void* value) {
126     return bpf_update_elem(desc.fd, key, value, 0) >= 0;
127   }
128 
remove(void * key)129   bool remove(void* key) { return bpf_delete_elem(desc.fd, key) >= 0; }
130 
131   const TableDesc& desc;
132 };
133 
134 class BPFTable : public BPFTableBase<void, void> {
135  public:
136   BPFTable(const TableDesc& desc);
137 
138   StatusTuple get_value(const std::string& key_str, std::string& value);
139   StatusTuple get_value(const std::string& key_str,
140                         std::vector<std::string>& value);
141 
142   StatusTuple update_value(const std::string& key_str,
143                            const std::string& value_str);
144   StatusTuple update_value(const std::string& key_str,
145                            const std::vector<std::string>& value_str);
146 
147   StatusTuple remove_value(const std::string& key_str);
148 
149   StatusTuple clear_table_non_atomic();
150   StatusTuple get_table_offline(std::vector<std::pair<std::string, std::string>> &res);
151 
152   static size_t get_possible_cpu_count();
153 };
154 
155 template <class ValueType>
get_value_addr(ValueType & t)156 void* get_value_addr(ValueType& t) {
157   return &t;
158 }
159 
160 template <class ValueType>
get_value_addr(std::vector<ValueType> & t)161 void* get_value_addr(std::vector<ValueType>& t) {
162   return t.data();
163 }
164 
165 template<class ValueType>
166 class BPFQueueStackTable : public BPFQueueStackTableBase<void> {
167  public:
BPFQueueStackTable(const TableDesc & desc)168   explicit BPFQueueStackTable(const TableDesc& desc) : BPFQueueStackTableBase(desc) {
169     if (desc.type != BPF_MAP_TYPE_QUEUE &&
170         desc.type != BPF_MAP_TYPE_STACK)
171       throw std::invalid_argument("Table '" + desc.name +
172                                   "' is not a queue/stack table");
173   }
174 
pop_value(ValueType & value)175   virtual StatusTuple pop_value(ValueType& value) {
176     if (!this->pop(get_value_addr(value)))
177       return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
178     return StatusTuple::OK();
179   }
180 
181   virtual StatusTuple push_value(const ValueType& value, unsigned long long int flags = 0) {
182     if (!this->push(get_value_addr(const_cast<ValueType&>(value)), flags))
183       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
184     return StatusTuple::OK();
185   }
186 
get_head(const ValueType & value)187   virtual StatusTuple get_head(const ValueType& value) {
188     if (!this->peek(get_value_addr(const_cast<ValueType&>(value))))
189       return StatusTuple(-1, "Error peeking value: %s", std::strerror(errno));
190     return StatusTuple::OK();
191   }
192 };
193 
194 template <class ValueType>
195 class BPFArrayTable : public BPFTableBase<int, ValueType> {
196  public:
BPFArrayTable(const TableDesc & desc)197   BPFArrayTable(const TableDesc& desc) : BPFTableBase<int, ValueType>(desc) {
198     if (desc.type != BPF_MAP_TYPE_ARRAY &&
199         desc.type != BPF_MAP_TYPE_PERCPU_ARRAY)
200       throw std::invalid_argument("Table '" + desc.name +
201                                   "' is not an array table");
202   }
203 
get_value(const int & index,ValueType & value)204   virtual StatusTuple get_value(const int& index, ValueType& value) {
205     if (!this->lookup(const_cast<int*>(&index), get_value_addr(value)))
206       return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
207     return StatusTuple::OK();
208   }
209 
update_value(const int & index,const ValueType & value)210   virtual StatusTuple update_value(const int& index, const ValueType& value) {
211     if (!this->update(const_cast<int*>(&index),
212                       get_value_addr(const_cast<ValueType&>(value))))
213       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
214     return StatusTuple::OK();
215   }
216 
217   ValueType operator[](const int& key) {
218     ValueType value;
219     get_value(key, value);
220     return value;
221   }
222 
get_table_offline()223   std::vector<ValueType> get_table_offline() {
224     std::vector<ValueType> res(this->capacity());
225 
226     for (int i = 0; i < (int)this->capacity(); i++) {
227       get_value(i, res[i]);
228     }
229 
230     return res;
231   }
232 };
233 
234 template <class ValueType>
235 class BPFPercpuArrayTable : public BPFArrayTable<std::vector<ValueType>> {
236  public:
BPFPercpuArrayTable(const TableDesc & desc)237   BPFPercpuArrayTable(const TableDesc& desc)
238       : BPFArrayTable<std::vector<ValueType>>(desc),
239         ncpus(BPFTable::get_possible_cpu_count()) {
240     if (desc.type != BPF_MAP_TYPE_PERCPU_ARRAY)
241       throw std::invalid_argument("Table '" + desc.name +
242                                   "' is not a percpu array table");
243     // leaf structures have to be aligned to 8 bytes as hardcoded in the linux
244     // kernel.
245     if (sizeof(ValueType) % 8)
246       throw std::invalid_argument("leaf must be aligned to 8 bytes");
247   }
248 
get_value(const int & index,std::vector<ValueType> & value)249   StatusTuple get_value(const int& index, std::vector<ValueType>& value) {
250     value.resize(ncpus);
251     return BPFArrayTable<std::vector<ValueType>>::get_value(index, value);
252   }
253 
update_value(const int & index,const std::vector<ValueType> & value)254   StatusTuple update_value(const int& index,
255                            const std::vector<ValueType>& value) {
256     if (value.size() != ncpus)
257       return StatusTuple(-1, "bad value size");
258     return BPFArrayTable<std::vector<ValueType>>::update_value(index, value);
259   }
260 
261  private:
262   unsigned int ncpus;
263 };
264 
265 template <class KeyType, class ValueType>
266 class BPFHashTable : public BPFTableBase<KeyType, ValueType> {
267  public:
BPFHashTable(const TableDesc & desc)268   explicit BPFHashTable(const TableDesc& desc)
269       : BPFTableBase<KeyType, ValueType>(desc) {
270     if (desc.type != BPF_MAP_TYPE_HASH &&
271         desc.type != BPF_MAP_TYPE_PERCPU_HASH &&
272         desc.type != BPF_MAP_TYPE_LRU_HASH &&
273         desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH)
274       throw std::invalid_argument("Table '" + desc.name +
275                                   "' is not a hash table");
276   }
277 
get_value(const KeyType & key,ValueType & value)278   virtual StatusTuple get_value(const KeyType& key, ValueType& value) {
279     if (!this->lookup(const_cast<KeyType*>(&key), get_value_addr(value)))
280       return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
281     return StatusTuple::OK();
282   }
283 
update_value(const KeyType & key,const ValueType & value)284   virtual StatusTuple update_value(const KeyType& key, const ValueType& value) {
285     if (!this->update(const_cast<KeyType*>(&key),
286                       get_value_addr(const_cast<ValueType&>(value))))
287       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
288     return StatusTuple::OK();
289   }
290 
remove_value(const KeyType & key)291   virtual StatusTuple remove_value(const KeyType& key) {
292     if (!this->remove(const_cast<KeyType*>(&key)))
293       return StatusTuple(-1, "Error removing value: %s", std::strerror(errno));
294     return StatusTuple::OK();
295   }
296 
297   ValueType operator[](const KeyType& key) {
298     ValueType value;
299     get_value(key, value);
300     return value;
301   }
302 
get_table_offline()303   std::vector<std::pair<KeyType, ValueType>> get_table_offline() {
304     std::vector<std::pair<KeyType, ValueType>> res;
305     KeyType cur;
306     ValueType value;
307 
308     StatusTuple r(0);
309 
310     if (!this->first(&cur))
311       return res;
312 
313     while (true) {
314       r = get_value(cur, value);
315       if (!r.ok())
316         break;
317       res.emplace_back(cur, value);
318       if (!this->next(&cur, &cur))
319         break;
320     }
321 
322     return res;
323   }
324 
clear_table_non_atomic()325   StatusTuple clear_table_non_atomic() {
326     KeyType cur;
327     while (this->first(&cur))
328       TRY2(remove_value(cur));
329 
330     return StatusTuple::OK();
331   }
332 };
333 
334 template <class KeyType, class ValueType>
335 class BPFPercpuHashTable
336     : public BPFHashTable<KeyType, std::vector<ValueType>> {
337  public:
BPFPercpuHashTable(const TableDesc & desc)338   explicit BPFPercpuHashTable(const TableDesc& desc)
339       : BPFHashTable<KeyType, std::vector<ValueType>>(desc),
340         ncpus(BPFTable::get_possible_cpu_count()) {
341     if (desc.type != BPF_MAP_TYPE_PERCPU_HASH &&
342         desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH)
343       throw std::invalid_argument("Table '" + desc.name +
344                                   "' is not a percpu hash table");
345     // leaf structures have to be aligned to 8 bytes as hardcoded in the linux
346     // kernel.
347     if (sizeof(ValueType) % 8)
348       throw std::invalid_argument("leaf must be aligned to 8 bytes");
349   }
350 
get_value(const KeyType & key,std::vector<ValueType> & value)351   StatusTuple get_value(const KeyType& key, std::vector<ValueType>& value) {
352     value.resize(ncpus);
353     return BPFHashTable<KeyType, std::vector<ValueType>>::get_value(key, value);
354   }
355 
update_value(const KeyType & key,const std::vector<ValueType> & value)356   StatusTuple update_value(const KeyType& key,
357                            const std::vector<ValueType>& value) {
358     if (value.size() != ncpus)
359       return StatusTuple(-1, "bad value size");
360     return BPFHashTable<KeyType, std::vector<ValueType>>::update_value(key,
361                                                                        value);
362   }
363 
364  private:
365   unsigned int ncpus;
366 };
367 
368 // From src/cc/export/helpers.h
369 static const int BPF_MAX_STACK_DEPTH = 127;
370 struct stacktrace_t {
371   uintptr_t ip[BPF_MAX_STACK_DEPTH];
372 };
373 
374 class BPFStackTable : public BPFTableBase<int, stacktrace_t> {
375  public:
376   BPFStackTable(const TableDesc& desc, bool use_debug_file,
377                 bool check_debug_file_crc);
378   BPFStackTable(BPFStackTable&& that);
379   ~BPFStackTable();
380 
381   void free_symcache(int pid);
382   void clear_table_non_atomic();
383   std::vector<uintptr_t> get_stack_addr(int stack_id);
384   std::vector<std::string> get_stack_symbol(int stack_id, int pid);
385 
386  private:
387   bcc_symbol_option symbol_option_;
388   std::map<int, void*> pid_sym_;
389 };
390 
391 // from src/cc/export/helpers.h
392 struct stacktrace_buildid_t {
393   struct bpf_stack_build_id trace[BPF_MAX_STACK_DEPTH];
394 };
395 
396 class BPFStackBuildIdTable : public BPFTableBase<int, stacktrace_buildid_t> {
397  public:
398   BPFStackBuildIdTable(const TableDesc& desc, bool use_debug_file,
399                        bool check_debug_file_crc, void *bsymcache);
400   ~BPFStackBuildIdTable() = default;
401 
402   void clear_table_non_atomic();
403   std::vector<bpf_stack_build_id> get_stack_addr(int stack_id);
404   std::vector<std::string> get_stack_symbol(int stack_id);
405 
406  private:
407   void *bsymcache_;
408   bcc_symbol_option symbol_option_;
409 };
410 
411 class BPFPerfBuffer : public BPFTableBase<int, int> {
412  public:
413   BPFPerfBuffer(const TableDesc& desc);
414   ~BPFPerfBuffer();
415 
416   StatusTuple open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb,
417                            void* cb_cookie, int page_cnt);
418   StatusTuple open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb,
419                            void* cb_cookie, int page_cnt, int wakeup_events);
420   StatusTuple close_all_cpu();
421   int poll(int timeout_ms);
422   int consume();
423 
424  private:
425   StatusTuple open_on_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb,
426                           void* cb_cookie, int page_cnt, struct bcc_perf_buffer_opts& opts);
427   StatusTuple close_on_cpu(int cpu);
428 
429   std::map<int, perf_reader*> cpu_readers_;
430 
431   int epfd_;
432   std::unique_ptr<epoll_event[]> ep_events_;
433 };
434 
435 class BPFPerfEventArray : public BPFTableBase<int, int> {
436  public:
437   BPFPerfEventArray(const TableDesc& desc);
438   ~BPFPerfEventArray();
439 
440   StatusTuple open_all_cpu(uint32_t type, uint64_t config);
441   StatusTuple close_all_cpu();
442 
443  private:
444   StatusTuple open_on_cpu(int cpu, uint32_t type, uint64_t config);
445   StatusTuple close_on_cpu(int cpu);
446 
447   std::map<int, int> cpu_fds_;
448 };
449 
450 class BPFProgTable : public BPFTableBase<int, int> {
451  public:
452   BPFProgTable(const TableDesc& desc);
453 
454   StatusTuple update_value(const int& index, const int& prog_fd);
455   StatusTuple remove_value(const int& index);
456 };
457 
458 class BPFCgroupArray : public BPFTableBase<int, int> {
459  public:
460   BPFCgroupArray(const TableDesc& desc);
461 
462   StatusTuple update_value(const int& index, const int& cgroup2_fd);
463   StatusTuple update_value(const int& index, const std::string& cgroup2_path);
464   StatusTuple remove_value(const int& index);
465 };
466 
467 class BPFDevmapTable : public BPFTableBase<int, int> {
468 public:
469   BPFDevmapTable(const TableDesc& desc);
470 
471   StatusTuple update_value(const int& index, const int& value);
472   StatusTuple get_value(const int& index, int& value);
473   StatusTuple remove_value(const int& index);
474 };
475 
476 class BPFXskmapTable : public BPFTableBase<int, int> {
477 public:
478   BPFXskmapTable(const TableDesc& desc);
479 
480   StatusTuple update_value(const int& index, const int& value);
481   StatusTuple get_value(const int& index, int& value);
482   StatusTuple remove_value(const int& index);
483 };
484 
485 template <class KeyType>
486 class BPFMapInMapTable : public BPFTableBase<KeyType, int> {
487  public:
BPFMapInMapTable(const TableDesc & desc)488   BPFMapInMapTable(const TableDesc& desc) : BPFTableBase<KeyType, int>(desc) {
489     if (desc.type != BPF_MAP_TYPE_ARRAY_OF_MAPS &&
490         desc.type != BPF_MAP_TYPE_HASH_OF_MAPS)
491       throw std::invalid_argument("Table '" + desc.name +
492                                   "' is not a map-in-map table");
493   }
update_value(const KeyType & key,const int & inner_map_fd)494   virtual StatusTuple update_value(const KeyType& key, const int& inner_map_fd) {
495     if (!this->update(const_cast<KeyType*>(&key),
496                       const_cast<int*>(&inner_map_fd)))
497       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
498     return StatusTuple::OK();
499   }
remove_value(const KeyType & key)500   virtual StatusTuple remove_value(const KeyType& key) {
501     if (!this->remove(const_cast<KeyType*>(&key)))
502       return StatusTuple(-1, "Error removing value: %s", std::strerror(errno));
503     return StatusTuple::OK();
504   }
505 };
506 
507 class BPFSockmapTable : public BPFTableBase<int, int> {
508 public:
509   BPFSockmapTable(const TableDesc& desc);
510 
511   StatusTuple update_value(const int& index, const int& value);
512   StatusTuple remove_value(const int& index);
513 };
514 
515 class BPFSockhashTable : public BPFTableBase<int, int> {
516 public:
517   BPFSockhashTable(const TableDesc& desc);
518 
519   StatusTuple update_value(const int& key, const int& value);
520   StatusTuple remove_value(const int& key);
521 };
522 
523 template <class ValueType>
524 class BPFSkStorageTable : public BPFTableBase<int, ValueType> {
525  public:
BPFSkStorageTable(const TableDesc & desc)526   BPFSkStorageTable(const TableDesc& desc) : BPFTableBase<int, ValueType>(desc) {
527     if (desc.type != BPF_MAP_TYPE_SK_STORAGE)
528       throw std::invalid_argument("Table '" + desc.name +
529                                   "' is not a sk_storage table");
530   }
531 
get_value(const int & sock_fd,ValueType & value)532   virtual StatusTuple get_value(const int& sock_fd, ValueType& value) {
533     if (!this->lookup(const_cast<int*>(&sock_fd), get_value_addr(value)))
534       return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
535     return StatusTuple::OK();
536   }
537 
update_value(const int & sock_fd,const ValueType & value)538   virtual StatusTuple update_value(const int& sock_fd, const ValueType& value) {
539     if (!this->update(const_cast<int*>(&sock_fd),
540                       get_value_addr(const_cast<ValueType&>(value))))
541       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
542     return StatusTuple::OK();
543   }
544 
remove_value(const int & sock_fd)545   virtual StatusTuple remove_value(const int& sock_fd) {
546     if (!this->remove(const_cast<int*>(&sock_fd)))
547       return StatusTuple(-1, "Error removing value: %s", std::strerror(errno));
548     return StatusTuple::OK();
549   }
550 };
551 
552 template <class ValueType>
553 class BPFInodeStorageTable : public BPFTableBase<int, ValueType> {
554  public:
BPFInodeStorageTable(const TableDesc & desc)555   BPFInodeStorageTable(const TableDesc& desc) : BPFTableBase<int, ValueType>(desc) {
556     if (desc.type != BPF_MAP_TYPE_INODE_STORAGE)
557       throw std::invalid_argument("Table '" + desc.name +
558                                   "' is not a inode_storage table");
559   }
560 
get_value(const int & fd,ValueType & value)561   virtual StatusTuple get_value(const int& fd, ValueType& value) {
562     if (!this->lookup(const_cast<int*>(&fd), get_value_addr(value)))
563       return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
564     return StatusTuple::OK();
565   }
566 
update_value(const int & fd,const ValueType & value)567   virtual StatusTuple update_value(const int& fd, const ValueType& value) {
568     if (!this->update(const_cast<int*>(&fd),
569                       get_value_addr(const_cast<ValueType&>(value))))
570       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
571     return StatusTuple::OK();
572   }
573 
remove_value(const int & fd)574   virtual StatusTuple remove_value(const int& fd) {
575     if (!this->remove(const_cast<int*>(&fd)))
576       return StatusTuple(-1, "Error removing value: %s", std::strerror(errno));
577     return StatusTuple::OK();
578   }
579 };
580 
581 template <class ValueType>
582 class BPFTaskStorageTable : public BPFTableBase<int, ValueType> {
583  public:
BPFTaskStorageTable(const TableDesc & desc)584   BPFTaskStorageTable(const TableDesc& desc) : BPFTableBase<int, ValueType>(desc) {
585     if (desc.type != BPF_MAP_TYPE_TASK_STORAGE)
586       throw std::invalid_argument("Table '" + desc.name +
587                                   "' is not a task_storage table");
588   }
589 
get_value(const int & fd,ValueType & value)590   virtual StatusTuple get_value(const int& fd, ValueType& value) {
591     if (!this->lookup(const_cast<int*>(&fd), get_value_addr(value)))
592       return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
593     return StatusTuple::OK();
594   }
595 
update_value(const int & fd,const ValueType & value)596   virtual StatusTuple update_value(const int& fd, const ValueType& value) {
597     if (!this->update(const_cast<int*>(&fd),
598                       get_value_addr(const_cast<ValueType&>(value))))
599       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
600     return StatusTuple::OK();
601   }
602 
remove_value(const int & fd)603   virtual StatusTuple remove_value(const int& fd) {
604     if (!this->remove(const_cast<int*>(&fd)))
605       return StatusTuple(-1, "Error removing value: %s", std::strerror(errno));
606     return StatusTuple::OK();
607   }
608 };
609 
610 template <class ValueType>
611 class BPFCgStorageTable : public BPFTableBase<int, ValueType> {
612  public:
BPFCgStorageTable(const TableDesc & desc)613   BPFCgStorageTable(const TableDesc& desc) : BPFTableBase<int, ValueType>(desc) {
614     if (desc.type != BPF_MAP_TYPE_CGROUP_STORAGE)
615       throw std::invalid_argument("Table '" + desc.name +
616                                   "' is not a cgroup_storage table");
617   }
618 
get_value(struct bpf_cgroup_storage_key & key,ValueType & value)619   virtual StatusTuple get_value(struct bpf_cgroup_storage_key& key,
620                                 ValueType& value) {
621     if (!this->lookup(const_cast<struct bpf_cgroup_storage_key*>(&key),
622                       get_value_addr(value)))
623       return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
624     return StatusTuple::OK();
625   }
626 
update_value(struct bpf_cgroup_storage_key & key,const ValueType & value)627   virtual StatusTuple update_value(struct bpf_cgroup_storage_key& key, const ValueType& value) {
628     if (!this->update(const_cast<struct bpf_cgroup_storage_key*>(&key),
629                       get_value_addr(const_cast<ValueType&>(value))))
630       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
631     return StatusTuple::OK();
632   }
633 };
634 
635 template <class ValueType>
636 class BPFPercpuCgStorageTable : public BPFTableBase<int, std::vector<ValueType>> {
637  public:
BPFPercpuCgStorageTable(const TableDesc & desc)638   BPFPercpuCgStorageTable(const TableDesc& desc)
639       : BPFTableBase<int, std::vector<ValueType>>(desc) {
640     if (desc.type != BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE)
641       throw std::invalid_argument("Table '" + desc.name +
642                                   "' is not a percpu_cgroup_storage table");
643     if (sizeof(ValueType) % 8)
644       throw std::invalid_argument("leaf must be aligned to 8 bytes");
645     ncpus = BPFTable::get_possible_cpu_count();
646   }
647 
get_value(struct bpf_cgroup_storage_key & key,std::vector<ValueType> & value)648   virtual StatusTuple get_value(struct bpf_cgroup_storage_key& key,
649                                 std::vector<ValueType>& value) {
650     value.resize(ncpus);
651     if (!this->lookup(const_cast<struct bpf_cgroup_storage_key*>(&key),
652                       get_value_addr(value)))
653       return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
654     return StatusTuple::OK();
655   }
656 
update_value(struct bpf_cgroup_storage_key & key,std::vector<ValueType> & value)657   virtual StatusTuple update_value(struct bpf_cgroup_storage_key& key,
658                                    std::vector<ValueType>& value) {
659     value.resize(ncpus);
660     if (!this->update(const_cast<struct bpf_cgroup_storage_key*>(&key),
661                       get_value_addr(const_cast<std::vector<ValueType>&>(value))))
662       return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
663     return StatusTuple::OK();
664   }
665  private:
666   unsigned int ncpus;
667 };
668 
669 }  // namespace ebpf
670