bcc/python: remove unused imports, remove redundant semicolon
[platform/upstream/bcc.git] / src / python / bcc / table.py
1 # Copyright 2015 PLUMgrid
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 from __future__ import print_function
16 try:
17     from collections.abc import MutableMapping
18 except ImportError:
19     from collections import MutableMapping
20 from time import strftime
21 import ctypes as ct
22 from functools import reduce
23 import os
24 import errno
25 import re
26 import sys
27
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
31
32 BPF_MAP_TYPE_HASH = 1
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
59
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",}
87
88 stars_max = 40
89 log2_index_max = 65
90 linear_index_max = 1025
91
92 # helper functions, consider moving these to a utils module
93 def _stars(val, val_max, width):
94     i = 0
95     text = ""
96     while (1):
97         if (i > (width * val / val_max) - 1) or (i > width - 1):
98             break
99         text += "*"
100         i += 1
101     if val > val_max:
102         text = text[:-1] + "+"
103     return text
104
105 def _print_json_hist(vals, val_type, section_bucket=None):
106     hist_list = []
107     max_nonzero_idx = 0
108     for i in range(len(vals)):
109         if vals[i] != 0:
110             max_nonzero_idx = i
111     index = 1
112     prev = 0
113     for i in range(len(vals)):
114         if i != 0 and i <= max_nonzero_idx:
115             index = index * 2
116
117             list_obj = {}
118             list_obj['interval-start'] = prev
119             list_obj['interval-end'] = int(index) - 1
120             list_obj['count'] = int(vals[i])
121
122             hist_list.append(list_obj)
123
124             prev = index
125     histogram = {"ts": strftime("%Y-%m-%d %H:%M:%S"), "val_type": val_type, "data": hist_list}
126     if section_bucket:
127         histogram[section_bucket[0]] = section_bucket[1]
128     print(histogram)
129
130 def _print_log2_hist(vals, val_type, strip_leading_zero):
131     global stars_max
132     log2_dist_max = 64
133     idx_max = -1
134     val_max = 0
135
136     for i, v in enumerate(vals):
137         if v > 0: idx_max = i
138         if v > val_max: val_max = v
139
140     if idx_max <= 32:
141         header = "     %-19s : count     distribution"
142         body = "%10d -> %-10d : %-8d |%-*s|"
143         stars = stars_max
144     else:
145         header = "               %-29s : count     distribution"
146         body = "%20d -> %-20d : %-8d |%-*s|"
147         stars = int(stars_max / 2)
148
149     if idx_max > 0:
150         print(header % val_type)
151
152     for i in range(1, idx_max + 1):
153         low = (1 << i) >> 1
154         high = (1 << i) - 1
155         if (low == high):
156             low -= 1
157         val = vals[i]
158
159         if strip_leading_zero:
160             if val:
161                 print(body % (low, high, val, stars,
162                               _stars(val, val_max, stars)))
163                 strip_leading_zero = False
164         else:
165             print(body % (low, high, val, stars,
166                           _stars(val, val_max, stars)))
167
168 def _print_linear_hist(vals, val_type, strip_leading_zero):
169     global stars_max
170     log2_dist_max = 64
171     idx_max = -1
172     val_max = 0
173
174     for i, v in enumerate(vals):
175         if v > 0: idx_max = i
176         if v > val_max: val_max = v
177
178     header = "     %-13s : count     distribution"
179     body = "        %-10d : %-8d |%-*s|"
180     stars = stars_max
181
182     if idx_max >= 0:
183         print(header % val_type)
184     for i in range(0, idx_max + 1):
185         val = vals[i]
186
187         if strip_leading_zero:
188             if val:
189                 print(body % (i, val, stars,
190                               _stars(val, val_max, stars)))
191                 strip_leading_zero = False
192         else:
193                 print(body % (i, val, stars,
194                               _stars(val, val_max, stars)))
195
196
197 def get_table_type_name(ttype):
198     try:
199         return map_type_name[ttype]
200     except KeyError:
201         return "<unknown>"
202
203
204 def _get_event_class(event_map):
205     ct_mapping = { 'char'              : ct.c_char,
206                    's8'                : ct.c_char,
207                    'unsigned char'     : ct.c_ubyte,
208                    'u8'                : ct.c_ubyte,
209                    'u8 *'              : ct.c_char_p,
210                    'char *'            : ct.c_char_p,
211                    'short'             : ct.c_short,
212                    's16'               : ct.c_short,
213                    'unsigned short'    : ct.c_ushort,
214                    'u16'               : ct.c_ushort,
215                    'int'               : ct.c_int,
216                    's32'               : ct.c_int,
217                    'enum'              : ct.c_int,
218                    'unsigned int'      : ct.c_uint,
219                    'u32'               : ct.c_uint,
220                    'long'              : ct.c_long,
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 }
229
230     # handle array types e.g. "int [16] foo"
231     array_type = re.compile(r"(.+) \[([0-9]+)\]$")
232
233     fields = []
234     num_fields = lib.bpf_perf_event_fields(event_map.bpf.module, event_map._name)
235     i = 0
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)
241
242         if re.match(r"enum .*", field_type):
243             field_type = "enum"
244
245         m = array_type.match(field_type)
246         try:
247             if m:
248                 fields.append((field_name, ct_mapping[m.group(1)] * int(m.group(2))))
249             else:
250                 fields.append((field_name, ct_mapping[field_type]))
251         except KeyError:
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)
256             sys.exit(1)
257         i += 1
258     return type('', (ct.Structure,), {'_fields_': fields})
259
260
261 def Table(bpf, map_id, map_fd, keytype, leaftype, name, **kwargs):
262     """Table(bpf, map_id, map_fd, keytype, leaftype, **kwargs)
263
264     Create a python object out of a reference to a bpf table handle"""
265
266     ttype = lib.bpf_table_type_id(bpf.module, map_id)
267     t = None
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)
304     if t == None:
305         raise Exception("Unknown table type %d" % ttype)
306     return t
307
308
309 class TableBase(MutableMapping):
310
311     def __init__(self, bpf, map_id, map_fd, keytype, leaftype, name=None):
312         self.bpf = bpf
313         self.map_id = map_id
314         self.map_fd = map_fd
315         self.Key = keytype
316         self.Leaf = leaftype
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)
319         self._cbs = {}
320         self._name = name
321         self.max_entries = int(lib.bpf_table_max_entries_id(self.bpf.module,
322                 self.map_id))
323
324     def get_fd(self):
325         return self.map_fd
326
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))
331         if res < 0:
332             raise Exception("Could not printf key")
333         return buf.value
334
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))
339         if res < 0:
340             raise Exception("Could not printf leaf")
341         return buf.value
342
343     def key_scanf(self, key_str):
344         key = self.Key()
345         res = lib.bpf_table_key_sscanf(self.bpf.module, self.map_id, key_str,
346                                        ct.byref(key))
347         if res < 0:
348             raise Exception("Could not scanf key")
349         return key
350
351     def leaf_scanf(self, leaf_str):
352         leaf = self.Leaf()
353         res = lib.bpf_table_leaf_sscanf(self.bpf.module, self.map_id, leaf_str,
354                                         ct.byref(leaf))
355         if res < 0:
356             raise Exception("Could not scanf leaf")
357         return leaf
358
359     def __getitem__(self, key):
360         leaf = self.Leaf()
361         res = lib.bpf_lookup_elem(self.map_fd, ct.byref(key), ct.byref(leaf))
362         if res < 0:
363             raise KeyError
364         return leaf
365
366     def __setitem__(self, key, leaf):
367         res = lib.bpf_update_elem(self.map_fd, ct.byref(key), ct.byref(leaf), 0)
368         if res < 0:
369             errstr = os.strerror(ct.get_errno())
370             raise Exception("Could not update table: %s" % errstr)
371
372     def __delitem__(self, key):
373         res = lib.bpf_delete_elem(self.map_fd, ct.byref(key))
374         if res < 0:
375             raise KeyError
376
377     # override the MutableMapping's implementation of these since they
378     # don't handle KeyError nicely
379     def itervalues(self):
380         for key in self:
381             # a map entry may be deleted in between discovering the key and
382             # fetching the value, suppress such errors
383             try:
384                 yield self[key]
385             except KeyError:
386                 pass
387
388     def iteritems(self):
389         for key in self:
390             try:
391                 yield (key, self[key])
392             except KeyError:
393                 pass
394
395     def items(self):
396         return [item for item in self.iteritems()]
397
398     def values(self):
399         return [value for value in self.itervalues()]
400
401     def clear(self):
402         # default clear uses popitem, which can race with the bpf prog
403         for k in self.keys():
404             self.__delitem__(k)
405
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.
408
409         Args:
410             alloc_k (bool): True to allocate keys array, False otherwise.
411             Default is False.
412             alloc_v (bool): True to allocate values array, False otherwise.
413             Default is False.
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
416             self.max_entries.
417
418         Returns:
419             tuple: (count, keys, values). Where count is ct.c_uint32,
420             and keys and values an instance of ct.Array
421         Raises:
422             ValueError: If count is less than 1 or greater than
423             self.max_entries.
424         """
425         keys = values = None
426         if not alloc_k and not alloc_v:
427             return (ct.c_uint32(0), None, None)
428
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")
433
434         if alloc_k:
435             keys = (self.Key * count)()
436         if alloc_v:
437             values = (self.Leaf * count)()
438
439         return (ct.c_uint32(count), keys, values)
440
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.
443
444         Args:
445             keys (ct.Array): keys array to check
446             values (ct.Array): values array to check
447         Returns:
448             ct.c_uint32 : the size of the array(s)
449         Raises:
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
453         """
454         arr_len = 0
455         for elem in [keys, values]:
456             if elem:
457                 if not isinstance(elem, ct.Array):
458                     raise TypeError
459
460                 arr_len = len(elem)
461                 if arr_len < 1 or arr_len > self.max_entries:
462                     raise ValueError("Array's length is wrong")
463
464         if keys and values:
465             # check both length are equal
466             if len(keys) != len(values):
467                 raise ValueError("keys array length != values array length")
468
469         return ct.c_uint32(arr_len)
470
471     def items_lookup_batch(self):
472         """Look up all the key-value pairs in the map.
473
474         Args:
475             None
476         Yields:
477             tuple: The tuple of (key,value) for every entries that have
478             been looked up.
479         Notes: lookup batch on a keys subset is not supported by the kernel.
480         """
481         for k, v in self._items_lookup_and_optionally_delete_batch(delete=False):
482             yield(k, v)
483         return
484
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.
490
491         Args:
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.
495         Yields:
496             tuple: The tuple of (key,value) for every entries that have
497             been deleted.
498         Raises:
499             Exception: If bpf syscall return value indicates an error.
500         """
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,
504                                        ct.byref(ct_keys),
505                                        ct.byref(ct_cnt)
506                                        )
507             if (res != 0):
508                 raise Exception("BPF_MAP_DELETE_BATCH has failed: %s"
509                                 % os.strerror(ct.get_errno()))
510
511         else:
512             for _ in self.items_lookup_and_delete_batch():
513                 return
514
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
518         of entries.
519
520         Args:
521             ct_keys (ct.Array): keys array to update
522             ct_values (ct.Array): values array to update
523         Raises:
524             Exception: If bpf syscall return value indicates an error.
525         """
526         ct_cnt = self._sanity_check_keys_values(keys=ct_keys, values=ct_values)
527         res = lib.bpf_update_batch(self.map_fd,
528                                    ct.byref(ct_keys),
529                                    ct.byref(ct_values),
530                                    ct.byref(ct_cnt)
531                                    )
532         if (res != 0):
533             raise Exception("BPF_MAP_UPDATE_BATCH has failed: %s"
534                             % os.strerror(ct.get_errno()))
535
536     def items_lookup_and_delete_batch(self):
537         """Look up and delete all the key-value pairs in the map.
538
539         Args:
540             None
541         Yields:
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
545         the kernel.
546         """
547         for k, v in self._items_lookup_and_optionally_delete_batch(delete=True):
548             yield(k, v)
549         return
550
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.
553
554         Args:
555             delete (bool) : look up and delete the key-value pairs when True,
556             else just look up.
557         Yields:
558             tuple: The tuple of (key,value) for every entries that have
559             been looked up and deleted.
560         Raises:
561             Exception: If bpf syscall return value indicates an error.
562         Notes: lookup and delete batch on a keys subset is not supported by
563         the kernel.
564         """
565         if delete is True:
566             bpf_batch = lib.bpf_lookup_and_delete_batch
567             bpf_cmd = "BPF_MAP_LOOKUP_AND_DELETE_BATCH"
568         else:
569             bpf_batch = lib.bpf_lookup_batch
570             bpf_cmd = "BPF_MAP_LOOKUP_BATCH"
571
572         # alloc keys and values to the max size
573         ct_buf_size, ct_keys, ct_values = self._alloc_keys_values(alloc_k=True,
574                                                                   alloc_v=True)
575         ct_out_batch = ct_cnt = ct.c_uint32(0)
576         total = 0
577         while True:
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),
584                             ct.byref(ct_cnt)
585                             )
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)))
591
592             if res != 0:
593                 break  # success
594
595             if total == ct_buf_size.value:  # buffer full, we can't progress
596                 break
597
598             if ct_cnt.value == 0:
599                 # no progress, probably because concurrent update
600                 # puts too many elements in one bucket.
601                 break
602
603         for i in range(0, total):
604             yield (ct_keys[i], ct_values[i])
605
606     def zero(self):
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()
613
614     def __iter__(self):
615         return TableBase.Iter(self)
616
617     def iter(self): return self.__iter__()
618     def keys(self): return self.__iter__()
619
620     class Iter(object):
621         def __init__(self, table):
622             self.table = table
623             self.key = None
624         def __iter__(self):
625             return self
626         def __next__(self):
627             return self.next()
628         def next(self):
629             self.key = self.table.next(self.key)
630             return self.key
631
632     def next(self, key):
633         next_key = self.Key()
634
635         if key is None:
636             res = lib.bpf_get_first_key(self.map_fd, ct.byref(next_key),
637                                         ct.sizeof(self.Key))
638         else:
639             res = lib.bpf_get_next_key(self.map_fd, ct.byref(key),
640                                        ct.byref(next_key))
641
642         if res < 0:
643             raise StopIteration()
644         return next_key
645
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)
658             if bucket_fn:
659                 bucket = bucket_fn(bucket)
660             vals = tmp[bucket] = tmp.get(bucket, [0] * log2_index_max)
661             slot = getattr(k, f2)
662             vals[slot] = v.value
663         buckets_lst = list(tmp.keys())
664         if bucket_sort_fn:
665             buckets_lst = bucket_sort_fn(buckets_lst)
666         for bucket in buckets_lst:
667             buckets.append(bucket)
668
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):
674
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.
686                 """
687         if isinstance(self.Key(), ct.Structure):
688             tmp = {}
689             buckets = []
690             self.decode_c_struct(tmp, buckets, bucket_fn, bucket_sort_fn)
691             for bucket in buckets:
692                 vals = tmp[bucket]
693                 if section_print_fn:
694                     section_bucket = (section_header, section_print_fn(bucket))
695                 else:
696                     section_bucket = (section_header, bucket)
697                 _print_json_hist(vals, val_type, section_bucket)
698
699         else:
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)
704
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):
711
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.
726         """
727         if isinstance(self.Key(), ct.Structure):
728             tmp = {}
729             buckets = []
730             self.decode_c_struct(tmp, buckets, bucket_fn, bucket_sort_fn)
731             for bucket in buckets:
732                 vals = tmp[bucket]
733                 if section_print_fn:
734                     print("\n%s = %s" % (section_header,
735                         section_print_fn(bucket)))
736                 else:
737                     print("\n%s = %r" % (section_header, bucket))
738                 _print_log2_hist(vals, val_type, strip_leading_zero)
739         else:
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)
744
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)
751
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.
766         """
767         if isinstance(self.Key(), ct.Structure):
768             tmp = {}
769             buckets = []
770             self.decode_c_struct(tmp, buckets, bucket_fn, bucket_sort_fn)
771
772             for bucket in buckets:
773                 vals = tmp[bucket]
774                 if section_print_fn:
775                     print("\n%s = %s" % (section_header,
776                         section_print_fn(bucket)))
777                 else:
778                     print("\n%s = %r" % (section_header, bucket))
779                 _print_linear_hist(vals, val_type, strip_leading_zero)
780         else:
781             vals = [0] * linear_index_max
782             for k, v in self.items():
783                 try:
784                     vals[k.value] = v.value
785                 except IndexError:
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)
791
792
793 class HashTable(TableBase):
794     def __init__(self, *args, **kwargs):
795         super(HashTable, self).__init__(*args, **kwargs)
796
797     def __len__(self):
798         i = 0
799         for k in self: i += 1
800         return i
801
802 class LruHash(HashTable):
803     def __init__(self, *args, **kwargs):
804         super(LruHash, self).__init__(*args, **kwargs)
805
806 class ArrayBase(TableBase):
807     def __init__(self, *args, **kwargs):
808         super(ArrayBase, self).__init__(*args, **kwargs)
809
810     def _normalize_key(self, key):
811         if isinstance(key, int):
812             if key < 0:
813                 key = len(self) + key
814             key = self.Key(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")
819         return key
820
821     def __len__(self):
822         return self.max_entries
823
824     def __getitem__(self, key):
825         key = self._normalize_key(key)
826         return super(ArrayBase, self).__getitem__(key)
827
828     def __setitem__(self, key, leaf):
829         key = self._normalize_key(key)
830         super(ArrayBase, self).__setitem__(key, leaf)
831
832     def __delitem__(self, key):
833         key = self._normalize_key(key)
834         super(ArrayBase, self).__delitem__(key)
835
836     def clearitem(self, key):
837         key = self._normalize_key(key)
838         leaf = self.Leaf()
839         res = lib.bpf_update_elem(self.map_fd, ct.byref(key), ct.byref(leaf), 0)
840         if res < 0:
841             raise Exception("Could not clear item")
842
843     def __iter__(self):
844         return ArrayBase.Iter(self, self.Key)
845
846     class Iter(object):
847         def __init__(self, table, keytype):
848             self.Key = keytype
849             self.table = table
850             self.i = -1
851
852         def __iter__(self):
853             return self
854         def __next__(self):
855             return self.next()
856         def next(self):
857             self.i += 1
858             if self.i == len(self.table):
859                 raise StopIteration()
860             return self.Key(self.i)
861
862 class Array(ArrayBase):
863     def __init__(self, *args, **kwargs):
864         super(Array, self).__init__(*args, **kwargs)
865
866     def __delitem__(self, key):
867         # Delete in Array type does not have an effect, so zero out instead
868         self.clearitem(key)
869
870 class ProgArray(ArrayBase):
871     def __init__(self, *args, **kwargs):
872         super(ProgArray, self).__init__(*args, **kwargs)
873
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)
880
881 class FileDesc:
882     def __init__(self, fd):
883         if (fd is None) or (fd < 0):
884             raise Exception("Invalid file descriptor")
885         self.fd = fd
886
887     def clean_up(self):
888         if (self.fd is not None) and (self.fd >= 0):
889             os.close(self.fd)
890             self.fd = None
891
892     def __del__(self):
893         self.clean_up()
894
895     def __enter__(self, *args, **kwargs):
896         return self
897
898     def __exit__(self, *args, **kwargs):
899         self.clean_up()
900
901 class CgroupArray(ArrayBase):
902     def __init__(self, *args, **kwargs):
903         super(CgroupArray, self).__init__(*args, **kwargs)
904
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))
912         else:
913             raise Exception("Cgroup array key must be either FD or cgroup path")
914
915 class PerfEventArray(ArrayBase):
916
917     def __init__(self, *args, **kwargs):
918         super(PerfEventArray, self).__init__(*args, **kwargs)
919         self._open_key_fds = {}
920         self._event_class = None
921
922     def __del__(self):
923         keys = list(self._open_key_fds.keys())
924         for key in keys:
925             del self[key]
926
927     def __delitem__(self, key):
928         if key not in self._open_key_fds:
929             return
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]
937             del self._cbs[key]
938         else:
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]
942
943     def event(self, data):
944         """event(data)
945
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.
950         """
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
954
955     def open_perf_buffer(self, callback, page_cnt=8, lost_cb=None):
956         """open_perf_buffers(callback)
957
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.
963         """
964
965         if page_cnt & (page_cnt - 1) != 0:
966             raise Exception("Perf buffer page_cnt must be a power of two")
967
968         for i in get_online_cpus():
969             self._open_perf_buffer(i, callback, page_cnt, lost_cb)
970
971     def _open_perf_buffer(self, cpu, callback, page_cnt, lost_cb):
972         def raw_cb_(_, data, size):
973             try:
974                 callback(cpu, data, size)
975             except IOError as e:
976                 if e.errno == errno.EPIPE:
977                     exit()
978                 else:
979                     raise e
980         def lost_cb_(_, lost):
981             try:
982                 lost_cb(lost)
983             except IOError as e:
984                 if e.errno == errno.EPIPE:
985                     exit()
986                 else:
987                     raise e
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)
991         if not reader:
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
996         # keep a refcnt
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
1000
1001     def _open_perf_event(self, cpu, typ, config):
1002         fd = lib.bpf_open_perf_event(typ, config, -1, cpu)
1003         if fd < 0:
1004             raise Exception("bpf_open_perf_event failed")
1005         self[self.Key(cpu)] = self.Leaf(fd)
1006         self._open_key_fds[cpu] = fd
1007
1008     def open_perf_event(self, typ, config):
1009         """open_perf_event(typ, config)
1010
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.
1014         """
1015         for i in get_online_cpus():
1016             self._open_perf_event(i, typ, config)
1017
1018
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
1029         else:
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
1035             else:
1036                 raise IndexError("Leaf must be aligned to 8 bytes")
1037
1038     def getvalue(self, key):
1039         result = super(PerCpuHash, self).__getitem__(key)
1040         if self.alignment == 0:
1041             ret = result
1042         else:
1043             ret = (self.sLeaf * self.total_cpu)()
1044             for i in range(0, self.total_cpu):
1045                 ret[i] = result[i]
1046         return ret
1047
1048     def __getitem__(self, key):
1049         if self.reducer:
1050             return reduce(self.reducer, self.getvalue(key))
1051         else:
1052             return self.getvalue(key)
1053
1054     def __setitem__(self, key, leaf):
1055         super(PerCpuHash, self).__setitem__(key, leaf)
1056
1057     def sum(self, key):
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)))
1061
1062     def max(self, 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)))
1066
1067     def average(self, key):
1068         result = self.sum(key)
1069         return result.value / self.total_cpu
1070
1071 class LruPerCpuHash(PerCpuHash):
1072     def __init__(self, *args, **kwargs):
1073         super(LruPerCpuHash, self).__init__(*args, **kwargs)
1074
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
1085         else:
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
1091             else:
1092                 raise IndexError("Leaf must be aligned to 8 bytes")
1093
1094     def getvalue(self, key):
1095         result = super(PerCpuArray, self).__getitem__(key)
1096         if self.alignment == 0:
1097             ret = result
1098         else:
1099             ret = (self.sLeaf * self.total_cpu)()
1100             for i in range(0, self.total_cpu):
1101                 ret[i] = result[i]
1102         return ret
1103
1104     def __getitem__(self, key):
1105         if (self.reducer):
1106             return reduce(self.reducer, self.getvalue(key))
1107         else:
1108             return self.getvalue(key)
1109
1110     def __setitem__(self, key, leaf):
1111         super(PerCpuArray, self).__setitem__(key, leaf)
1112
1113     def __delitem__(self, key):
1114         # Delete in this type does not have an effect, so zero out instead
1115         self.clearitem(key)
1116
1117     def sum(self, key):
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)))
1121
1122     def max(self, 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)))
1126
1127     def average(self, key):
1128         result = self.sum(key)
1129         return result.value / self.total_cpu
1130
1131 class LpmTrie(TableBase):
1132     def __init__(self, *args, **kwargs):
1133         super(LpmTrie, self).__init__(*args, **kwargs)
1134
1135     def __len__(self):
1136         raise NotImplementedError
1137
1138
1139 class StackTrace(TableBase):
1140     MAX_DEPTH = 127
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
1145
1146     def __init__(self, *args, **kwargs):
1147         super(StackTrace, self).__init__(*args, **kwargs)
1148
1149     class StackWalker(object):
1150         def __init__(self, stack, flags, resolve=None):
1151             self.stack = stack
1152             self.n = -1
1153             self.resolve = resolve
1154             self.flags = flags
1155
1156         def __iter__(self):
1157             return self
1158
1159         def __next__(self):
1160             return self.next()
1161
1162         def next(self):
1163             self.n += 1
1164             if self.n == StackTrace.MAX_DEPTH:
1165                 raise StopIteration()
1166
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()
1172             else:
1173               addr = self.stack.ip[self.n]
1174
1175             if addr == 0 :
1176                 raise StopIteration()
1177
1178             return self.resolve(addr) if self.resolve else addr
1179
1180     def walk(self, stack_id, resolve=None):
1181         return StackTrace.StackWalker(self[self.Key(stack_id)], self.flags, resolve)
1182
1183     def __len__(self):
1184         i = 0
1185         for k in self: i += 1
1186         return i
1187
1188     def clear(self):
1189         pass
1190
1191 class DevMap(ArrayBase):
1192     def __init__(self, *args, **kwargs):
1193         super(DevMap, self).__init__(*args, **kwargs)
1194
1195 class CpuMap(ArrayBase):
1196     def __init__(self, *args, **kwargs):
1197         super(CpuMap, self).__init__(*args, **kwargs)
1198
1199 class XskMap(ArrayBase):
1200     def __init__(self, *args, **kwargs):
1201         super(XskMap, self).__init__(*args, **kwargs)
1202
1203 class MapInMapArray(ArrayBase):
1204     def __init__(self, *args, **kwargs):
1205         super(MapInMapArray, self).__init__(*args, **kwargs)
1206
1207 class MapInMapHash(HashTable):
1208     def __init__(self, *args, **kwargs):
1209         super(MapInMapHash, self).__init__(*args, **kwargs)
1210
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
1216
1217     def __delitem(self, key):
1218         pass
1219
1220     def __del__(self):
1221         pass
1222
1223     def __len__(self):
1224         return 0
1225
1226     def event(self, data):
1227         """event(data)
1228
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.
1233         """
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
1237
1238     def open_ring_buffer(self, callback, ctx=None):
1239         """open_ring_buffer(callback)
1240
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.
1244         """
1245
1246         def ringbuf_cb_(ctx, data, size):
1247             try:
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.
1252                 try:
1253                     ret = int(ret)
1254                 except:
1255                     ret = 0
1256             except IOError as e:
1257                 if e.errno == errno.EPIPE:
1258                     exit()
1259                 else:
1260                     raise e
1261             return ret
1262
1263         fn = _RINGBUF_CB_TYPE(ringbuf_cb_)
1264         self.bpf._open_ring_buffer(self.map_fd, fn, ctx)
1265         # keep a refcnt
1266         self._cbs[0] = fn
1267
1268 class QueueStack:
1269     # Flag for map.push
1270     BPF_EXIST = 2
1271
1272     def __init__(self, bpf, map_id, map_fd, leaftype):
1273         self.bpf = bpf
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,
1280                 self.map_id))
1281
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))
1286         if res < 0:
1287             raise Exception("Could not printf leaf")
1288         return buf.value
1289
1290     def leaf_scanf(self, leaf_str):
1291         leaf = self.Leaf()
1292         res = lib.bpf_table_leaf_sscanf(self.bpf.module, self.map_id, leaf_str,
1293                                         ct.byref(leaf))
1294         if res < 0:
1295             raise Exception("Could not scanf leaf")
1296         return leaf
1297
1298     def push(self, leaf, flags=0):
1299         res = lib.bpf_update_elem(self.map_fd, None, ct.byref(leaf), flags)
1300         if res < 0:
1301             errstr = os.strerror(ct.get_errno())
1302             raise Exception("Could not push to table: %s" % errstr)
1303
1304     def pop(self):
1305         leaf = self.Leaf()
1306         res = lib.bpf_lookup_and_delete(self.map_fd, None, ct.byref(leaf))
1307         if res < 0:
1308             raise KeyError("Could not pop from table")
1309         return leaf
1310
1311     def peek(self):
1312         leaf = self.Leaf()
1313         res = lib.bpf_lookup_elem(self.map_fd, None, ct.byref(leaf))
1314         if res < 0:
1315             raise KeyError("Could not peek table")
1316         return leaf
1317
1318     def itervalues(self):
1319         # to avoid infinite loop, set maximum pops to max_entries
1320         cnt = self.max_entries
1321         while cnt:
1322             try:
1323                 yield(self.pop())
1324                 cnt -= 1
1325             except KeyError:
1326                 return
1327
1328     def values(self):
1329         return [value for value in self.itervalues()]