diff --git a/ros2trace/ros2trace/command/trace.py b/ros2trace/ros2trace/command/trace.py index 798770b4..5e30f21c 100644 --- a/ros2trace/ros2trace/command/trace.py +++ b/ros2trace/ros2trace/command/trace.py @@ -32,7 +32,7 @@ def main(self, *, parser, args): args.path, args.events_ust, args.events_kernel, - args.context_names, + args.context_fields, args.list, ) fini( diff --git a/test_tracetools_launch/test/test_tracetools_launch/test_trace_action.py b/test_tracetools_launch/test/test_tracetools_launch/test_trace_action.py index e6502ac8..964990a0 100644 --- a/test_tracetools_launch/test/test_tracetools_launch/test_trace_action.py +++ b/test_tracetools_launch/test/test_tracetools_launch/test_trace_action.py @@ -142,7 +142,7 @@ def test_action_context_per_domain(self) -> None: 'ros2:*', '*', ], - context_names={ + context_fields={ 'kernel': [], 'userspace': ['vpid', 'vtid'], }, @@ -151,7 +151,7 @@ def test_action_context_per_domain(self) -> None: self._check_trace_action(action, tmpdir) self.assertDictEqual( - action.context_names, + action.context_fields, { 'kernel': [], 'userspace': ['vpid', 'vtid'], @@ -182,7 +182,7 @@ def test_action_substitutions(self) -> None: EnvironmentVariable(name='TestTraceAction__event_ust'), TextSubstitution(text='*'), ], - context_names={ + context_fields={ 'kernel': [], 'userspace': [ EnvironmentVariable(name='TestTraceAction__context_field'), @@ -194,7 +194,7 @@ def test_action_substitutions(self) -> None: self._check_trace_action(action, tmpdir) self.assertDictEqual( - action.context_names, + action.context_fields, { 'kernel': [], 'userspace': ['vpid', 'vtid'], diff --git a/tracetools_launch/tracetools_launch/action.py b/tracetools_launch/tracetools_launch/action.py index 10f0bfbc..59c2ab3e 100644 --- a/tracetools_launch/tracetools_launch/action.py +++ b/tracetools_launch/tracetools_launch/action.py @@ -81,9 +81,14 @@ def __init__( base_path: Optional[SomeSubstitutionsType] = None, events_ust: Iterable[SomeSubstitutionsType] = names.DEFAULT_EVENTS_ROS, events_kernel: Iterable[SomeSubstitutionsType] = names.DEFAULT_EVENTS_KERNEL, - context_names: + context_fields: Union[Iterable[SomeSubstitutionsType], Dict[str, Iterable[SomeSubstitutionsType]]] = names.DEFAULT_CONTEXT, + context_names: + Optional[ + Union[Iterable[SomeSubstitutionsType], Dict[str, Iterable[SomeSubstitutionsType]]] + ] + = None, profile_fast: bool = True, **kwargs, ) -> None: @@ -91,7 +96,7 @@ def __init__( Create a Trace. Substitutions are supported for the session name, - base path, and the lists of events and context names. + base path, and the lists of events and context fields. For the lists of events, wildcards can be used, e.g., 'ros2:*' for all events from the 'ros2' tracepoint provider or '*' for all events. @@ -106,13 +111,15 @@ def __init__( or `None` for default :param events_ust: the list of ROS UST events to enable :param events_kernel: the list of kernel events to enable - :param context_names: the names of context fields to enable + :param context_fields: the names of context fields to enable if it's a list or a set, the context fields are enabled for both kernel and userspace; if it's a dictionary: { domain type string -> context fields list } with the domain type string being either 'kernel' or 'userspace' + :param context_names: DEPRECATED, use context_fields instead :param profile_fast: `True` to use fast profiling, `False` for normal (only if necessary) """ super().__init__(**kwargs) + self.__logger = logging.get_logger(__name__) self.__append_timestamp = append_timestamp self.__session_name = normalize_to_list_of_substitutions(session_name) self.__base_path = base_path \ @@ -120,15 +127,19 @@ def __init__( self.__trace_directory = None self.__events_ust = [normalize_to_list_of_substitutions(x) for x in events_ust] self.__events_kernel = [normalize_to_list_of_substitutions(x) for x in events_kernel] - self.__context_names = \ + # Use value from deprecated param if it is provided + # TODO(christophebedard) remove context_names param in Rolling after Humble release + if context_names is not None: + context_fields = context_names + self.__logger.warning('context_names parameter is deprecated, use context_fields') + self.__context_fields = \ { - d: [normalize_to_list_of_substitutions(n) for n in names] - for d, names in context_names.items() + domain: [normalize_to_list_of_substitutions(field) for field in fields] + for domain, fields in context_fields.items() } \ - if isinstance(context_names, dict) \ - else [normalize_to_list_of_substitutions(x) for x in context_names] + if isinstance(context_fields, dict) \ + else [normalize_to_list_of_substitutions(field) for field in context_fields] self.__profile_fast = profile_fast - self.__logger = logging.get_logger(__name__) self.__ld_preload_actions: List[LdPreload] = [] @property @@ -151,9 +162,14 @@ def events_ust(self): def events_kernel(self): return self.__events_kernel + @property + def context_fields(self): + return self.__context_fields + @property def context_names(self): - return self.__context_names + self.__logger.warning('context_names parameter is deprecated, use context_fields') + return self.__context_fields @property def profile_fast(self): @@ -243,6 +259,10 @@ def parse(cls, entity: Entity, parser: Parser): if events_kernel is not None: kwargs['events_kernel'] = cls._parse_cmdline(events_kernel, parser) \ if events_kernel else [] + context_fields = entity.get_attr('context-fields', optional=True) + if context_fields is not None: + kwargs['context_fields'] = cls._parse_cmdline(context_fields, parser) \ + if context_fields else [] context_names = entity.get_attr('context-names', optional=True) if context_names is not None: kwargs['context_names'] = cls._parse_cmdline(context_names, parser) \ @@ -297,13 +317,13 @@ def __perform_substitutions(self, context: LaunchContext) -> None: if self.__base_path else path.get_tracing_directory() self.__events_ust = [perform_substitutions(context, x) for x in self.__events_ust] self.__events_kernel = [perform_substitutions(context, x) for x in self.__events_kernel] - self.__context_names = \ + self.__context_fields = \ { - d: [perform_substitutions(context, n) for n in names] - for d, names in self.__context_names.items() + domain: [perform_substitutions(context, field) for field in fields] + for domain, fields in self.__context_fields.items() } \ - if isinstance(self.__context_names, dict) \ - else [perform_substitutions(context, x) for x in self.__context_names] + if isinstance(self.__context_fields, dict) \ + else [perform_substitutions(context, field) for field in self.__context_fields] # Add LD_PRELOAD actions if corresponding events are enabled if self.has_profiling_events(self.__events_ust): @@ -330,12 +350,12 @@ def _setup(self) -> None: base_path=self.__base_path, ros_events=self.__events_ust, kernel_events=self.__events_kernel, - context_names=self.__context_names, + context_fields=self.__context_fields, ) self.__logger.info(f'Writing tracing session to: {self.__trace_directory}') self.__logger.debug(f'UST events: {self.__events_ust}') self.__logger.debug(f'Kernel events: {self.__events_kernel}') - self.__logger.debug(f'Context names: {self.__context_names}') + self.__logger.debug(f'Context fields: {self.__context_fields}') self.__logger.debug(f'LD_PRELOAD: {self.__ld_preload_actions}') def _destroy(self, event: Event, context: LaunchContext) -> None: @@ -350,7 +370,7 @@ def __repr__(self): f'trace_directory={self.__trace_directory}, ' f'events_ust={self.__events_ust}, ' f'events_kernel={self.__events_kernel}, ' - f'context_names={self.__context_names}, ' + f'context_fields={self.__context_fields}, ' f'profile_fast={self.__profile_fast}, ' f'ld_preload_actions={self.__ld_preload_actions})' ) diff --git a/tracetools_trace/tracetools_trace/tools/args.py b/tracetools_trace/tracetools_trace/tools/args.py index ab8112bf..dcb58d3a 100644 --- a/tracetools_trace/tracetools_trace/tools/args.py +++ b/tracetools_trace/tracetools_trace/tools/args.py @@ -63,11 +63,11 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: 'provide this flag without any event name]') events_kernel_arg.completer = DefaultArgValueCompleter(events_kernel_arg) # type: ignore context_arg = parser.add_argument( # type: ignore - '-c', '--context', nargs='*', dest='context_names', metavar='CONTEXT', + '-c', '--context', nargs='*', dest='context_fields', metavar='CONTEXT', default=names.DEFAULT_CONTEXT, - help='the context names to enable (default: see tracetools_trace.tools.names) ' - '[to disable all context names, ' - 'provide this flag without any name]') + help='the context fields to enable (default: see tracetools_trace.tools.names) ' + '[to disable all context fields, ' + 'provide this flag without any field names]') context_arg.completer = DefaultArgValueCompleter(context_arg) # type: ignore parser.add_argument( '-l', '--list', dest='list', action='store_true', diff --git a/tracetools_trace/tracetools_trace/tools/lttng_impl.py b/tracetools_trace/tracetools_trace/tools/lttng_impl.py index b491f4c4..38225e5e 100644 --- a/tracetools_trace/tracetools_trace/tools/lttng_impl.py +++ b/tracetools_trace/tracetools_trace/tools/lttng_impl.py @@ -24,6 +24,7 @@ from typing import Optional from typing import Set from typing import Union +import warnings import lttng @@ -59,7 +60,8 @@ def setup( base_path: str, ros_events: Union[List[str], Set[str]] = DEFAULT_EVENTS_ROS, kernel_events: Union[List[str], Set[str]] = DEFAULT_EVENTS_KERNEL, - context_names: Union[List[str], Set[str], Dict[str, List[str]]] = DEFAULT_CONTEXT, + context_fields: Union[List[str], Set[str], Dict[str, List[str]]] = DEFAULT_CONTEXT, + context_names: Optional[Union[List[str], Set[str], Dict[str, List[str]]]] = None, channel_name_ust: str = 'ros2', channel_name_kernel: str = 'kchan', ) -> Optional[str]: @@ -72,13 +74,20 @@ def setup( :param base_path: the path to the directory in which to create the tracing session directory :param ros_events: list of ROS events to enable :param kernel_events: list of kernel events to enable - :param context_names: the names of context fields to enable + :param context_fields: the names of context fields to enable if it's a list or a set, the context fields are enabled for both kernel and userspace; if it's a dictionary: { domain type string -> context fields list } + :param context_names: DEPRECATED, use context_fields instead :param channel_name_ust: the UST channel name :param channel_name_kernel: the kernel channel name :return: the full path to the trace directory """ + # Use value from deprecated param if it is provided + # TODO(christophebedard) remove context_names param in Rolling after Humble release + if context_names is not None: + context_fields = context_names + warnings.warn('context_names parameter is deprecated, use context_fields', stacklevel=4) + # Check if there is a session daemon running if lttng.session_daemon_alive() == 0: # Otherwise spawn one without doing any error checks @@ -91,8 +100,8 @@ def setup( ros_events = set(ros_events) if not isinstance(kernel_events, set): kernel_events = set(kernel_events) - if isinstance(context_names, list): - context_names = set(context_names) + if isinstance(context_fields, list): + context_fields = set(context_fields) # Resolve full tracing directory path full_path = os.path.join(base_path, session_name) @@ -156,7 +165,7 @@ def setup( # Context contexts_dict = _normalize_contexts_dict( - {'kernel': handle_kernel, 'userspace': handle_ust}, context_names) + {'kernel': handle_kernel, 'userspace': handle_ust}, context_fields) _add_context(contexts_dict) return full_path @@ -304,57 +313,58 @@ def _enable_events( } -def _context_name_to_type( - context_name: str, +def _context_field_name_to_type( + context_field_name: str, ) -> Optional[int]: """ Convert from context name to LTTng enum/constant type. - :param context_name: the generic name for the context + :param context_field_name: the generic name for the context field :return: the associated type, or `None` if it cannot be found """ - return context_map.get(context_name, None) + return context_map.get(context_field_name, None) def _create_context_list( - context_names: Set[str], + context_fields: Set[str], ) -> List[lttng.EventContext]: """ - Create context list from names, and check for errors. + Create context list from field names, and check for errors. - :param context_names: the set of context names + :param context_fields: the set of context fields :return: the event context list """ context_list = [] - for context_name in context_names: + for context_field_name in context_fields: ec = lttng.EventContext() - context_type = _context_name_to_type(context_name) + context_type = _context_field_name_to_type(context_field_name) if context_type is not None: ec.ctx = context_type context_list.append(ec) else: - raise RuntimeError(f'failed to find context type: {context_name}') + raise RuntimeError(f'failed to find context type: {context_field_name}') return context_list def _normalize_contexts_dict( handles: Dict[str, Optional[lttng.Handle]], - contexts: Union[Set[str], Dict[str, List[str]]], + context_fields: Union[Set[str], Dict[str, List[str]]], ) -> Dict[lttng.Handle, List[lttng.EventContext]]: """ Normalize context list/set/dict to dict. :param handles: the mapping from domain type name to handle - :param contexts: the set of context field names, + :param context_fields: the set of context field names, or mapping from domain type name to list of context field names :return: a dictionary of handle to list of event contexts """ handles = {domain: handle for domain, handle in handles.items() if handle is not None} contexts_dict = {} - if isinstance(contexts, set): - contexts_dict = {h: _create_context_list(contexts) for _, h in handles.items()} - elif isinstance(contexts, dict): - contexts_dict = {h: _create_context_list(set(contexts[d])) for d, h in handles.items()} + if isinstance(context_fields, set): + contexts_dict = {h: _create_context_list(context_fields) for _, h in handles.items()} + elif isinstance(context_fields, dict): + contexts_dict = \ + {h: _create_context_list(set(context_fields[d])) for d, h in handles.items()} else: assert False return contexts_dict @@ -369,7 +379,8 @@ def _add_context( :param contexts: the dictionay of context handles -> event contexts """ for handle, contexts_list in contexts.items(): - for contex in contexts_list: - result = lttng.add_context(handle, contex, None, None) + for context in contexts_list: + result = lttng.add_context(handle, context, None, None) if result < 0: - raise RuntimeError(f'failed to add context: {lttng.strerror(result)}') + raise RuntimeError( + f'failed to add context field {str(context)}: {lttng.strerror(result)}') diff --git a/tracetools_trace/tracetools_trace/trace.py b/tracetools_trace/tracetools_trace/trace.py index cd662b5d..9e551f0c 100644 --- a/tracetools_trace/tracetools_trace/trace.py +++ b/tracetools_trace/tracetools_trace/trace.py @@ -20,6 +20,7 @@ import sys from typing import List from typing import Optional +import warnings from tracetools_trace.tools import args from tracetools_trace.tools import lttng @@ -33,7 +34,8 @@ def init( base_path: Optional[str], ros_events: List[str], kernel_events: List[str], - context_names: List[str], + context_fields: List[str], + context_names: Optional[List[str]] = None, display_list: bool = False, ) -> None: """ @@ -44,9 +46,16 @@ def init( or `None` for default :param ros_events: list of ROS events to enable :param kernel_events: list of kernel events to enable - :param context_names: list of context names to enable + :param context_fields: list of context fields to enable + :param context_names: DEPRECATED, use context_fields instead :param display_list: whether to display list(s) of enabled events and context names """ + # Use value from deprecated param if it is provided + # TODO(christophebedard) remove context_names param in Rolling after Humble release + if context_names is not None: + context_fields = context_names + warnings.warn('context_names parameter is deprecated, use context_fields', stacklevel=4) + if not lttng.is_lttng_installed(): sys.exit(2) @@ -64,10 +73,10 @@ def init( print_names_list(kernel_events) else: print('kernel tracing disabled') - if len(context_names) > 0: - print(f'context ({len(context_names)} names)') + if len(context_fields) > 0: + print(f'context ({len(context_fields)} names)') if display_list: - print_names_list(context_names) + print_names_list(context_fields) if not base_path: base_path = path.get_tracing_directory() @@ -80,7 +89,7 @@ def init( base_path=base_path, ros_events=ros_events, kernel_events=kernel_events, - context_names=context_names, + context_fields=context_fields, ) # Simple sanity check assert trace_directory == full_session_path @@ -112,7 +121,7 @@ def main(): params.path, params.events_ust, params.events_kernel, - params.context_names, + params.context_fields, params.list, ) fini(