Fix: check whether analysis has ended before setting last timestamp
[deliverable/lttng-analyses.git] / lttnganalyses / core / analysis.py
CommitLineData
4ed24f86
JD
1# The MIT License (MIT)
2#
a3fa57c0 3# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
4ed24f86
JD
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 THE
21# SOFTWARE.
22
b6d9132b
AB
23
24class AnalysisConfig:
25 def __init__(self):
26 self.refresh_period = None
43a3c04c
AB
27 self.period_begin_ev_name = None
28 self.period_end_ev_name = None
05684c5e
AB
29 self.period_begin_key_fields = None
30 self.period_end_key_fields = None
31 self.period_key_value = None
b6d9132b
AB
32 self.begin_ts = None
33 self.end_ts = None
34 self.min_duration = None
35 self.max_duration = None
43b66dd6
AB
36 self.proc_list = None
37 self.tid_list = None
a621ba35 38 self.cpu_list = None
b6d9132b 39
4ed24f86 40
323b3fd6 41class Analysis:
a0acc08c
PP
42 TICK_CB = 'tick'
43
b6d9132b
AB
44 def __init__(self, state, conf):
45 self._state = state
46 self._conf = conf
43a3c04c 47 self._period_key = None
b6d9132b
AB
48 self._period_start_ts = None
49 self._last_event_ts = None
50 self._notification_cbs = {}
51 self._cbs = {}
52
b6d9132b
AB
53 self.started = False
54 self.ended = False
55
323b3fd6 56 def process_event(self, ev):
e350f2be
AB
57 self._check_analysis_end(ev)
58 if self.ended:
59 return
60
b6d9132b
AB
61 self._last_event_ts = ev.timestamp
62
63 if not self.started:
64 if self._conf.begin_ts:
65 self._check_analysis_begin(ev)
66 if not self.started:
67 return
68 else:
69 self._period_start_ts = ev.timestamp
70 self.started = True
71
43a3c04c
AB
72 # Prioritise period events over refresh period
73 if self._conf.period_begin_ev_name is not None:
74 self._handle_period_event(ev)
75 elif self._conf.refresh_period is not None:
b6d9132b 76 self._check_refresh(ev)
b88c1bbc 77
b5db5706
AB
78 def reset(self):
79 raise NotImplementedError()
80
b6d9132b 81 def end(self):
5422fc1d
AB
82 if self._period_start_ts:
83 self._end_period()
b6d9132b
AB
84
85 def register_notification_cbs(self, cbs):
86 for name in cbs:
87 if name not in self._notification_cbs:
88 self._notification_cbs[name] = []
89
90 self._notification_cbs[name].append(cbs[name])
91
92 def _send_notification_cb(self, name, **kwargs):
93 if name in self._notification_cbs:
94 for cb in self._notification_cbs[name]:
95 cb(**kwargs)
96
b88c1bbc
AB
97 def _register_cbs(self, cbs):
98 self._cbs = cbs
99
100 def _process_event_cb(self, ev):
101 name = ev.name
102
103 if name in self._cbs:
104 self._cbs[name](ev)
c2e5e625 105 elif 'syscall_entry' in self._cbs and \
771c7e66 106 (name.startswith('sys_') or name.startswith('syscall_entry_')):
c2e5e625
AB
107 self._cbs['syscall_entry'](ev)
108 elif 'syscall_exit' in self._cbs and \
109 (name.startswith('exit_syscall') or
110 name.startswith('syscall_exit_')):
111 self._cbs['syscall_exit'](ev)
b6d9132b
AB
112
113 def _check_analysis_begin(self, ev):
85bf812e 114 if self._conf.begin_ts and ev.timestamp >= self._conf.begin_ts:
b6d9132b 115 self.started = True
85bf812e 116 self._period_start_ts = ev.timestamp
b6d9132b
AB
117 self.reset()
118
119 def _check_analysis_end(self, ev):
120 if self._conf.end_ts and ev.timestamp > self._conf.end_ts:
121 self.ended = True
122
123 def _check_refresh(self, ev):
124 if not self._period_start_ts:
125 self._period_start_ts = ev.timestamp
126 elif ev.timestamp >= (self._period_start_ts +
127 self._conf.refresh_period):
128 self._end_period()
129 self._period_start_ts = ev.timestamp
130
43a3c04c
AB
131 def _handle_period_event(self, ev):
132 if ev.name != self._conf.period_begin_ev_name and \
133 ev.name != self._conf.period_end_ev_name:
134 return
135
43a3c04c 136 if self._period_key:
05684c5e
AB
137 period_key = Analysis._get_period_event_key(
138 ev, self._conf.period_end_key_fields)
139
140 if not period_key:
141 # There was an error caused by a missing field, ignore
142 # this period event
143 return
144
43a3c04c
AB
145 if period_key == self._period_key:
146 if self._conf.period_end_ev_name:
147 if ev.name == self._conf.period_end_ev_name:
148 self._end_period()
149 self._period_key = None
150 self._period_start_ts = None
151 elif ev.name == self._conf.period_begin_ev_name:
152 self._end_period()
72e07bd5 153 self._begin_period(period_key, ev.timestamp)
5422fc1d 154 elif ev.name == self._conf.period_begin_ev_name:
05684c5e
AB
155 period_key = Analysis._get_period_event_key(
156 ev, self._conf.period_begin_key_fields)
157
158 if not period_key:
159 return
160
161 if self._conf.period_key_value:
162 # Must convert the period key to string for comparison
163 str_period_key = tuple(map(str, period_key))
164 if self._conf.period_key_value != str_period_key:
165 return
166
72e07bd5
AB
167 self._begin_period(period_key, ev.timestamp)
168
169 def _begin_period(self, period_key, timestamp):
170 self._period_key = period_key
171 self._period_start_ts = timestamp
172 self.reset()
43a3c04c 173
b6d9132b
AB
174 def _end_period(self):
175 self._end_period_cb()
a0acc08c 176 self._send_notification_cb(Analysis.TICK_CB,
b6d9132b
AB
177 begin_ns=self._period_start_ts,
178 end_ns=self._last_event_ts)
b6d9132b
AB
179
180 def _end_period_cb(self):
181 pass
43a3c04c 182
05684c5e
AB
183 @staticmethod
184 def _get_period_event_key(ev, key_fields):
185 if not key_fields:
5422fc1d
AB
186 return None
187
188 key_values = []
189
05684c5e 190 for field in key_fields:
5422fc1d
AB
191 try:
192 key_values.append(ev[field])
193 except KeyError:
194 # Error: missing field
195 return None
196
197 return tuple(key_values)
43b66dd6
AB
198
199 def _filter_process(self, proc):
200 if not proc:
201 return True
202 if self._conf.proc_list and proc.comm not in self._conf.proc_list:
203 return False
204 if self._conf.tid_list and proc.tid not in self._conf.tid_list:
205 return False
206 return True
a621ba35
AB
207
208 def _filter_cpu(self, cpu):
209 return not (self._conf.cpu_list and cpu not in self._conf.cpu_list)
This page took 0.03293 seconds and 5 git commands to generate.