1 # Copyright 2015 PLUMgrid
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 from __future__ import print_function
17 from collections.abc import MutableMapping
19 from collections import MutableMapping
20 from time import strftime
22 from functools import reduce
28 from .libbcc import lib, _RAW_CB_TYPE, _LOST_CB_TYPE, _RINGBUF_CB_TYPE
29 from .utils import get_online_cpus
30 from .utils import get_possible_cpus
33 BPF_MAP_TYPE_ARRAY = 2
34 BPF_MAP_TYPE_PROG_ARRAY = 3
35 BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4
36 BPF_MAP_TYPE_PERCPU_HASH = 5
37 BPF_MAP_TYPE_PERCPU_ARRAY = 6
38 BPF_MAP_TYPE_STACK_TRACE = 7
39 BPF_MAP_TYPE_CGROUP_ARRAY = 8
40 BPF_MAP_TYPE_LRU_HASH = 9
41 BPF_MAP_TYPE_LRU_PERCPU_HASH = 10
42 BPF_MAP_TYPE_LPM_TRIE = 11
43 BPF_MAP_TYPE_ARRAY_OF_MAPS = 12
44 BPF_MAP_TYPE_HASH_OF_MAPS = 13
45 BPF_MAP_TYPE_DEVMAP = 14
46 BPF_MAP_TYPE_SOCKMAP = 15
47 BPF_MAP_TYPE_CPUMAP = 16
48 BPF_MAP_TYPE_XSKMAP = 17
49 BPF_MAP_TYPE_SOCKHASH = 18
50 BPF_MAP_TYPE_CGROUP_STORAGE = 19
51 BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20
52 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21
53 BPF_MAP_TYPE_QUEUE = 22
54 BPF_MAP_TYPE_STACK = 23
55 BPF_MAP_TYPE_SK_STORAGE = 24
56 BPF_MAP_TYPE_DEVMAP_HASH = 25
57 BPF_MAP_TYPE_STRUCT_OPS = 26
58 BPF_MAP_TYPE_RINGBUF = 27
60 map_type_name = {BPF_MAP_TYPE_HASH: "HASH",
61 BPF_MAP_TYPE_ARRAY: "ARRAY",
62 BPF_MAP_TYPE_PROG_ARRAY: "PROG_ARRAY",
63 BPF_MAP_TYPE_PERF_EVENT_ARRAY: "PERF_EVENT_ARRAY",
64 BPF_MAP_TYPE_PERCPU_HASH: "PERCPU_HASH",
65 BPF_MAP_TYPE_PERCPU_ARRAY: "PERCPU_ARRAY",
66 BPF_MAP_TYPE_STACK_TRACE: "STACK_TRACE",
67 BPF_MAP_TYPE_CGROUP_ARRAY: "CGROUP_ARRAY",
68 BPF_MAP_TYPE_LRU_HASH: "LRU_HASH",
69 BPF_MAP_TYPE_LRU_PERCPU_HASH: "LRU_PERCPU_HASH",
70 BPF_MAP_TYPE_LPM_TRIE: "LPM_TRIE",
71 BPF_MAP_TYPE_ARRAY_OF_MAPS: "ARRAY_OF_MAPS",
72 BPF_MAP_TYPE_HASH_OF_MAPS: "HASH_OF_MAPS",
73 BPF_MAP_TYPE_DEVMAP: "DEVMAP",
74 BPF_MAP_TYPE_SOCKMAP: "SOCKMAP",
75 BPF_MAP_TYPE_CPUMAP: "CPUMAP",
76 BPF_MAP_TYPE_XSKMAP: "XSKMAP",
77 BPF_MAP_TYPE_SOCKHASH: "SOCKHASH",
78 BPF_MAP_TYPE_CGROUP_STORAGE: "CGROUP_STORAGE",
79 BPF_MAP_TYPE_REUSEPORT_SOCKARRAY: "REUSEPORT_SOCKARRAY",
80 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: "PERCPU_CGROUP_STORAGE",
81 BPF_MAP_TYPE_QUEUE: "QUEUE",
82 BPF_MAP_TYPE_STACK: "STACK",
83 BPF_MAP_TYPE_SK_STORAGE: "SK_STORAGE",
84 BPF_MAP_TYPE_DEVMAP_HASH: "DEVMAP_HASH",
85 BPF_MAP_TYPE_STRUCT_OPS: "STRUCT_OPS",
86 BPF_MAP_TYPE_RINGBUF: "RINGBUF",}
90 linear_index_max = 1025
92 # helper functions, consider moving these to a utils module
93 def _stars(val, val_max, width):
97 if (i > (width * val / val_max) - 1) or (i > width - 1):
102 text = text[:-1] + "+"
105 def _print_json_hist(vals, val_type, section_bucket=None):
108 for i in range(len(vals)):
113 for i in range(len(vals)):
114 if i != 0 and i <= max_nonzero_idx:
118 list_obj['interval-start'] = prev
119 list_obj['interval-end'] = int(index) - 1
120 list_obj['count'] = int(vals[i])
122 hist_list.append(list_obj)
125 histogram = {"ts": strftime("%Y-%m-%d %H:%M:%S"), "val_type": val_type, "data": hist_list}
127 histogram[section_bucket[0]] = section_bucket[1]
130 def _print_log2_hist(vals, val_type, strip_leading_zero):
136 for i, v in enumerate(vals):
137 if v > 0: idx_max = i
138 if v > val_max: val_max = v
141 header = " %-19s : count distribution"
142 body = "%10d -> %-10d : %-8d |%-*s|"
145 header = " %-29s : count distribution"
146 body = "%20d -> %-20d : %-8d |%-*s|"
147 stars = int(stars_max / 2)
150 print(header % val_type)
152 for i in range(1, idx_max + 1):
159 if strip_leading_zero:
161 print(body % (low, high, val, stars,
162 _stars(val, val_max, stars)))
163 strip_leading_zero = False
165 print(body % (low, high, val, stars,
166 _stars(val, val_max, stars)))
168 def _print_linear_hist(vals, val_type, strip_leading_zero):
174 for i, v in enumerate(vals):
175 if v > 0: idx_max = i
176 if v > val_max: val_max = v
178 header = " %-13s : count distribution"
179 body = " %-10d : %-8d |%-*s|"
183 print(header % val_type)
184 for i in range(0, idx_max + 1):
187 if strip_leading_zero:
189 print(body % (i, val, stars,
190 _stars(val, val_max, stars)))
191 strip_leading_zero = False
193 print(body % (i, val, stars,
194 _stars(val, val_max, stars)))
197 def get_table_type_name(ttype):
199 return map_type_name[ttype]
204 def _get_event_class(event_map):
205 ct_mapping = { 'char' : ct.c_char,
207 'unsigned char' : ct.c_ubyte,
209 'u8 *' : ct.c_char_p,
210 'char *' : ct.c_char_p,
211 'short' : ct.c_short,
213 'unsigned short' : ct.c_ushort,
218 'unsigned int' : ct.c_uint,
221 'unsigned long' : ct.c_ulong,
222 'long long' : ct.c_longlong,
223 's64' : ct.c_longlong,
224 'unsigned long long': ct.c_ulonglong,
225 'u64' : ct.c_ulonglong,
226 '__int128' : (ct.c_longlong * 2),
227 'unsigned __int128' : (ct.c_ulonglong * 2),
228 'void *' : ct.c_void_p }
230 # handle array types e.g. "int [16] foo"
231 array_type = re.compile(r"(.+) \[([0-9]+)\]$")
234 num_fields = lib.bpf_perf_event_fields(event_map.bpf.module, event_map._name)
236 while i < num_fields:
237 field = lib.bpf_perf_event_field(event_map.bpf.module, event_map._name, i).decode()
238 m = re.match(r"(.*)#(.*)", field)
239 field_name = m.group(1)
240 field_type = m.group(2)
242 if re.match(r"enum .*", field_type):
245 m = array_type.match(field_type)
248 fields.append((field_name, ct_mapping[m.group(1)] * int(m.group(2))))
250 fields.append((field_name, ct_mapping[field_type]))
252 # Using print+sys.exit instead of raising exceptions,
253 # because exceptions are caught by the caller.
254 print("Type: '%s' not recognized. Please define the data with ctypes manually."
255 % field_type, file=sys.stderr)
258 return type('', (ct.Structure,), {'_fields_': fields})
261 def Table(bpf, map_id, map_fd, keytype, leaftype, name, **kwargs):
262 """Table(bpf, map_id, map_fd, keytype, leaftype, **kwargs)
264 Create a python object out of a reference to a bpf table handle"""
266 ttype = lib.bpf_table_type_id(bpf.module, map_id)
268 if ttype == BPF_MAP_TYPE_HASH:
269 t = HashTable(bpf, map_id, map_fd, keytype, leaftype)
270 elif ttype == BPF_MAP_TYPE_ARRAY:
271 t = Array(bpf, map_id, map_fd, keytype, leaftype)
272 elif ttype == BPF_MAP_TYPE_PROG_ARRAY:
273 t = ProgArray(bpf, map_id, map_fd, keytype, leaftype)
274 elif ttype == BPF_MAP_TYPE_PERF_EVENT_ARRAY:
275 t = PerfEventArray(bpf, map_id, map_fd, keytype, leaftype, name)
276 elif ttype == BPF_MAP_TYPE_PERCPU_HASH:
277 t = PerCpuHash(bpf, map_id, map_fd, keytype, leaftype, **kwargs)
278 elif ttype == BPF_MAP_TYPE_PERCPU_ARRAY:
279 t = PerCpuArray(bpf, map_id, map_fd, keytype, leaftype, **kwargs)
280 elif ttype == BPF_MAP_TYPE_LPM_TRIE:
281 t = LpmTrie(bpf, map_id, map_fd, keytype, leaftype)
282 elif ttype == BPF_MAP_TYPE_STACK_TRACE:
283 t = StackTrace(bpf, map_id, map_fd, keytype, leaftype)
284 elif ttype == BPF_MAP_TYPE_LRU_HASH:
285 t = LruHash(bpf, map_id, map_fd, keytype, leaftype)
286 elif ttype == BPF_MAP_TYPE_LRU_PERCPU_HASH:
287 t = LruPerCpuHash(bpf, map_id, map_fd, keytype, leaftype)
288 elif ttype == BPF_MAP_TYPE_CGROUP_ARRAY:
289 t = CgroupArray(bpf, map_id, map_fd, keytype, leaftype)
290 elif ttype == BPF_MAP_TYPE_DEVMAP:
291 t = DevMap(bpf, map_id, map_fd, keytype, leaftype)
292 elif ttype == BPF_MAP_TYPE_CPUMAP:
293 t = CpuMap(bpf, map_id, map_fd, keytype, leaftype)
294 elif ttype == BPF_MAP_TYPE_XSKMAP:
295 t = XskMap(bpf, map_id, map_fd, keytype, leaftype)
296 elif ttype == BPF_MAP_TYPE_ARRAY_OF_MAPS:
297 t = MapInMapArray(bpf, map_id, map_fd, keytype, leaftype)
298 elif ttype == BPF_MAP_TYPE_HASH_OF_MAPS:
299 t = MapInMapHash(bpf, map_id, map_fd, keytype, leaftype)
300 elif ttype == BPF_MAP_TYPE_QUEUE or ttype == BPF_MAP_TYPE_STACK:
301 t = QueueStack(bpf, map_id, map_fd, leaftype)
302 elif ttype == BPF_MAP_TYPE_RINGBUF:
303 t = RingBuf(bpf, map_id, map_fd, keytype, leaftype, name)
305 raise Exception("Unknown table type %d" % ttype)
309 class TableBase(MutableMapping):
311 def __init__(self, bpf, map_id, map_fd, keytype, leaftype, name=None):
317 self.ttype = lib.bpf_table_type_id(self.bpf.module, self.map_id)
318 self.flags = lib.bpf_table_flags_id(self.bpf.module, self.map_id)
321 self.max_entries = int(lib.bpf_table_max_entries_id(self.bpf.module,
327 def key_sprintf(self, key):
328 buf = ct.create_string_buffer(ct.sizeof(self.Key) * 8)
329 res = lib.bpf_table_key_snprintf(self.bpf.module, self.map_id, buf,
330 len(buf), ct.byref(key))
332 raise Exception("Could not printf key")
335 def leaf_sprintf(self, leaf):
336 buf = ct.create_string_buffer(ct.sizeof(self.Leaf) * 8)
337 res = lib.bpf_table_leaf_snprintf(self.bpf.module, self.map_id, buf,
338 len(buf), ct.byref(leaf))
340 raise Exception("Could not printf leaf")
343 def key_scanf(self, key_str):
345 res = lib.bpf_table_key_sscanf(self.bpf.module, self.map_id, key_str,
348 raise Exception("Could not scanf key")
351 def leaf_scanf(self, leaf_str):
353 res = lib.bpf_table_leaf_sscanf(self.bpf.module, self.map_id, leaf_str,
356 raise Exception("Could not scanf leaf")
359 def __getitem__(self, key):
361 res = lib.bpf_lookup_elem(self.map_fd, ct.byref(key), ct.byref(leaf))
366 def __setitem__(self, key, leaf):
367 res = lib.bpf_update_elem(self.map_fd, ct.byref(key), ct.byref(leaf), 0)
369 errstr = os.strerror(ct.get_errno())
370 raise Exception("Could not update table: %s" % errstr)
372 def __delitem__(self, key):
373 res = lib.bpf_delete_elem(self.map_fd, ct.byref(key))
377 # override the MutableMapping's implementation of these since they
378 # don't handle KeyError nicely
379 def itervalues(self):
381 # a map entry may be deleted in between discovering the key and
382 # fetching the value, suppress such errors
391 yield (key, self[key])
396 return [item for item in self.iteritems()]
399 return [value for value in self.itervalues()]
402 # default clear uses popitem, which can race with the bpf prog
403 for k in self.keys():
406 def _alloc_keys_values(self, alloc_k=False, alloc_v=False, count=None):
407 """Allocate keys and/or values arrays. Useful for in items_*_batch.
410 alloc_k (bool): True to allocate keys array, False otherwise.
412 alloc_v (bool): True to allocate values array, False otherwise.
414 count (int): number of elements in the array(s) to allocate. If
415 count is None then it allocates the maximum number of elements i.e
419 tuple: (count, keys, values). Where count is ct.c_uint32,
420 and keys and values an instance of ct.Array
422 ValueError: If count is less than 1 or greater than
426 if not alloc_k and not alloc_v:
427 return (ct.c_uint32(0), None, None)
429 if not count: # means alloc maximum size
430 count = self.max_entries
431 elif count < 1 or count > self.max_entries:
432 raise ValueError("Wrong count")
435 keys = (self.Key * count)()
437 values = (self.Leaf * count)()
439 return (ct.c_uint32(count), keys, values)
441 def _sanity_check_keys_values(self, keys=None, values=None):
442 """Check if the given keys or values have the right type and size.
445 keys (ct.Array): keys array to check
446 values (ct.Array): values array to check
448 ct.c_uint32 : the size of the array(s)
450 ValueError: If length of arrays is less than 1 or greater than
451 self.max_entries, or when both arrays length are different.
452 TypeError: If the keys and values are not an instance of ct.Array
455 for elem in [keys, values]:
457 if not isinstance(elem, ct.Array):
461 if arr_len < 1 or arr_len > self.max_entries:
462 raise ValueError("Array's length is wrong")
465 # check both length are equal
466 if len(keys) != len(values):
467 raise ValueError("keys array length != values array length")
469 return ct.c_uint32(arr_len)
471 def items_lookup_batch(self):
472 """Look up all the key-value pairs in the map.
477 tuple: The tuple of (key,value) for every entries that have
479 Notes: lookup batch on a keys subset is not supported by the kernel.
481 for k, v in self._items_lookup_and_optionally_delete_batch(delete=False):
485 def items_delete_batch(self, ct_keys=None):
486 """Delete the key-value pairs related to the keys given as parameters.
487 Note that if no key are given, it is faster to call
488 lib.bpf_lookup_and_delete_batch than create keys array and then call
489 lib.bpf_delete_batch on these keys.
492 ct_keys (ct.Array): keys array to delete. If an array of keys is
493 given then it deletes all the related keys-values.
494 If keys is None (default) then it deletes all entries.
496 tuple: The tuple of (key,value) for every entries that have
499 Exception: If bpf syscall return value indicates an error.
501 if ct_keys is not None:
502 ct_cnt = self._sanity_check_keys_values(keys=ct_keys)
503 res = lib.bpf_delete_batch(self.map_fd,
508 raise Exception("BPF_MAP_DELETE_BATCH has failed: %s"
509 % os.strerror(ct.get_errno()))
512 for _ in self.items_lookup_and_delete_batch():
515 def items_update_batch(self, ct_keys, ct_values):
516 """Update all the key-value pairs in the map provided.
517 The arrays must be the same length, between 1 and the maximum number
521 ct_keys (ct.Array): keys array to update
522 ct_values (ct.Array): values array to update
524 Exception: If bpf syscall return value indicates an error.
526 ct_cnt = self._sanity_check_keys_values(keys=ct_keys, values=ct_values)
527 res = lib.bpf_update_batch(self.map_fd,
533 raise Exception("BPF_MAP_UPDATE_BATCH has failed: %s"
534 % os.strerror(ct.get_errno()))
536 def items_lookup_and_delete_batch(self):
537 """Look up and delete all the key-value pairs in the map.
542 tuple: The tuple of (key,value) for every entries that have
543 been looked up and deleted.
544 Notes: lookup and delete batch on a keys subset is not supported by
547 for k, v in self._items_lookup_and_optionally_delete_batch(delete=True):
551 def _items_lookup_and_optionally_delete_batch(self, delete=True):
552 """Look up and optionally delete all the key-value pairs in the map.
555 delete (bool) : look up and delete the key-value pairs when True,
558 tuple: The tuple of (key,value) for every entries that have
559 been looked up and deleted.
561 Exception: If bpf syscall return value indicates an error.
562 Notes: lookup and delete batch on a keys subset is not supported by
566 bpf_batch = lib.bpf_lookup_and_delete_batch
567 bpf_cmd = "BPF_MAP_LOOKUP_AND_DELETE_BATCH"
569 bpf_batch = lib.bpf_lookup_batch
570 bpf_cmd = "BPF_MAP_LOOKUP_BATCH"
572 # alloc keys and values to the max size
573 ct_buf_size, ct_keys, ct_values = self._alloc_keys_values(alloc_k=True,
575 ct_out_batch = ct_cnt = ct.c_uint32(0)
578 ct_cnt.value = ct_buf_size.value - total
579 res = bpf_batch(self.map_fd,
580 ct.byref(ct_out_batch) if total else None,
581 ct.byref(ct_out_batch),
582 ct.byref(ct_keys, ct.sizeof(self.Key) * total),
583 ct.byref(ct_values, ct.sizeof(self.Leaf) * total),
586 errcode = ct.get_errno()
587 total += ct_cnt.value
588 if (res != 0 and errcode != errno.ENOENT):
589 raise Exception("%s has failed: %s" % (bpf_cmd,
590 os.strerror(errcode)))
595 if total == ct_buf_size.value: # buffer full, we can't progress
598 if ct_cnt.value == 0:
599 # no progress, probably because concurrent update
600 # puts too many elements in one bucket.
603 for i in range(0, total):
604 yield (ct_keys[i], ct_values[i])
607 # Even though this is not very efficient, we grab the entire list of
608 # keys before enumerating it. This helps avoid a potential race where
609 # the leaf assignment changes a hash table bucket that is being
610 # enumerated by the same loop, and may lead to a hang.
611 for k in list(self.keys()):
612 self[k] = self.Leaf()
615 return TableBase.Iter(self)
617 def iter(self): return self.__iter__()
618 def keys(self): return self.__iter__()
621 def __init__(self, table):
629 self.key = self.table.next(self.key)
633 next_key = self.Key()
636 res = lib.bpf_get_first_key(self.map_fd, ct.byref(next_key),
639 res = lib.bpf_get_next_key(self.map_fd, ct.byref(key),
643 raise StopIteration()
646 def decode_c_struct(self, tmp, buckets, bucket_fn, bucket_sort_fn):
647 f1 = self.Key._fields_[0][0]
648 f2 = self.Key._fields_[1][0]
649 # The above code assumes that self.Key._fields_[1][0] holds the
650 # slot. But a padding member may have been inserted here, which
651 # breaks the assumption and leads to chaos.
652 # TODO: this is a quick fix. Fixing/working around in the BCC
653 # internal library is the right thing to do.
654 if f2 == '__pad_1' and len(self.Key._fields_) == 3:
655 f2 = self.Key._fields_[2][0]
656 for k, v in self.items():
657 bucket = getattr(k, f1)
659 bucket = bucket_fn(bucket)
660 vals = tmp[bucket] = tmp.get(bucket, [0] * log2_index_max)
661 slot = getattr(k, f2)
663 buckets_lst = list(tmp.keys())
665 buckets_lst = bucket_sort_fn(buckets_lst)
666 for bucket in buckets_lst:
667 buckets.append(bucket)
669 def print_json_hist(self, val_type="value", section_header="Bucket ptr",
670 section_print_fn=None, bucket_fn=None, bucket_sort_fn=None):
671 """print_json_hist(val_type="value", section_header="Bucket ptr",
672 section_print_fn=None, bucket_fn=None,
673 bucket_sort_fn=None):
675 Prints a table as a json histogram. The table must be stored as
676 log2. The val_type argument is optional, and is a column header.
677 If the histogram has a secondary key, the dictionary will be split by secondary key
678 If section_print_fn is not None, it will be passed the bucket value
679 to format into a string as it sees fit. If bucket_fn is not None,
680 it will be used to produce a bucket value for the histogram keys.
681 If bucket_sort_fn is not None, it will be used to sort the buckets
682 before iterating them, and it is useful when there are multiple fields
683 in the secondary key.
684 The maximum index allowed is log2_index_max (65), which will
685 accommodate any 64-bit integer in the histogram.
687 if isinstance(self.Key(), ct.Structure):
690 self.decode_c_struct(tmp, buckets, bucket_fn, bucket_sort_fn)
691 for bucket in buckets:
694 section_bucket = (section_header, section_print_fn(bucket))
696 section_bucket = (section_header, bucket)
697 _print_json_hist(vals, val_type, section_bucket)
700 vals = [0] * log2_index_max
701 for k, v in self.items():
702 vals[k.value] = v.value
703 _print_json_hist(vals, val_type)
705 def print_log2_hist(self, val_type="value", section_header="Bucket ptr",
706 section_print_fn=None, bucket_fn=None, strip_leading_zero=None,
707 bucket_sort_fn=None):
708 """print_log2_hist(val_type="value", section_header="Bucket ptr",
709 section_print_fn=None, bucket_fn=None,
710 strip_leading_zero=None, bucket_sort_fn=None):
712 Prints a table as a log2 histogram. The table must be stored as
713 log2. The val_type argument is optional, and is a column header.
714 If the histogram has a secondary key, multiple tables will print
715 and section_header can be used as a header description for each.
716 If section_print_fn is not None, it will be passed the bucket value
717 to format into a string as it sees fit. If bucket_fn is not None,
718 it will be used to produce a bucket value for the histogram keys.
719 If the value of strip_leading_zero is not False, prints a histogram
720 that is omitted leading zeros from the beginning.
721 If bucket_sort_fn is not None, it will be used to sort the buckets
722 before iterating them, and it is useful when there are multiple fields
723 in the secondary key.
724 The maximum index allowed is log2_index_max (65), which will
725 accommodate any 64-bit integer in the histogram.
727 if isinstance(self.Key(), ct.Structure):
730 self.decode_c_struct(tmp, buckets, bucket_fn, bucket_sort_fn)
731 for bucket in buckets:
734 print("\n%s = %s" % (section_header,
735 section_print_fn(bucket)))
737 print("\n%s = %r" % (section_header, bucket))
738 _print_log2_hist(vals, val_type, strip_leading_zero)
740 vals = [0] * log2_index_max
741 for k, v in self.items():
742 vals[k.value] = v.value
743 _print_log2_hist(vals, val_type, strip_leading_zero)
745 def print_linear_hist(self, val_type="value", section_header="Bucket ptr",
746 section_print_fn=None, bucket_fn=None, strip_leading_zero=None,
747 bucket_sort_fn=None):
748 """print_linear_hist(val_type="value", section_header="Bucket ptr",
749 section_print_fn=None, bucket_fn=None,
750 strip_leading_zero=None, bucket_sort_fn=None)
752 Prints a table as a linear histogram. This is intended to span integer
753 ranges, eg, from 0 to 100. The val_type argument is optional, and is a
754 column header. If the histogram has a secondary key, multiple tables
755 will print and section_header can be used as a header description for
756 each. If section_print_fn is not None, it will be passed the bucket
757 value to format into a string as it sees fit. If bucket_fn is not None,
758 it will be used to produce a bucket value for the histogram keys.
759 If the value of strip_leading_zero is not False, prints a histogram
760 that is omitted leading zeros from the beginning.
761 If bucket_sort_fn is not None, it will be used to sort the buckets
762 before iterating them, and it is useful when there are multiple fields
763 in the secondary key.
764 The maximum index allowed is linear_index_max (1025), which is hoped
765 to be sufficient for integer ranges spanned.
767 if isinstance(self.Key(), ct.Structure):
770 self.decode_c_struct(tmp, buckets, bucket_fn, bucket_sort_fn)
772 for bucket in buckets:
775 print("\n%s = %s" % (section_header,
776 section_print_fn(bucket)))
778 print("\n%s = %r" % (section_header, bucket))
779 _print_linear_hist(vals, val_type, strip_leading_zero)
781 vals = [0] * linear_index_max
782 for k, v in self.items():
784 vals[k.value] = v.value
786 # Improve error text. If the limit proves a nusiance, this
787 # function be rewritten to avoid having one.
788 raise IndexError(("Index in print_linear_hist() of %d " +
789 "exceeds max of %d.") % (k.value, linear_index_max))
790 _print_linear_hist(vals, val_type, strip_leading_zero)
793 class HashTable(TableBase):
794 def __init__(self, *args, **kwargs):
795 super(HashTable, self).__init__(*args, **kwargs)
799 for k in self: i += 1
802 class LruHash(HashTable):
803 def __init__(self, *args, **kwargs):
804 super(LruHash, self).__init__(*args, **kwargs)
806 class ArrayBase(TableBase):
807 def __init__(self, *args, **kwargs):
808 super(ArrayBase, self).__init__(*args, **kwargs)
810 def _normalize_key(self, key):
811 if isinstance(key, int):
813 key = len(self) + key
815 if not isinstance(key, ct._SimpleCData):
816 raise IndexError("Array index must be an integer type")
817 if key.value >= len(self):
818 raise IndexError("Array index out of range")
822 return self.max_entries
824 def __getitem__(self, key):
825 key = self._normalize_key(key)
826 return super(ArrayBase, self).__getitem__(key)
828 def __setitem__(self, key, leaf):
829 key = self._normalize_key(key)
830 super(ArrayBase, self).__setitem__(key, leaf)
832 def __delitem__(self, key):
833 key = self._normalize_key(key)
834 super(ArrayBase, self).__delitem__(key)
836 def clearitem(self, key):
837 key = self._normalize_key(key)
839 res = lib.bpf_update_elem(self.map_fd, ct.byref(key), ct.byref(leaf), 0)
841 raise Exception("Could not clear item")
844 return ArrayBase.Iter(self, self.Key)
847 def __init__(self, table, keytype):
858 if self.i == len(self.table):
859 raise StopIteration()
860 return self.Key(self.i)
862 class Array(ArrayBase):
863 def __init__(self, *args, **kwargs):
864 super(Array, self).__init__(*args, **kwargs)
866 def __delitem__(self, key):
867 # Delete in Array type does not have an effect, so zero out instead
870 class ProgArray(ArrayBase):
871 def __init__(self, *args, **kwargs):
872 super(ProgArray, self).__init__(*args, **kwargs)
874 def __setitem__(self, key, leaf):
875 if isinstance(leaf, int):
876 leaf = self.Leaf(leaf)
877 if isinstance(leaf, self.bpf.Function):
878 leaf = self.Leaf(leaf.fd)
879 super(ProgArray, self).__setitem__(key, leaf)
882 def __init__(self, fd):
883 if (fd is None) or (fd < 0):
884 raise Exception("Invalid file descriptor")
888 if (self.fd is not None) and (self.fd >= 0):
895 def __enter__(self, *args, **kwargs):
898 def __exit__(self, *args, **kwargs):
901 class CgroupArray(ArrayBase):
902 def __init__(self, *args, **kwargs):
903 super(CgroupArray, self).__init__(*args, **kwargs)
905 def __setitem__(self, key, leaf):
906 if isinstance(leaf, int):
907 super(CgroupArray, self).__setitem__(key, self.Leaf(leaf))
908 elif isinstance(leaf, str):
909 # TODO: Add os.O_CLOEXEC once we move to Python version >3.3
910 with FileDesc(os.open(leaf, os.O_RDONLY)) as f:
911 super(CgroupArray, self).__setitem__(key, self.Leaf(f.fd))
913 raise Exception("Cgroup array key must be either FD or cgroup path")
915 class PerfEventArray(ArrayBase):
917 def __init__(self, *args, **kwargs):
918 super(PerfEventArray, self).__init__(*args, **kwargs)
919 self._open_key_fds = {}
920 self._event_class = None
923 keys = list(self._open_key_fds.keys())
927 def __delitem__(self, key):
928 if key not in self._open_key_fds:
930 # Delete entry from the array
931 super(PerfEventArray, self).__delitem__(key)
932 key_id = (id(self), key)
933 if key_id in self.bpf.perf_buffers:
934 # The key is opened for perf ring buffer
935 lib.perf_reader_free(self.bpf.perf_buffers[key_id])
936 del self.bpf.perf_buffers[key_id]
939 # The key is opened for perf event read
940 lib.bpf_close_perf_event_fd(self._open_key_fds[key])
941 del self._open_key_fds[key]
943 def event(self, data):
946 When perf buffers are opened to receive custom perf event,
947 the underlying event data struct which is defined in C in
948 the BPF program can be deduced via this function. This avoids
949 redundant definitions in Python.
951 if self._event_class == None:
952 self._event_class = _get_event_class(self)
953 return ct.cast(data, ct.POINTER(self._event_class)).contents
955 def open_perf_buffer(self, callback, page_cnt=8, lost_cb=None):
956 """open_perf_buffers(callback)
958 Opens a set of per-cpu ring buffer to receive custom perf event
959 data from the bpf program. The callback will be invoked for each
960 event submitted from the kernel, up to millions per second. Use
961 page_cnt to change the size of the per-cpu ring buffer. The value
962 must be a power of two and defaults to 8.
965 if page_cnt & (page_cnt - 1) != 0:
966 raise Exception("Perf buffer page_cnt must be a power of two")
968 for i in get_online_cpus():
969 self._open_perf_buffer(i, callback, page_cnt, lost_cb)
971 def _open_perf_buffer(self, cpu, callback, page_cnt, lost_cb):
972 def raw_cb_(_, data, size):
974 callback(cpu, data, size)
976 if e.errno == errno.EPIPE:
980 def lost_cb_(_, lost):
984 if e.errno == errno.EPIPE:
988 fn = _RAW_CB_TYPE(raw_cb_)
989 lost_fn = _LOST_CB_TYPE(lost_cb_) if lost_cb else ct.cast(None, _LOST_CB_TYPE)
990 reader = lib.bpf_open_perf_buffer(fn, lost_fn, None, -1, cpu, page_cnt)
992 raise Exception("Could not open perf buffer")
993 fd = lib.perf_reader_fd(reader)
994 self[self.Key(cpu)] = self.Leaf(fd)
995 self.bpf.perf_buffers[(id(self), cpu)] = reader
997 self._cbs[cpu] = (fn, lost_fn)
998 # The actual fd is held by the perf reader, add to track opened keys
999 self._open_key_fds[cpu] = -1
1001 def _open_perf_event(self, cpu, typ, config):
1002 fd = lib.bpf_open_perf_event(typ, config, -1, cpu)
1004 raise Exception("bpf_open_perf_event failed")
1005 self[self.Key(cpu)] = self.Leaf(fd)
1006 self._open_key_fds[cpu] = fd
1008 def open_perf_event(self, typ, config):
1009 """open_perf_event(typ, config)
1011 Configures the table such that calls from the bpf program to
1012 table.perf_read(CUR_CPU_IDENTIFIER) will return the hardware
1013 counter denoted by event ev on the local cpu.
1015 for i in get_online_cpus():
1016 self._open_perf_event(i, typ, config)
1019 class PerCpuHash(HashTable):
1020 def __init__(self, *args, **kwargs):
1021 self.reducer = kwargs.pop("reducer", None)
1022 super(PerCpuHash, self).__init__(*args, **kwargs)
1023 self.sLeaf = self.Leaf
1024 self.total_cpu = len(get_possible_cpus())
1025 # This needs to be 8 as hard coded into the linux kernel.
1026 self.alignment = ct.sizeof(self.sLeaf) % 8
1027 if self.alignment == 0:
1028 self.Leaf = self.sLeaf * self.total_cpu
1030 # Currently Float, Char, un-aligned structs are not supported
1031 if self.sLeaf == ct.c_uint:
1032 self.Leaf = ct.c_uint64 * self.total_cpu
1033 elif self.sLeaf == ct.c_int:
1034 self.Leaf = ct.c_int64 * self.total_cpu
1036 raise IndexError("Leaf must be aligned to 8 bytes")
1038 def getvalue(self, key):
1039 result = super(PerCpuHash, self).__getitem__(key)
1040 if self.alignment == 0:
1043 ret = (self.sLeaf * self.total_cpu)()
1044 for i in range(0, self.total_cpu):
1048 def __getitem__(self, key):
1050 return reduce(self.reducer, self.getvalue(key))
1052 return self.getvalue(key)
1054 def __setitem__(self, key, leaf):
1055 super(PerCpuHash, self).__setitem__(key, leaf)
1058 if isinstance(self.Leaf(), ct.Structure):
1059 raise IndexError("Leaf must be an integer type for default sum functions")
1060 return self.sLeaf(sum(self.getvalue(key)))
1063 if isinstance(self.Leaf(), ct.Structure):
1064 raise IndexError("Leaf must be an integer type for default max functions")
1065 return self.sLeaf(max(self.getvalue(key)))
1067 def average(self, key):
1068 result = self.sum(key)
1069 return result.value / self.total_cpu
1071 class LruPerCpuHash(PerCpuHash):
1072 def __init__(self, *args, **kwargs):
1073 super(LruPerCpuHash, self).__init__(*args, **kwargs)
1075 class PerCpuArray(ArrayBase):
1076 def __init__(self, *args, **kwargs):
1077 self.reducer = kwargs.pop("reducer", None)
1078 super(PerCpuArray, self).__init__(*args, **kwargs)
1079 self.sLeaf = self.Leaf
1080 self.total_cpu = len(get_possible_cpus())
1081 # This needs to be 8 as hard coded into the linux kernel.
1082 self.alignment = ct.sizeof(self.sLeaf) % 8
1083 if self.alignment == 0:
1084 self.Leaf = self.sLeaf * self.total_cpu
1086 # Currently Float, Char, un-aligned structs are not supported
1087 if self.sLeaf == ct.c_uint:
1088 self.Leaf = ct.c_uint64 * self.total_cpu
1089 elif self.sLeaf == ct.c_int:
1090 self.Leaf = ct.c_int64 * self.total_cpu
1092 raise IndexError("Leaf must be aligned to 8 bytes")
1094 def getvalue(self, key):
1095 result = super(PerCpuArray, self).__getitem__(key)
1096 if self.alignment == 0:
1099 ret = (self.sLeaf * self.total_cpu)()
1100 for i in range(0, self.total_cpu):
1104 def __getitem__(self, key):
1106 return reduce(self.reducer, self.getvalue(key))
1108 return self.getvalue(key)
1110 def __setitem__(self, key, leaf):
1111 super(PerCpuArray, self).__setitem__(key, leaf)
1113 def __delitem__(self, key):
1114 # Delete in this type does not have an effect, so zero out instead
1118 if isinstance(self.Leaf(), ct.Structure):
1119 raise IndexError("Leaf must be an integer type for default sum functions")
1120 return self.sLeaf(sum(self.getvalue(key)))
1123 if isinstance(self.Leaf(), ct.Structure):
1124 raise IndexError("Leaf must be an integer type for default max functions")
1125 return self.sLeaf(max(self.getvalue(key)))
1127 def average(self, key):
1128 result = self.sum(key)
1129 return result.value / self.total_cpu
1131 class LpmTrie(TableBase):
1132 def __init__(self, *args, **kwargs):
1133 super(LpmTrie, self).__init__(*args, **kwargs)
1136 raise NotImplementedError
1139 class StackTrace(TableBase):
1141 BPF_F_STACK_BUILD_ID = (1<<5)
1142 BPF_STACK_BUILD_ID_EMPTY = 0 #can't get stacktrace
1143 BPF_STACK_BUILD_ID_VALID = 1 #valid build-id,ip
1144 BPF_STACK_BUILD_ID_IP = 2 #fallback to ip
1146 def __init__(self, *args, **kwargs):
1147 super(StackTrace, self).__init__(*args, **kwargs)
1149 class StackWalker(object):
1150 def __init__(self, stack, flags, resolve=None):
1153 self.resolve = resolve
1164 if self.n == StackTrace.MAX_DEPTH:
1165 raise StopIteration()
1167 if self.flags & StackTrace.BPF_F_STACK_BUILD_ID:
1168 addr = self.stack.trace[self.n]
1169 if addr.status == StackTrace.BPF_STACK_BUILD_ID_IP or \
1170 addr.status == StackTrace.BPF_STACK_BUILD_ID_EMPTY:
1171 raise StopIteration()
1173 addr = self.stack.ip[self.n]
1176 raise StopIteration()
1178 return self.resolve(addr) if self.resolve else addr
1180 def walk(self, stack_id, resolve=None):
1181 return StackTrace.StackWalker(self[self.Key(stack_id)], self.flags, resolve)
1185 for k in self: i += 1
1191 class DevMap(ArrayBase):
1192 def __init__(self, *args, **kwargs):
1193 super(DevMap, self).__init__(*args, **kwargs)
1195 class CpuMap(ArrayBase):
1196 def __init__(self, *args, **kwargs):
1197 super(CpuMap, self).__init__(*args, **kwargs)
1199 class XskMap(ArrayBase):
1200 def __init__(self, *args, **kwargs):
1201 super(XskMap, self).__init__(*args, **kwargs)
1203 class MapInMapArray(ArrayBase):
1204 def __init__(self, *args, **kwargs):
1205 super(MapInMapArray, self).__init__(*args, **kwargs)
1207 class MapInMapHash(HashTable):
1208 def __init__(self, *args, **kwargs):
1209 super(MapInMapHash, self).__init__(*args, **kwargs)
1211 class RingBuf(TableBase):
1212 def __init__(self, *args, **kwargs):
1213 super(RingBuf, self).__init__(*args, **kwargs)
1214 self._ringbuf = None
1215 self._event_class = None
1217 def __delitem(self, key):
1226 def event(self, data):
1229 When ring buffers are opened to receive custom event,
1230 the underlying event data struct which is defined in C in
1231 the BPF program can be deduced via this function. This avoids
1232 redundant definitions in Python.
1234 if self._event_class == None:
1235 self._event_class = _get_event_class(self)
1236 return ct.cast(data, ct.POINTER(self._event_class)).contents
1238 def open_ring_buffer(self, callback, ctx=None):
1239 """open_ring_buffer(callback)
1241 Opens a ring buffer to receive custom event data from the bpf program.
1242 The callback will be invoked for each event submitted from the kernel,
1243 up to millions per second.
1246 def ringbuf_cb_(ctx, data, size):
1248 ret = callback(ctx, data, size)
1249 # Callback for ringbufs should _always_ return an integer.
1250 # If the function the user registers does not,
1251 # simply fall back to returning 0.
1256 except IOError as e:
1257 if e.errno == errno.EPIPE:
1263 fn = _RINGBUF_CB_TYPE(ringbuf_cb_)
1264 self.bpf._open_ring_buffer(self.map_fd, fn, ctx)
1272 def __init__(self, bpf, map_id, map_fd, leaftype):
1274 self.map_id = map_id
1275 self.map_fd = map_fd
1276 self.Leaf = leaftype
1277 self.ttype = lib.bpf_table_type_id(self.bpf.module, self.map_id)
1278 self.flags = lib.bpf_table_flags_id(self.bpf.module, self.map_id)
1279 self.max_entries = int(lib.bpf_table_max_entries_id(self.bpf.module,
1282 def leaf_sprintf(self, leaf):
1283 buf = ct.create_string_buffer(ct.sizeof(self.Leaf) * 8)
1284 res = lib.bpf_table_leaf_snprintf(self.bpf.module, self.map_id, buf,
1285 len(buf), ct.byref(leaf))
1287 raise Exception("Could not printf leaf")
1290 def leaf_scanf(self, leaf_str):
1292 res = lib.bpf_table_leaf_sscanf(self.bpf.module, self.map_id, leaf_str,
1295 raise Exception("Could not scanf leaf")
1298 def push(self, leaf, flags=0):
1299 res = lib.bpf_update_elem(self.map_fd, None, ct.byref(leaf), flags)
1301 errstr = os.strerror(ct.get_errno())
1302 raise Exception("Could not push to table: %s" % errstr)
1306 res = lib.bpf_lookup_and_delete(self.map_fd, None, ct.byref(leaf))
1308 raise KeyError("Could not pop from table")
1313 res = lib.bpf_lookup_elem(self.map_fd, None, ct.byref(leaf))
1315 raise KeyError("Could not peek table")
1318 def itervalues(self):
1319 # to avoid infinite loop, set maximum pops to max_entries
1320 cnt = self.max_entries
1329 return [value for value in self.itervalues()]