Exit gracefully on SIGINT in run()
[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
5422fc1d 29 self.period_key_fields = None
b6d9132b
AB
30 self.begin_ts = None
31 self.end_ts = None
32 self.min_duration = None
33 self.max_duration = None
43b66dd6
AB
34 self.proc_list = None
35 self.tid_list = None
a621ba35 36 self.cpu_list = None
b6d9132b 37
4ed24f86 38
323b3fd6 39class Analysis:
a0acc08c
PP
40 TICK_CB = 'tick'
41
b6d9132b
AB
42 def __init__(self, state, conf):
43 self._state = state
44 self._conf = conf
43a3c04c 45 self._period_key = None
b6d9132b
AB
46 self._period_start_ts = None
47 self._last_event_ts = None
48 self._notification_cbs = {}
49 self._cbs = {}
50
b6d9132b
AB
51 self.started = False
52 self.ended = False
53
323b3fd6 54 def process_event(self, ev):
b6d9132b
AB
55 self._last_event_ts = ev.timestamp
56
57 if not self.started:
58 if self._conf.begin_ts:
59 self._check_analysis_begin(ev)
60 if not self.started:
61 return
62 else:
63 self._period_start_ts = ev.timestamp
64 self.started = True
65
66 self._check_analysis_end(ev)
67 if self.ended:
68 return
69
43a3c04c
AB
70 # Prioritise period events over refresh period
71 if self._conf.period_begin_ev_name is not None:
72 self._handle_period_event(ev)
73 elif self._conf.refresh_period is not None:
b6d9132b 74 self._check_refresh(ev)
b88c1bbc 75
b5db5706
AB
76 def reset(self):
77 raise NotImplementedError()
78
b6d9132b 79 def end(self):
5422fc1d
AB
80 if self._period_start_ts:
81 self._end_period()
b6d9132b
AB
82
83 def register_notification_cbs(self, cbs):
84 for name in cbs:
85 if name not in self._notification_cbs:
86 self._notification_cbs[name] = []
87
88 self._notification_cbs[name].append(cbs[name])
89
90 def _send_notification_cb(self, name, **kwargs):
91 if name in self._notification_cbs:
92 for cb in self._notification_cbs[name]:
93 cb(**kwargs)
94
b88c1bbc
AB
95 def _register_cbs(self, cbs):
96 self._cbs = cbs
97
98 def _process_event_cb(self, ev):
99 name = ev.name
100
101 if name in self._cbs:
102 self._cbs[name](ev)
c2e5e625 103 elif 'syscall_entry' in self._cbs and \
771c7e66 104 (name.startswith('sys_') or name.startswith('syscall_entry_')):
c2e5e625
AB
105 self._cbs['syscall_entry'](ev)
106 elif 'syscall_exit' in self._cbs and \
107 (name.startswith('exit_syscall') or
108 name.startswith('syscall_exit_')):
109 self._cbs['syscall_exit'](ev)
b6d9132b
AB
110
111 def _check_analysis_begin(self, ev):
85bf812e 112 if self._conf.begin_ts and ev.timestamp >= self._conf.begin_ts:
b6d9132b 113 self.started = True
85bf812e 114 self._period_start_ts = ev.timestamp
b6d9132b
AB
115 self.reset()
116
117 def _check_analysis_end(self, ev):
118 if self._conf.end_ts and ev.timestamp > self._conf.end_ts:
119 self.ended = True
120
121 def _check_refresh(self, ev):
122 if not self._period_start_ts:
123 self._period_start_ts = ev.timestamp
124 elif ev.timestamp >= (self._period_start_ts +
125 self._conf.refresh_period):
126 self._end_period()
127 self._period_start_ts = ev.timestamp
128
43a3c04c
AB
129 def _handle_period_event(self, ev):
130 if ev.name != self._conf.period_begin_ev_name and \
131 ev.name != self._conf.period_end_ev_name:
132 return
133
134 period_key = self._get_period_event_key(ev)
135 if not period_key:
5422fc1d 136 # There was an error caused by a missing field, ignore
43a3c04c
AB
137 # this period event
138 return
139
140 if self._period_key:
141 if period_key == self._period_key:
142 if self._conf.period_end_ev_name:
143 if ev.name == self._conf.period_end_ev_name:
144 self._end_period()
145 self._period_key = None
146 self._period_start_ts = None
147 elif ev.name == self._conf.period_begin_ev_name:
148 self._end_period()
149 self._period_key = period_key
150 self._period_start_ts = ev.timestamp
5422fc1d 151 elif ev.name == self._conf.period_begin_ev_name:
43a3c04c
AB
152 self._period_key = period_key
153 self._period_start_ts = ev.timestamp
154
b6d9132b
AB
155 def _end_period(self):
156 self._end_period_cb()
a0acc08c 157 self._send_notification_cb(Analysis.TICK_CB,
b6d9132b
AB
158 begin_ns=self._period_start_ts,
159 end_ns=self._last_event_ts)
160 self.reset()
161
162 def _end_period_cb(self):
163 pass
43a3c04c
AB
164
165 def _get_period_event_key(self, ev):
5422fc1d
AB
166 if not self._conf.period_key_fields:
167 return None
168
169 key_values = []
170
171 for field in self._conf.period_key_fields:
172 try:
173 key_values.append(ev[field])
174 except KeyError:
175 # Error: missing field
176 return None
177
178 return tuple(key_values)
43b66dd6
AB
179
180 def _filter_process(self, proc):
181 if not proc:
182 return True
183 if self._conf.proc_list and proc.comm not in self._conf.proc_list:
184 return False
185 if self._conf.tid_list and proc.tid not in self._conf.tid_list:
186 return False
187 return True
a621ba35
AB
188
189 def _filter_cpu(self, cpu):
190 return not (self._conf.cpu_list and cpu not in self._conf.cpu_list)
This page took 0.03306 seconds and 5 git commands to generate.