Prevent division by zero in core/cputop.py
[deliverable/lttng-analyses.git] / lttnganalyses / core / analysis.py
CommitLineData
4ed24f86
JD
1#!/usr/bin/env python3
2#
3# The MIT License (MIT)
4#
a3fa57c0 5# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
4ed24f86
JD
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the "Software"), to deal
9# in the Software without restriction, including without limitation the rights
10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11# copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in
15# all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
24
b6d9132b
AB
25from ..linuxautomaton import common
26
27
28class AnalysisConfig:
29 def __init__(self):
30 self.refresh_period = None
43a3c04c
AB
31 self.period_begin_ev_name = None
32 self.period_end_ev_name = None
b6d9132b
AB
33 self.begin_ts = None
34 self.end_ts = None
35 self.min_duration = None
36 self.max_duration = None
37
4ed24f86 38
323b3fd6 39class Analysis:
b6d9132b
AB
40 def __init__(self, state, conf):
41 self._state = state
42 self._conf = conf
43a3c04c 43 self._period_key = None
b6d9132b
AB
44 self._period_start_ts = None
45 self._last_event_ts = None
46 self._notification_cbs = {}
47 self._cbs = {}
48
b6d9132b
AB
49 self.started = False
50 self.ended = False
51
323b3fd6 52 def process_event(self, ev):
b6d9132b
AB
53 self._last_event_ts = ev.timestamp
54
55 if not self.started:
56 if self._conf.begin_ts:
57 self._check_analysis_begin(ev)
58 if not self.started:
59 return
60 else:
61 self._period_start_ts = ev.timestamp
62 self.started = True
63
64 self._check_analysis_end(ev)
65 if self.ended:
66 return
67
43a3c04c
AB
68 # Prioritise period events over refresh period
69 if self._conf.period_begin_ev_name is not None:
70 self._handle_period_event(ev)
71 elif self._conf.refresh_period is not None:
b6d9132b 72 self._check_refresh(ev)
b88c1bbc 73
b5db5706
AB
74 def reset(self):
75 raise NotImplementedError()
76
b6d9132b
AB
77 def end(self):
78 self._end_period()
79
80 def register_notification_cbs(self, cbs):
81 for name in cbs:
82 if name not in self._notification_cbs:
83 self._notification_cbs[name] = []
84
85 self._notification_cbs[name].append(cbs[name])
86
87 def _send_notification_cb(self, name, **kwargs):
88 if name in self._notification_cbs:
89 for cb in self._notification_cbs[name]:
90 cb(**kwargs)
91
b88c1bbc
AB
92 def _register_cbs(self, cbs):
93 self._cbs = cbs
94
95 def _process_event_cb(self, ev):
96 name = ev.name
97
98 if name in self._cbs:
99 self._cbs[name](ev)
c2e5e625 100 elif 'syscall_entry' in self._cbs and \
771c7e66 101 (name.startswith('sys_') or name.startswith('syscall_entry_')):
c2e5e625
AB
102 self._cbs['syscall_entry'](ev)
103 elif 'syscall_exit' in self._cbs and \
104 (name.startswith('exit_syscall') or
105 name.startswith('syscall_exit_')):
106 self._cbs['syscall_exit'](ev)
b6d9132b
AB
107
108 def _check_analysis_begin(self, ev):
109 if self._conf.begin_ts and ev.timestamp >= self._conf.begin:
110 self.started = True
111 self._period_start_ts = ev.ts
112 self.reset()
113
114 def _check_analysis_end(self, ev):
115 if self._conf.end_ts and ev.timestamp > self._conf.end_ts:
116 self.ended = True
117
118 def _check_refresh(self, ev):
119 if not self._period_start_ts:
120 self._period_start_ts = ev.timestamp
121 elif ev.timestamp >= (self._period_start_ts +
122 self._conf.refresh_period):
123 self._end_period()
124 self._period_start_ts = ev.timestamp
125
43a3c04c
AB
126 def _handle_period_event(self, ev):
127 if ev.name != self._conf.period_begin_ev_name and \
128 ev.name != self._conf.period_end_ev_name:
129 return
130
131 period_key = self._get_period_event_key(ev)
132 if not period_key:
133 # There was an error caused by missing context, ignore
134 # this period event
135 return
136
137 if self._period_key:
138 if period_key == self._period_key:
139 if self._conf.period_end_ev_name:
140 if ev.name == self._conf.period_end_ev_name:
141 self._end_period()
142 self._period_key = None
143 self._period_start_ts = None
144 elif ev.name == self._conf.period_begin_ev_name:
145 self._end_period()
146 self._period_key = period_key
147 self._period_start_ts = ev.timestamp
148 else:
149 self._period_key = period_key
150 self._period_start_ts = ev.timestamp
151
b6d9132b
AB
152 def _end_period(self):
153 self._end_period_cb()
154 self._send_notification_cb('output_results',
155 begin_ns=self._period_start_ts,
156 end_ns=self._last_event_ts)
157 self.reset()
158
159 def _end_period_cb(self):
160 pass
43a3c04c
AB
161
162 def _get_period_event_key(self, ev):
163 # TODO: currently the key is hardcoded to the vtid of the
164 # thread which generated the event, but eventually there
165 # should be the option for a user to specify what fields
166 # (context or payload) make up the key.
167 try:
168 key = ev.vtid
169 except AttributeError:
170 # TODO warn user of missing context?
171 key = None
172
173 return key
This page took 0.032518 seconds and 5 git commands to generate.