Extract tracetools_trace from tracetools_analysis
This commit is contained in:
parent
6aba4109c3
commit
17fe8d2245
22 changed files with 614 additions and 272 deletions
5
analysis/.gitignore
vendored
Normal file
5
analysis/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
*.svg
|
||||
*.png
|
||||
*.pdf
|
||||
.ipynb_checkpoints
|
||||
|
506
analysis/Callback_duration.ipynb
Normal file
506
analysis/Callback_duration.ipynb
Normal file
File diff suppressed because one or more lines are too long
15
package.xml
Normal file
15
package.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>tracetools_analysis</name>
|
||||
<version>0.1.0</version>
|
||||
<description>Tools for analysing trace data</description>
|
||||
|
||||
<maintainer email="ingo.luetkebohle@de.bosch.com">Ingo Luetkebohle</maintainer>
|
||||
<author email="ingo.luetkebohle@de.bosch.com">Ingo Luetkebohle</author>
|
||||
<license>GPLv3</license>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
4
setup.cfg
Normal file
4
setup.cfg
Normal file
|
@ -0,0 +1,4 @@
|
|||
[develop]
|
||||
script-dir=$base/lib/tracetools_analysis
|
||||
[install]
|
||||
install-scripts=$base/lib/tracetools_analysis
|
21
setup.py
Normal file
21
setup.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from setuptools import setup
|
||||
|
||||
package_name = 'tracetools_analysis'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.1.0',
|
||||
packages=[package_name],
|
||||
data_files=[
|
||||
('share/' + package_name, ['package.xml']),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
keywords=['ROS'],
|
||||
description='Tools for analysing trace data',
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
f'convert = {package_name}.convert:main',
|
||||
f'process = {package_name}.process:main',
|
||||
],
|
||||
},
|
||||
)
|
22
test/test_tracepoints.py
Normal file
22
test/test_tracepoints.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Test tracetools tracepoints
|
||||
|
||||
import unittest
|
||||
from tracetools_analysis.test.utils import *
|
||||
|
||||
class TestTracepoints(unittest.TestCase):
|
||||
|
||||
def test_something(self):
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_publisher_creation(self):
|
||||
session_name = 'test-session'
|
||||
path = '/tmp'
|
||||
package_name = ''
|
||||
executable_name = ''
|
||||
run_and_trace(package_name, executable_name, session_name, path)
|
||||
event_names = get_trace_event_names(f'{path}/{session_name}')
|
||||
self.assertTrue('ros2:rcl_publisher_init' in event_names)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
25
trace.py
25
trace.py
|
@ -1,25 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Entrypoint/script to setup and start an LTTng tracing session
|
||||
# TODO
|
||||
|
||||
import sys
|
||||
import time
|
||||
from tracetools_analysis.tracing.lttng import *
|
||||
|
||||
def main(argv=sys.argv):
|
||||
if len(argv) != 3:
|
||||
print("usage: session-name /path")
|
||||
exit(1)
|
||||
|
||||
session_name = argv[1]
|
||||
path = argv[2] + '/' + session_name
|
||||
lttng_setup(session_name, path)
|
||||
lttng_start(session_name)
|
||||
print(f'tracing session started: {path}')
|
||||
|
||||
# TODO integrate this with launch + ROS shutdown
|
||||
input('press enter to stop...')
|
||||
|
||||
print('stopping & destroying tracing session')
|
||||
lttng_stop(session_name)
|
||||
lttng_destroy(session_name)
|
0
tracetools_analysis/test/__init__.py
Normal file
0
tracetools_analysis/test/__init__.py
Normal file
41
tracetools_analysis/test/utils.py
Normal file
41
tracetools_analysis/test/utils.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Utils for tracetools testing
|
||||
|
||||
import subprocess
|
||||
import babeltrace
|
||||
from ..trace import *
|
||||
|
||||
def get_trace_event_names(trace_directory):
|
||||
"""
|
||||
Get a set of event names in a trace
|
||||
:param trace_directory (str): the path to the main/top trace directory
|
||||
:return: event names (set(str))
|
||||
"""
|
||||
tc = babeltrace.TraceCollection()
|
||||
tc.add_traces_recursive(trace_directory, 'ctf')
|
||||
|
||||
event_names = set()
|
||||
|
||||
for event in tc.events:
|
||||
event_names.add(event.name)
|
||||
|
||||
return event_names
|
||||
|
||||
|
||||
def run_and_trace(package_name, executable_name, session_name, path):
|
||||
"""
|
||||
Setup, start tracing, and run a ROS 2 executable
|
||||
:param package_name (str): the name of the package
|
||||
:param executable_name (str): the name of the executable to run
|
||||
:param session_name (str): the name of the session
|
||||
:param directory (str): the path of the main directory to write trace data to
|
||||
"""
|
||||
# Enable all events
|
||||
lttng_setup(session_name, path)
|
||||
lttng_start(session_name)
|
||||
_run(package_name, executable_name)
|
||||
lttng_stop(session_name)
|
||||
lttng_destroy(session_name)
|
||||
|
||||
|
||||
def _run(package_name, executable_name):
|
||||
subprocess.check_call(['ros2', 'run', package_name, executable_name])
|
0
tracetools_analysis/tracing/__init__.py
Normal file
0
tracetools_analysis/tracing/__init__.py
Normal file
183
tracing/lttng.py
183
tracing/lttng.py
|
@ -1,183 +0,0 @@
|
|||
# LTTng tracing interface
|
||||
|
||||
# Temporary workaround
|
||||
import sys
|
||||
sys.path = ['/usr/local/lib/python3.6/site-packages'] + sys.path
|
||||
|
||||
from lttng import *
|
||||
from .names import DEFAULT_EVENTS_ROS, DEFAULT_EVENTS_KERNEL, DEFAULT_CONTEXT
|
||||
|
||||
def lttng_setup(session_name, directory, ros_events=DEFAULT_EVENTS_ROS, kernel_events=DEFAULT_EVENTS_KERNEL, context_names=DEFAULT_CONTEXT):
|
||||
"""
|
||||
Setup LTTng session, with events and context
|
||||
:param session_name (str): the name of the session
|
||||
:param directory (str): the path of the main directory to write trace data to
|
||||
:param ros_events (list(str)): list of ROS events to enable
|
||||
:param kernel_events (list(str)): list of kernel events to enable
|
||||
:param context_names (list(str)): list of context elements to enable
|
||||
"""
|
||||
ust_enabled = ros_events is not None and len(ros_events) > 0
|
||||
kernel_enabled = kernel_events is not None and len(kernel_events) > 0
|
||||
print(f'UST tracing {f"enabled ({len(ros_events)} events)" if ust_enabled else "disabled"}')
|
||||
print(f'kernel tracing {f"enabled ({len(kernel_events)} events)" if kernel_enabled else "disabled"}')
|
||||
|
||||
# Domains
|
||||
if ust_enabled:
|
||||
domain_ust = Domain()
|
||||
domain_ust.type = DOMAIN_UST
|
||||
domain_ust.buf_type = BUFFER_PER_UID
|
||||
channel_ust = Channel()
|
||||
channel_ust.name = 'ros2'
|
||||
channel_ust.attr.overwrite = 0
|
||||
channel_ust.attr.subbuf_size = 2*4096
|
||||
channel_ust.attr.num_subbuf = 8
|
||||
channel_ust.attr.switch_timer_interval = 0
|
||||
channel_ust.attr.read_timer_interval = 200
|
||||
channel_ust.attr.output = EVENT_MMAP
|
||||
events_list_ust = _create_events(ros_events)
|
||||
if kernel_enabled:
|
||||
domain_kernel = Domain()
|
||||
domain_kernel.type = DOMAIN_KERNEL
|
||||
domain_kernel.buf_type = BUFFER_GLOBAL
|
||||
channel_kernel = Channel()
|
||||
channel_kernel.name = 'kchan'
|
||||
channel_kernel.attr.overwrite = 0
|
||||
channel_kernel.attr.subbuf_size = 8*4096
|
||||
channel_kernel.attr.num_subbuf = 8
|
||||
channel_kernel.attr.switch_timer_interval = 0
|
||||
channel_kernel.attr.read_timer_interval = 200
|
||||
channel_kernel.attr.output = EVENT_MMAP
|
||||
events_list_kernel = _create_events(kernel_events)
|
||||
|
||||
# Session
|
||||
_create_session(session_name, directory)
|
||||
|
||||
# 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):
|
||||
"""
|
||||
Start LTTng session, and check for errors
|
||||
"""
|
||||
result = start(session_name)
|
||||
if result < 0:
|
||||
raise RuntimeError(f'failed to start tracing: {strerror(result)}')
|
||||
|
||||
def lttng_stop(session_name):
|
||||
"""
|
||||
Stop LTTng session, and check for errors
|
||||
"""
|
||||
result = stop(session_name)
|
||||
if result < 0:
|
||||
raise RuntimeError(f'failed to stop tracing: {strerror(result)}')
|
||||
|
||||
def lttng_destroy(session_name):
|
||||
"""
|
||||
Destroy LTTng session, and check for errors
|
||||
"""
|
||||
result = destroy(session_name)
|
||||
if result < 0:
|
||||
raise RuntimeError(f'failed to destroy tracing session: {strerror(result)}')
|
||||
|
||||
def _create_events(event_names_list):
|
||||
"""
|
||||
Create events list from names
|
||||
"""
|
||||
events_list = []
|
||||
for event_name in event_names_list:
|
||||
e = Event()
|
||||
e.name = event_name
|
||||
e.type = EVENT_TRACEPOINT
|
||||
e.loglevel_type = EVENT_LOGLEVEL_ALL
|
||||
events_list.append(e)
|
||||
return events_list
|
||||
|
||||
def _create_session(session_name, directory):
|
||||
"""
|
||||
Create session from name and directory path, and check for errors
|
||||
"""
|
||||
result = create(session_name, directory)
|
||||
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 = create(session_name, directory)
|
||||
if result < 0:
|
||||
raise RuntimeError(f'session creation failed: {strerror(result)}')
|
||||
|
||||
def _create_handle(session_name, domain):
|
||||
"""
|
||||
Create a handle for a given session name and a domain, and check for errors
|
||||
"""
|
||||
handle = None
|
||||
handle = Handle(session_name, domain)
|
||||
if handle is None:
|
||||
raise RuntimeError('handle creation failed')
|
||||
return handle
|
||||
|
||||
def _enable_channel(handle, channel):
|
||||
"""
|
||||
Enable channel for a handle, and check for errors
|
||||
"""
|
||||
result = enable_channel(handle, channel)
|
||||
if result < 0:
|
||||
raise RuntimeError(f'channel enabling failed: {strerror(result)}')
|
||||
|
||||
def _enable_events(handle, events_list, channel_name):
|
||||
"""
|
||||
Enable events list for a given handle and channel name, and check for errors
|
||||
"""
|
||||
for event in events_list:
|
||||
result = enable_event(handle, event, channel_name)
|
||||
if result < 0:
|
||||
raise RuntimeError(f'event enabling failed: {strerror(result)}')
|
||||
|
||||
context_map = {
|
||||
'procname': EVENT_CONTEXT_PROCNAME,
|
||||
'pid': EVENT_CONTEXT_PID,
|
||||
'vpid': EVENT_CONTEXT_VPID,
|
||||
'vtid': EVENT_CONTEXT_VTID,
|
||||
}
|
||||
def _context_name_to_type(context_name):
|
||||
"""
|
||||
Convert from context name to LTTng enum/constant type
|
||||
"""
|
||||
return context_map.get(context_name)
|
||||
|
||||
def _create_context_list(context_names_list):
|
||||
"""
|
||||
Create context list from names, and check for errors
|
||||
"""
|
||||
context_list = []
|
||||
for c in context_names_list:
|
||||
ec = 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, context_list):
|
||||
"""
|
||||
Add context list to given handles, and check for errors
|
||||
"""
|
||||
for handle in handles:
|
||||
for contex in context_list:
|
||||
result = add_context(handle, contex, None, None)
|
||||
if result < 0:
|
||||
raise RuntimeError(f'failed to add context: {strerror(result)}')
|
|
@ -1,64 +0,0 @@
|
|||
# Lists of names (events, context)
|
||||
|
||||
DEFAULT_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_ROS=[
|
||||
'ros2:rcl_init',
|
||||
'ros2:rcl_node_init',
|
||||
'ros2:rcl_publisher_init',
|
||||
'ros2:rcl_subscription_init',
|
||||
'ros2:rclcpp_subscription_callback_added',
|
||||
'ros2:rclcpp_subscription_callback_start',
|
||||
'ros2:rclcpp_subscription_callback_end',
|
||||
'ros2:rcl_service_init',
|
||||
'ros2:rclcpp_service_callback_added',
|
||||
'ros2:rclcpp_service_callback_start',
|
||||
'ros2:rclcpp_service_callback_end',
|
||||
'ros2:rcl_client_init',
|
||||
'ros2:rcl_timer_init',
|
||||
'ros2:rclcpp_timer_callback_added',
|
||||
'ros2:rclcpp_timer_callback_start',
|
||||
'ros2:rclcpp_timer_callback_end',
|
||||
'ros2:rclcpp_callback_register',
|
||||
]
|
||||
|
||||
DEFAULT_CONTEXT=[
|
||||
'procname',
|
||||
'perf:thread:instructions',
|
||||
'perf:thread:cycles',
|
||||
'perf:thread:cpu-cycles',
|
||||
'vpid',
|
||||
'vtid',
|
||||
]
|
Loading…
Add table
Add a link
Reference in a new issue