python: reimplement the babeltrace package as a bt2 wrapper
[babeltrace.git] / bindings / python / babeltrace / babeltrace / reader_trace_collection.py
diff --git a/bindings/python/babeltrace/babeltrace/reader_trace_collection.py b/bindings/python/babeltrace/babeltrace/reader_trace_collection.py
new file mode 100644 (file)
index 0000000..d888f8d
--- /dev/null
@@ -0,0 +1,201 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2013-2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import bt2
+import babeltrace.common as common
+from babeltrace import reader_trace_handle
+from babeltrace import reader_event
+import os
+
+
+class TraceCollection:
+    """
+    A :class:`TraceCollection` is a collection of opened traces.
+
+    Once a trace collection is created, you can add traces to the
+    collection by using the :meth:`add_trace` or
+    :meth:`add_traces_recursive`, and then iterate on the merged
+    events using :attr:`events`.
+
+    You may use :meth:`remove_trace` to close and remove a specific
+    trace from a trace collection.
+    """
+
+    def __init__(self, intersect_mode=False):
+        """
+        Creates an empty trace collection.
+        """
+
+        self._intersect_mode = intersect_mode
+        self._trace_handles = set()
+        self._next_th_id = 0
+        self._ctf_plugin = bt2.find_plugin('ctf')
+        assert(self._ctf_plugin is not None)
+        self._fs_comp_cls = self._ctf_plugin.source_component_classes['fs']
+
+    def add_trace(self, path, format_str):
+        """
+        Adds a trace to the trace collection.
+
+        *path* is the exact path of the trace on the filesystem.
+
+        *format_str* is a string indicating the type of trace to
+        add. ``ctf`` is currently the only supported trace format.
+
+        Returns the corresponding :class:`TraceHandle` instance for
+        this opened trace on success, or ``None`` on error.
+
+        This function **does not** recurse directories to find a
+        trace.  See :meth:`add_traces_recursive` for a recursive
+        version of this function.
+        """
+
+        if format_str != 'ctf':
+            raise ValueError('only the "ctf" format is supported')
+
+        if not os.path.isfile(os.path.join(path, 'metadata')):
+            raise ValueError('no "metadata" file found in "{}"'.format(path))
+
+        th = reader_trace_handle.TraceHandle.__new__(reader_trace_handle.TraceHandle)
+        th._id = self._next_th_id
+        self._next_th_id += 1
+        th._trace_collection = self
+        th._path = path
+        self._trace_handles.add(th)
+        return th
+
+    def add_traces_recursive(self, path, format_str):
+        """
+        Adds traces to this trace collection by recursively searching
+        in the *path* directory.
+
+        *format_str* is a string indicating the type of trace to add.
+        ``ctf`` is currently the only supported trace format.
+
+        Returns a :class:`dict` object mapping full paths to trace
+        handles for each trace found, or ``None`` on error.
+
+        See also :meth:`add_trace`.
+        """
+
+        trace_handles = {}
+        noTrace = True
+        error = False
+
+        for fullpath, dirs, files in os.walk(path):
+            if "metadata" in files:
+                trace_handle = self.add_trace(fullpath, format_str)
+
+                if trace_handle is None:
+                    error = True
+                    continue
+
+                trace_handles[fullpath] = trace_handle
+                noTrace = False
+
+        if noTrace and error:
+            return None
+
+        return trace_handles
+
+    def remove_trace(self, handle):
+        """
+        Removes a trace from the trace collection using its trace
+        handle *trace_handle*.
+
+        :class:`TraceHandle` objects are returned by :meth:`add_trace`
+        and :meth:`add_traces_recursive`.
+        """
+
+        if not isinstance(handle, reader_trace_handle.TraceHandle):
+            raise TypeError("'{}' is not a 'TraceHandle' object".format(
+                handle.__class__.__name__))
+
+        # this can raise but it would mean the trace handle is not part
+        # of this trace collection anyway
+        self._trace_handles.remove(handle)
+
+    @property
+    def intersect_mode(self):
+        return self._intersect_mode
+
+    @property
+    def has_intersection(self):
+        return any(th._has_intersection for th in self._trace_handles)
+
+    @property
+    def events(self):
+        """
+        Generates the ordered :class:`Event` objects of all the opened
+        traces contained in this trace collection.
+        """
+
+        return self._gen_events()
+
+    def events_timestamps(self, timestamp_begin, timestamp_end):
+        """
+        Generates the ordered :class:`Event` objects of all the opened
+        traces contained in this trace collection from *timestamp_begin*
+        to *timestamp_end*.
+
+        *timestamp_begin* and *timestamp_end* are given in nanoseconds
+        since Epoch.
+
+        See :attr:`events` for notes and limitations.
+        """
+
+        return self._gen_events(timestamp_begin / 1e9, timestamp_end / 1e9)
+
+    def _gen_events(self, begin_s=None, end_s=None):
+        specs = [bt2.SourceComponentSpec('ctf', 'fs', th.path) for th in self._trace_handles]
+
+        try:
+            iter_cls = bt2.TraceCollectionNotificationIterator
+            tc_iter = iter_cls(specs,
+                               stream_intersection_mode=self._intersect_mode,
+                               begin=begin_s, end=end_s,
+                               notification_types=[bt2.EventNotification])
+            return map(reader_event._create_event, tc_iter)
+        except:
+            raise ValueError
+
+    @property
+    def timestamp_begin(self):
+        """
+        Begin timestamp of this trace collection (nanoseconds since Epoch).
+        """
+
+        if not self._trace_handles:
+            return
+
+        return min(th.timestamp_begin for th in self._trace_handles)
+
+    @property
+    def timestamp_end(self):
+        """
+        End timestamp of this trace collection (nanoseconds since Epoch).
+        """
+
+        if not self._trace_handles:
+            return
+
+        return max(th.timestamp_end for th in self._trace_handles)
This page took 0.037625 seconds and 4 git commands to generate.