Commit | Line | Data |
---|---|---|
be5a4e67 PP |
1 | # reader.py |
2 | # | |
3 | # Babeltrace reader interface Python module | |
4 | # | |
5 | # Copyright 2012-2015 EfficiOS Inc. | |
6 | # | |
7 | # Author: Danny Serres <danny.serres@efficios.com> | |
8 | # Author: Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
9 | # | |
10 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
11 | # of this software and associated documentation files (the "Software"), to deal | |
12 | # in the Software without restriction, including without limitation the rights | |
13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
14 | # copies of the Software, and to permit persons to whom the Software is | |
15 | # furnished to do so, subject to the following conditions: | |
16 | # | |
17 | # The above copyright notice and this permission notice shall be included in | |
18 | # all copies or substantial portions of the Software. | |
19 | # | |
20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
26 | # SOFTWARE. | |
27 | ||
28 | import babeltrace.nativebt as nbt | |
29 | import babeltrace.common as common | |
30 | import collections | |
31 | import os | |
32 | from datetime import datetime | |
33 | ||
34 | ||
35 | class TraceCollection: | |
36 | """ | |
37 | A :class:`TraceCollection` is a collection of opened traces. | |
38 | ||
39 | Once a trace collection is created, you can add traces to the | |
40 | collection by using the :meth:`add_trace` or | |
41 | :meth:`add_traces_recursive`, and then iterate on the merged | |
42 | events using :attr:`events`. | |
43 | ||
44 | You may use :meth:`remove_trace` to close and remove a specific | |
45 | trace from a trace collection. | |
46 | """ | |
47 | ||
9b93351a | 48 | def __init__(self, intersect_mode=False): |
be5a4e67 PP |
49 | """ |
50 | Creates an empty trace collection. | |
51 | """ | |
52 | ||
53 | self._tc = nbt._bt_context_create() | |
192803e1 | 54 | self._intersect_mode = intersect_mode |
be5a4e67 PP |
55 | |
56 | def __del__(self): | |
57 | nbt._bt_context_put(self._tc) | |
58 | ||
59 | def add_trace(self, path, format_str): | |
60 | """ | |
61 | Adds a trace to the trace collection. | |
62 | ||
63 | *path* is the exact path of the trace on the filesystem. | |
64 | ||
65 | *format_str* is a string indicating the type of trace to | |
66 | add. ``ctf`` is currently the only supported trace format. | |
67 | ||
68 | Returns the corresponding :class:`TraceHandle` instance for | |
69 | this opened trace on success, or ``None`` on error. | |
70 | ||
71 | This function **does not** recurse directories to find a | |
72 | trace. See :meth:`add_traces_recursive` for a recursive | |
73 | version of this function. | |
74 | """ | |
75 | ||
76 | ret = nbt._bt_context_add_trace(self._tc, path, format_str, | |
77 | None, None, None) | |
78 | ||
79 | if ret < 0: | |
80 | return None | |
81 | ||
82 | th = TraceHandle.__new__(TraceHandle) | |
83 | th._id = ret | |
84 | th._trace_collection = self | |
85 | ||
86 | return th | |
87 | ||
88 | def add_traces_recursive(self, path, format_str): | |
89 | """ | |
90 | Adds traces to this trace collection by recursively searching | |
91 | in the *path* directory. | |
92 | ||
93 | *format_str* is a string indicating the type of trace to add. | |
94 | ``ctf`` is currently the only supported trace format. | |
95 | ||
96 | Returns a :class:`dict` object mapping full paths to trace | |
97 | handles for each trace found, or ``None`` on error. | |
98 | ||
99 | See also :meth:`add_trace`. | |
100 | """ | |
101 | ||
102 | trace_handles = {} | |
103 | noTrace = True | |
104 | error = False | |
105 | ||
106 | for fullpath, dirs, files in os.walk(path): | |
107 | if "metadata" in files: | |
108 | trace_handle = self.add_trace(fullpath, format_str) | |
109 | ||
110 | if trace_handle is None: | |
111 | error = True | |
112 | continue | |
113 | ||
114 | trace_handles[fullpath] = trace_handle | |
115 | noTrace = False | |
116 | ||
117 | if noTrace and error: | |
118 | return None | |
119 | ||
120 | return trace_handles | |
121 | ||
122 | def remove_trace(self, trace_handle): | |
123 | """ | |
124 | Removes a trace from the trace collection using its trace | |
125 | handle *trace_handle*. | |
126 | ||
127 | :class:`TraceHandle` objects are returned by :meth:`add_trace` | |
128 | and :meth:`add_traces_recursive`. | |
129 | """ | |
130 | ||
131 | try: | |
132 | nbt._bt_context_remove_trace(self._tc, trace_handle._id) | |
133 | except AttributeError: | |
134 | raise TypeError("in remove_trace, argument 2 must be a TraceHandle instance") | |
135 | ||
192803e1 AB |
136 | @property |
137 | def intersect_mode(self): | |
138 | return self._intersect_mode | |
139 | ||
ae512132 AB |
140 | @property |
141 | def has_intersection(self): | |
40af9f9f | 142 | return nbt._bt_python_trace_collection_has_intersection(self._tc) |
ae512132 | 143 | |
be5a4e67 PP |
144 | @property |
145 | def events(self): | |
146 | """ | |
147 | Generates the ordered :class:`Event` objects of all the opened | |
148 | traces contained in this trace collection. | |
149 | ||
150 | Due to limitations of the native Babeltrace API, only one event | |
151 | may be "alive" at a given time, i.e. a user **should never** | |
152 | store a copy of the events returned by this function for | |
153 | ulterior use. Users shall make sure to copy the information | |
154 | they need *from* an event before accessing the next one. | |
155 | """ | |
156 | ||
1fa8e237 JG |
157 | begin_pos_ptr = nbt._bt_python_create_iter_pos() |
158 | end_pos_ptr = nbt._bt_python_create_iter_pos() | |
9b93351a AB |
159 | |
160 | if not self.intersect_mode: | |
161 | begin_pos_ptr.type = nbt.SEEK_BEGIN | |
162 | end_pos_ptr.type = nbt.SEEK_LAST | |
be5a4e67 PP |
163 | |
164 | for event in self._events(begin_pos_ptr, end_pos_ptr): | |
165 | yield event | |
166 | ||
83e0efce JG |
167 | nbt._bt_iter_free_pos(begin_pos_ptr) |
168 | nbt._bt_iter_free_pos(end_pos_ptr) | |
1fa8e237 | 169 | |
be5a4e67 PP |
170 | def events_timestamps(self, timestamp_begin, timestamp_end): |
171 | """ | |
172 | Generates the ordered :class:`Event` objects of all the opened | |
173 | traces contained in this trace collection from *timestamp_begin* | |
174 | to *timestamp_end*. | |
175 | ||
176 | *timestamp_begin* and *timestamp_end* are given in nanoseconds | |
177 | since Epoch. | |
178 | ||
179 | See :attr:`events` for notes and limitations. | |
180 | """ | |
181 | ||
1fa8e237 JG |
182 | begin_pos_ptr = nbt._bt_python_create_iter_pos() |
183 | end_pos_ptr = nbt._bt_python_create_iter_pos() | |
be5a4e67 PP |
184 | begin_pos_ptr.type = end_pos_ptr.type = nbt.SEEK_TIME |
185 | begin_pos_ptr.u.seek_time = timestamp_begin | |
186 | end_pos_ptr.u.seek_time = timestamp_end | |
187 | ||
188 | for event in self._events(begin_pos_ptr, end_pos_ptr): | |
189 | yield event | |
190 | ||
83e0efce JG |
191 | nbt._bt_iter_free_pos(begin_pos_ptr) |
192 | nbt._bt_iter_free_pos(end_pos_ptr) | |
1fa8e237 | 193 | |
be5a4e67 PP |
194 | @property |
195 | def timestamp_begin(self): | |
196 | """ | |
197 | Begin timestamp of this trace collection (nanoseconds since | |
198 | Epoch). | |
199 | """ | |
200 | ||
201 | pos_ptr = nbt._bt_iter_pos() | |
202 | pos_ptr.type = nbt.SEEK_BEGIN | |
203 | ||
204 | return self._timestamp_at_pos(pos_ptr) | |
205 | ||
206 | @property | |
207 | def timestamp_end(self): | |
208 | """ | |
209 | End timestamp of this trace collection (nanoseconds since | |
210 | Epoch). | |
211 | """ | |
212 | ||
213 | pos_ptr = nbt._bt_iter_pos() | |
214 | pos_ptr.type = nbt.SEEK_LAST | |
215 | ||
216 | return self._timestamp_at_pos(pos_ptr) | |
217 | ||
218 | def _timestamp_at_pos(self, pos_ptr): | |
219 | ctf_it_ptr = nbt._bt_ctf_iter_create(self._tc, pos_ptr, pos_ptr) | |
220 | ||
221 | if ctf_it_ptr is None: | |
222 | raise NotImplementedError("Creation of multiple iterators is unsupported.") | |
223 | ||
224 | ev_ptr = nbt._bt_ctf_iter_read_event(ctf_it_ptr) | |
225 | nbt._bt_ctf_iter_destroy(ctf_it_ptr) | |
226 | ||
6c0c4a6a AB |
227 | ev = Event.__new__(Event) |
228 | ev._e = ev_ptr | |
229 | ||
230 | return ev.timestamp | |
231 | ||
be5a4e67 | 232 | def _events(self, begin_pos_ptr, end_pos_ptr): |
9b93351a | 233 | if self.intersect_mode: |
ae512132 | 234 | if not self.has_intersection: |
9b93351a AB |
235 | # There are no events to provide. |
236 | return | |
237 | ||
238 | ctf_it_ptr = nbt._bt_python_ctf_iter_create_intersect( | |
239 | self._tc, begin_pos_ptr, end_pos_ptr | |
240 | ) | |
241 | else: | |
242 | ctf_it_ptr = nbt._bt_ctf_iter_create( | |
243 | self._tc, begin_pos_ptr, end_pos_ptr | |
244 | ) | |
be5a4e67 PP |
245 | |
246 | if ctf_it_ptr is None: | |
247 | raise NotImplementedError("Creation of multiple iterators is unsupported.") | |
248 | ||
249 | while True: | |
250 | ev_ptr = nbt._bt_ctf_iter_read_event(ctf_it_ptr) | |
251 | ||
252 | if ev_ptr is None: | |
253 | break | |
254 | ||
255 | ev = Event.__new__(Event) | |
256 | ev._e = ev_ptr | |
257 | ||
258 | try: | |
259 | yield ev | |
260 | except GeneratorExit: | |
261 | break | |
262 | ||
263 | ret = nbt._bt_iter_next(nbt._bt_ctf_get_iter(ctf_it_ptr)) | |
264 | ||
265 | if ret != 0: | |
266 | break | |
267 | ||
268 | nbt._bt_ctf_iter_destroy(ctf_it_ptr) | |
269 | ||
270 | ||
271 | # Based on enum bt_clock_type in clock-type.h | |
272 | class _ClockType: | |
273 | CLOCK_CYCLES = 0 | |
274 | CLOCK_REAL = 1 | |
275 | ||
276 | ||
277 | class TraceHandle: | |
278 | """ | |
279 | A :class:`TraceHandle` is a handle allowing the user to manipulate | |
280 | a specific trace directly. It is a unique identifier representing a | |
281 | trace, and is not meant to be instantiated by the user. | |
282 | """ | |
283 | ||
284 | def __init__(self): | |
285 | raise NotImplementedError("TraceHandle cannot be instantiated") | |
286 | ||
287 | def __repr__(self): | |
288 | return "Babeltrace TraceHandle: trace_id('{0}')".format(self._id) | |
289 | ||
290 | @property | |
291 | def id(self): | |
292 | """ | |
293 | Numeric ID of this trace handle. | |
294 | """ | |
295 | ||
296 | return self._id | |
297 | ||
298 | @property | |
299 | def path(self): | |
300 | """ | |
301 | Path of the underlying trace. | |
302 | """ | |
303 | ||
304 | return nbt._bt_trace_handle_get_path(self._trace_collection._tc, | |
305 | self._id) | |
306 | ||
307 | @property | |
308 | def timestamp_begin(self): | |
309 | """ | |
310 | Buffers creation timestamp (nanoseconds since Epoch) of the | |
311 | underlying trace. | |
312 | """ | |
313 | ||
7a5f95fa JG |
314 | ret, value = nbt._bt_trace_handle_get_timestamp_begin( |
315 | self._trace_collection._tc, self._id, _ClockType.CLOCK_REAL) | |
316 | if ret != 0: | |
317 | raise ValueError("Invalid TraceHandle") | |
318 | return value | |
be5a4e67 PP |
319 | |
320 | @property | |
321 | def timestamp_end(self): | |
322 | """ | |
323 | Buffers destruction timestamp (nanoseconds since Epoch) of the | |
324 | underlying trace. | |
325 | """ | |
326 | ||
7a5f95fa JG |
327 | ret, value = nbt._bt_trace_handle_get_timestamp_end( |
328 | self._trace_collection._tc, self._id, _ClockType.CLOCK_REAL) | |
329 | if ret != 0: | |
330 | raise ValueError("Invalid TraceHandle") | |
331 | return value | |
be5a4e67 PP |
332 | |
333 | @property | |
334 | def events(self): | |
335 | """ | |
336 | Generates all the :class:`EventDeclaration` objects of the | |
337 | underlying trace. | |
338 | """ | |
339 | ||
340 | ret = nbt._bt_python_event_decl_listcaller(self.id, | |
341 | self._trace_collection._tc) | |
342 | ||
343 | if not isinstance(ret, list): | |
344 | return | |
345 | ||
346 | ptr_list, count = ret | |
347 | ||
348 | for i in range(count): | |
349 | tmp = EventDeclaration.__new__(EventDeclaration) | |
350 | tmp._ed = nbt._bt_python_decl_one_from_list(ptr_list, i) | |
351 | yield tmp | |
352 | ||
353 | ||
354 | ||
355 | ||
356 | # Priority of the scopes when searching for event fields | |
357 | _scopes = [ | |
358 | common.CTFScope.EVENT_FIELDS, | |
359 | common.CTFScope.EVENT_CONTEXT, | |
360 | common.CTFScope.STREAM_EVENT_CONTEXT, | |
361 | common.CTFScope.STREAM_EVENT_HEADER, | |
362 | common.CTFScope.STREAM_PACKET_CONTEXT, | |
363 | common.CTFScope.TRACE_PACKET_HEADER | |
364 | ] | |
365 | ||
366 | ||
367 | class Event(collections.Mapping): | |
368 | """ | |
369 | An :class:`Event` object represents a trace event. :class:`Event` | |
370 | objects are returned by :attr:`TraceCollection.events` and are | |
371 | not meant to be instantiated by the user. | |
372 | ||
373 | :class:`Event` has a :class:`dict`-like interface for accessing | |
374 | an event's field value by field name: | |
375 | ||
376 | .. code-block:: python | |
377 | ||
378 | event['my_field'] | |
379 | ||
380 | If a field name exists in multiple scopes, the value of the first | |
381 | field found is returned. The scopes are searched in the following | |
382 | order: | |
383 | ||
384 | 1. Event fields (:attr:`babeltrace.common.CTFScope.EVENT_FIELDS`) | |
385 | 2. Event context (:attr:`babeltrace.common.CTFScope.EVENT_CONTEXT`) | |
386 | 3. Stream event context (:attr:`babeltrace.common.CTFScope.STREAM_EVENT_CONTEXT`) | |
387 | 4. Event header (:attr:`babeltrace.common.CTFScope.STREAM_EVENT_HEADER`) | |
388 | 5. Packet context (:attr:`babeltrace.common.CTFScope.STREAM_PACKET_CONTEXT`) | |
389 | 6. Packet header (:attr:`babeltrace.common.CTFScope.TRACE_PACKET_HEADER`) | |
390 | ||
391 | It is still possible to obtain a field's value from a specific | |
392 | scope using :meth:`field_with_scope`. | |
393 | ||
394 | Field values are returned as native Python types, that is: | |
395 | ||
396 | +-----------------------+----------------------------------+ | |
397 | | Field type | Python type | | |
398 | +=======================+==================================+ | |
399 | | Integer | :class:`int` | | |
400 | +-----------------------+----------------------------------+ | |
401 | | Floating point number | :class:`float` | | |
402 | +-----------------------+----------------------------------+ | |
403 | | Enumeration | :class:`str` (enumeration label) | | |
404 | +-----------------------+----------------------------------+ | |
405 | | String | :class:`str` | | |
406 | +-----------------------+----------------------------------+ | |
407 | | Array | :class:`list` of native Python | | |
408 | | | objects | | |
409 | +-----------------------+----------------------------------+ | |
410 | | Sequence | :class:`list` of native Python | | |
411 | | | objects | | |
412 | +-----------------------+----------------------------------+ | |
413 | | Structure | :class:`dict` mapping field | | |
414 | | | names to native Python objects | | |
415 | +-----------------------+----------------------------------+ | |
416 | ||
417 | For example, printing the third element of a sequence named ``seq`` | |
418 | in a structure named ``my_struct`` of the ``event``'s field named | |
419 | ``my_field`` is done this way: | |
420 | ||
421 | .. code-block:: python | |
422 | ||
423 | print(event['my_field']['my_struct']['seq'][2]) | |
424 | """ | |
425 | ||
426 | def __init__(self): | |
427 | raise NotImplementedError("Event cannot be instantiated") | |
428 | ||
429 | @property | |
430 | def name(self): | |
431 | """ | |
432 | Event name or ``None`` on error. | |
433 | """ | |
434 | ||
435 | return nbt._bt_ctf_event_name(self._e) | |
436 | ||
437 | @property | |
438 | def cycles(self): | |
439 | """ | |
440 | Event timestamp in cycles or -1 on error. | |
441 | """ | |
442 | ||
443 | return nbt._bt_ctf_get_cycles(self._e) | |
444 | ||
445 | @property | |
446 | def timestamp(self): | |
447 | """ | |
7a5f95fa | 448 | Event timestamp (nanoseconds since Epoch). |
be5a4e67 PP |
449 | """ |
450 | ||
7a5f95fa JG |
451 | ret, value = nbt._bt_ctf_get_timestamp(self._e) |
452 | if ret < 0: | |
453 | raise RuntimeError("Failed to get event timestamp") | |
454 | return value | |
be5a4e67 PP |
455 | |
456 | @property | |
457 | def datetime(self): | |
458 | """ | |
459 | Event timestamp as a standard :class:`datetime.datetime` | |
460 | object. | |
461 | ||
462 | Note that the :class:`datetime.datetime` class' precision | |
463 | is limited to microseconds, whereas :attr:`timestamp` provides | |
464 | the event's timestamp with a nanosecond resolution. | |
465 | """ | |
466 | ||
467 | return datetime.fromtimestamp(self.timestamp / 1E9) | |
468 | ||
469 | def field_with_scope(self, field_name, scope): | |
470 | """ | |
471 | Returns the value of a field named *field_name* within the | |
472 | scope *scope*, or ``None`` if the field cannot be found. | |
473 | ||
474 | *scope* must be one of :class:`babeltrace.common.CTFScope` | |
475 | constants. | |
476 | """ | |
477 | ||
478 | if scope not in _scopes: | |
479 | raise ValueError("Invalid scope provided") | |
480 | ||
481 | field = self._field_with_scope(field_name, scope) | |
482 | ||
483 | if field is not None: | |
484 | return field.value | |
485 | ||
486 | def field_list_with_scope(self, scope): | |
487 | """ | |
488 | Returns a list of field names in the scope *scope*. | |
489 | """ | |
490 | ||
491 | if scope not in _scopes: | |
492 | raise ValueError("Invalid scope provided") | |
493 | ||
494 | field_names = [] | |
495 | ||
496 | for field in self._field_list_with_scope(scope): | |
497 | field_names.append(field.name) | |
498 | ||
499 | return field_names | |
500 | ||
501 | @property | |
502 | def handle(self): | |
503 | """ | |
504 | :class:`TraceHandle` object containing this event, or ``None`` | |
505 | on error. | |
506 | """ | |
507 | ||
508 | ret = nbt._bt_ctf_event_get_handle_id(self._e) | |
509 | ||
510 | if ret < 0: | |
511 | return None | |
512 | ||
513 | th = TraceHandle.__new__(TraceHandle) | |
514 | th._id = ret | |
515 | th._trace_collection = self.get_trace_collection() | |
516 | ||
517 | return th | |
518 | ||
519 | @property | |
520 | def trace_collection(self): | |
521 | """ | |
522 | :class:`TraceCollection` object containing this event, or | |
523 | ``None`` on error. | |
524 | """ | |
525 | ||
526 | trace_collection = TraceCollection() | |
527 | trace_collection._tc = nbt._bt_ctf_event_get_context(self._e) | |
528 | ||
529 | if trace_collection._tc is not None: | |
530 | return trace_collection | |
531 | ||
532 | def __getitem__(self, field_name): | |
533 | field = self._field(field_name) | |
534 | ||
535 | if field is not None: | |
536 | return field.value | |
537 | ||
538 | raise KeyError(field_name) | |
539 | ||
540 | def __iter__(self): | |
541 | for key in self.keys(): | |
542 | yield key | |
543 | ||
544 | def __len__(self): | |
545 | count = 0 | |
546 | ||
547 | for scope in _scopes: | |
548 | scope_ptr = nbt._bt_ctf_get_top_level_scope(self._e, scope) | |
549 | ret = nbt._bt_python_field_listcaller(self._e, scope_ptr) | |
550 | ||
551 | if isinstance(ret, list): | |
552 | count += ret[1] | |
553 | ||
554 | return count | |
555 | ||
556 | def __contains__(self, field_name): | |
557 | return self._field(field_name) is not None | |
558 | ||
559 | def keys(self): | |
560 | """ | |
561 | Returns the list of field names. | |
562 | ||
563 | Note: field names are unique within the returned list, although | |
564 | a field name could exist in multiple scopes. Use | |
565 | :meth:`field_list_with_scope` to obtain the list of field names | |
566 | of a given scope. | |
567 | """ | |
568 | ||
569 | field_names = set() | |
570 | ||
571 | for scope in _scopes: | |
572 | for name in self.field_list_with_scope(scope): | |
573 | field_names.add(name) | |
574 | ||
575 | return list(field_names) | |
576 | ||
577 | def get(self, field_name, default=None): | |
578 | """ | |
579 | Returns the value of the field named *field_name*, or *default* | |
580 | when not found. | |
581 | ||
582 | See :class:`Event` note about how fields are retrieved by | |
583 | name when multiple fields share the same name in different | |
584 | scopes. | |
585 | """ | |
586 | ||
587 | field = self._field(field_name) | |
588 | ||
589 | if field is None: | |
590 | return default | |
591 | ||
592 | return field.value | |
593 | ||
594 | def items(self): | |
595 | """ | |
596 | Generates pairs of (field name, field value). | |
597 | ||
598 | This method iterates :meth:`keys` to find field names, which | |
599 | means some fields could be unavailable if other fields share | |
600 | their names in scopes with higher priorities. | |
601 | """ | |
602 | ||
603 | for field in self.keys(): | |
604 | yield (field, self[field]) | |
605 | ||
606 | def _field_with_scope(self, field_name, scope): | |
607 | scope_ptr = nbt._bt_ctf_get_top_level_scope(self._e, scope) | |
608 | ||
609 | if scope_ptr is None: | |
610 | return None | |
611 | ||
612 | definition_ptr = nbt._bt_ctf_get_field(self._e, scope_ptr, field_name) | |
613 | ||
614 | if definition_ptr is None: | |
615 | return None | |
616 | ||
617 | field = _Definition(definition_ptr, scope) | |
618 | ||
619 | return field | |
620 | ||
621 | def _field(self, field_name): | |
622 | field = None | |
623 | ||
624 | for scope in _scopes: | |
625 | field = self._field_with_scope(field_name, scope) | |
626 | ||
627 | if field is not None: | |
628 | break | |
629 | ||
630 | return field | |
631 | ||
632 | def _field_list_with_scope(self, scope): | |
633 | fields = [] | |
634 | scope_ptr = nbt._bt_ctf_get_top_level_scope(self._e, scope) | |
635 | ||
636 | # Returns a list [list_ptr, count]. If list_ptr is NULL, SWIG will only | |
637 | # provide the "count" return value | |
638 | count = 0 | |
639 | list_ptr = None | |
640 | ret = nbt._bt_python_field_listcaller(self._e, scope_ptr) | |
641 | ||
642 | if isinstance(ret, list): | |
643 | list_ptr, count = ret | |
644 | ||
645 | for i in range(count): | |
646 | definition_ptr = nbt._bt_python_field_one_from_list(list_ptr, i) | |
647 | ||
648 | if definition_ptr is not None: | |
649 | definition = _Definition(definition_ptr, scope) | |
650 | fields.append(definition) | |
651 | ||
652 | return fields | |
653 | ||
654 | ||
655 | class FieldError(Exception): | |
656 | """ | |
657 | Field error, raised when the value of a field cannot be accessed. | |
658 | """ | |
659 | ||
660 | def __init__(self, value): | |
661 | self.value = value | |
662 | ||
663 | def __str__(self): | |
664 | return repr(self.value) | |
665 | ||
666 | ||
667 | class EventDeclaration: | |
668 | """ | |
669 | An event declaration contains the properties of a class of events, | |
670 | that is, the common properties and fields layout of all the actual | |
671 | recorded events associated with this declaration. | |
672 | ||
673 | This class is not meant to be instantiated by the user. It is | |
674 | returned by :attr:`TraceHandle.events`. | |
675 | """ | |
676 | ||
677 | MAX_UINT64 = 0xFFFFFFFFFFFFFFFF | |
678 | ||
679 | def __init__(self): | |
680 | raise NotImplementedError("EventDeclaration cannot be instantiated") | |
681 | ||
682 | @property | |
683 | def name(self): | |
684 | """ | |
685 | Event name, or ``None`` on error. | |
686 | """ | |
687 | ||
688 | return nbt._bt_ctf_get_decl_event_name(self._ed) | |
689 | ||
690 | @property | |
691 | def id(self): | |
692 | """ | |
693 | Event numeric ID, or -1 on error. | |
694 | """ | |
695 | ||
696 | id = nbt._bt_ctf_get_decl_event_id(self._ed) | |
697 | ||
698 | if id == self.MAX_UINT64: | |
699 | id = -1 | |
700 | ||
701 | return id | |
702 | ||
703 | @property | |
704 | def fields(self): | |
705 | """ | |
706 | Generates all the field declarations of this event, going | |
707 | through each scope in the following order: | |
708 | ||
709 | 1. Event fields (:attr:`babeltrace.common.CTFScope.EVENT_FIELDS`) | |
710 | 2. Event context (:attr:`babeltrace.common.CTFScope.EVENT_CONTEXT`) | |
711 | 3. Stream event context (:attr:`babeltrace.common.CTFScope.STREAM_EVENT_CONTEXT`) | |
712 | 4. Event header (:attr:`babeltrace.common.CTFScope.STREAM_EVENT_HEADER`) | |
713 | 5. Packet context (:attr:`babeltrace.common.CTFScope.STREAM_PACKET_CONTEXT`) | |
714 | 6. Packet header (:attr:`babeltrace.common.CTFScope.TRACE_PACKET_HEADER`) | |
715 | ||
716 | All the generated field declarations inherit | |
717 | :class:`FieldDeclaration`, and are among: | |
718 | ||
719 | * :class:`IntegerFieldDeclaration` | |
720 | * :class:`FloatFieldDeclaration` | |
721 | * :class:`EnumerationFieldDeclaration` | |
722 | * :class:`StringFieldDeclaration` | |
723 | * :class:`ArrayFieldDeclaration` | |
724 | * :class:`SequenceFieldDeclaration` | |
725 | * :class:`StructureFieldDeclaration` | |
726 | * :class:`VariantFieldDeclaration` | |
727 | """ | |
728 | ||
729 | for scope in _scopes: | |
730 | for declaration in self.fields_scope(scope): | |
731 | yield declaration | |
732 | ||
733 | def fields_scope(self, scope): | |
734 | """ | |
735 | Generates all the field declarations of the event's scope | |
736 | *scope*. | |
737 | ||
738 | *scope* must be one of :class:`babeltrace.common.CTFScope` constants. | |
739 | ||
740 | All the generated field declarations inherit | |
741 | :class:`FieldDeclaration`, and are among: | |
742 | ||
743 | * :class:`IntegerFieldDeclaration` | |
744 | * :class:`FloatFieldDeclaration` | |
745 | * :class:`EnumerationFieldDeclaration` | |
746 | * :class:`StringFieldDeclaration` | |
747 | * :class:`ArrayFieldDeclaration` | |
748 | * :class:`SequenceFieldDeclaration` | |
749 | * :class:`StructureFieldDeclaration` | |
750 | * :class:`VariantFieldDeclaration` | |
751 | """ | |
752 | ret = nbt._by_python_field_decl_listcaller(self._ed, scope) | |
753 | ||
754 | if not isinstance(ret, list): | |
755 | return | |
756 | ||
757 | list_ptr, count = ret | |
758 | ||
759 | for i in range(count): | |
760 | field_decl_ptr = nbt._bt_python_field_decl_one_from_list(list_ptr, i) | |
761 | ||
762 | if field_decl_ptr is not None: | |
763 | decl_ptr = nbt._bt_ctf_get_decl_from_field_decl(field_decl_ptr) | |
764 | name = nbt._bt_ctf_get_decl_field_name(field_decl_ptr) | |
765 | field_declaration = _create_field_declaration(decl_ptr, name, | |
766 | scope) | |
767 | yield field_declaration | |
768 | ||
769 | ||
770 | class FieldDeclaration: | |
771 | """ | |
772 | Base class for concrete field declarations. | |
773 | ||
774 | This class is not meant to be instantiated by the user. | |
775 | """ | |
776 | ||
777 | def __init__(self): | |
778 | raise NotImplementedError("FieldDeclaration cannot be instantiated") | |
779 | ||
780 | def __repr__(self): | |
781 | return "({0}) {1} {2}".format(common.CTFScope.scope_name(self.scope), | |
782 | common.CTFTypeId.type_name(self.type), | |
783 | self.name) | |
784 | ||
785 | @property | |
786 | def name(self): | |
787 | """ | |
788 | Field name, or ``None`` on error. | |
789 | """ | |
790 | ||
791 | return self._name | |
792 | ||
793 | @property | |
794 | def type(self): | |
795 | """ | |
796 | Field type (one of :class:`babeltrace.common.CTFTypeId` | |
797 | constants). | |
798 | """ | |
799 | ||
800 | return nbt._bt_ctf_field_type(self._fd) | |
801 | ||
802 | @property | |
803 | def scope(self): | |
804 | """ | |
805 | Field scope (one of:class:`babeltrace.common.CTFScope` | |
806 | constants). | |
807 | """ | |
808 | ||
809 | return self._s | |
810 | ||
811 | ||
812 | class IntegerFieldDeclaration(FieldDeclaration): | |
813 | """ | |
814 | Integer field declaration. | |
815 | """ | |
816 | ||
817 | def __init__(self): | |
818 | raise NotImplementedError("IntegerFieldDeclaration cannot be instantiated") | |
819 | ||
820 | @property | |
821 | def signedness(self): | |
822 | """ | |
823 | 0 if this integer is unsigned, 1 if signed, or -1 on error. | |
824 | """ | |
825 | ||
826 | return nbt._bt_ctf_get_int_signedness(self._fd) | |
827 | ||
828 | @property | |
829 | def base(self): | |
830 | """ | |
831 | Integer base (:class:`int`), or a negative value on error. | |
832 | """ | |
833 | ||
834 | return nbt._bt_ctf_get_int_base(self._fd) | |
835 | ||
836 | @property | |
837 | def byte_order(self): | |
838 | """ | |
839 | Integer byte order (one of | |
840 | :class:`babeltrace.common.ByteOrder` constants). | |
841 | """ | |
842 | ||
843 | ret = nbt._bt_ctf_get_int_byte_order(self._fd) | |
844 | ||
845 | if ret == 1234: | |
846 | return common.ByteOrder.BYTE_ORDER_LITTLE_ENDIAN | |
847 | elif ret == 4321: | |
848 | return common.ByteOrder.BYTE_ORDER_BIG_ENDIAN | |
849 | else: | |
850 | return common.ByteOrder.BYTE_ORDER_UNKNOWN | |
851 | ||
852 | @property | |
3f217dff | 853 | def size(self): |
be5a4e67 PP |
854 | """ |
855 | Integer size in bits, or a negative value on error. | |
856 | """ | |
be5a4e67 PP |
857 | return nbt._bt_ctf_get_int_len(self._fd) |
858 | ||
3f217dff PP |
859 | @property |
860 | def length(self): | |
861 | return self.size | |
862 | ||
be5a4e67 PP |
863 | @property |
864 | def encoding(self): | |
865 | """ | |
866 | Integer encoding (one of | |
867 | :class:`babeltrace.common.CTFStringEncoding` constants). | |
868 | """ | |
869 | ||
870 | return nbt._bt_ctf_get_encoding(self._fd) | |
871 | ||
872 | ||
873 | class EnumerationFieldDeclaration(FieldDeclaration): | |
874 | """ | |
875 | Enumeration field declaration. | |
876 | ||
877 | .. note:: | |
878 | ||
879 | As of this version, this class is missing some properties. | |
880 | """ | |
881 | ||
882 | def __init__(self): | |
883 | raise NotImplementedError("EnumerationFieldDeclaration cannot be instantiated") | |
884 | ||
885 | ||
886 | class ArrayFieldDeclaration(FieldDeclaration): | |
887 | """ | |
888 | Static array field declaration. | |
889 | """ | |
890 | ||
891 | def __init__(self): | |
892 | raise NotImplementedError("ArrayFieldDeclaration cannot be instantiated") | |
893 | ||
894 | @property | |
895 | def length(self): | |
896 | """ | |
897 | Fixed length of this static array (number of contained | |
898 | elements), or a negative value on error. | |
899 | """ | |
900 | ||
901 | return nbt._bt_ctf_get_array_len(self._fd) | |
902 | ||
903 | @property | |
904 | def element_declaration(self): | |
905 | """ | |
906 | Field declaration of the underlying element. | |
907 | """ | |
908 | ||
909 | field_decl_ptr = nbt._bt_python_get_array_element_declaration(self._fd) | |
910 | ||
911 | return _create_field_declaration(field_decl_ptr, "", self.scope) | |
912 | ||
913 | ||
914 | class SequenceFieldDeclaration(FieldDeclaration): | |
915 | """ | |
916 | Sequence (dynamic array) field declaration. | |
917 | ||
918 | .. note:: | |
919 | ||
920 | As of this version, this class is missing some properties. | |
921 | """ | |
922 | ||
923 | def __init__(self): | |
924 | raise NotImplementedError("SequenceFieldDeclaration cannot be instantiated") | |
925 | ||
926 | @property | |
927 | def element_declaration(self): | |
928 | """ | |
929 | Field declaration of the underlying element. | |
930 | """ | |
931 | ||
932 | field_decl_ptr = nbt._bt_python_get_sequence_element_declaration(self._fd) | |
933 | ||
934 | return _create_field_declaration(field_decl_ptr, "", self.scope) | |
935 | ||
936 | ||
937 | class FloatFieldDeclaration(FieldDeclaration): | |
938 | """ | |
939 | Floating point number field declaration. | |
940 | ||
941 | .. note:: | |
942 | ||
943 | As of this version, this class is missing some properties. | |
944 | """ | |
945 | ||
946 | def __init__(self): | |
947 | raise NotImplementedError("FloatFieldDeclaration cannot be instantiated") | |
948 | ||
949 | ||
950 | class StructureFieldDeclaration(FieldDeclaration): | |
951 | """ | |
952 | Structure (ordered map of field names to field declarations) field | |
953 | declaration. | |
954 | ||
955 | .. note:: | |
956 | ||
957 | As of this version, this class is missing some properties. | |
958 | """ | |
959 | ||
960 | def __init__(self): | |
961 | raise NotImplementedError("StructureFieldDeclaration cannot be instantiated") | |
962 | ||
963 | ||
964 | class StringFieldDeclaration(FieldDeclaration): | |
965 | """ | |
966 | String (NULL-terminated array of bytes) field declaration. | |
967 | ||
968 | .. note:: | |
969 | ||
970 | As of this version, this class is missing some properties. | |
971 | """ | |
972 | ||
973 | def __init__(self): | |
974 | raise NotImplementedError("StringFieldDeclaration cannot be instantiated") | |
975 | ||
976 | ||
977 | class VariantFieldDeclaration(FieldDeclaration): | |
978 | """ | |
979 | Variant (dynamic selection between different types) field declaration. | |
980 | ||
981 | .. note:: | |
982 | ||
983 | As of this version, this class is missing some properties. | |
984 | """ | |
985 | ||
986 | def __init__(self): | |
987 | raise NotImplementedError("VariantFieldDeclaration cannot be instantiated") | |
988 | ||
989 | ||
990 | def field_error(): | |
991 | """ | |
992 | Return the last error code encountered while | |
993 | accessing a field and reset the error flag. | |
994 | Return 0 if no error, a negative value otherwise. | |
995 | """ | |
996 | ||
997 | return nbt._bt_ctf_field_get_error() | |
998 | ||
999 | ||
1000 | def _create_field_declaration(declaration_ptr, name, scope): | |
1001 | """ | |
1002 | Private field declaration factory. | |
1003 | """ | |
1004 | ||
1005 | if declaration_ptr is None: | |
1006 | raise ValueError("declaration_ptr must be valid") | |
1007 | if scope not in _scopes: | |
1008 | raise ValueError("Invalid scope provided") | |
1009 | ||
1010 | type = nbt._bt_ctf_field_type(declaration_ptr) | |
1011 | declaration = None | |
1012 | ||
1013 | if type == common.CTFTypeId.INTEGER: | |
1014 | declaration = IntegerFieldDeclaration.__new__(IntegerFieldDeclaration) | |
1015 | elif type == common.CTFTypeId.ENUM: | |
1016 | declaration = EnumerationFieldDeclaration.__new__(EnumerationFieldDeclaration) | |
1017 | elif type == common.CTFTypeId.ARRAY: | |
1018 | declaration = ArrayFieldDeclaration.__new__(ArrayFieldDeclaration) | |
1019 | elif type == common.CTFTypeId.SEQUENCE: | |
1020 | declaration = SequenceFieldDeclaration.__new__(SequenceFieldDeclaration) | |
1021 | elif type == common.CTFTypeId.FLOAT: | |
1022 | declaration = FloatFieldDeclaration.__new__(FloatFieldDeclaration) | |
1023 | elif type == common.CTFTypeId.STRUCT: | |
1024 | declaration = StructureFieldDeclaration.__new__(StructureFieldDeclaration) | |
1025 | elif type == common.CTFTypeId.STRING: | |
1026 | declaration = StringFieldDeclaration.__new__(StringFieldDeclaration) | |
1027 | elif type == common.CTFTypeId.VARIANT: | |
1028 | declaration = VariantFieldDeclaration.__new__(VariantFieldDeclaration) | |
1029 | else: | |
1030 | return declaration | |
1031 | ||
1032 | declaration._fd = declaration_ptr | |
1033 | declaration._s = scope | |
1034 | declaration._name = name | |
1035 | ||
1036 | return declaration | |
1037 | ||
1038 | ||
1039 | class _Definition: | |
1040 | def __init__(self, definition_ptr, scope): | |
1041 | self._d = definition_ptr | |
1042 | self._s = scope | |
1043 | ||
1044 | if scope not in _scopes: | |
1045 | ValueError("Invalid scope provided") | |
1046 | ||
1047 | @property | |
1048 | def name(self): | |
1049 | """Return the name of a field or None on error.""" | |
1050 | ||
1051 | return nbt._bt_ctf_field_name(self._d) | |
1052 | ||
1053 | @property | |
1054 | def type(self): | |
1055 | """Return the type of a field or -1 if unknown.""" | |
1056 | ||
1057 | return nbt._bt_ctf_field_type(nbt._bt_ctf_get_decl_from_def(self._d)) | |
1058 | ||
1059 | @property | |
1060 | def declaration(self): | |
1061 | """Return the associated Definition object.""" | |
1062 | ||
1063 | return _create_field_declaration( | |
1064 | nbt._bt_ctf_get_decl_from_def(self._d), self.name, self.scope) | |
1065 | ||
1066 | def _get_enum_str(self): | |
1067 | """ | |
1068 | Return the string matching the current enumeration. | |
1069 | Return None on error. | |
1070 | """ | |
1071 | ||
1072 | return nbt._bt_ctf_get_enum_str(self._d) | |
1073 | ||
1074 | def _get_array_element_at(self, index): | |
1075 | """ | |
1076 | Return the array's element at position index. | |
1077 | Return None on error | |
1078 | """ | |
1079 | ||
1080 | array_ptr = nbt._bt_python_get_array_from_def(self._d) | |
1081 | ||
1082 | if array_ptr is None: | |
1083 | return None | |
1084 | ||
1085 | definition_ptr = nbt._bt_array_index(array_ptr, index) | |
1086 | ||
1087 | if definition_ptr is None: | |
1088 | return None | |
1089 | ||
1090 | return _Definition(definition_ptr, self.scope) | |
1091 | ||
1092 | def _get_sequence_len(self): | |
1093 | """ | |
1094 | Return the len of a sequence or a negative | |
1095 | value on error. | |
1096 | """ | |
1097 | ||
1098 | seq = nbt._bt_python_get_sequence_from_def(self._d) | |
1099 | ||
1100 | return nbt._bt_sequence_len(seq) | |
1101 | ||
1102 | def _get_sequence_element_at(self, index): | |
1103 | """ | |
1104 | Return the sequence's element at position index, | |
1105 | otherwise return None | |
1106 | """ | |
1107 | ||
1108 | seq = nbt._bt_python_get_sequence_from_def(self._d) | |
1109 | ||
1110 | if seq is not None: | |
1111 | definition_ptr = nbt._bt_sequence_index(seq, index) | |
1112 | ||
1113 | if definition_ptr is not None: | |
1114 | return _Definition(definition_ptr, self.scope) | |
1115 | ||
1116 | def _get_uint64(self): | |
1117 | """ | |
1118 | Return the value associated with the field. | |
1119 | If the field does not exist or is not of the type requested, | |
1120 | the value returned is undefined. To check if an error occured, | |
1121 | use the field_error() function after accessing a field. | |
1122 | """ | |
1123 | ||
1124 | return nbt._bt_ctf_get_uint64(self._d) | |
1125 | ||
1126 | def _get_int64(self): | |
1127 | """ | |
1128 | Return the value associated with the field. | |
1129 | If the field does not exist or is not of the type requested, | |
1130 | the value returned is undefined. To check if an error occured, | |
1131 | use the field_error() function after accessing a field. | |
1132 | """ | |
1133 | ||
1134 | return nbt._bt_ctf_get_int64(self._d) | |
1135 | ||
1136 | def _get_char_array(self): | |
1137 | """ | |
1138 | Return the value associated with the field. | |
1139 | If the field does not exist or is not of the type requested, | |
1140 | the value returned is undefined. To check if an error occurred, | |
1141 | use the field_error() function after accessing a field. | |
1142 | """ | |
1143 | ||
1144 | return nbt._bt_ctf_get_char_array(self._d) | |
1145 | ||
1146 | def _get_str(self): | |
1147 | """ | |
1148 | Return the value associated with the field. | |
1149 | If the field does not exist or is not of the type requested, | |
1150 | the value returned is undefined. To check if an error occurred, | |
1151 | use the field_error() function after accessing a field. | |
1152 | """ | |
1153 | ||
1154 | return nbt._bt_ctf_get_string(self._d) | |
1155 | ||
1156 | def _get_float(self): | |
1157 | """ | |
1158 | Return the value associated with the field. | |
1159 | If the field does not exist or is not of the type requested, | |
1160 | the value returned is undefined. To check if an error occurred, | |
1161 | use the field_error() function after accessing a field. | |
1162 | """ | |
1163 | ||
1164 | return nbt._bt_ctf_get_float(self._d) | |
1165 | ||
1166 | def _get_variant(self): | |
1167 | """ | |
1168 | Return the variant's selected field. | |
1169 | If the field does not exist or is not of the type requested, | |
1170 | the value returned is undefined. To check if an error occurred, | |
1171 | use the field_error() function after accessing a field. | |
1172 | """ | |
1173 | ||
1174 | return nbt._bt_ctf_get_variant(self._d) | |
1175 | ||
1176 | def _get_struct_field_count(self): | |
1177 | """ | |
1178 | Return the number of fields contained in the structure. | |
1179 | If the field does not exist or is not of the type requested, | |
1180 | the value returned is undefined. | |
1181 | """ | |
1182 | ||
1183 | return nbt._bt_ctf_get_struct_field_count(self._d) | |
1184 | ||
1185 | def _get_struct_field_at(self, i): | |
1186 | """ | |
1187 | Return the structure's field at position i. | |
1188 | If the field does not exist or is not of the type requested, | |
1189 | the value returned is undefined. To check if an error occurred, | |
1190 | use the field_error() function after accessing a field. | |
1191 | """ | |
1192 | ||
1193 | return nbt._bt_ctf_get_struct_field_index(self._d, i) | |
1194 | ||
1195 | @property | |
1196 | def value(self): | |
1197 | """ | |
1198 | Return the value associated with the field according to its type. | |
1199 | Return None on error. | |
1200 | """ | |
1201 | ||
1202 | id = self.type | |
1203 | value = None | |
1204 | ||
1205 | if id == common.CTFTypeId.STRING: | |
1206 | value = self._get_str() | |
1207 | elif id == common.CTFTypeId.ARRAY: | |
1208 | element_decl = self.declaration.element_declaration | |
1209 | ||
1210 | if ((element_decl.type == common.CTFTypeId.INTEGER | |
1211 | and element_decl.length == 8) | |
1212 | and (element_decl.encoding == common.CTFStringEncoding.ASCII or element_decl.encoding == common.CTFStringEncoding.UTF8)): | |
1213 | value = nbt._bt_python_get_array_string(self._d) | |
1214 | else: | |
1215 | value = [] | |
1216 | ||
1217 | for i in range(self.declaration.length): | |
1218 | element = self._get_array_element_at(i) | |
1219 | value.append(element.value) | |
1220 | elif id == common.CTFTypeId.INTEGER: | |
1221 | if self.declaration.signedness == 0: | |
1222 | value = self._get_uint64() | |
1223 | else: | |
1224 | value = self._get_int64() | |
1225 | elif id == common.CTFTypeId.ENUM: | |
1226 | value = self._get_enum_str() | |
1227 | elif id == common.CTFTypeId.SEQUENCE: | |
1228 | element_decl = self.declaration.element_declaration | |
1229 | ||
1230 | if ((element_decl.type == common.CTFTypeId.INTEGER | |
1231 | and element_decl.length == 8) | |
1232 | and (element_decl.encoding == common.CTFStringEncoding.ASCII or element_decl.encoding == common.CTFStringEncoding.UTF8)): | |
1233 | value = nbt._bt_python_get_sequence_string(self._d) | |
1234 | else: | |
1235 | seq_len = self._get_sequence_len() | |
1236 | value = [] | |
1237 | ||
1238 | for i in range(seq_len): | |
1239 | evDef = self._get_sequence_element_at(i) | |
1240 | value.append(evDef.value) | |
1241 | elif id == common.CTFTypeId.FLOAT: | |
1242 | value = self._get_float() | |
1243 | elif id == common.CTFTypeId.VARIANT: | |
1244 | variant = _Definition.__new__(_Definition) | |
1245 | variant._d = self._get_variant() | |
1246 | value = variant.value | |
1247 | elif id == common.CTFTypeId.STRUCT: | |
1248 | value = {} | |
1249 | ||
1250 | for i in range(self._get_struct_field_count()): | |
1251 | member = _Definition(self._get_struct_field_at(i), self.scope) | |
1252 | value[member.name] = member.value | |
1253 | ||
1254 | if field_error(): | |
1255 | raise FieldError( | |
1256 | "Error occurred while accessing field {} of type {}".format( | |
1257 | self.name, | |
1258 | common.CTFTypeId.type_name(id))) | |
1259 | ||
1260 | return value | |
1261 | ||
1262 | @property | |
1263 | def scope(self): | |
1264 | """Return the scope of a field or None on error.""" | |
1265 | ||
1266 | return self._s |