3 # Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
5 # SPDX-License-Identifier: GPL-2.0-only
12 from typing
import Optional
, Type
, Union
, List
15 Defines an abstract interface to control LTTng tracing.
17 The various control concepts are defined by this module. You can use them with a
18 Controller to interact with a session daemon.
20 This interface is not comprehensive; it currently provides a subset of the
21 control functionality that is used by tests.
25 def _generate_random_string(length
):
28 random
.choice(string
.ascii_lowercase
+ string
.digits
) for _
in range(length
)
32 class ContextType(abc
.ABC
):
33 """Base class representing a tracing context field."""
38 class VpidContextType(ContextType
):
39 """Application's virtual process id."""
44 class VuidContextType(ContextType
):
45 """Application's virtual user id."""
50 class VgidContextType(ContextType
):
51 """Application's virtual group id."""
56 class JavaApplicationContextType(ContextType
):
57 """A java application-specific context field is a piece of state which the application provides."""
61 retriever_name
, # type: str
62 field_name
, # type: str
64 self
._retriever
_name
= retriever_name
# type: str
65 self
._field
_name
= field_name
# type: str
68 def retriever_name(self
):
70 return self
._retriever
_name
75 return self
._field
_name
79 class TracingDomain(enum
.Enum
):
82 User
= "User space tracing domain"
83 Kernel
= "Linux kernel tracing domain."
84 Log4j
= "Log4j tracing back-end."
85 JUL
= "Java Util Logging tracing back-end."
86 Python
= "Python logging module tracing back-end."
89 return "<%s.%s>" % (self
.__class
__.__name
__, self
.name
)
93 class BufferSharingPolicy(enum
.Enum
):
94 """Buffer sharing policy."""
96 PerUID
= "Per-UID buffering"
97 PerPID
= "Per-PID buffering"
100 return "<%s.%s>" % (self
.__class
__.__name
__, self
.name
)
103 class EventRule(abc
.ABC
):
104 """Event rule base class, see LTTNG-EVENT-RULE(7)."""
110 def __eq__(self
, other
):
111 # type (LogLevelRule) -> bool
112 if type(self
) != type(other
):
115 return self
.level
== other
.level
119 class LogLevel(enum
.Enum
):
124 class UserLogLevel(LogLevel
):
143 class JULLogLevel(LogLevel
):
156 class Log4jLogLevel(LogLevel
):
168 class PythonLogLevel(LogLevel
):
177 class LogLevelRuleAsSevereAs(LogLevelRule
):
178 def __init__(self
, level
):
184 # type: () -> LogLevel
188 class LogLevelRuleExactly(LogLevelRule
):
189 def __init__(self
, level
):
195 # type: () -> LogLevel
199 class TracepointEventRule(EventRule
):
202 name_pattern
=None, # type: Optional[str]
203 filter_expression
=None, # type: Optional[str]
205 self
._name
_pattern
= name_pattern
# type: Optional[str]
206 self
._filter
_expression
= filter_expression
# type: Optional[str]
208 def _equals(self
, other
):
209 # type (TracepointEventRule) -> bool
210 # Overridden by derived classes that have supplementary attributes.
213 def __eq__(self
, other
):
214 # type (TracepointEventRule) -> bool
215 if type(self
) != type(other
):
218 if self
.name_pattern
!= other
.name_pattern
:
221 if self
.filter_expression
!= other
.filter_expression
:
224 return self
._equals
(other
)
227 def name_pattern(self
):
228 # type: () -> Optional[str]
229 return self
._name
_pattern
232 def filter_expression(self
):
233 # type: () -> Optional[str]
234 return self
._filter
_expression
237 class UserTracepointEventRule(TracepointEventRule
):
240 name_pattern
=None, # type: Optional[str]
241 filter_expression
=None, # type: Optional[str]
242 log_level_rule
=None, # type: Optional[LogLevelRule]
243 name_pattern_exclusions
=None, # type: Optional[List[str]]
245 TracepointEventRule
.__init
__(self
, name_pattern
, filter_expression
)
246 self
._log
_level
_rule
= log_level_rule
# type: Optional[LogLevelRule]
247 self
._name
_pattern
_exclusions
= (
248 name_pattern_exclusions
249 ) # type: Optional[List[str]]
251 if log_level_rule
and not isinstance(log_level_rule
.level
, UserLogLevel
):
252 raise ValueError("Log level rule must use a UserLogLevel as its value")
254 def _equals(self
, other
):
255 # type (UserTracepointEventRule) -> bool
257 self
.log_level_rule
== other
.log_level_rule
258 and self
.name_pattern_exclusions
== other
.name_pattern_exclusions
262 def log_level_rule(self
):
263 # type: () -> Optional[LogLevelRule]
264 return self
._log
_level
_rule
267 def name_pattern_exclusions(self
):
268 # type: () -> Optional[List[str]]
269 return self
._name
_pattern
_exclusions
272 class Log4jTracepointEventRule(TracepointEventRule
):
275 name_pattern
=None, # type: Optional[str]
276 filter_expression
=None, # type: Optional[str]
277 log_level_rule
=None, # type: Optional[LogLevelRule]
278 name_pattern_exclusions
=None, # type: Optional[List[str]]
280 TracepointEventRule
.__init
__(self
, name_pattern
, filter_expression
)
281 self
._log
_level
_rule
= log_level_rule
# type: Optional[LogLevelRule]
282 self
._name
_pattern
_exclusions
= (
283 name_pattern_exclusions
284 ) # type: Optional[List[str]]
286 if log_level_rule
and not isinstance(log_level_rule
.level
, Log4jLogLevel
):
287 raise ValueError("Log level rule must use a Log4jLogLevel as its value")
289 def _equals(self
, other
):
290 # type (Log4jTracepointEventRule) -> bool
292 self
.log_level_rule
== other
.log_level_rule
293 and self
.name_pattern_exclusions
== other
.name_pattern_exclusions
297 def log_level_rule(self
):
298 # type: () -> Optional[LogLevelRule]
299 return self
._log
_level
_rule
302 def name_pattern_exclusions(self
):
303 # type: () -> Optional[List[str]]
304 return self
._name
_pattern
_exclusions
307 class JULTracepointEventRule(TracepointEventRule
):
310 name_pattern
=None, # type: Optional[str]
311 filter_expression
=None, # type: Optional[str]
312 log_level_rule
=None, # type: Optional[LogLevelRule]
313 name_pattern_exclusions
=None, # type: Optional[List[str]]
315 TracepointEventRule
.__init
__(self
, name_pattern
, filter_expression
)
316 self
._log
_level
_rule
= log_level_rule
# type: Optional[LogLevelRule]
317 self
._name
_pattern
_exclusions
= (
318 name_pattern_exclusions
319 ) # type: Optional[List[str]]
321 if log_level_rule
and not isinstance(log_level_rule
.level
, JULLogLevel
):
322 raise ValueError("Log level rule must use a JULLogLevel as its value")
324 def _equals(self
, other
):
325 # type (JULTracepointEventRule) -> bool
327 self
.log_level_rule
== other
.log_level_rule
328 and self
.name_pattern_exclusions
== other
.name_pattern_exclusions
332 def log_level_rule(self
):
333 # type: () -> Optional[LogLevelRule]
334 return self
._log
_level
_rule
337 def name_pattern_exclusions(self
):
338 # type: () -> Optional[List[str]]
339 return self
._name
_pattern
_exclusions
342 class PythonTracepointEventRule(TracepointEventRule
):
345 name_pattern
=None, # type: Optional[str]
346 filter_expression
=None, # type: Optional[str]
347 log_level_rule
=None, # type: Optional[LogLevelRule]
348 name_pattern_exclusions
=None, # type: Optional[List[str]]
350 TracepointEventRule
.__init
__(self
, name_pattern
, filter_expression
)
351 self
._log
_level
_rule
= log_level_rule
# type: Optional[LogLevelRule]
352 self
._name
_pattern
_exclusions
= (
353 name_pattern_exclusions
354 ) # type: Optional[List[str]]
356 if log_level_rule
and not isinstance(log_level_rule
.level
, PythonLogLevel
):
357 raise ValueError("Log level rule must use a PythonLogLevel as its value")
359 def _equals(self
, other
):
360 # type (PythonTracepointEventRule) -> bool
362 self
.log_level_rule
== other
.log_level_rule
363 and self
.name_pattern_exclusions
== other
.name_pattern_exclusions
367 def log_level_rule(self
):
368 # type: () -> Optional[LogLevelRule]
369 return self
._log
_level
_rule
372 def name_pattern_exclusions(self
):
373 # type: () -> Optional[List[str]]
374 return self
._name
_pattern
_exclusions
377 class KernelTracepointEventRule(TracepointEventRule
):
380 name_pattern
=None, # type: Optional[str]
381 filter_expression
=None, # type: Optional[str]
383 TracepointEventRule
.__init
__(**locals())
386 class Channel(abc
.ABC
):
388 A channel is an object which is responsible for a set of ring buffers. It is
389 associated to a domain and
393 def _generate_name():
395 return "channel_{random_id}".format(random_id
=_generate_random_string(8))
398 def add_context(self
, context_type
):
399 # type: (ContextType) -> None
400 raise NotImplementedError
405 # type: () -> TracingDomain
406 raise NotImplementedError
412 raise NotImplementedError
415 def add_recording_rule(self
, rule
) -> None:
416 # type: (Type[EventRule]) -> None
417 raise NotImplementedError
420 class SessionOutputLocation(abc
.ABC
):
424 class LocalSessionOutputLocation(SessionOutputLocation
):
425 def __init__(self
, trace_path
):
426 # type: (pathlib.Path)
427 self
._path
= trace_path
431 # type: () -> pathlib.Path
435 class NetworkSessionOutputLocation(SessionOutputLocation
):
436 def __init__(self
, set_url
):
438 self
._set
_url
= set_url
446 class ProcessAttributeTracker(abc
.ABC
):
448 Process attribute tracker used to filter before the evaluation of event
451 Note that this interface is currently limited as it doesn't allow changing
452 the tracking policy. For instance, it is not possible to set the tracking
453 policy back to "all" once it has transitioned to "include set".
457 class TrackingPolicy(enum
.Enum
):
459 Track all possible process attribute value of a given type (i.e. no filtering).
460 This is the default state of a process attribute tracker.
462 EXCLUDE_ALL
= "Exclude all possible process attribute values of a given type."
463 INCLUDE_SET
= "Track a set of specific process attribute values."
466 return "<%s.%s>" % (self
.__class
__.__name
__, self
.name
)
468 def __init__(self
, policy
):
469 # type: (TrackingPolicy)
470 self
._policy
= policy
473 def tracking_policy(self
):
474 # type: () -> TrackingPolicy
478 class ProcessIDProcessAttributeTracker(ProcessAttributeTracker
):
480 def track(self
, pid
):
481 # type: (int) -> None
482 raise NotImplementedError
485 def untrack(self
, pid
):
486 # type: (int) -> None
487 raise NotImplementedError
490 class VirtualProcessIDProcessAttributeTracker(ProcessAttributeTracker
):
492 def track(self
, vpid
):
493 # type: (int) -> None
494 raise NotImplementedError
497 def untrack(self
, vpid
):
498 # type: (int) -> None
499 raise NotImplementedError
502 class UserIDProcessAttributeTracker(ProcessAttributeTracker
):
504 def track(self
, uid
):
505 # type: (Union[int, str]) -> None
506 raise NotImplementedError
509 def untrack(self
, uid
):
510 # type: (Union[int, str]) -> None
511 raise NotImplementedError
514 class VirtualUserIDProcessAttributeTracker(ProcessAttributeTracker
):
516 def track(self
, vuid
):
517 # type: (Union[int, str]) -> None
518 raise NotImplementedError
521 def untrack(self
, vuid
):
522 # type: (Union[int, str]) -> None
523 raise NotImplementedError
526 class GroupIDProcessAttributeTracker(ProcessAttributeTracker
):
528 def track(self
, gid
):
529 # type: (Union[int, str]) -> None
530 raise NotImplementedError
533 def untrack(self
, gid
):
534 # type: (Union[int, str]) -> None
535 raise NotImplementedError
538 class VirtualGroupIDProcessAttributeTracker(ProcessAttributeTracker
):
540 def track(self
, vgid
):
541 # type: (Union[int, str]) -> None
542 raise NotImplementedError
545 def untrack(self
, vgid
):
546 # type: (Union[int, str]) -> None
547 raise NotImplementedError
550 class Session(abc
.ABC
):
552 def _generate_name():
554 return "session_{random_id}".format(random_id
=_generate_random_string(8))
560 raise NotImplementedError
565 # type: () -> Optional[Type[SessionOutputLocation]]
566 raise NotImplementedError
573 buffer_sharing_policy
=BufferSharingPolicy
.PerUID
,
575 # type: (TracingDomain, Optional[str], BufferSharingPolicy) -> Channel
576 """Add a channel with default attributes to the session."""
577 raise NotImplementedError
582 raise NotImplementedError
587 raise NotImplementedError
592 raise NotImplementedError
597 raise NotImplementedError
602 raise NotImplementedError
604 @abc.abstractproperty
605 def kernel_pid_process_attribute_tracker(self
):
606 # type: () -> Type[ProcessIDProcessAttributeTracker]
607 raise NotImplementedError
609 @abc.abstractproperty
610 def kernel_vpid_process_attribute_tracker(self
):
611 # type: () -> Type[VirtualProcessIDProcessAttributeTracker]
612 raise NotImplementedError
614 @abc.abstractproperty
615 def user_vpid_process_attribute_tracker(
617 ) -> Type
[VirtualProcessIDProcessAttributeTracker
]:
618 # type: () -> Type[VirtualProcessIDProcessAttributeTracker]
619 raise NotImplementedError
621 @abc.abstractproperty
622 def kernel_gid_process_attribute_tracker(self
):
623 # type: () -> Type[GroupIDProcessAttributeTracker]
624 raise NotImplementedError
626 @abc.abstractproperty
627 def kernel_vgid_process_attribute_tracker(self
):
628 # type: () -> Type[VirtualGroupIDProcessAttributeTracker]
629 raise NotImplementedError
631 @abc.abstractproperty
632 def user_vgid_process_attribute_tracker(self
):
633 # type: () -> Type[VirtualGroupIDProcessAttributeTracker]
634 raise NotImplementedError
636 @abc.abstractproperty
637 def kernel_uid_process_attribute_tracker(self
):
638 # type: () -> Type[UserIDProcessAttributeTracker]
639 raise NotImplementedError
641 @abc.abstractproperty
642 def kernel_vuid_process_attribute_tracker(self
):
643 # type: () -> Type[VirtualUserIDProcessAttributeTracker]
644 raise NotImplementedError
646 @abc.abstractproperty
647 def user_vuid_process_attribute_tracker(self
):
648 # type: () -> Type[VirtualUserIDProcessAttributeTracker]
649 raise NotImplementedError
652 class ControlException(RuntimeError):
653 """Base type for exceptions thrown by a controller."""
655 def __init__(self
, msg
):
657 super().__init
__(msg
)
660 class Controller(abc
.ABC
):
662 Interface of a top-level control interface. A control interface can be, for
663 example, the LTTng client or a wrapper around liblttng-ctl. It is used to
664 create and manage top-level objects of a session daemon instance.
668 def create_session(self
, name
=None, output
=None):
669 # type: (Optional[str], Optional[SessionOutputLocation]) -> Session
671 Create a session with an output. Don't specify an output
672 to create a session without an output.
674 raise NotImplementedError
677 def start_session_by_name(self
, name
):
678 # type: (str) -> None
680 Start a session by name.
682 raise NotImplementedError
685 def start_session_by_glob_pattern(self
, pattern
):
686 # type: (str) -> None
688 Start sessions whose name matches `pattern`, see GLOB(7).
690 raise NotImplementedError
693 def start_sessions_all(self
):
695 Start all sessions visible to the current user.
698 raise NotImplementedError
701 def stop_session_by_name(self
, name
):
702 # type: (str) -> None
704 Stop a session by name.
706 raise NotImplementedError
709 def stop_session_by_glob_pattern(self
, pattern
):
710 # type: (str) -> None
712 Stop sessions whose name matches `pattern`, see GLOB(7).
714 raise NotImplementedError
717 def stop_sessions_all(self
):
719 Stop all sessions visible to the current user.
722 raise NotImplementedError
725 def destroy_session_by_name(self
, name
):
726 # type: (str) -> None
728 Destroy a session by name.
730 raise NotImplementedError
733 def destroy_session_by_glob_pattern(self
, pattern
):
734 # type: (str) -> None
736 Destroy sessions whose name matches `pattern`, see GLOB(7).
738 raise NotImplementedError
741 def destroy_sessions_all(self
):
744 Destroy all sessions visible to the current user.
746 raise NotImplementedError
749 def list_sessions(self
):
750 # type: () -> List[Session]
752 List all sessions visible to the current user.
754 raise NotImplementedError
757 def rotate_session_by_name(self
, name
, wait
=True):
758 # type: (str, bool) -> None
762 raise NotImplementedError
765 def schedule_size_based_rotation(self
, name
, size_bytes
):
766 # type: (str, int) -> None
768 Schedule automatic size-based rotations.
770 raise NotImplementedError
773 def schedule_time_based_rotation(self
, name
, period_seconds
):
774 # type: (str, int) -> None
776 Schedule automatic time-based rotations.
778 raise NotImplementedError