Use compiler-agnostic defines to silence warning
[lttng-tools.git] / tests / utils / lttngtest / lttng.py
CommitLineData
ef945e4d
JG
1#!/usr/bin/env python3
2#
3# Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4#
5# SPDX-License-Identifier: GPL-2.0-only
6
b9780062 7
ef945e4d 8from . import lttngctl, logger, environment
ef945e4d 9import os
03f7bb03 10from typing import Callable, Optional, Type, Union, Iterator
ef945e4d
JG
11import shlex
12import subprocess
13import enum
b9780062 14import xml.etree.ElementTree
ef945e4d
JG
15
16"""
17Implementation of the lttngctl interface based on the `lttng` command line client.
18"""
19
20
21class Unsupported(lttngctl.ControlException):
ce8470c9
MJ
22 def __init__(self, msg):
23 # type: (str) -> None
ef945e4d
JG
24 super().__init__(msg)
25
26
b9780062
JG
27class InvalidMI(lttngctl.ControlException):
28 def __init__(self, msg):
29 # type: (str) -> None
30 super().__init__(msg)
31
32
03f7bb03
JG
33class ChannelNotFound(lttngctl.ControlException):
34 def __init__(self, msg):
35 # type: (str) -> None
36 super().__init__(msg)
37
38
ce8470c9
MJ
39def _get_domain_option_name(domain):
40 # type: (lttngctl.TracingDomain) -> str
03f7bb03
JG
41 return {
42 lttngctl.TracingDomain.User: "userspace",
43 lttngctl.TracingDomain.Kernel: "kernel",
44 lttngctl.TracingDomain.Log4j: "log4j",
5fd4fb34 45 lttngctl.TracingDomain.Log4j2: "log4j2",
03f7bb03
JG
46 lttngctl.TracingDomain.Python: "python",
47 lttngctl.TracingDomain.JUL: "jul",
48 }[domain]
49
50
51def _get_domain_xml_mi_name(domain):
52 # type: (lttngctl.TracingDomain) -> str
53 return {
54 lttngctl.TracingDomain.User: "UST",
55 lttngctl.TracingDomain.Kernel: "KERNEL",
56 lttngctl.TracingDomain.Log4j: "LOG4J",
5fd4fb34 57 lttngctl.TracingDomain.Log4j2: "LOG4J2",
03f7bb03
JG
58 lttngctl.TracingDomain.Python: "PYTHON",
59 lttngctl.TracingDomain.JUL: "JUL",
60 }[domain]
ef945e4d
JG
61
62
ce8470c9
MJ
63def _get_context_type_name(context):
64 # type: (lttngctl.ContextType) -> str
ef945e4d
JG
65 if isinstance(context, lttngctl.VgidContextType):
66 return "vgid"
67 elif isinstance(context, lttngctl.VuidContextType):
68 return "vuid"
69 elif isinstance(context, lttngctl.VpidContextType):
70 return "vpid"
71 elif isinstance(context, lttngctl.JavaApplicationContextType):
72 return "$app.{retriever}:{field}".format(
73 retriever=context.retriever_name, field=context.field_name
74 )
75 else:
76 raise Unsupported(
77 "Context `{context_name}` is not supported by the LTTng client".format(
78 type(context).__name__
79 )
80 )
81
82
03f7bb03
JG
83def _get_log_level_argument_name(log_level):
84 # type: (lttngctl.LogLevel) -> str
85 if isinstance(log_level, lttngctl.UserLogLevel):
86 return {
87 lttngctl.UserLogLevel.EMERGENCY: "EMER",
88 lttngctl.UserLogLevel.ALERT: "ALERT",
89 lttngctl.UserLogLevel.CRITICAL: "CRIT",
90 lttngctl.UserLogLevel.ERROR: "ERR",
91 lttngctl.UserLogLevel.WARNING: "WARNING",
92 lttngctl.UserLogLevel.NOTICE: "NOTICE",
93 lttngctl.UserLogLevel.INFO: "INFO",
94 lttngctl.UserLogLevel.DEBUG_SYSTEM: "DEBUG_SYSTEM",
95 lttngctl.UserLogLevel.DEBUG_PROGRAM: "DEBUG_PROGRAM",
96 lttngctl.UserLogLevel.DEBUG_PROCESS: "DEBUG_PROCESS",
97 lttngctl.UserLogLevel.DEBUG_MODULE: "DEBUG_MODULE",
98 lttngctl.UserLogLevel.DEBUG_UNIT: "DEBUG_UNIT",
99 lttngctl.UserLogLevel.DEBUG_FUNCTION: "DEBUG_FUNCTION",
100 lttngctl.UserLogLevel.DEBUG_LINE: "DEBUG_LINE",
101 lttngctl.UserLogLevel.DEBUG: "DEBUG",
102 }[log_level]
103 elif isinstance(log_level, lttngctl.JULLogLevel):
104 return {
105 lttngctl.JULLogLevel.OFF: "OFF",
106 lttngctl.JULLogLevel.SEVERE: "SEVERE",
107 lttngctl.JULLogLevel.WARNING: "WARNING",
108 lttngctl.JULLogLevel.INFO: "INFO",
109 lttngctl.JULLogLevel.CONFIG: "CONFIG",
110 lttngctl.JULLogLevel.FINE: "FINE",
111 lttngctl.JULLogLevel.FINER: "FINER",
112 lttngctl.JULLogLevel.FINEST: "FINEST",
113 lttngctl.JULLogLevel.ALL: "ALL",
114 }[log_level]
115 elif isinstance(log_level, lttngctl.Log4jLogLevel):
116 return {
117 lttngctl.Log4jLogLevel.OFF: "OFF",
118 lttngctl.Log4jLogLevel.FATAL: "FATAL",
119 lttngctl.Log4jLogLevel.ERROR: "ERROR",
120 lttngctl.Log4jLogLevel.WARN: "WARN",
121 lttngctl.Log4jLogLevel.INFO: "INFO",
122 lttngctl.Log4jLogLevel.DEBUG: "DEBUG",
123 lttngctl.Log4jLogLevel.TRACE: "TRACE",
124 lttngctl.Log4jLogLevel.ALL: "ALL",
125 }[log_level]
5fd4fb34
MJ
126 elif isinstance(log_level, lttngctl.Log4j2LogLevel):
127 return {
128 lttngctl.Log4j2LogLevel.OFF: "OFF",
129 lttngctl.Log4j2LogLevel.FATAL: "FATAL",
130 lttngctl.Log4j2LogLevel.ERROR: "ERROR",
131 lttngctl.Log4j2LogLevel.WARN: "WARN",
132 lttngctl.Log4j2LogLevel.INFO: "INFO",
133 lttngctl.Log4j2LogLevel.DEBUG: "DEBUG",
134 lttngctl.Log4j2LogLevel.TRACE: "TRACE",
135 lttngctl.Log4j2LogLevel.ALL: "ALL",
136 }[log_level]
03f7bb03
JG
137 elif isinstance(log_level, lttngctl.PythonLogLevel):
138 return {
139 lttngctl.PythonLogLevel.CRITICAL: "CRITICAL",
140 lttngctl.PythonLogLevel.ERROR: "ERROR",
141 lttngctl.PythonLogLevel.WARNING: "WARNING",
142 lttngctl.PythonLogLevel.INFO: "INFO",
143 lttngctl.PythonLogLevel.DEBUG: "DEBUG",
144 lttngctl.PythonLogLevel.NOTSET: "NOTSET",
145 }[log_level]
146
147 raise TypeError("Unknown log level type")
148
149
150def _get_log_level_from_mi_log_level_name(mi_log_level_name):
151 # type: (str) -> lttngctl.LogLevel
152 return {
153 "TRACE_EMERG": lttngctl.UserLogLevel.EMERGENCY,
154 "TRACE_ALERT": lttngctl.UserLogLevel.ALERT,
155 "TRACE_CRIT": lttngctl.UserLogLevel.CRITICAL,
156 "TRACE_ERR": lttngctl.UserLogLevel.ERROR,
157 "TRACE_WARNING": lttngctl.UserLogLevel.WARNING,
158 "TRACE_NOTICE": lttngctl.UserLogLevel.NOTICE,
159 "TRACE_INFO": lttngctl.UserLogLevel.INFO,
160 "TRACE_DEBUG_SYSTEM": lttngctl.UserLogLevel.DEBUG_SYSTEM,
161 "TRACE_DEBUG_PROGRAM": lttngctl.UserLogLevel.DEBUG_PROGRAM,
162 "TRACE_DEBUG_PROCESS": lttngctl.UserLogLevel.DEBUG_PROCESS,
163 "TRACE_DEBUG_MODULE": lttngctl.UserLogLevel.DEBUG_MODULE,
164 "TRACE_DEBUG_UNIT": lttngctl.UserLogLevel.DEBUG_UNIT,
165 "TRACE_DEBUG_FUNCTION": lttngctl.UserLogLevel.DEBUG_FUNCTION,
166 "TRACE_DEBUG_LINE": lttngctl.UserLogLevel.DEBUG_LINE,
167 "TRACE_DEBUG": lttngctl.UserLogLevel.DEBUG,
168 "JUL_OFF": lttngctl.JULLogLevel.OFF,
169 "JUL_SEVERE": lttngctl.JULLogLevel.SEVERE,
170 "JUL_WARNING": lttngctl.JULLogLevel.WARNING,
171 "JUL_INFO": lttngctl.JULLogLevel.INFO,
172 "JUL_CONFIG": lttngctl.JULLogLevel.CONFIG,
173 "JUL_FINE": lttngctl.JULLogLevel.FINE,
174 "JUL_FINER": lttngctl.JULLogLevel.FINER,
175 "JUL_FINEST": lttngctl.JULLogLevel.FINEST,
176 "JUL_ALL": lttngctl.JULLogLevel.ALL,
177 "LOG4J_OFF": lttngctl.Log4jLogLevel.OFF,
178 "LOG4J_FATAL": lttngctl.Log4jLogLevel.FATAL,
179 "LOG4J_ERROR": lttngctl.Log4jLogLevel.ERROR,
180 "LOG4J_WARN": lttngctl.Log4jLogLevel.WARN,
181 "LOG4J_INFO": lttngctl.Log4jLogLevel.INFO,
182 "LOG4J_DEBUG": lttngctl.Log4jLogLevel.DEBUG,
183 "LOG4J_TRACE": lttngctl.Log4jLogLevel.TRACE,
184 "LOG4J_ALL": lttngctl.Log4jLogLevel.ALL,
5fd4fb34
MJ
185 "LOG4J2_OFF": lttngctl.Log4j2LogLevel.OFF,
186 "LOG4J2_FATAL": lttngctl.Log4j2LogLevel.FATAL,
187 "LOG4J2_ERROR": lttngctl.Log4j2LogLevel.ERROR,
188 "LOG4J2_WARN": lttngctl.Log4j2LogLevel.WARN,
189 "LOG4J2_INFO": lttngctl.Log4j2LogLevel.INFO,
190 "LOG4J2_DEBUG": lttngctl.Log4j2LogLevel.DEBUG,
191 "LOG4J2_TRACE": lttngctl.Log4j2LogLevel.TRACE,
192 "LOG4J2_ALL": lttngctl.Log4j2LogLevel.ALL,
03f7bb03
JG
193 "PYTHON_CRITICAL": lttngctl.PythonLogLevel.CRITICAL,
194 "PYTHON_ERROR": lttngctl.PythonLogLevel.ERROR,
195 "PYTHON_WARNING": lttngctl.PythonLogLevel.WARNING,
196 "PYTHON_INFO": lttngctl.PythonLogLevel.INFO,
197 "PYTHON_DEBUG": lttngctl.PythonLogLevel.DEBUG,
198 "PYTHON_NOTSET": lttngctl.PythonLogLevel.NOTSET,
199 }[mi_log_level_name]
200
201
202def _get_tracepoint_event_rule_class_from_domain_type(domain_type):
5fd4fb34 203 # type: (lttngctl.TracingDomain) -> Type[lttngctl.UserTracepointEventRule] | Type[lttngctl.Log4jTracepointEventRule] | Type[lttngctl.Log4j2TracepointEventRule] | Type[lttngctl.JULTracepointEventRule] | Type[lttngctl.PythonTracepointEventRule] | Type[lttngctl.KernelTracepointEventRule]
03f7bb03
JG
204 return {
205 lttngctl.TracingDomain.User: lttngctl.UserTracepointEventRule,
206 lttngctl.TracingDomain.JUL: lttngctl.JULTracepointEventRule,
207 lttngctl.TracingDomain.Log4j: lttngctl.Log4jTracepointEventRule,
5fd4fb34 208 lttngctl.TracingDomain.Log4j2: lttngctl.Log4j2TracepointEventRule,
03f7bb03
JG
209 lttngctl.TracingDomain.Python: lttngctl.PythonTracepointEventRule,
210 lttngctl.TracingDomain.Kernel: lttngctl.KernelTracepointEventRule,
211 }[domain_type]
212
213
ef945e4d
JG
214class _Channel(lttngctl.Channel):
215 def __init__(
216 self,
ce8470c9
MJ
217 client, # type: LTTngClient
218 name, # type: str
219 domain, # type: lttngctl.TracingDomain
220 session, # type: _Session
ef945e4d 221 ):
ce8470c9
MJ
222 self._client = client # type: LTTngClient
223 self._name = name # type: str
224 self._domain = domain # type: lttngctl.TracingDomain
225 self._session = session # type: _Session
ef945e4d 226
ce8470c9
MJ
227 def add_context(self, context_type):
228 # type: (lttngctl.ContextType) -> None
ef945e4d
JG
229 domain_option_name = _get_domain_option_name(self.domain)
230 context_type_name = _get_context_type_name(context_type)
231 self._client._run_cmd(
b9780062 232 "add-context --{domain_option_name} --channel '{channel_name}' --type {context_type_name}".format(
ef945e4d 233 domain_option_name=domain_option_name,
b9780062 234 channel_name=self.name,
ef945e4d
JG
235 context_type_name=context_type_name,
236 )
237 )
238
ce8470c9
MJ
239 def add_recording_rule(self, rule):
240 # type: (Type[lttngctl.EventRule]) -> None
ef945e4d
JG
241 client_args = (
242 "enable-event --session {session_name} --channel {channel_name}".format(
243 session_name=self._session.name, channel_name=self.name
244 )
245 )
246 if isinstance(rule, lttngctl.TracepointEventRule):
247 domain_option_name = (
248 "userspace"
249 if isinstance(rule, lttngctl.UserTracepointEventRule)
250 else "kernel"
251 )
252 client_args = client_args + " --{domain_option_name}".format(
253 domain_option_name=domain_option_name
254 )
255
256 if rule.name_pattern:
257 client_args = client_args + " " + rule.name_pattern
258 else:
259 client_args = client_args + " --all"
260
261 if rule.filter_expression:
262 client_args = client_args + " " + rule.filter_expression
263
264 if rule.log_level_rule:
265 if isinstance(rule.log_level_rule, lttngctl.LogLevelRuleAsSevereAs):
266 client_args = client_args + " --loglevel {log_level}".format(
03f7bb03
JG
267 log_level=_get_log_level_argument_name(
268 rule.log_level_rule.level
269 )
ef945e4d
JG
270 )
271 elif isinstance(rule.log_level_rule, lttngctl.LogLevelRuleExactly):
272 client_args = client_args + " --loglevel-only {log_level}".format(
03f7bb03
JG
273 log_level=_get_log_level_argument_name(
274 rule.log_level_rule.level
275 )
ef945e4d
JG
276 )
277 else:
278 raise Unsupported(
279 "Unsupported log level rule type `{log_level_rule_type}`".format(
280 log_level_rule_type=type(rule.log_level_rule).__name__
281 )
282 )
283
284 if rule.name_pattern_exclusions:
285 client_args = client_args + " --exclude "
286 for idx, pattern in enumerate(rule.name_pattern_exclusions):
287 if idx != 0:
288 client_args = client_args + ","
289 client_args = client_args + pattern
290 else:
291 raise Unsupported(
292 "event rule type `{event_rule_type}` is unsupported by LTTng client".format(
293 event_rule_type=type(rule).__name__
294 )
295 )
296
297 self._client._run_cmd(client_args)
298
299 @property
ce8470c9
MJ
300 def name(self):
301 # type: () -> str
ef945e4d
JG
302 return self._name
303
304 @property
ce8470c9
MJ
305 def domain(self):
306 # type: () -> lttngctl.TracingDomain
ef945e4d
JG
307 return self._domain
308
03f7bb03
JG
309 @property
310 def recording_rules(self):
311 # type: () -> Iterator[lttngctl.EventRule]
312 list_session_xml = self._client._run_cmd(
313 "list '{session_name}'".format(session_name=self._session.name),
314 LTTngClient.CommandOutputFormat.MI_XML,
315 )
316
317 root = xml.etree.ElementTree.fromstring(list_session_xml)
318 command_output = LTTngClient._mi_get_in_element(root, "output")
319 sessions = LTTngClient._mi_get_in_element(command_output, "sessions")
320
321 # The channel's session is supposed to be the only session returned by the command
322 if len(sessions) != 1:
323 raise InvalidMI(
324 "Only one session expected when listing with an explicit session name"
325 )
326 session = sessions[0]
327
328 # Look for the channel's domain
329 target_domain = None
330 target_domain_mi_name = _get_domain_xml_mi_name(self.domain)
331 for domain in LTTngClient._mi_get_in_element(session, "domains"):
332 if (
333 LTTngClient._mi_get_in_element(domain, "type").text
334 == target_domain_mi_name
335 ):
336 target_domain = domain
337
338 if target_domain is None:
339 raise ChannelNotFound(
340 "Failed to find channel `{channel_name}`: no channel in target domain".format(
341 channel_name=self.name
342 )
343 )
344
345 target_channel = None
346 for channel in LTTngClient._mi_get_in_element(target_domain, "channels"):
347 if LTTngClient._mi_get_in_element(channel, "name").text == self.name:
348 target_channel = channel
349 break
350
351 if target_channel is None:
352 raise ChannelNotFound(
353 "Failed to find channel `{channel_name}`: no such channel in target domain".format(
354 channel_name=self.name
355 )
356 )
357
358 tracepoint_event_rule_class = None
359
360 for event in LTTngClient._mi_get_in_element(target_channel, "events"):
361 # Note that the "enabled" property is ignored as it is not exposed by
362 # the EventRule interface.
363 pattern = LTTngClient._mi_get_in_element(event, "name").text
364 type = LTTngClient._mi_get_in_element(event, "type").text
365
366 filter_expression = None
367 filter_expression_element = LTTngClient._mi_find_in_element(
368 event, "filter_expression"
369 )
370 if filter_expression_element:
371 filter_expression = filter_expression_element.text
372
373 exclusions = []
374 for exclusion in LTTngClient._mi_get_in_element(event, "exclusions"):
375 exclusions.append(exclusion.text)
376
377 exclusions = exclusions if len(exclusions) > 0 else None
378
379 if type != "TRACEPOINT":
380 raise Unsupported(
381 "Non-tracepoint event rules are not supported by this Controller implementation"
382 )
383
384 tracepoint_event_rule_class = (
385 _get_tracepoint_event_rule_class_from_domain_type(self.domain)
386 )
387 event_rule = None
388 if self.domain != lttngctl.TracingDomain.Kernel:
389 log_level_element = LTTngClient._mi_find_in_element(event, "loglevel")
390 log_level_type_element = LTTngClient._mi_find_in_element(
391 event, "loglevel_type"
392 )
393
394 log_level_rule = None
395 if log_level_element is not None and log_level_type_element is not None:
396 if log_level_element.text is None:
397 raise InvalidMI("`loglevel` element of event rule has no text")
398
399 if log_level_type_element.text == "RANGE":
400 log_level_rule = lttngctl.LogLevelRuleAsSevereAs(
401 _get_log_level_from_mi_log_level_name(
402 log_level_element.text
403 )
404 )
405 elif log_level_type_element.text == "SINGLE":
406 log_level_rule = lttngctl.LogLevelRuleExactly(
407 _get_log_level_from_mi_log_level_name(
408 log_level_element.text
409 )
410 )
411
412 yield tracepoint_event_rule_class(
413 pattern, filter_expression, log_level_rule, exclusions
414 )
415 else:
416 yield tracepoint_event_rule_class(pattern, filter_expression)
417
ef945e4d 418
544d8425 419@enum.unique
ef945e4d 420class _ProcessAttribute(enum.Enum):
544d8425
MJ
421 PID = "Process ID"
422 VPID = "Virtual Process ID"
423 UID = "User ID"
424 VUID = "Virtual User ID"
425 GID = "Group ID"
426 VGID = "Virtual Group ID"
427
428 def __repr__(self):
429 return "<%s.%s>" % (self.__class__.__name__, self.name)
ef945e4d
JG
430
431
ce8470c9
MJ
432def _get_process_attribute_option_name(attribute):
433 # type: (_ProcessAttribute) -> str
ef945e4d
JG
434 return {
435 _ProcessAttribute.PID: "pid",
436 _ProcessAttribute.VPID: "vpid",
437 _ProcessAttribute.UID: "uid",
438 _ProcessAttribute.VUID: "vuid",
439 _ProcessAttribute.GID: "gid",
440 _ProcessAttribute.VGID: "vgid",
441 }[attribute]
442
443
444class _ProcessAttributeTracker(lttngctl.ProcessAttributeTracker):
445 def __init__(
446 self,
ce8470c9
MJ
447 client, # type: LTTngClient
448 attribute, # type: _ProcessAttribute
449 domain, # type: lttngctl.TracingDomain
450 session, # type: _Session
ef945e4d 451 ):
ce8470c9
MJ
452 self._client = client # type: LTTngClient
453 self._tracked_attribute = attribute # type: _ProcessAttribute
454 self._domain = domain # type: lttngctl.TracingDomain
455 self._session = session # type: _Session
ef945e4d 456 if attribute == _ProcessAttribute.PID or attribute == _ProcessAttribute.VPID:
ce8470c9 457 self._allowed_value_types = [int, str] # type: list[type]
ef945e4d 458 else:
ce8470c9 459 self._allowed_value_types = [int] # type: list[type]
ef945e4d 460
ce8470c9
MJ
461 def _call_client(self, cmd_name, value):
462 # type: (str, Union[int, str]) -> None
ef945e4d
JG
463 if type(value) not in self._allowed_value_types:
464 raise TypeError(
465 "Value of type `{value_type}` is not allowed for process attribute {attribute_name}".format(
466 value_type=type(value).__name__,
467 attribute_name=self._tracked_attribute.name,
468 )
469 )
470
471 process_attribute_option_name = _get_process_attribute_option_name(
472 self._tracked_attribute
473 )
474 domain_name = _get_domain_option_name(self._domain)
475 self._client._run_cmd(
b9780062 476 "{cmd_name} --session '{session_name}' --{domain_name} --{tracked_attribute_name} {value}".format(
ef945e4d
JG
477 cmd_name=cmd_name,
478 session_name=self._session.name,
479 domain_name=domain_name,
480 tracked_attribute_name=process_attribute_option_name,
481 value=value,
482 )
483 )
484
ce8470c9
MJ
485 def track(self, value):
486 # type: (Union[int, str]) -> None
ef945e4d
JG
487 self._call_client("track", value)
488
ce8470c9
MJ
489 def untrack(self, value):
490 # type: (Union[int, str]) -> None
ef945e4d
JG
491 self._call_client("untrack", value)
492
493
494class _Session(lttngctl.Session):
495 def __init__(
496 self,
ce8470c9
MJ
497 client, # type: LTTngClient
498 name, # type: str
499 output, # type: Optional[lttngctl.SessionOutputLocation]
ef945e4d 500 ):
ce8470c9
MJ
501 self._client = client # type: LTTngClient
502 self._name = name # type: str
503 self._output = output # type: Optional[lttngctl.SessionOutputLocation]
ef945e4d
JG
504
505 @property
ce8470c9
MJ
506 def name(self):
507 # type: () -> str
ef945e4d
JG
508 return self._name
509
a631186c
JG
510 def add_channel(
511 self,
512 domain,
513 channel_name=None,
514 buffer_sharing_policy=lttngctl.BufferSharingPolicy.PerUID,
515 ):
516 # type: (lttngctl.TracingDomain, Optional[str], lttngctl.BufferSharingPolicy) -> lttngctl.Channel
ef945e4d
JG
517 channel_name = lttngctl.Channel._generate_name()
518 domain_option_name = _get_domain_option_name(domain)
519 self._client._run_cmd(
a631186c 520 "enable-channel --session '{session_name}' --{domain_name} '{channel_name}' {buffer_sharing_policy}".format(
b9780062
JG
521 session_name=self.name,
522 domain_name=domain_option_name,
523 channel_name=channel_name,
6a871bbe
KS
524 buffer_sharing_policy=(
525 "--buffers-uid"
526 if buffer_sharing_policy == lttngctl.BufferSharingPolicy.PerUID
527 else "--buffers-pid"
528 ),
ef945e4d
JG
529 )
530 )
531 return _Channel(self._client, channel_name, domain, self)
532
ce8470c9
MJ
533 def add_context(self, context_type):
534 # type: (lttngctl.ContextType) -> None
ef945e4d
JG
535 pass
536
537 @property
ce8470c9
MJ
538 def output(self):
539 # type: () -> "Optional[Type[lttngctl.SessionOutputLocation]]"
540 return self._output # type: ignore
ef945e4d 541
ce8470c9
MJ
542 def start(self):
543 # type: () -> None
b9780062 544 self._client._run_cmd("start '{session_name}'".format(session_name=self.name))
ef945e4d 545
ce8470c9
MJ
546 def stop(self):
547 # type: () -> None
b9780062 548 self._client._run_cmd("stop '{session_name}'".format(session_name=self.name))
ef945e4d 549
4bd88205
KS
550 def clear(self):
551 # type: () -> None
552 self._client._run_cmd("clear '{session_name}'".format(session_name=self.name))
553
ce8470c9
MJ
554 def destroy(self):
555 # type: () -> None
b9780062
JG
556 self._client._run_cmd("destroy '{session_name}'".format(session_name=self.name))
557
a8cac44b
JG
558 def rotate(self, wait=True):
559 # type: (bool) -> None
560 self._client.rotate_session_by_name(self.name, wait)
561
b9780062
JG
562 @property
563 def is_active(self):
564 # type: () -> bool
565 list_session_xml = self._client._run_cmd(
566 "list '{session_name}'".format(session_name=self.name),
567 LTTngClient.CommandOutputFormat.MI_XML,
568 )
569
570 root = xml.etree.ElementTree.fromstring(list_session_xml)
03f7bb03
JG
571 command_output = LTTngClient._mi_get_in_element(root, "output")
572 sessions = LTTngClient._mi_get_in_element(command_output, "sessions")
573 session_mi = LTTngClient._mi_get_in_element(sessions, "session")
b9780062 574
03f7bb03 575 enabled_text = LTTngClient._mi_get_in_element(session_mi, "enabled").text
b9780062
JG
576 if enabled_text not in ["true", "false"]:
577 raise InvalidMI(
578 "Expected boolean value in element '{}': value='{}'".format(
579 session_mi.tag, enabled_text
580 )
581 )
582
583 return enabled_text == "true"
ef945e4d
JG
584
585 @property
ce8470c9
MJ
586 def kernel_pid_process_attribute_tracker(self):
587 # type: () -> Type[lttngctl.ProcessIDProcessAttributeTracker]
ef945e4d
JG
588 return _ProcessAttributeTracker(self._client, _ProcessAttribute.PID, lttngctl.TracingDomain.Kernel, self) # type: ignore
589
590 @property
ce8470c9
MJ
591 def kernel_vpid_process_attribute_tracker(self):
592 # type: () -> Type[lttngctl.VirtualProcessIDProcessAttributeTracker]
ef945e4d
JG
593 return _ProcessAttributeTracker(self._client, _ProcessAttribute.VPID, lttngctl.TracingDomain.Kernel, self) # type: ignore
594
595 @property
ce8470c9
MJ
596 def user_vpid_process_attribute_tracker(self):
597 # type: () -> Type[lttngctl.VirtualProcessIDProcessAttributeTracker]
ef945e4d
JG
598 return _ProcessAttributeTracker(self._client, _ProcessAttribute.VPID, lttngctl.TracingDomain.User, self) # type: ignore
599
600 @property
ce8470c9
MJ
601 def kernel_gid_process_attribute_tracker(self):
602 # type: () -> Type[lttngctl.GroupIDProcessAttributeTracker]
ef945e4d
JG
603 return _ProcessAttributeTracker(self._client, _ProcessAttribute.GID, lttngctl.TracingDomain.Kernel, self) # type: ignore
604
605 @property
ce8470c9
MJ
606 def kernel_vgid_process_attribute_tracker(self):
607 # type: () -> Type[lttngctl.VirtualGroupIDProcessAttributeTracker]
ef945e4d
JG
608 return _ProcessAttributeTracker(self._client, _ProcessAttribute.VGID, lttngctl.TracingDomain.Kernel, self) # type: ignore
609
610 @property
ce8470c9
MJ
611 def user_vgid_process_attribute_tracker(self):
612 # type: () -> Type[lttngctl.VirtualGroupIDProcessAttributeTracker]
ef945e4d
JG
613 return _ProcessAttributeTracker(self._client, _ProcessAttribute.VGID, lttngctl.TracingDomain.User, self) # type: ignore
614
615 @property
ce8470c9
MJ
616 def kernel_uid_process_attribute_tracker(self):
617 # type: () -> Type[lttngctl.UserIDProcessAttributeTracker]
ef945e4d
JG
618 return _ProcessAttributeTracker(self._client, _ProcessAttribute.UID, lttngctl.TracingDomain.Kernel, self) # type: ignore
619
620 @property
ce8470c9
MJ
621 def kernel_vuid_process_attribute_tracker(self):
622 # type: () -> Type[lttngctl.VirtualUserIDProcessAttributeTracker]
ef945e4d
JG
623 return _ProcessAttributeTracker(self._client, _ProcessAttribute.VUID, lttngctl.TracingDomain.Kernel, self) # type: ignore
624
625 @property
ce8470c9
MJ
626 def user_vuid_process_attribute_tracker(self):
627 # type: () -> Type[lttngctl.VirtualUserIDProcessAttributeTracker]
ef945e4d
JG
628 return _ProcessAttributeTracker(self._client, _ProcessAttribute.VUID, lttngctl.TracingDomain.User, self) # type: ignore
629
630
631class LTTngClientError(lttngctl.ControlException):
ce8470c9
MJ
632 def __init__(
633 self,
634 command_args, # type: str
635 error_output, # type: str
636 ):
637 self._command_args = command_args # type: str
638 self._output = error_output # type: str
ef945e4d
JG
639
640
641class LTTngClient(logger._Logger, lttngctl.Controller):
642 """
643 Implementation of a LTTngCtl Controller that uses the `lttng` client as a back-end.
644 """
645
b9780062
JG
646 class CommandOutputFormat(enum.Enum):
647 MI_XML = 0
648 HUMAN = 1
649
650 _MI_NS = "{https://lttng.org/xml/ns/lttng-mi}"
651
ef945e4d
JG
652 def __init__(
653 self,
ce8470c9
MJ
654 test_environment, # type: environment._Environment
655 log, # type: Optional[Callable[[str], None]]
ef945e4d
JG
656 ):
657 logger._Logger.__init__(self, log)
ce8470c9 658 self._environment = test_environment # type: environment._Environment
ef945e4d 659
b9780062
JG
660 @staticmethod
661 def _namespaced_mi_element(property):
662 # type: (str) -> str
663 return LTTngClient._MI_NS + property
664
665 def _run_cmd(self, command_args, output_format=CommandOutputFormat.MI_XML):
666 # type: (str, CommandOutputFormat) -> str
ef945e4d
JG
667 """
668 Invoke the `lttng` client with a set of arguments. The command is
669 executed in the context of the client's test environment.
670 """
ce8470c9 671 args = [str(self._environment.lttng_client_path)] # type: list[str]
b9780062
JG
672 if output_format == LTTngClient.CommandOutputFormat.MI_XML:
673 args.extend(["--mi", "xml"])
674
ef945e4d
JG
675 args.extend(shlex.split(command_args))
676
677 self._log("lttng {command_args}".format(command_args=command_args))
678
ce8470c9 679 client_env = os.environ.copy() # type: dict[str, str]
ef945e4d
JG
680 client_env["LTTNG_HOME"] = str(self._environment.lttng_home_location)
681
682 process = subprocess.Popen(
683 args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=client_env
684 )
685
686 out = process.communicate()[0]
687
688 if process.returncode != 0:
689 decoded_output = out.decode("utf-8")
690 for error_line in decoded_output.splitlines():
691 self._log(error_line)
03f7bb03 692
ef945e4d 693 raise LTTngClientError(command_args, decoded_output)
b9780062
JG
694 else:
695 return out.decode("utf-8")
ef945e4d 696
45ce5eed
KS
697 def create_session(self, name=None, output=None, live=False):
698 # type: (Optional[str], Optional[lttngctl.SessionOutputLocation], bool) -> lttngctl.Session
ef945e4d
JG
699 name = name if name else lttngctl.Session._generate_name()
700
701 if isinstance(output, lttngctl.LocalSessionOutputLocation):
30536180 702 output_option = "--output '{output_path}'".format(output_path=output.path)
45ce5eed
KS
703 elif isinstance(output, lttngctl.NetworkSessionOutputLocation):
704 output_option = "--set-url '{}'".format(output.url)
ef945e4d
JG
705 elif output is None:
706 output_option = "--no-output"
707 else:
708 raise TypeError("LTTngClient only supports local or no output")
709
710 self._run_cmd(
45ce5eed
KS
711 "create '{session_name}' {output_option} {live_option}".format(
712 session_name=name,
713 output_option=output_option,
714 live_option="--live" if live else "",
ef945e4d
JG
715 )
716 )
717 return _Session(self, name, output)
b9780062
JG
718
719 def start_session_by_name(self, name):
720 # type: (str) -> None
721 self._run_cmd("start '{session_name}'".format(session_name=name))
722
723 def start_session_by_glob_pattern(self, pattern):
724 # type: (str) -> None
725 self._run_cmd("start --glob '{pattern}'".format(pattern=pattern))
726
727 def start_sessions_all(self):
728 # type: () -> None
729 self._run_cmd("start --all")
730
731 def stop_session_by_name(self, name):
732 # type: (str) -> None
733 self._run_cmd("stop '{session_name}'".format(session_name=name))
734
735 def stop_session_by_glob_pattern(self, pattern):
736 # type: (str) -> None
737 self._run_cmd("stop --glob '{pattern}'".format(pattern=pattern))
738
739 def stop_sessions_all(self):
740 # type: () -> None
741 self._run_cmd("stop --all")
742
743 def destroy_session_by_name(self, name):
744 # type: (str) -> None
745 self._run_cmd("destroy '{session_name}'".format(session_name=name))
746
747 def destroy_session_by_glob_pattern(self, pattern):
748 # type: (str) -> None
749 self._run_cmd("destroy --glob '{pattern}'".format(pattern=pattern))
750
751 def destroy_sessions_all(self):
752 # type: () -> None
753 self._run_cmd("destroy --all")
754
a8cac44b
JG
755 def rotate_session_by_name(self, name, wait=True):
756 self._run_cmd(
757 "rotate '{session_name}' {wait_option}".format(
758 session_name=name, wait_option="-n" if wait is False else ""
759 )
760 )
761
762 def schedule_size_based_rotation(self, name, size_bytes):
763 # type (str, int) -> None
764 self._run_cmd(
765 "enable-rotation --session '{session_name}' --size {size}".format(
766 session_name=name, size=size_bytes
767 )
768 )
769
770 def schedule_time_based_rotation(self, name, period_seconds):
771 # type (str, int) -> None
772 self._run_cmd(
773 "enable-rotation --session '{session_name}' --timer {period_seconds}s".format(
774 session_name=name, period_seconds=period_seconds
775 )
776 )
777
b9780062
JG
778 @staticmethod
779 def _mi_find_in_element(element, sub_element_name):
03f7bb03
JG
780 # type: (xml.etree.ElementTree.Element, str) -> Optional[xml.etree.ElementTree.Element]
781 return element.find(LTTngClient._namespaced_mi_element(sub_element_name))
782
783 @staticmethod
784 def _mi_get_in_element(element, sub_element_name):
b9780062 785 # type: (xml.etree.ElementTree.Element, str) -> xml.etree.ElementTree.Element
03f7bb03 786 result = LTTngClient._mi_find_in_element(element, sub_element_name)
b9780062
JG
787 if result is None:
788 raise InvalidMI(
789 "Failed to find element '{}' within command MI element '{}'".format(
790 element.tag, sub_element_name
791 )
792 )
793
794 return result
795
796 def list_sessions(self):
797 # type () -> List[Session]
798 list_sessions_xml = self._run_cmd(
799 "list", LTTngClient.CommandOutputFormat.MI_XML
800 )
801
802 root = xml.etree.ElementTree.fromstring(list_sessions_xml)
03f7bb03
JG
803 command_output = self._mi_get_in_element(root, "output")
804 sessions = self._mi_get_in_element(command_output, "sessions")
b9780062
JG
805
806 ctl_sessions = [] # type: list[lttngctl.Session]
807
808 for session_mi in sessions:
03f7bb03
JG
809 name = self._mi_get_in_element(session_mi, "name").text
810 path = self._mi_get_in_element(session_mi, "path").text
b9780062
JG
811
812 if name is None:
813 raise InvalidMI(
814 "Invalid empty 'name' element in '{}'".format(session_mi.tag)
815 )
816 if path is None:
817 raise InvalidMI(
818 "Invalid empty 'path' element in '{}'".format(session_mi.tag)
819 )
820 if not path.startswith("/"):
821 raise Unsupported(
822 "{} does not support non-local session outputs".format(type(self))
823 )
824
825 ctl_sessions.append(
826 _Session(self, name, lttngctl.LocalSessionOutputLocation(path))
827 )
828
829 return ctl_sessions
This page took 0.071073 seconds and 4 git commands to generate.