1 # Copyright (c) 2022 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """BPF performance bundle."""
16 from logging import getLogger
20 from .constants import Constants
25 Creates a BPF object. This is the main object for defining a BPF program,
26 and interacting with its output.
28 Syntax: BPF({text=BPF_program | src_file=filename}
29 [, usdt_contexts=[USDT_object, ...]]
30 [, cflags=[arg1, ...]] [, debug=int]
33 Exactly one of text or src_file must be supplied (not both).
35 def __init__(self, program, serializer, hook):
36 """Initialize Bundle BPF Perf event class.
38 :param program: BPF C code.
39 :param serializer: Metric serializer.
40 :param hook: Process ID.
42 :type serializer: Serializer
46 self.code = program[u"code"]
47 self.metrics = program[u"metrics"]
48 self.events = program[u"events"]
49 self.api_replies_list = list()
50 self.serializer = serializer
53 self.obj = BPF(text=self.code)
56 def attach(self, sample_period):
60 :param sample_period: A "sampling" event is one that generates
61 an overflow notification every N events, where N is given by
63 :type sample_period: int
66 for event in self.events:
67 self.obj.attach_perf_event(
68 ev_type=event[u"type"],
69 ev_config=event[u"name"],
70 fn_name=event[u"target"],
71 sample_period=sample_period
73 except AttributeError:
74 getLogger("console_stderr").error(f"Could not attach BPF event: "
76 sys.exit(Constants.err_linux_attach)
80 Detach events from BPF.
83 for event in self.events:
84 self.obj.detach_perf_event(
85 ev_type=event[u"type"],
86 ev_config=event[u"name"]
88 except AttributeError:
89 getLogger("console_stderr").error(u"Could not detach BPF events!")
90 sys.exit(Constants.err_linux_detach)
94 Fetch data by invoking API calls to BPF.
96 self.serializer.create(metrics=self.metrics)
98 max_len = {"cpu": 3, "pid": 3, "name": 4, "value": 5}
103 for _, metric_list in self.metrics.items():
104 for metric in metric_list:
105 if table_name != metric[u"name"]:
106 table_name = metric[u"name"]
107 text += f"{table_name}\n"
108 for (key, val) in self.obj.get_table(metric[u"name"]).items():
111 item[u"name"] = metric[u"name"]
112 item[u"value"] = val.value
113 for label in metric[u"labelnames"]:
114 labels[label] = getattr(key, label)
115 item[u"labels"] = labels
116 item[u'labels'][u'name'] = \
117 item[u'labels'][u'name'].decode(u'utf-8')
118 if item[u"labels"][u"name"] == u"python3":
120 if len(str(item[u'labels'][u'cpu'])) > max_len["cpu"]:
121 max_len["cpu"] = len(str(item[u'labels'][u'cpu']))
122 if len(str(item[u'labels'][u'pid'])) > max_len[u"pid"]:
123 max_len[u"pid"] = len(str(item[u'labels'][u'pid']))
124 if len(str(item[u'labels'][u'name'])) > max_len[u"name"]:
125 max_len[u"name"] = len(str(item[u'labels'][u'name']))
126 if len(str(item[u'value'])) > max_len[u"value"]:
127 max_len[u"value"] = len(str(item[u'value']))
129 self.api_replies_list.append(item)
130 item_list.append(item)
132 item_list = sorted(item_list, key=lambda x: x['labels']['cpu'])
133 item_list = sorted(item_list, key=lambda x: x['name'])
135 for itl in item_list:
136 if table_name != itl[u"name"]:
137 table_name = itl[u"name"]
138 text += f"\n==={table_name}===\n" \
139 f"cpu {u' ' * (max_len[u'cpu'] - 3)} " \
140 f"pid {u' ' * (max_len[u'pid'] - 3)} " \
141 f"name {u' ' * (max_len[u'name'] - 4)} " \
142 f"value {u' ' * (max_len[u'value'] - 5)}\n"
144 f"""{str(itl[u'labels'][u'cpu']) + u' ' *
145 (max_len[u"cpu"] - len(str(itl[u'labels'][u'cpu'])))} """
146 f"""{str(itl[u'labels'][u'pid']) + u' ' *
147 (max_len[u"pid"] - len(str(itl[u'labels'][u'pid'])))} """
148 f"""{str(itl[u'labels'][u'name']) + u' ' *
149 (max_len[u"name"] - len(str(itl[u'labels'][u'name'])))} """
150 f"""{str(itl[u'value']) + u' ' *
151 (max_len[u"value"] - len(str(itl[u'value'])))}\n""")
152 getLogger(u"console_stdout").info(text)
154 def process_data(self):
156 Post process API replies.
158 for item in self.api_replies_list:
159 self.serializer.serialize(
160 metric=item[u"name"], labels=item[u"labels"], item=item