Merge branch 'staging'
[deliverable/lttng-analyses.git] / lttnganalyses / cli / command.py
CommitLineData
4ed24f86
JD
1# The MIT License (MIT)
2#
a3fa57c0 3# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
cee855a2 4# 2015 - Philippe Proulx <pproulx@efficios.com>
0b250a71 5# 2015 - Antoine Busque <abusque@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
323b3fd6 25import argparse
a0acc08c 26import json
4ac5e240 27import os
a0acc08c 28import re
0b250a71
AB
29import sys
30import subprocess
810e6be8 31import traceback
0b250a71 32from babeltrace import TraceCollection
9079847d 33from . import mi, progressbar
9463174b 34from .. import __version__
0b250a71 35from ..core import analysis
9079847d 36from ..common import (
e4a48ea8 37 format_utils, parse_utils, trace_utils, version_utils
9079847d 38)
0b250a71 39from ..linuxautomaton import automaton
323b3fd6
PP
40
41
42class Command:
a0acc08c
PP
43 _MI_BASE_TAGS = ['linux-kernel', 'lttng-analyses']
44 _MI_AUTHORS = [
45 'Julien Desfossez',
46 'Antoine Busque',
47 'Philippe Proulx',
48 ]
49 _MI_URL = 'https://github.com/lttng/lttng-analyses'
9463174b 50 _VERSION = version_utils.Version.new_from_string(__version__)
a0ea9266 51 _BT_INTERSECT_VERSION = version_utils.Version(1, 4, 0)
810e6be8 52 _DEBUG_ENV_VAR = 'LTTNG_ANALYSES_DEBUG'
a0acc08c
PP
53
54 def __init__(self, mi_mode=False):
b6d9132b
AB
55 self._analysis = None
56 self._analysis_conf = None
57 self._args = None
a0ea9266 58 self._babeltrace_version = None
b6d9132b
AB
59 self._handles = None
60 self._traces = None
a0acc08c
PP
61 self._ticks = 0
62 self._mi_mode = mi_mode
810e6be8 63 self._debug_mode = os.environ.get(self._DEBUG_ENV_VAR)
5bc26da4
PP
64 self._run_step('create automaton', self._create_automaton)
65 self._run_step('setup MI', self._mi_setup)
a0acc08c
PP
66
67 @property
68 def mi_mode(self):
69 return self._mi_mode
323b3fd6 70
5bc26da4 71 def _run_step(self, action_title, fn):
74d112b5 72 try:
5bc26da4 73 fn()
74d112b5 74 except KeyboardInterrupt:
5bc26da4 75 self._print('Cancelled by user')
74d112b5 76 sys.exit(0)
5bc26da4 77 except Exception as e:
810e6be8
AB
78 if self._debug_mode:
79 traceback.print_exc()
80
5bc26da4
PP
81 self._gen_error('Cannot {}: {}'.format(action_title, e))
82
83 def run(self):
84 self._run_step('parse arguments', self._parse_args)
85 self._run_step('open trace', self._open_trace)
86 self._run_step('create analysis', self._create_analysis)
87
78f6647f 88 if not self._mi_mode or not self._args.test_compatibility:
5bc26da4
PP
89 self._run_step('run analysis', self._run_analysis)
90
91 self._run_step('close trace', self._close_trace)
b6d9132b 92
8311b968
PP
93 def _mi_error(self, msg, code=None):
94 print(json.dumps(mi.get_error(msg, code)))
95
96 def _non_mi_error(self, msg):
dc3ccf03
AB
97 if self._args.color:
98 try:
99 import termcolor
d6c76c60 100
dc3ccf03
AB
101 msg = termcolor.colored(msg, 'red', attrs=['bold'])
102 except ImportError:
103 pass
d6c76c60 104
323b3fd6 105 print(msg, file=sys.stderr)
8311b968 106
45d44f25 107 def _error(self, msg, exit_code=1):
8311b968
PP
108 if self._mi_mode:
109 self._mi_error(msg)
110 else:
111 self._non_mi_error(msg)
112
323b3fd6
PP
113 sys.exit(exit_code)
114
115 def _gen_error(self, msg, exit_code=1):
116 self._error('Error: {}'.format(msg), exit_code)
117
118 def _cmdline_error(self, msg, exit_code=1):
119 self._error('Command line error: {}'.format(msg), exit_code)
120
a0acc08c
PP
121 def _print(self, msg):
122 if not self._mi_mode:
123 print(msg)
124
125 def _mi_create_result_table(self, table_class_name, begin, end,
126 subtitle=None):
127 return mi.ResultTable(self._mi_table_classes[table_class_name],
128 begin, end, subtitle)
129
130 def _mi_setup(self):
131 self._mi_table_classes = {}
132
133 for tc_tuple in self._MI_TABLE_CLASSES:
134 table_class = mi.TableClass(tc_tuple[0], tc_tuple[1], tc_tuple[2])
135 self._mi_table_classes[table_class.name] = table_class
136
137 self._mi_clear_result_tables()
138
139 def _mi_print_metadata(self):
140 tags = self._MI_BASE_TAGS + self._MI_TAGS
9463174b 141 infos = mi.get_metadata(version=self._VERSION, title=self._MI_TITLE,
a0acc08c
PP
142 description=self._MI_DESCRIPTION,
143 authors=self._MI_AUTHORS, url=self._MI_URL,
144 tags=tags,
145 table_classes=self._mi_table_classes.values())
146 print(json.dumps(infos))
147
148 def _mi_append_result_table(self, result_table):
149 if not result_table or not result_table.rows:
150 return
151
152 tc_name = result_table.table_class.name
153 self._mi_get_result_tables(tc_name).append(result_table)
154
155 def _mi_append_result_tables(self, result_tables):
156 if not result_tables:
157 return
158
159 for result_table in result_tables:
160 self._mi_append_result_table(result_table)
161
162 def _mi_clear_result_tables(self):
163 self._result_tables = {}
164
165 def _mi_get_result_tables(self, table_class_name):
166 if table_class_name not in self._result_tables:
167 self._result_tables[table_class_name] = []
168
169 return self._result_tables[table_class_name]
170
171 def _mi_print(self):
172 results = []
173
174 for result_tables in self._result_tables.values():
175 for result_table in result_tables:
176 results.append(result_table.to_native_object())
177
178 obj = {
179 'results': results,
180 }
181
182 print(json.dumps(obj))
183
184 def _create_summary_result_tables(self):
185 pass
186
bd3cd7c5 187 def _open_trace(self):
a0ea9266
AB
188 self._read_babeltrace_version()
189 if self._babeltrace_version >= self._BT_INTERSECT_VERSION:
190 traces = TraceCollection(intersect_mode=self._args.intersect_mode)
191 else:
192 if self._args.intersect_mode:
193 self._print('Warning: intersect mode not available - disabling')
194 self._print(' Use babeltrace {} or later to enable'.format(
195 self._BT_INTERSECT_VERSION))
196 self._args.intersect_mode = False
197 traces = TraceCollection()
b6d9132b 198 handles = traces.add_traces_recursive(self._args.path, 'ctf')
ced36aab 199 if handles == {}:
b6d9132b 200 self._gen_error('Failed to open ' + self._args.path, -1)
ced36aab 201 self._handles = handles
bd3cd7c5 202 self._traces = traces
dd2efe70
PP
203 self._ts_begin = traces.timestamp_begin
204 self._ts_end = traces.timestamp_end
652bc6b7 205 self._process_date_args()
ee6a5866 206 self._read_tracer_version()
b6d9132b 207 if not self._args.skip_validation:
d3014022 208 self._check_lost_events()
bd3cd7c5
JD
209
210 def _close_trace(self):
ced36aab
AB
211 for handle in self._handles.values():
212 self._traces.remove_trace(handle)
bd3cd7c5 213
ee6a5866 214 def _read_tracer_version(self):
4ac5e240 215 kernel_path = None
2dca9c55
JD
216 # remove the trailing /
217 while self._args.path.endswith('/'):
218 self._args.path = self._args.path[:-1]
4ac5e240
AB
219 for root, _, _ in os.walk(self._args.path):
220 if root.endswith('kernel'):
221 kernel_path = root
222 break
223
224 if kernel_path is None:
225 self._gen_error('Could not find kernel trace directory')
226
ee6a5866 227 try:
0349f942 228 ret, metadata = subprocess.getstatusoutput(
4ac5e240 229 'babeltrace -o ctf-metadata "%s"' % kernel_path)
ee6a5866
AB
230 except subprocess.CalledProcessError:
231 self._gen_error('Cannot run babeltrace on the trace, cannot read'
232 ' tracer version')
233
0349f942
JD
234 # fallback to reading the text metadata if babeltrace failed to
235 # output the CTF metadata
236 if ret != 0:
237 try:
238 metadata = subprocess.getoutput(
239 'cat "%s"' % os.path.join(kernel_path, 'metadata'))
240 except subprocess.CalledProcessError:
241 self._gen_error('Cannot read the metadata of the trace, cannot'
242 'extract tracer version')
243
244 major_match = re.search(r'tracer_major = "*(\d+)"*', metadata)
245 minor_match = re.search(r'tracer_minor = "*(\d+)"*', metadata)
246 patch_match = re.search(r'tracer_patchlevel = "*(\d+)"*', metadata)
ee6a5866
AB
247
248 if not major_match or not minor_match or not patch_match:
249 self._gen_error('Malformed metadata, cannot read tracer version')
250
251 self.state.tracer_version = version_utils.Version(
252 int(major_match.group(1)),
253 int(minor_match.group(1)),
254 int(patch_match.group(1)),
255 )
256
a0ea9266
AB
257 def _read_babeltrace_version(self):
258 try:
259 output = subprocess.check_output('babeltrace')
260 except subprocess.CalledProcessError:
261 self._gen_error('Could not run babeltrace to verify version')
262
263 output = output.decode(sys.stdout.encoding)
264 first_line = output.splitlines()[0]
265 version_string = first_line.split()[-1]
266
267 self._babeltrace_version = \
268 version_utils.Version.new_from_string(version_string)
269
d3014022 270 def _check_lost_events(self):
73f9d005
PP
271 msg = 'Checking the trace for lost events...'
272 self._print(msg)
273
274 if self._mi_mode and self._args.output_progress:
275 mi.print_progress(0, msg)
276
d3014022 277 try:
e0bc16fe 278 subprocess.check_output('babeltrace "%s"' % self._args.path,
d3014022
JD
279 shell=True)
280 except subprocess.CalledProcessError:
b9f05f8d
AB
281 self._gen_error('Cannot run babeltrace on the trace, cannot verify'
282 ' if events were lost during the trace recording')
a0acc08c
PP
283
284 def _pre_analysis(self):
285 pass
286
287 def _post_analysis(self):
288 if not self._mi_mode:
289 return
290
291 if self._ticks > 1:
292 self._create_summary_result_tables()
293
294 self._mi_print()
d3014022 295
73f9d005 296 def _pb_setup(self):
dd2efe70
PP
297 if self._args.no_progress:
298 return
299
300 ts_end = self._ts_end
301
302 if self._analysis_conf.end_ts is not None:
303 ts_end = self._analysis_conf.end_ts
73f9d005 304
73f9d005 305 if self._mi_mode:
dd2efe70 306 cls = progressbar.MiProgress
73f9d005 307 else:
dd2efe70
PP
308 cls = progressbar.FancyProgressBar
309
310 self._progress = cls(self._ts_begin, ts_end, self._args.path,
311 self._args.progress_use_size)
312
313 def _pb_update(self, event):
314 if self._args.no_progress:
315 return
316
317 self._progress.update(event)
73f9d005
PP
318
319 def _pb_finish(self):
dd2efe70
PP
320 if self._args.no_progress:
321 return
322
323 self._progress.finalize()
73f9d005 324
b6d9132b 325 def _run_analysis(self):
a0acc08c 326 self._pre_analysis()
73f9d005 327 self._pb_setup()
b6d9132b 328
a0ea9266
AB
329 if self._args.intersect_mode:
330 if not self._traces.has_intersection:
331 self._gen_error('Trace has no intersection. '
332 'Use --no-intersection to override')
333
bd3cd7c5 334 for event in self._traces.events:
dd2efe70 335 self._pb_update(event)
bd3cd7c5 336 self._analysis.process_event(event)
b6d9132b
AB
337 if self._analysis.ended:
338 break
47ba125c 339 self._automaton.process_event(event)
bd3cd7c5 340
73f9d005 341 self._pb_finish()
b6d9132b 342 self._analysis.end()
a0acc08c 343 self._post_analysis()
bd3cd7c5 344
3664e4b0 345 def _print_date(self, begin_ns, end_ns):
9079847d
AB
346 time_range_str = format_utils.format_time_range(
347 begin_ns, end_ns, print_date=True, gmt=self._args.gmt
348 )
349 date = 'Timerange: {}'.format(time_range_str)
350
a0acc08c 351 self._print(date)
3664e4b0 352
9079847d
AB
353 def _format_timestamp(self, timestamp):
354 return format_utils.format_timestamp(
355 timestamp, print_date=self._args.multi_day, gmt=self._args.gmt
356 )
357
dbbdd963
PP
358 def _get_uniform_freq_values(self, durations):
359 if self._args.uniform_step is not None:
360 return (self._args.uniform_min, self._args.uniform_max,
361 self._args.uniform_step)
362
363 if self._args.min is not None:
364 self._args.uniform_min = self._args.min
365 else:
366 self._args.uniform_min = min(durations)
367 if self._args.max is not None:
368 self._args.uniform_max = self._args.max
369 else:
370 self._args.uniform_max = max(durations)
371
372 # ns to µs
373 self._args.uniform_min /= 1000
374 self._args.uniform_max /= 1000
375 self._args.uniform_step = (
376 (self._args.uniform_max - self._args.uniform_min) /
377 self._args.freq_resolution
378 )
379
380 return self._args.uniform_min, self._args.uniform_max, \
650e7f57 381 self._args.uniform_step
dbbdd963 382
bd3cd7c5 383 def _validate_transform_common_args(self, args):
83ad157b
AB
384 refresh_period_ns = None
385 if args.refresh is not None:
386 try:
9079847d 387 refresh_period_ns = parse_utils.parse_duration(args.refresh)
83ad157b
AB
388 except ValueError as e:
389 self._cmdline_error(str(e))
390
b6d9132b 391 self._analysis_conf = analysis.AnalysisConfig()
83ad157b 392 self._analysis_conf.refresh_period = refresh_period_ns
43a3c04c
AB
393 self._analysis_conf.period_begin_ev_name = args.period_begin
394 self._analysis_conf.period_end_ev_name = args.period_end
05684c5e 395 self._analysis_conf.period_begin_key_fields = \
007d3fe0 396 args.period_begin_key.split(',')
05684c5e
AB
397
398 if args.period_end_key:
399 self._analysis_conf.period_end_key_fields = \
007d3fe0 400 args.period_end_key.split(',')
05684c5e
AB
401 else:
402 self._analysis_conf.period_end_key_fields = \
007d3fe0 403 self._analysis_conf.period_begin_key_fields
05684c5e
AB
404
405 if args.period_key_value:
406 self._analysis_conf.period_key_value = \
007d3fe0 407 tuple(args.period_key_value.split(','))
05684c5e 408
a621ba35
AB
409 if args.cpu:
410 self._analysis_conf.cpu_list = args.cpu.split(',')
411 self._analysis_conf.cpu_list = [int(cpu) for cpu in
412 self._analysis_conf.cpu_list]
b6d9132b 413
810e6be8
AB
414 if args.debug:
415 self._debug_mode = True
416
b6d9132b
AB
417 # convert min/max args from µs to ns, if needed
418 if hasattr(args, 'min') and args.min is not None:
419 args.min *= 1000
420 self._analysis_conf.min_duration = args.min
421 if hasattr(args, 'max') and args.max is not None:
422 args.max *= 1000
423 self._analysis_conf.max_duration = args.max
424
425 if hasattr(args, 'procname'):
47ba125c 426 if args.procname:
43b66dd6 427 self._analysis_conf.proc_list = args.procname.split(',')
28ad5ec8 428
43b66dd6
AB
429 if hasattr(args, 'tid'):
430 if args.tid:
431 self._analysis_conf.tid_list = args.tid.split(',')
432 self._analysis_conf.tid_list = [int(tid) for tid in
433 self._analysis_conf.tid_list]
f89605f0 434
1a68e04c
AB
435 if hasattr(args, 'freq'):
436 args.uniform_min = None
437 args.uniform_max = None
438 args.uniform_step = None
439
dbbdd963
PP
440 if args.freq_series:
441 # implies uniform buckets
442 args.freq_uniform = True
443
a0acc08c 444 if self._mi_mode:
1ab6b93a
PP
445 # print MI version if required
446 if args.mi_version:
447 print(mi.get_version_string())
448 sys.exit(0)
449
a0acc08c
PP
450 # print MI metadata if required
451 if args.metadata:
452 self._mi_print_metadata()
453 sys.exit(0)
454
455 # validate path argument (required at this point)
456 if not args.path:
457 self._cmdline_error('Please specify a trace path')
458
459 if type(args.path) is list:
460 args.path = args.path[0]
461
b6d9132b
AB
462 def _validate_transform_args(self, args):
463 pass
f89605f0 464
323b3fd6
PP
465 def _parse_args(self):
466 ap = argparse.ArgumentParser(description=self._DESC)
467
468 # common arguments
83ad157b
AB
469 ap.add_argument('-r', '--refresh', type=str,
470 help='Refresh period, with optional units suffix '
471 '(default units: s)')
a0acc08c
PP
472 ap.add_argument('--gmt', action='store_true',
473 help='Manipulate timestamps based on GMT instead '
474 'of local time')
73b71522 475 ap.add_argument('--skip-validation', action='store_true',
d3014022 476 help='Skip the trace validation')
bd3cd7c5
JD
477 ap.add_argument('--begin', type=str, help='start time: '
478 'hh:mm:ss[.nnnnnnnnn]')
479 ap.add_argument('--end', type=str, help='end time: '
480 'hh:mm:ss[.nnnnnnnnn]')
43a3c04c
AB
481 ap.add_argument('--period-begin', type=str,
482 help='Analysis period start marker event name')
483 ap.add_argument('--period-end', type=str,
484 help='Analysis period end marker event name '
485 '(requires --period-begin)')
05684c5e 486 ap.add_argument('--period-begin-key', type=str, default='cpu_id',
b9f05f8d
AB
487 help='Optional, list of event field names used to '
488 'match period markers (default: cpu_id)')
05684c5e
AB
489 ap.add_argument('--period-end-key', type=str,
490 help='Optional, list of event field names used to '
491 'match period marker. If none specified, use the same '
492 ' --period-begin-key')
493 ap.add_argument('--period-key-value', type=str,
494 help='Optional, define a fixed key value to which a'
495 ' period must correspond to be considered.')
a621ba35
AB
496 ap.add_argument('--cpu', type=str,
497 help='Filter the results only for this list of '
498 'CPU IDs')
a0acc08c
PP
499 ap.add_argument('--timerange', type=str, help='time range: '
500 '[begin,end]')
dd2efe70
PP
501 ap.add_argument('--progress-use-size', action='store_true',
502 help='use trace size to approximate progress')
a0ea9266
AB
503 ap.add_argument('--no-intersection', action='store_false',
504 dest='intersect_mode',
505 help='disable stream intersection mode')
323b3fd6 506 ap.add_argument('-V', '--version', action='version',
9463174b 507 version='LTTng Analyses v{}'.format(self._VERSION))
810e6be8
AB
508 ap.add_argument('--debug', action='store_true',
509 help='Enable debug mode (or set {} environment variable)'.format(
510 self._DEBUG_ENV_VAR))
dc3ccf03
AB
511 ap.add_argument('--no-color', action='store_false', dest='color',
512 help='Disable colored output')
323b3fd6 513
a0acc08c
PP
514 # MI mode-dependent arguments
515 if self._mi_mode:
1ab6b93a
PP
516 ap.add_argument('--mi-version', action='store_true',
517 help='Print MI version')
a0acc08c 518 ap.add_argument('--metadata', action='store_true',
1ab6b93a 519 help='Print analysis\' metadata')
ee39b192
PP
520 ap.add_argument('--test-compatibility', action='store_true',
521 help='Check if the provided trace is supported and exit')
b9f05f8d
AB
522 ap.add_argument('path', metavar='<path/to/trace>',
523 help='trace path', nargs='*')
73f9d005
PP
524 ap.add_argument('--output-progress', action='store_true',
525 help='Print progress indication lines')
a0acc08c
PP
526 else:
527 ap.add_argument('--no-progress', action='store_true',
528 help='Don\'t display the progress bar')
b9f05f8d
AB
529 ap.add_argument('path', metavar='<path/to/trace>',
530 help='trace path')
a0acc08c 531
b6d9132b
AB
532 # Used to add command-specific args
533 self._add_arguments(ap)
323b3fd6 534
b6d9132b 535 args = ap.parse_args()
dd2efe70
PP
536
537 if self._mi_mode:
538 args.no_progress = True
539
540 if args.output_progress:
541 args.no_progress = False
542
bd3cd7c5 543 self._validate_transform_common_args(args)
b6d9132b 544 self._validate_transform_args(args)
323b3fd6
PP
545 self._args = args
546
b6d9132b
AB
547 @staticmethod
548 def _add_proc_filter_args(ap):
549 ap.add_argument('--procname', type=str,
550 help='Filter the results only for this list of '
551 'process names')
43b66dd6
AB
552 ap.add_argument('--tid', type=str,
553 help='Filter the results only for this list of TIDs')
b6d9132b
AB
554
555 @staticmethod
556 def _add_min_max_args(ap):
557 ap.add_argument('--min', type=float,
558 help='Filter out durations shorter than min usec')
559 ap.add_argument('--max', type=float,
560 help='Filter out durations longer than max usec')
561
562 @staticmethod
563 def _add_freq_args(ap, help=None):
564 if not help:
565 help = 'Output the frequency distribution'
566
567 ap.add_argument('--freq', action='store_true', help=help)
568 ap.add_argument('--freq-resolution', type=int, default=20,
569 help='Frequency distribution resolution '
570 '(default 20)')
1a68e04c
AB
571 ap.add_argument('--freq-uniform', action='store_true',
572 help='Use a uniform resolution across distributions')
86ea0394 573 ap.add_argument('--freq-series', action='store_true',
650e7f57
AB
574 help='Consolidate frequency distribution histogram '
575 'as a single one')
b6d9132b
AB
576
577 @staticmethod
578 def _add_log_args(ap, help=None):
579 if not help:
580 help = 'Output the events in chronological order'
581
582 ap.add_argument('--log', action='store_true', help=help)
583
b9f05f8d
AB
584 @staticmethod
585 def _add_top_args(ap, help=None):
586 if not help:
587 help = 'Output the top results'
588
589 ap.add_argument('--limit', type=int, default=10,
590 help='Limit to top X (default = 10)')
591 ap.add_argument('--top', action='store_true', help=help)
592
b6d9132b
AB
593 @staticmethod
594 def _add_stats_args(ap, help=None):
595 if not help:
596 help = 'Output statistics'
597
598 ap.add_argument('--stats', action='store_true', help=help)
599
600 def _add_arguments(self, ap):
601 pass
602
652bc6b7 603 def _process_date_args(self):
9079847d
AB
604 def parse_date(date):
605 try:
606 ts = parse_utils.parse_trace_collection_date(
607 self._traces, date, self._args.gmt
608 )
609 except ValueError as e:
610 self._cmdline_error(str(e))
b6d9132b
AB
611
612 return ts
613
9079847d
AB
614 self._args.multi_day = trace_utils.is_multi_day_trace_collection(
615 self._traces
616 )
602ac199
PP
617 begin_ts = None
618 end_ts = None
619
620 if self._args.timerange:
9079847d
AB
621 try:
622 begin_ts, end_ts = (
623 parse_utils.parse_trace_collection_time_range(
624 self._traces, self._args.timerange, self._args.gmt
625 )
626 )
627 except ValueError as e:
628 self._cmdline_error(str(e))
652bc6b7 629 else:
b6d9132b 630 if self._args.begin:
9079847d 631 begin_ts = parse_date(self._args.begin)
b6d9132b 632 if self._args.end:
9079847d 633 end_ts = parse_date(self._args.end)
652bc6b7 634
93c7af7d
AB
635 # We have to check if timestamp_begin is None, which
636 # it always is in older versions of babeltrace. In
637 # that case, the test is simply skipped and an invalid
638 # --end value will cause an empty analysis
dd2efe70
PP
639 if self._ts_begin is not None and \
640 end_ts < self._ts_begin:
b6d9132b
AB
641 self._cmdline_error(
642 '--end timestamp before beginning of trace')
643
602ac199
PP
644 self._analysis_conf.begin_ts = begin_ts
645 self._analysis_conf.end_ts = end_ts
b6d9132b
AB
646
647 def _create_analysis(self):
648 notification_cbs = {
a0acc08c 649 analysis.Analysis.TICK_CB: self._analysis_tick_cb
b6d9132b
AB
650 }
651
652 self._analysis = self._ANALYSIS_CLASS(self.state, self._analysis_conf)
653 self._analysis.register_notification_cbs(notification_cbs)
93c7af7d 654
323b3fd6 655 def _create_automaton(self):
56936af2 656 self._automaton = automaton.Automaton()
6e01ed18 657 self.state = self._automaton.state
bfb81992 658
a0acc08c 659 def _analysis_tick_cb(self, **kwargs):
b6d9132b
AB
660 begin_ns = kwargs['begin_ns']
661 end_ns = kwargs['end_ns']
662
a0acc08c
PP
663 self._analysis_tick(begin_ns, end_ns)
664 self._ticks += 1
b6d9132b 665
a0acc08c 666 def _analysis_tick(self, begin_ns, end_ns):
b6d9132b 667 raise NotImplementedError()
This page took 0.058687 seconds and 5 git commands to generate.