Commit | Line | Data |
---|---|---|
00f7eedc JG |
1 | # The MIT License (MIT) |
2 | # | |
3 | # Copyright (c) 2013-2017 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
4 | # | |
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | # of this software and associated documentation files (the "Software"), to deal | |
7 | # in the Software without restriction, including without limitation the rights | |
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | # copies of the Software, and to permit persons to whom the Software is | |
10 | # furnished to do so, subject to the following conditions: | |
11 | # | |
12 | # The above copyright notice and this permission notice shall be included in | |
13 | # all copies or substantial portions of the Software. | |
14 | # | |
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | # THE SOFTWARE. | |
22 | ||
23 | import bt2 | |
24 | import babeltrace.common as common | |
25 | import babeltrace.reader_field_definition as field_definition | |
26 | import datetime | |
27 | import collections | |
28 | ||
5c8b3186 | 29 | |
00f7eedc JG |
30 | def _create_event(event_notification, trace_handle=None, trace_collection=None): |
31 | event = Event.__new__(Event) | |
32 | event._event_notification = event_notification | |
33 | event._trace_handle = trace_handle | |
34 | event._trace_collection = trace_collection | |
35 | return event | |
36 | ||
5c8b3186 | 37 | |
00f7eedc JG |
38 | class Event(collections.Mapping): |
39 | """ | |
40 | An :class:`Event` object represents a trace event. :class:`Event` | |
41 | objects are returned by :attr:`TraceCollection.events` and are | |
42 | not meant to be instantiated by the user. | |
43 | ||
44 | :class:`Event` has a :class:`dict`-like interface for accessing | |
45 | an event's field value by field name: | |
46 | ||
47 | .. code-block:: python | |
48 | ||
49 | event['my_field'] | |
50 | ||
51 | If a field name exists in multiple scopes, the value of the first | |
52 | field found is returned. The scopes are searched in the following | |
53 | order: | |
54 | ||
55 | 1. Event fields (:attr:`babeltrace.common.CTFScope.EVENT_FIELDS`) | |
56 | 2. Event context (:attr:`babeltrace.common.CTFScope.EVENT_CONTEXT`) | |
57 | 3. Stream event context (:attr:`babeltrace.common.CTFScope.STREAM_EVENT_CONTEXT`) | |
58 | 4. Event header (:attr:`babeltrace.common.CTFScope.STREAM_EVENT_HEADER`) | |
59 | 5. Packet context (:attr:`babeltrace.common.CTFScope.STREAM_PACKET_CONTEXT`) | |
60 | 6. Packet header (:attr:`babeltrace.common.CTFScope.TRACE_PACKET_HEADER`) | |
61 | ||
62 | It is still possible to obtain a field's value from a specific | |
63 | scope using :meth:`field_with_scope`. | |
64 | ||
65 | Field values are returned as native Python types, that is: | |
66 | ||
67 | +-----------------------+----------------------------------+ | |
68 | | Field type | Python type | | |
69 | +=======================+==================================+ | |
70 | | Integer | :class:`int` | | |
71 | +-----------------------+----------------------------------+ | |
72 | | Floating point number | :class:`float` | | |
73 | +-----------------------+----------------------------------+ | |
74 | | Enumeration | :class:`str` (enumeration label) | | |
75 | +-----------------------+----------------------------------+ | |
76 | | String | :class:`str` | | |
77 | +-----------------------+----------------------------------+ | |
78 | | Array | :class:`list` of native Python | | |
79 | | | objects | | |
80 | +-----------------------+----------------------------------+ | |
81 | | Sequence | :class:`list` of native Python | | |
82 | | | objects | | |
83 | +-----------------------+----------------------------------+ | |
84 | | Structure | :class:`dict` mapping field | | |
85 | | | names to native Python objects | | |
86 | +-----------------------+----------------------------------+ | |
87 | ||
88 | For example, printing the third element of a sequence named ``seq`` | |
89 | in a structure named ``my_struct`` of the ``event``'s field named | |
90 | ``my_field`` is done this way: | |
91 | ||
92 | .. code-block:: python | |
93 | ||
94 | print(event['my_field']['my_struct']['seq'][2]) | |
95 | """ | |
96 | ||
97 | def __init__(self): | |
98 | raise NotImplementedError("Event cannot be instantiated") | |
99 | ||
100 | @property | |
101 | def name(self): | |
102 | """ | |
103 | Event name or ``None`` on error. | |
104 | """ | |
105 | ||
106 | try: | |
107 | return self._event_notification.event.name | |
108 | except bt2.Error: | |
109 | pass | |
110 | ||
111 | def _clock_value(self): | |
112 | cc_prio_map = self._event_notification.clock_class_priority_map | |
113 | clock_class = cc_prio_map.highest_priority_clock_class | |
114 | if not clock_class: | |
115 | return | |
116 | ||
117 | return self._event_notification.event.clock_value(clock_class) | |
118 | ||
119 | @property | |
120 | def cycles(self): | |
121 | """ | |
122 | Event timestamp in cycles or -1 on error. | |
123 | """ | |
124 | ||
125 | try: | |
126 | clock_value = self._clock_value() | |
127 | except bt2.Error: | |
128 | return -1 | |
129 | ||
130 | if clock_value is not None: | |
131 | return clock_value.cycles | |
132 | else: | |
133 | return -1 | |
134 | ||
135 | @property | |
136 | def timestamp(self): | |
137 | """ | |
138 | Event timestamp (nanoseconds since Epoch). | |
139 | """ | |
140 | ||
141 | try: | |
142 | clock_value = self._clock_value() | |
143 | except bt2.Error: | |
144 | raise RuntimeError("Failed to get event timestamp") | |
145 | ||
146 | if clock_value is not None: | |
147 | return clock_value.ns_from_epoch | |
148 | else: | |
149 | raise RuntimeError("Failed to get event timestamp") | |
150 | ||
151 | @property | |
152 | def datetime(self): | |
153 | """ | |
154 | Event timestamp as a standard :class:`datetime.datetime` | |
155 | object. | |
156 | ||
157 | Note that the :class:`datetime.datetime` class' precision | |
158 | is limited to microseconds, whereas :attr:`timestamp` provides | |
159 | the event's timestamp with a nanosecond resolution. | |
160 | """ | |
161 | ||
162 | return datetime.date.fromtimestamp(self.timestamp / 1E9) | |
163 | ||
164 | def field_with_scope(self, field_name, scope): | |
165 | """ | |
166 | Returns the value of a field named *field_name* within the | |
167 | scope *scope*, or ``None`` if the field cannot be found. | |
168 | ||
169 | *scope* must be one of :class:`babeltrace.common.CTFScope` | |
170 | constants. | |
171 | """ | |
172 | ||
173 | if scope not in _SCOPES: | |
174 | raise ValueError("Invalid scope provided") | |
175 | ||
176 | field = self._field_with_scope(field_name, scope) | |
177 | ||
178 | if field is not None: | |
179 | return field.value | |
180 | ||
181 | def field_list_with_scope(self, scope): | |
182 | """ | |
183 | Returns a list of field names in the scope *scope*. | |
184 | """ | |
185 | ||
186 | if scope not in _SCOPES: | |
187 | raise ValueError("Invalid scope provided") | |
188 | ||
189 | field_names = [] | |
190 | ||
191 | for field in self._field_list_with_scope(scope): | |
192 | field_names.append(field.name) | |
193 | ||
194 | return field_names | |
195 | ||
196 | @property | |
197 | def handle(self): | |
198 | """ | |
199 | :class:`TraceHandle` object containing this event, or ``None`` | |
200 | on error. | |
201 | """ | |
202 | ||
203 | try: | |
204 | return self._trace_handle | |
205 | except AttributeError: | |
206 | return None | |
207 | ||
208 | @property | |
209 | def trace_collection(self): | |
210 | """ | |
211 | :class:`TraceCollection` object containing this event, or | |
212 | ``None`` on error. | |
213 | """ | |
214 | ||
215 | try: | |
216 | return self._trace_collection | |
217 | except AttributeError: | |
218 | return | |
219 | ||
220 | def __getitem__(self, field_name): | |
221 | field = self._field(field_name) | |
222 | if field is None: | |
223 | raise KeyError(field_name) | |
224 | return field.value | |
225 | ||
226 | def __iter__(self): | |
227 | for key in self.keys(): | |
228 | yield key | |
229 | ||
230 | def __len__(self): | |
231 | count = 0 | |
232 | for scope in _SCOPES: | |
233 | scope_field = self._get_scope_field(scope) | |
234 | if scope_field is not None and isinstance(scope_field, | |
235 | bt2._StructureField): | |
236 | count += len(scope_field) | |
237 | return count | |
238 | ||
239 | def __contains__(self, field_name): | |
240 | return self._field(field_name) is not None | |
241 | ||
242 | def keys(self): | |
243 | """ | |
244 | Returns the list of field names. | |
245 | ||
246 | Note: field names are unique within the returned list, although | |
247 | a field name could exist in multiple scopes. Use | |
248 | :meth:`field_list_with_scope` to obtain the list of field names | |
249 | of a given scope. | |
250 | """ | |
251 | ||
252 | field_names = set() | |
253 | ||
254 | for scope in _SCOPES: | |
255 | for name in self.field_list_with_scope(scope): | |
256 | field_names.add(name) | |
257 | ||
258 | return list(field_names) | |
259 | ||
260 | def get(self, field_name, default=None): | |
261 | """ | |
262 | Returns the value of the field named *field_name*, or *default* | |
263 | when not found. | |
264 | ||
265 | See :class:`Event` note about how fields are retrieved by | |
266 | name when multiple fields share the same name in different | |
267 | scopes. | |
268 | """ | |
269 | ||
270 | field = self._field(field_name) | |
271 | ||
272 | if field is None: | |
273 | return default | |
274 | ||
275 | return field.value | |
276 | ||
277 | def items(self): | |
278 | """ | |
279 | Generates pairs of (field name, field value). | |
280 | ||
281 | This method iterates :meth:`keys` to find field names, which | |
282 | means some fields could be unavailable if other fields share | |
283 | their names in scopes with higher priorities. | |
284 | """ | |
285 | ||
286 | for field in self.keys(): | |
287 | yield (field, self[field]) | |
288 | ||
289 | def _get_scope_field(self, scope): | |
290 | try: | |
291 | event = self._event_notification.event | |
292 | if scope is common.CTFScope.EVENT_FIELDS: | |
293 | return event.payload_field | |
294 | ||
295 | if scope is common.CTFScope.EVENT_CONTEXT: | |
296 | return event.context_field | |
297 | ||
298 | if scope is common.CTFScope.STREAM_EVENT_CONTEXT: | |
299 | return event.stream_event_context_field | |
300 | ||
301 | if scope is common.CTFScope.STREAM_EVENT_HEADER: | |
302 | return event.header_field | |
303 | ||
304 | if scope is common.CTFScope.STREAM_PACKET_CONTEXT: | |
305 | return event.packet.context_field | |
306 | ||
307 | if scope is common.CTFScope.TRACE_PACKET_HEADER: | |
308 | return event.packet.header_field | |
309 | except bt2.Error: | |
310 | return | |
311 | ||
312 | raise ValueError("Invalid scope provided") | |
313 | ||
314 | def _field_with_scope(self, field_name, scope): | |
315 | scope_field = self._get_scope_field(scope) | |
316 | if scope_field is not None: | |
317 | try: | |
318 | bt2_field = scope_field[field_name] | |
319 | if bt2_field is not None: | |
320 | return field_definition._Definition(scope, bt2_field, | |
321 | field_name) | |
322 | except (KeyError, bt2.Error): | |
323 | return None | |
324 | ||
325 | def _field(self, field_name): | |
326 | for scope in _SCOPES: | |
327 | field = self._field_with_scope(field_name, scope) | |
328 | if field is not None: | |
329 | return field | |
330 | ||
331 | def _field_list_with_scope(self, scope): | |
332 | fields = [] | |
333 | scope_field = self._get_scope_field(scope) | |
334 | ||
335 | if scope_field is None or not isinstance(scope_field, | |
336 | bt2._StructureField): | |
337 | return fields | |
338 | ||
339 | for name, field in scope_field.items(): | |
340 | fields.append(field_definition._Definition(scope, field, name)) | |
341 | ||
342 | return fields | |
343 | ||
344 | ||
345 | # Priority of the scopes when searching for event fields | |
346 | _SCOPES = [ | |
347 | common.CTFScope.EVENT_FIELDS, | |
348 | common.CTFScope.EVENT_CONTEXT, | |
349 | common.CTFScope.STREAM_EVENT_CONTEXT, | |
350 | common.CTFScope.STREAM_EVENT_HEADER, | |
351 | common.CTFScope.STREAM_PACKET_CONTEXT, | |
352 | common.CTFScope.TRACE_PACKET_HEADER | |
353 | ] |