1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from telemetry.timeline import async_slice as async_slice_module
6from telemetry.timeline import flow_event as flow_event_module
7from telemetry.timeline import slice as slice_module
8
9
10class TimelineEventContainer(object):
11  """Represents a container for events.
12
13  """
14  def __init__(self, name, parent):
15    self.parent = parent
16    self.name = name
17
18  @staticmethod
19  def IsAsyncSlice(t):
20    return t == async_slice_module.AsyncSlice
21
22  # Basic functions that subclasses of TimelineEventContainer should implement
23  # in order to expose their events. New methods should be added to this part of
24  # the code only when absolutely certain they're needed.
25
26  def IterChildContainers(self):
27    raise NotImplementedError()
28
29  def IterEventsInThisContainer(self, event_type_predicate, event_predicate):
30    """Iterates all the TimelineEvents in this container.
31
32    Only events with a type matching event_type_predicate AND matching event
33    event_predicate will be yielded.
34
35    event_type_predicate is given an actual type object, e.g.:
36        event_type_predicate(slice_module.Slice)
37
38    event_predicate is given actual events:
39        event_predicate(thread.slices[7])
40
41    DO NOT ASSUME that the event_type_predicate will be called for every event
42    found. The relative calling order of the two is left up to the implementer
43    of the method.
44
45    """
46    del event_type_predicate, event_predicate  # unused
47    return
48    yield # pylint: disable=unreachable
49
50
51  def IterAllEvents(self,
52                    recursive=True,
53                    event_type_predicate=lambda t: True,
54                    event_predicate=lambda e: True):
55    """Iterates all events in this container, pre-filtered by two predicates.
56
57    Only events with a type matching event_type_predicate AND matching event
58    event_predicate will be yielded.
59
60    event_type_predicate is given an actual type object, e.g.:
61        event_type_predicate(slice_module.Slice)
62
63    event_predicate is given actual events:
64        event_predicate(thread.slices[7])
65    """
66    if not recursive:
67      for e in self.IterEventsInThisContainer(
68          event_type_predicate, event_predicate):
69        yield e
70      return
71
72    # TODO(nduca): Write this as a proper iterator instead of one that creates a
73    # list and then iterates it.
74    containers = []
75    def GetContainersRecursive(container):
76      containers.append(container)
77      for container in container.IterChildContainers():
78        GetContainersRecursive(container)
79    GetContainersRecursive(self)
80
81    # Actually create the iterator.
82    for c in containers:
83      for e in c.IterEventsInThisContainer(event_type_predicate,
84                                           event_predicate):
85        yield e
86
87  # Helper functions for finding common kinds of events. Must always take an
88  # optinal recurisve parameter and be implemented in terms fo IterAllEvents.
89  def IterAllEventsOfName(self, name, recursive=True):
90    return self.IterAllEvents(
91      recursive=recursive,
92      event_type_predicate=lambda t: True,
93      event_predicate=lambda e: e.name == name)
94
95  def IterAllSlices(self, recursive=True):
96    return self.IterAllEvents(
97      recursive=recursive,
98      event_type_predicate=lambda t: t == slice_module.Slice)
99
100  def IterAllSlicesInRange(self, start, end, recursive=True):
101    return self.IterAllEvents(
102      recursive=recursive,
103      event_type_predicate=lambda t: t == slice_module.Slice,
104      event_predicate=lambda s: s.start >= start and s.end <= end)
105
106  def IterAllSlicesOfName(self, name, recursive=True):
107    return self.IterAllEvents(
108      recursive=recursive,
109      event_type_predicate=lambda t: t == slice_module.Slice,
110      event_predicate=lambda e: e.name == name)
111
112  def IterAllToplevelSlicesOfName(self, name, recursive=True):
113    return self.IterAllEvents(
114      recursive=recursive,
115      event_type_predicate=lambda t: t == slice_module.Slice,
116      event_predicate=lambda e: e.name == name and e.parent_slice == None)
117
118  def IterAllAsyncSlicesOfName(self, name, recursive=True):
119    return self.IterAllEvents(
120      recursive=recursive,
121      event_type_predicate=self.IsAsyncSlice,
122      event_predicate=lambda e: e.name == name)
123
124  def IterAllAsyncSlicesStartsWithName(self, name, recursive=True):
125    return self.IterAllEvents(
126      recursive=recursive,
127      event_type_predicate=self.IsAsyncSlice,
128      event_predicate=lambda e: e.name.startswith(name))
129
130  def IterAllFlowEvents(self, recursive=True):
131    return self.IterAllEvents(
132      recursive=recursive,
133      event_type_predicate=lambda t: t == flow_event_module.FlowEvent)
134
135  # List versions. These should always be simple expressions that list() on
136  # an underlying iter method.
137  def GetAllEvents(self, recursive=True):
138    return list(self.IterAllEvents(recursive=recursive))
139
140  def GetAllEventsOfName(self, name, recursive=True):
141    return list(self.IterAllEventsOfName(name, recursive))
142
143  def GetAllToplevelSlicesOfName(self, name, recursive=True):
144    return list(self.IterAllToplevelSlicesOfName(name, recursive))
145