diff --git a/ros2trace/.gitignore b/ros2trace/.gitignore
new file mode 100644
index 0000000..eef29c1
--- /dev/null
+++ b/ros2trace/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.pyc
+
diff --git a/ros2trace/package.xml b/ros2trace/package.xml
new file mode 100644
index 0000000..096a8b0
--- /dev/null
+++ b/ros2trace/package.xml
@@ -0,0 +1,26 @@
+
+
+
+ ros2trace
+ 0.0.1
+
+ The trace command for ROS 2 command line tools.
+
+ Christophe Bedard
+ Ingo Lütkebohle
+ Apache 2.0
+ Christophe Bedard
+
+ ros2cli
+ tracetools_trace
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ ament_xmllint
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/ros2trace/ros2trace/__init__.py b/ros2trace/ros2trace/__init__.py
new file mode 100644
index 0000000..4b18865
--- /dev/null
+++ b/ros2trace/ros2trace/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/ros2trace/ros2trace/api/__init__.py b/ros2trace/ros2trace/api/__init__.py
new file mode 100644
index 0000000..d39da83
--- /dev/null
+++ b/ros2trace/ros2trace/api/__init__.py
@@ -0,0 +1,68 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""API functions for the ROS 2 trace command."""
+
+import os
+
+from tracetools_trace.tools import args
+from tracetools_trace.tools import lttng
+
+
+def add_trace_arguments(parser):
+ args.add_arguments(parser)
+
+
+def init(args):
+ """
+ Init and start tracing.
+
+ :param args: the parsed arguments object containing the right fields
+ """
+ session_name = args.session_name
+ base_path = args.path
+ full_path = os.path.join(base_path, session_name)
+ ros_events = args.events_ust
+ kernel_events = args.events_kernel
+
+ ust_enabled = len(ros_events) > 0
+ kernel_enabled = len(kernel_events) > 0
+ if ust_enabled:
+ print(f'UST tracing enabled ({len(ros_events)} events)')
+ if args.list:
+ print(f'\tevents: {ros_events}')
+ else:
+ print('UST tracing disabled')
+ if kernel_enabled:
+ print(f'kernel tracing enabled ({len(kernel_events)} events)')
+ if args.list:
+ print(f'\tevents: {kernel_events}')
+ else:
+ print('kernel tracing disabled')
+
+ print(f'writting tracing session to: {full_path}')
+ input('press enter to start...')
+ lttng.lttng_init(session_name, full_path, ros_events=ros_events, kernel_events=kernel_events)
+
+
+def fini(args):
+ """
+ Stop and finalize tracing.
+
+ :param args: the parsed arguments object containing the right fields
+ """
+ session_name = args.session_name
+ input('press enter to stop...')
+ print('stopping & destroying tracing session')
+ lttng.lttng_fini(session_name)
diff --git a/ros2trace/ros2trace/command/__init__.py b/ros2trace/ros2trace/command/__init__.py
new file mode 100644
index 0000000..4b18865
--- /dev/null
+++ b/ros2trace/ros2trace/command/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/ros2trace/ros2trace/command/trace.py b/ros2trace/ros2trace/command/trace.py
new file mode 100644
index 0000000..ee03363
--- /dev/null
+++ b/ros2trace/ros2trace/command/trace.py
@@ -0,0 +1,32 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Module for trace command extension implementation."""
+
+from ros2cli.command import CommandExtension
+from ros2trace.api import add_trace_arguments
+from ros2trace.api import init
+from ros2trace.api import fini
+
+
+class TraceCommand(CommandExtension):
+ """Trace ROS nodes to get information on their execution."""
+
+ def add_arguments(self, parser, cli_name):
+ add_trace_arguments(parser)
+
+ def main(self, *, parser, args):
+ init(args)
+ fini(args)
+ return 0
diff --git a/ros2trace/setup.py b/ros2trace/setup.py
new file mode 100644
index 0000000..4add34d
--- /dev/null
+++ b/ros2trace/setup.py
@@ -0,0 +1,26 @@
+from setuptools import find_packages
+from setuptools import setup
+
+setup(
+ name='ros2trace',
+ version='0.0.1',
+ packages=find_packages(exclude=['test']),
+ install_requires=['ros2cli'],
+ zip_safe=True,
+ maintainer='Christophe Bedard, Ingo Lütkebohle',
+ maintainer_email='fixed-term.christophe.bourquebedard@de.bosch.com, ingo.luetkebohle@de.bosch.com',
+ author='Christophe Bedard',
+ author_email='fixed-term.christophe.bourquebedard@de.bosch.com',
+ # url=',
+ keywords=[],
+ description='The run command for ROS 2 command line tools.',
+ long_description="""\
+The package provides the trace command for the ROS 2 command line tools.""",
+ license='Apache 2.0',
+ tests_require=['pytest'],
+ entry_points={
+ 'ros2cli.command': [
+ 'trace = ros2trace.command.trace:TraceCommand',
+ ],
+ }
+)
diff --git a/ros2trace/test/test_copyright.py b/ros2trace/test/test_copyright.py
new file mode 100644
index 0000000..cf0fae3
--- /dev/null
+++ b/ros2trace/test/test_copyright.py
@@ -0,0 +1,23 @@
+# Copyright 2017 Open Source Robotics Foundation, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ament_copyright.main import main
+import pytest
+
+
+@pytest.mark.copyright
+@pytest.mark.linter
+def test_copyright():
+ rc = main(argv=['.', 'test'])
+ assert rc == 0, 'Found errors'
diff --git a/ros2trace/test/test_flake8.py b/ros2trace/test/test_flake8.py
new file mode 100644
index 0000000..eff8299
--- /dev/null
+++ b/ros2trace/test/test_flake8.py
@@ -0,0 +1,23 @@
+# Copyright 2017 Open Source Robotics Foundation, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ament_flake8.main import main
+import pytest
+
+
+@pytest.mark.flake8
+@pytest.mark.linter
+def test_flake8():
+ rc = main(argv=[])
+ assert rc == 0, 'Found errors'
diff --git a/ros2trace/test/test_pep257.py b/ros2trace/test/test_pep257.py
new file mode 100644
index 0000000..0e38a6c
--- /dev/null
+++ b/ros2trace/test/test_pep257.py
@@ -0,0 +1,23 @@
+# Copyright 2017 Open Source Robotics Foundation, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ament_pep257.main import main
+import pytest
+
+
+@pytest.mark.linter
+@pytest.mark.pep257
+def test_pep257():
+ rc = main(argv=[])
+ assert rc == 0, 'Found code style errors / warnings'
diff --git a/ros2trace/test/test_xmllint.py b/ros2trace/test/test_xmllint.py
new file mode 100644
index 0000000..f46285e
--- /dev/null
+++ b/ros2trace/test/test_xmllint.py
@@ -0,0 +1,23 @@
+# Copyright 2019 Open Source Robotics Foundation, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ament_xmllint.main import main
+import pytest
+
+
+@pytest.mark.linter
+@pytest.mark.xmllint
+def test_xmllint():
+ rc = main(argv=[])
+ assert rc == 0, 'Found errors'
diff --git a/tracetools/package.xml b/tracetools/package.xml
index 6bb4256..90ca10a 100644
--- a/tracetools/package.xml
+++ b/tracetools/package.xml
@@ -6,7 +6,7 @@
ROS 2 wrapper for instrumentation
Christophe Bedard
Ingo Luetkebohle
- Apache Software License 2.0
+ Apache 2.0
Ingo Luetkebohle
Christophe Bedard
diff --git a/tracetools_launch/.gitignore b/tracetools_launch/.gitignore
new file mode 100644
index 0000000..eef29c1
--- /dev/null
+++ b/tracetools_launch/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.pyc
+
diff --git a/tracetools_launch/launch/example.launch.py b/tracetools_launch/launch/example.launch.py
new file mode 100644
index 0000000..325daa3
--- /dev/null
+++ b/tracetools_launch/launch/example.launch.py
@@ -0,0 +1,35 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Example launch file for the Trace action."""
+
+from launch import LaunchDescription
+from launch_ros.actions import Node
+from tracetools_launch.trace import Trace
+
+
+def generate_launch_description():
+ return LaunchDescription([
+ Trace(
+ session_name='my-tracing-session',
+ base_path='/tmp'),
+ Node(
+ package='examples_rclcpp_minimal_publisher',
+ node_executable='publisher_member_function',
+ output='screen'),
+ Node(
+ package='examples_rclcpp_minimal_subscriber',
+ node_executable='subscriber_member_function',
+ output='screen'),
+ ])
diff --git a/tracetools_launch/package.xml b/tracetools_launch/package.xml
new file mode 100644
index 0000000..c09d6f5
--- /dev/null
+++ b/tracetools_launch/package.xml
@@ -0,0 +1,24 @@
+
+
+
+ tracetools_launch
+ 0.0.1
+ Launch integration for tracing
+ Christophe Bedard
+ Ingo Lütkebohle
+ Apache 2.0
+ Christophe Bedard
+
+ launch
+ launch_ros
+ tracetools_trace
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/tracetools_launch/setup.cfg b/tracetools_launch/setup.cfg
new file mode 100644
index 0000000..b7ef5cb
--- /dev/null
+++ b/tracetools_launch/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script-dir=$base/lib/tracetools_launch
+[install]
+install-scripts=$base/lib/tracetools_launch
diff --git a/tracetools_launch/setup.py b/tracetools_launch/setup.py
new file mode 100644
index 0000000..f6c6015
--- /dev/null
+++ b/tracetools_launch/setup.py
@@ -0,0 +1,26 @@
+import glob
+
+from setuptools import find_packages
+from setuptools import setup
+
+package_name = 'tracetools_launch'
+
+setup(
+ name=package_name,
+ version='0.0.1',
+ packages=find_packages(exclude=['test']),
+ data_files=[
+ ('share/' + package_name, ['package.xml']),
+ ('share/' + package_name + '/launch', glob.glob('launch/*.launch.py')),
+ ],
+ install_requires=['setuptools'],
+ maintainer='Christophe Bedard, Ingo Lütkebohle',
+ maintainer_email='fixed-term.christophe.bourquebedard@de.bosch.com, ingo.luetkebohle@de.bosch.com',
+ author='Christophe Bedard',
+ author_email='fixed-term.christophe.bourquebedard@de.bosch.com',
+ # url='',
+ keywords=['ROS'],
+ description='Launch integration for tracing',
+ license='Apache 2.0',
+ tests_require=['pytest'],
+)
diff --git a/tracetools_launch/tracetools_launch/__init__.py b/tracetools_launch/tracetools_launch/__init__.py
new file mode 100644
index 0000000..4b18865
--- /dev/null
+++ b/tracetools_launch/tracetools_launch/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tracetools_launch/tracetools_launch/trace.py b/tracetools_launch/tracetools_launch/trace.py
new file mode 100644
index 0000000..e46ea28
--- /dev/null
+++ b/tracetools_launch/tracetools_launch/trace.py
@@ -0,0 +1,82 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Module for the Trace action."""
+
+import os
+from typing import List
+from typing import Optional
+
+from launch.action import Action
+from launch.event import Event
+from launch.event_handlers import OnShutdown
+from launch.launch_context import LaunchContext
+from tracetools_trace.tools import lttng
+from tracetools_trace.tools import names
+
+
+class Trace(Action):
+ """
+ Tracing action for launch.
+
+ Sets up and enables tracing through a launch file description.
+ """
+
+ def __init__(
+ self,
+ *,
+ session_name: str,
+ base_path: str = '/tmp',
+ events_ust: List[str] = names.DEFAULT_EVENTS_ROS,
+ events_kernel: List[str] = names.DEFAULT_EVENTS_KERNEL,
+ **kwargs,
+ ) -> None:
+ """
+ Constructor.
+
+ :param session_name: the name of the tracing session
+ :param base_path: the base directory in which to create the trace directory
+ :param events_ust: the list of ROS UST events to enable
+ :param events_kernel: the list of kernel events to enable
+ """
+ super().__init__(**kwargs)
+ self.__session_name = session_name
+ self.__path = os.path.join(base_path, session_name)
+ self.__events_ust = events_ust
+ self.__events_kernel = events_kernel
+
+ def execute(self, context: LaunchContext) -> Optional[List[Action]]:
+ # TODO make sure this is done as late as possible
+ context.register_event_handler(OnShutdown(on_shutdown=self._destroy))
+ # TODO make sure this is done as early as possible
+ self._setup()
+
+ def _setup(self) -> None:
+ lttng.lttng_init(
+ self.__session_name,
+ self.__path,
+ ros_events=self.__events_ust,
+ kernel_events=self.__events_kernel)
+
+ def _destroy(self, event: Event, context: LaunchContext) -> None:
+ lttng.lttng_fini(self.__session_name)
+
+ def __repr__(self):
+ return (
+ "Trace("
+ f"session_name='{self.__session_name}', "
+ f"path='{self.__path}', "
+ f"num_events_ust={len(self.__events_ust)}, "
+ f"num_events_kernel={len(self.__events_kernel)})"
+ )
diff --git a/tracetools_test/package.xml b/tracetools_test/package.xml
index 2a62e8d..abc5734 100644
--- a/tracetools_test/package.xml
+++ b/tracetools_test/package.xml
@@ -6,7 +6,7 @@
Separate test package for tracetools
Christophe Bedard
Ingo Luetkebohle
- Apache Software License 2.0
+ Apache 2.0
Christophe Bedard
ament_cmake
diff --git a/tracetools_trace/.gitignore b/tracetools_trace/.gitignore
new file mode 100644
index 0000000..eef29c1
--- /dev/null
+++ b/tracetools_trace/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.pyc
+
diff --git a/tracetools_trace/package.xml b/tracetools_trace/package.xml
new file mode 100644
index 0000000..1361ead
--- /dev/null
+++ b/tracetools_trace/package.xml
@@ -0,0 +1,24 @@
+
+
+
+ tracetools_trace
+ 0.0.1
+ Tools for setting up tracing sessions
+ Christophe Bedard
+ Ingo Lütkebohle
+ Apache 2.0
+ Christophe Bedard
+
+
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/tracetools_trace/setup.cfg b/tracetools_trace/setup.cfg
new file mode 100644
index 0000000..6bbabc6
--- /dev/null
+++ b/tracetools_trace/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script-dir=$base/lib/tracetools_trace
+[install]
+install-scripts=$base/lib/tracetools_trace
diff --git a/tracetools_trace/setup.py b/tracetools_trace/setup.py
new file mode 100644
index 0000000..c28f6b1
--- /dev/null
+++ b/tracetools_trace/setup.py
@@ -0,0 +1,28 @@
+from setuptools import find_packages
+from setuptools import setup
+
+package_name = 'tracetools_trace'
+
+setup(
+ name=package_name,
+ version='0.0.1',
+ packages=find_packages(exclude=['test']),
+ data_files=[
+ ('share/' + package_name, ['package.xml']),
+ ],
+ install_requires=['setuptools'],
+ maintainer='Christophe Bedard, Ingo Lütkebohle',
+ maintainer_email='fixed-term.christophe.bourquebedard@de.bosch.com, ingo.luetkebohle@de.bosch.com',
+ author='Christophe Bedard',
+ author_email='fixed-term.christophe.bourquebedard@de.bosch.com',
+ # url='',
+ keywords=['ROS'],
+ description='Tools for setting up tracing sessions',
+ entry_points={
+ 'console_scripts': [
+ f'trace = {package_name}.trace:main',
+ ],
+ },
+ license='Apache 2.0',
+ tests_require=['pytest'],
+)
diff --git a/tracetools_trace/test/test_copyright.py b/tracetools_trace/test/test_copyright.py
new file mode 100644
index 0000000..cf0fae3
--- /dev/null
+++ b/tracetools_trace/test/test_copyright.py
@@ -0,0 +1,23 @@
+# Copyright 2017 Open Source Robotics Foundation, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ament_copyright.main import main
+import pytest
+
+
+@pytest.mark.copyright
+@pytest.mark.linter
+def test_copyright():
+ rc = main(argv=['.', 'test'])
+ assert rc == 0, 'Found errors'
diff --git a/tracetools_trace/test/test_flake8.py b/tracetools_trace/test/test_flake8.py
new file mode 100644
index 0000000..eff8299
--- /dev/null
+++ b/tracetools_trace/test/test_flake8.py
@@ -0,0 +1,23 @@
+# Copyright 2017 Open Source Robotics Foundation, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ament_flake8.main import main
+import pytest
+
+
+@pytest.mark.flake8
+@pytest.mark.linter
+def test_flake8():
+ rc = main(argv=[])
+ assert rc == 0, 'Found errors'
diff --git a/tracetools_trace/test/test_pep257.py b/tracetools_trace/test/test_pep257.py
new file mode 100644
index 0000000..3aeb4d3
--- /dev/null
+++ b/tracetools_trace/test/test_pep257.py
@@ -0,0 +1,23 @@
+# Copyright 2015 Open Source Robotics Foundation, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ament_pep257.main import main
+import pytest
+
+
+@pytest.mark.linter
+@pytest.mark.pep257
+def test_pep257():
+ rc = main(argv=[])
+ assert rc == 0, 'Found code style errors / warnings'
diff --git a/tracetools_trace/tracetools_trace/__init__.py b/tracetools_trace/tracetools_trace/__init__.py
new file mode 100644
index 0000000..4b18865
--- /dev/null
+++ b/tracetools_trace/tracetools_trace/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tracetools_trace/tracetools_trace/tools/__init__.py b/tracetools_trace/tracetools_trace/tools/__init__.py
new file mode 100644
index 0000000..4b18865
--- /dev/null
+++ b/tracetools_trace/tracetools_trace/tools/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tracetools_trace/tracetools_trace/tools/args.py b/tracetools_trace/tracetools_trace/tools/args.py
new file mode 100644
index 0000000..09938ae
--- /dev/null
+++ b/tracetools_trace/tracetools_trace/tools/args.py
@@ -0,0 +1,67 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Module containing parsing functions for tracing commands."""
+
+import argparse
+import time
+
+from . import names
+
+
+class DefaultArgValueCompleter:
+ """Callable returning an arg's default value."""
+
+ def __init__(self, arg):
+ default = arg.default
+ self.list = default if isinstance(default, list) else [default]
+
+ def __call__(self, **kwargs):
+ return self.list
+
+
+def parse_args():
+ """
+ Parse args for tracing.
+ """
+ parser = argparse.ArgumentParser(description='Setup and launch an LTTng tracing session.')
+ add_arguments(parser)
+ return parser.parse_args()
+
+
+def add_arguments(parser):
+ parser.add_argument(
+ '--session-name', '-s', dest='session_name',
+ default=f'session-{time.strftime("%Y%m%d%H%M%S")}',
+ help='the name of the tracing session (default: session-YYYYMMDDHHMMSS)')
+ parser.add_argument(
+ '--path', '-p', dest='path',
+ default='/tmp',
+ help='path of the base directory for trace data (default: %(default)s)')
+ arg = parser.add_argument(
+ '--ust', '-u', nargs='*', dest='events_ust', default=names.DEFAULT_EVENTS_ROS,
+ help='the ROS UST events to enable (default: all events) '
+ '[to disable all UST events, '
+ 'provide this flag without any event name]')
+ arg.completer = DefaultArgValueCompleter(arg)
+ arg = parser.add_argument(
+ '--kernel', '-k', nargs='*', dest='events_kernel',
+ default=names.DEFAULT_EVENTS_KERNEL,
+ help='the kernel events to enable (default: all events) '
+ '[to disable all UST events, '
+ 'provide this flag without any event name]')
+ arg.completer = DefaultArgValueCompleter(arg)
+ parser.add_argument(
+ '--list', '-l', dest='list', action='store_true',
+ help='display lists of enabled events (default: %(default)s)')
diff --git a/tracetools_trace/tracetools_trace/tools/lttng.py b/tracetools_trace/tracetools_trace/tools/lttng.py
new file mode 100644
index 0000000..3c54c91
--- /dev/null
+++ b/tracetools_trace/tracetools_trace/tools/lttng.py
@@ -0,0 +1,309 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Interface for tracing with LTTng."""
+
+import sys
+
+# Temporary workaround
+sys.path = ['/usr/local/lib/python3.6/site-packages'] + sys.path
+from typing import List
+
+import lttng # noqa: E402
+
+from .names import ( # noqa: E402
+ DEFAULT_CONTEXT,
+ DEFAULT_EVENTS_KERNEL,
+ DEFAULT_EVENTS_ROS,
+)
+
+
+def lttng_init(
+ session_name: str,
+ full_path: str,
+ ros_events: List[str] = DEFAULT_EVENTS_ROS,
+ kernel_events: List[str] = DEFAULT_EVENTS_KERNEL,
+ context_names: List[str] = DEFAULT_CONTEXT
+) -> None:
+ """
+ Set up and start LTTng session.
+
+ :param session_name: the name of the session
+ :param full_path: the full path to the main directory to write trace data to
+ :param ros_events: list of ROS events to enable
+ :param kernel_events: list of kernel events to enable
+ :param context_names: list of context elements to enable
+ """
+ _lttng_setup(session_name, full_path, ros_events, kernel_events, context_names)
+ _lttng_start(session_name)
+
+
+def lttng_fini(session_name: str) -> None:
+ """
+ Stop and destroy LTTng session.
+
+ :param session_name: the name of the session
+ """
+ _lttng_stop(session_name)
+ _lttng_destroy(session_name)
+
+
+def _lttng_setup(
+ session_name: str,
+ full_path: str,
+ ros_events: List[str] = DEFAULT_EVENTS_ROS,
+ kernel_events: List[str] = DEFAULT_EVENTS_KERNEL,
+ context_names: List[str] = DEFAULT_CONTEXT,
+ channel_name_ust: str = 'ros2',
+ channel_name_kernel: str = 'kchan',
+) -> None:
+ """
+ Set up LTTng session, with events and context.
+
+ See: https://lttng.org/docs/#doc-core-concepts
+
+ :param session_name: the name of the session
+ :param full_path: the full path to the main directory to write trace data to
+ :param ros_events: list of ROS events to enable
+ :param kernel_events: list of kernel events to enable
+ :param context_names: list of context elements to enable
+ :param channel_name_ust: the UST channel name
+ :param channel_name_kernel: the kernel channel name
+ """
+ ust_enabled = ros_events is not None and len(ros_events) > 0
+ kernel_enabled = kernel_events is not None and len(kernel_events) > 0
+
+ # Domains
+ if ust_enabled:
+ domain_ust = lttng.Domain()
+ domain_ust.type = lttng.DOMAIN_UST
+ # Per-user buffer
+ domain_ust.buf_type = lttng.BUFFER_PER_UID
+ channel_ust = lttng.Channel()
+ channel_ust.name = channel_name_ust
+ # Discard, do not overwrite
+ channel_ust.attr.overwrite = 0
+ # 8 sub-buffers of 2 times the usual page size
+ channel_ust.attr.subbuf_size = 2 * 4096
+ channel_ust.attr.num_subbuf = 8
+ # Ignore switch timer interval and use read timer instead
+ channel_ust.attr.switch_timer_interval = 0
+ channel_ust.attr.read_timer_interval = 200
+ # mmap channel output instead of splice
+ channel_ust.attr.output = lttng.EVENT_MMAP
+ events_list_ust = _create_events(ros_events)
+ if kernel_enabled:
+ domain_kernel = lttng.Domain()
+ domain_kernel.type = lttng.DOMAIN_KERNEL
+ # Global buffer (only option for kernel domain)
+ domain_kernel.buf_type = lttng.BUFFER_GLOBAL
+ channel_kernel = lttng.Channel()
+ channel_kernel.name = channel_name_kernel
+ # Discard, do not overwrite
+ channel_kernel.attr.overwrite = 0
+ # 8 sub-buffers of 8 times the usual page size, since
+ # there can be way more kernel events than UST events
+ channel_kernel.attr.subbuf_size = 8 * 4096
+ channel_kernel.attr.num_subbuf = 8
+ # Ignore switch timer interval and use read timer instead
+ channel_kernel.attr.switch_timer_interval = 0
+ channel_kernel.attr.read_timer_interval = 200
+ # mmap channel output instead of splice
+ channel_kernel.attr.output = lttng.EVENT_MMAP
+ events_list_kernel = _create_events(kernel_events)
+
+ # Session
+ _create_session(session_name, full_path)
+
+ # Handles, channels, events
+ handle_ust = None
+ if ust_enabled:
+ handle_ust = _create_handle(session_name, domain_ust)
+ _enable_channel(handle_ust, channel_ust)
+ _enable_events(handle_ust, events_list_ust, channel_ust.name)
+ handle_kernel = None
+ if kernel_enabled:
+ handle_kernel = _create_handle(session_name, domain_kernel)
+ _enable_channel(handle_kernel, channel_kernel)
+ _enable_events(handle_kernel, events_list_kernel, channel_kernel.name)
+
+ # Context
+ context_list = _create_context_list(context_names)
+ enabled_handles = [h for h in [handle_ust, handle_kernel] if h is not None]
+ _add_context(enabled_handles, context_list)
+
+
+def _lttng_start(session_name: str) -> None:
+ """
+ Start LTTng session, and check for errors.
+
+ :param session_name: the name of the session
+ """
+ result = lttng.start(session_name)
+ if result < 0:
+ raise RuntimeError(f'failed to start tracing: {lttng.strerror(result)}')
+
+
+def _lttng_stop(session_name: str) -> None:
+ """
+ Stop LTTng session, and check for errors.
+
+ :param session_name: the name of the session
+ """
+ result = lttng.stop(session_name)
+ if result < 0:
+ raise RuntimeError(f'failed to stop tracing: {lttng.strerror(result)}')
+
+
+def _lttng_destroy(session_name: str) -> None:
+ """
+ Destroy LTTng session, and check for errors.
+
+ :param session_name: the name of the session
+ """
+ result = lttng.destroy(session_name)
+ if result < 0:
+ raise RuntimeError(f'failed to destroy tracing session: {lttng.strerror(result)}')
+
+
+def _create_events(event_names_list: List[str]) -> List[lttng.Event]:
+ """
+ Create events list from names.
+
+ :param event_names_list: a list of names to create events for
+ :return: the list of events
+ """
+ events_list = []
+ for event_name in event_names_list:
+ e = lttng.Event()
+ e.name = event_name
+ e.type = lttng.EVENT_TRACEPOINT
+ e.loglevel_type = lttng.EVENT_LOGLEVEL_ALL
+ events_list.append(e)
+ return events_list
+
+
+def _create_session(session_name: str, full_path: str) -> None:
+ """
+ Create session from name and full directory path, and check for errors.
+
+ :param session_name: the name of the session
+ :param full_path: the full path to the main directory to write trace data to
+ """
+ result = lttng.create(session_name, full_path)
+ LTTNG_ERR_EXIST_SESS = 28
+ if result == -LTTNG_ERR_EXIST_SESS:
+ # Sessions seem to persist, so if it already exists,
+ # just destroy it and try again
+ lttng_destroy(session_name)
+ result = lttng.create(session_name, full_path)
+ if result < 0:
+ raise RuntimeError(f'session creation failed: {lttng.strerror(result)}')
+
+
+def _create_handle(session_name: str, domain: lttng.Domain) -> lttng.Handle:
+ """
+ Create a handle for a given session name and a domain, and check for errors.
+
+ :param session_name: the name of the session
+ :param domain: the domain to be used
+ :return: the handle
+ """
+ handle = None
+ handle = lttng.Handle(session_name, domain)
+ if handle is None:
+ raise RuntimeError('handle creation failed')
+ return handle
+
+
+def _enable_channel(handle: lttng.Handle, channel: lttng.Channel) -> None:
+ """
+ Enable channel for a handle, and check for errors.
+
+ :param handle: the handle to be used
+ :param channel: the channel to enable
+ """
+ result = lttng.enable_channel(handle, channel)
+ if result < 0:
+ raise RuntimeError(f'channel enabling failed: {lttng.strerror(result)}')
+
+
+def _enable_events(
+ handle: lttng.Handle,
+ events_list: List[lttng.Event],
+ channel_name: str,
+) -> None:
+ """
+ Enable events list for a given handle and channel name, and check for errors.
+
+ :param handle: the handle to be used
+ :param events_list: the list of events to enable
+ :param channel_name: the name of the channel to associate
+ """
+ for event in events_list:
+ result = lttng.enable_event(handle, event, channel_name)
+ if result < 0:
+ raise RuntimeError(f'event enabling failed: {lttng.strerror(result)}')
+
+
+context_map = {
+ 'procname': lttng.EVENT_CONTEXT_PROCNAME,
+ 'pid': lttng.EVENT_CONTEXT_PID,
+ 'vpid': lttng.EVENT_CONTEXT_VPID,
+ 'vtid': lttng.EVENT_CONTEXT_VTID,
+}
+
+
+def _context_name_to_type(context_name: str) -> int:
+ """
+ Convert from context name to LTTng enum/constant type.
+
+ :param context_name: the generic name for the context
+ :return: the associated type
+ """
+ return context_map.get(context_name)
+
+
+def _create_context_list(context_names_list: List[str]) -> List[lttng.EventContext]:
+ """
+ Create context list from names, and check for errors.
+
+ :param context_names_list: the list of context names
+ :return: the event context list
+ """
+ context_list = []
+ for c in context_names_list:
+ ec = lttng.EventContext()
+ context_type = _context_name_to_type(c)
+ if context_type is not None:
+ ec.ctx = context_type
+ context_list.append(ec)
+ return context_list
+
+
+def _add_context(
+ handles: List[lttng.Handle],
+ context_list: List[lttng.EventContext],
+) -> None:
+ """
+ Add context list to given handles, and check for errors.
+
+ :param handles: the list of handles for which to add context
+ :param context_list: the list of event contexts to add to the handles
+ """
+ for handle in handles:
+ for contex in context_list:
+ result = lttng.add_context(handle, contex, None, None)
+ if result < 0:
+ raise RuntimeError(f'failed to add context: {lttng.strerror(result)}')
diff --git a/tracetools_trace/tracetools_trace/tools/names.py b/tracetools_trace/tracetools_trace/tools/names.py
new file mode 100644
index 0000000..43ad7b0
--- /dev/null
+++ b/tracetools_trace/tracetools_trace/tools/names.py
@@ -0,0 +1,81 @@
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Lists of names (events, context) for tracing."""
+
+EVENTS_KERNEL = [
+ 'block_rq_complete',
+ 'block_rq_insert',
+ 'block_rq_issue',
+ 'block_bio_frontmerge',
+ 'irq_softirq_entry',
+ 'irq_softirq_raise',
+ 'irq_softirq_exit',
+ 'irq_handler_entry',
+ 'irq_handler_exit',
+ 'lttng_statedump_process_state',
+ 'lttng_statedump_start',
+ 'lttng_statedump_end',
+ 'lttng_statedump_network_interface',
+ 'lttng_statedump_block_device',
+ 'net_dev_queue',
+ 'netif_receive_skb',
+ 'net_if_receive_skb',
+ 'power_cpu_frequency',
+ 'sched_switch',
+ 'sched_waking',
+ 'sched_pi_setprio',
+ 'sched_process_fork',
+ 'sched_process_exit',
+ 'sched_process_free',
+ 'sched_wakeup',
+ 'sched_migrate',
+ 'sched_migrate_task',
+ 'timer_hrtimer_start',
+ 'timer_hrtimer_cancel',
+ 'timer_hrtimer_expire_entry',
+ 'timer_hrtimer_expire_exit',
+]
+
+DEFAULT_EVENTS_KERNEL = [
+ 'power_cpu_frequency',
+ 'sched_switch',
+ 'sched_waking',
+ 'sched_wakeup',
+]
+
+DEFAULT_EVENTS_ROS = [
+ 'ros2:rcl_init',
+ 'ros2:rcl_node_init',
+ 'ros2:rcl_publisher_init',
+ 'ros2:rcl_subscription_init',
+ 'ros2:rclcpp_subscription_callback_added',
+ 'ros2:rcl_service_init',
+ 'ros2:rclcpp_service_callback_added',
+ 'ros2:rcl_client_init',
+ 'ros2:rcl_timer_init',
+ 'ros2:rclcpp_timer_callback_added',
+ 'ros2:rclcpp_callback_register',
+ 'ros2:callback_start',
+ 'ros2:callback_end',
+]
+
+DEFAULT_CONTEXT = [
+ 'procname',
+ 'perf:thread:instructions',
+ 'perf:thread:cycles',
+ 'perf:thread:cpu-cycles',
+ 'vpid',
+ 'vtid',
+]
diff --git a/tracetools_trace/tracetools_trace/trace.py b/tracetools_trace/tracetools_trace/trace.py
new file mode 100644
index 0000000..055e63d
--- /dev/null
+++ b/tracetools_trace/tracetools_trace/trace.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# Copyright 2019 Robert Bosch GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Entrypoint/script to setup and start an LTTng tracing session."""
+
+import os
+
+from tracetools_trace.tools import args
+from tracetools_trace.tools import lttng
+from tracetools_trace.tools import names
+
+
+def main():
+ params = args.parse_args()
+
+ session_name = params.session_name
+ base_path = params.path
+ full_path = os.path.join(base_path, session_name)
+ ros_events = params.events_ust
+ kernel_events = params.events_kernel
+
+ ust_enabled = len(ros_events) > 0
+ kernel_enabled = len(kernel_events) > 0
+ if ust_enabled:
+ print(f'UST tracing enabled ({len(ros_events)} events)')
+ if params.list:
+ print(f'\tevents: {ros_events}')
+ else:
+ print('UST tracing disabled')
+ if kernel_enabled:
+ print(f'kernel tracing enabled ({len(kernel_events)} events)')
+ if params.list:
+ print(f'\tevents: {kernel_events}')
+ else:
+ print('kernel tracing disabled')
+
+ print(f'writting tracing session to: {full_path}')
+ input('press enter to start...')
+ lttng.lttng_init(session_name, full_path, ros_events=ros_events, kernel_events=kernel_events)
+ input('press enter to stop...')
+
+ print('stopping & destroying tracing session')
+ lttng.lttng_fini(session_name)