Merge branch '19-update-after-tracepoint-to-fix-new-intra-process-communication' into 'master'

Resolve "Update after tracepoint to fix new intra-process communication"

Closes #19

See merge request micro-ROS/ros_tracing/tracetools_analysis!28
This commit is contained in:
Christophe Bedard 2019-11-17 20:17:47 +00:00
commit f16fa313a5
4 changed files with 152 additions and 63 deletions

View file

@ -56,6 +56,10 @@ class RosDataModel(DataModel):
'topic_name', 'topic_name',
'depth']) 'depth'])
self.subscriptions.set_index(['subscription_handle'], inplace=True, drop=True) self.subscriptions.set_index(['subscription_handle'], inplace=True, drop=True)
self.subscription_objects = pd.DataFrame(columns=['subscription',
'timestamp',
'subscription_handle'])
self.subscription_objects.set_index(['subscription'], inplace=True, drop=True)
self.services = pd.DataFrame(columns=['service_handle', self.services = pd.DataFrame(columns=['service_handle',
'timestamp', 'timestamp',
'node_handle', 'node_handle',
@ -74,10 +78,10 @@ class RosDataModel(DataModel):
'tid']) 'tid'])
self.timers.set_index(['timer_handle'], inplace=True, drop=True) self.timers.set_index(['timer_handle'], inplace=True, drop=True)
self.callback_objects = pd.DataFrame(columns=['handle', self.callback_objects = pd.DataFrame(columns=['reference',
'timestamp', 'timestamp',
'callback_object']) 'callback_object'])
self.callback_objects.set_index(['handle'], inplace=True, drop=True) self.callback_objects.set_index(['reference'], inplace=True, drop=True)
self.callback_symbols = pd.DataFrame(columns=['callback_object', self.callback_symbols = pd.DataFrame(columns=['callback_object',
'timestamp', 'timestamp',
'symbol']) 'symbol'])
@ -104,11 +108,16 @@ class RosDataModel(DataModel):
) -> None: ) -> None:
self.publishers.loc[handle] = [timestamp, node_handle, rmw_handle, topic_name, depth] self.publishers.loc[handle] = [timestamp, node_handle, rmw_handle, topic_name, depth]
def add_subscription( def add_rcl_subscription(
self, handle, timestamp, node_handle, rmw_handle, topic_name, depth self, handle, timestamp, node_handle, rmw_handle, topic_name, depth
) -> None: ) -> None:
self.subscriptions.loc[handle] = [timestamp, node_handle, rmw_handle, topic_name, depth] self.subscriptions.loc[handle] = [timestamp, node_handle, rmw_handle, topic_name, depth]
def add_rclcpp_subscription(
self, subscription_pointer, timestamp, subscription_handle
) -> None:
self.subscription_objects.loc[subscription_pointer] = [timestamp, subscription_handle]
def add_service( def add_service(
self, handle, timestamp, node_handle, rmw_handle, service_name self, handle, timestamp, node_handle, rmw_handle, service_name
) -> None: ) -> None:
@ -125,9 +134,9 @@ class RosDataModel(DataModel):
self.timers.loc[handle] = [timestamp, period, tid] self.timers.loc[handle] = [timestamp, period, tid]
def add_callback_object( def add_callback_object(
self, handle, timestamp, callback_object self, reference, timestamp, callback_object
) -> None: ) -> None:
self.callback_objects.loc[handle] = [timestamp, callback_object] self.callback_objects.loc[reference] = [timestamp, callback_object]
def add_callback_symbol( def add_callback_symbol(
self, callback_object, timestamp, symbol self, callback_object, timestamp, symbol
@ -156,6 +165,8 @@ class RosDataModel(DataModel):
print() print()
print(f'Subscriptions:\n{self.subscriptions.to_string()}') print(f'Subscriptions:\n{self.subscriptions.to_string()}')
print() print()
print(f'Subscription objects:\n{self.subscription_objects.to_string()}')
print()
print(f'Services:\n{self.services.to_string()}') print(f'Services:\n{self.services.to_string()}')
print() print()
print(f'Clients:\n{self.clients.to_string()}') print(f'Clients:\n{self.clients.to_string()}')

View file

@ -43,7 +43,9 @@ class Ros2Handler(EventHandler):
'ros2:rcl_publisher_init': 'ros2:rcl_publisher_init':
self._handle_rcl_publisher_init, self._handle_rcl_publisher_init,
'ros2:rcl_subscription_init': 'ros2:rcl_subscription_init':
self._handle_subscription_init, self._handle_rcl_subscription_init,
'ros2:rclcpp_subscription_init':
self._handle_rclcpp_subscription_init,
'ros2:rclcpp_subscription_callback_added': 'ros2:rclcpp_subscription_callback_added':
self._handle_rclcpp_subscription_callback_added, self._handle_rclcpp_subscription_callback_added,
'ros2:rcl_service_init': 'ros2:rcl_service_init':
@ -78,7 +80,7 @@ class Ros2Handler(EventHandler):
return self._data_model return self._data_model
def _handle_rcl_init( def _handle_rcl_init(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
context_handle = get_field(event, 'context_handle') context_handle = get_field(event, 'context_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -87,7 +89,7 @@ class Ros2Handler(EventHandler):
self.data.add_context(context_handle, timestamp, pid, version) self.data.add_context(context_handle, timestamp, pid, version)
def _handle_rcl_node_init( def _handle_rcl_node_init(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'node_handle') handle = get_field(event, 'node_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -98,7 +100,7 @@ class Ros2Handler(EventHandler):
self.data.add_node(handle, timestamp, tid, rmw_handle, name, namespace) self.data.add_node(handle, timestamp, tid, rmw_handle, name, namespace)
def _handle_rcl_publisher_init( def _handle_rcl_publisher_init(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'publisher_handle') handle = get_field(event, 'publisher_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -108,8 +110,8 @@ class Ros2Handler(EventHandler):
depth = get_field(event, 'queue_depth') depth = get_field(event, 'queue_depth')
self.data.add_publisher(handle, timestamp, node_handle, rmw_handle, topic_name, depth) self.data.add_publisher(handle, timestamp, node_handle, rmw_handle, topic_name, depth)
def _handle_subscription_init( def _handle_rcl_subscription_init(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'subscription_handle') handle = get_field(event, 'subscription_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -117,18 +119,28 @@ class Ros2Handler(EventHandler):
rmw_handle = get_field(event, 'rmw_subscription_handle') rmw_handle = get_field(event, 'rmw_subscription_handle')
topic_name = get_field(event, 'topic_name') topic_name = get_field(event, 'topic_name')
depth = get_field(event, 'queue_depth') depth = get_field(event, 'queue_depth')
self.data.add_subscription(handle, timestamp, node_handle, rmw_handle, topic_name, depth) self.data.add_rcl_subscription(
handle, timestamp, node_handle, rmw_handle, topic_name, depth,
)
def _handle_rclcpp_subscription_init(
self, event: Dict, metadata: EventMetadata,
) -> None:
subscription_pointer = get_field(event, 'subscription')
timestamp = metadata.timestamp
handle = get_field(event, 'subscription_handle')
self.data.add_rclcpp_subscription(subscription_pointer, timestamp, handle)
def _handle_rclcpp_subscription_callback_added( def _handle_rclcpp_subscription_callback_added(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'subscription_handle') subscription_pointer = get_field(event, 'subscription')
timestamp = metadata.timestamp timestamp = metadata.timestamp
callback_object = get_field(event, 'callback') callback_object = get_field(event, 'callback')
self.data.add_callback_object(handle, timestamp, callback_object) self.data.add_callback_object(subscription_pointer, timestamp, callback_object)
def _handle_rcl_service_init( def _handle_rcl_service_init(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'service_handle') handle = get_field(event, 'service_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -138,7 +150,7 @@ class Ros2Handler(EventHandler):
self.data.add_service(handle, timestamp, node_handle, rmw_handle, service_name) self.data.add_service(handle, timestamp, node_handle, rmw_handle, service_name)
def _handle_rclcpp_service_callback_added( def _handle_rclcpp_service_callback_added(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'service_handle') handle = get_field(event, 'service_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -146,7 +158,7 @@ class Ros2Handler(EventHandler):
self.data.add_callback_object(handle, timestamp, callback_object) self.data.add_callback_object(handle, timestamp, callback_object)
def _handle_rcl_client_init( def _handle_rcl_client_init(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'client_handle') handle = get_field(event, 'client_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -156,7 +168,7 @@ class Ros2Handler(EventHandler):
self.data.add_client(handle, timestamp, node_handle, rmw_handle, service_name) self.data.add_client(handle, timestamp, node_handle, rmw_handle, service_name)
def _handle_rcl_timer_init( def _handle_rcl_timer_init(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'timer_handle') handle = get_field(event, 'timer_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -165,7 +177,7 @@ class Ros2Handler(EventHandler):
self.data.add_timer(handle, timestamp, period, tid) self.data.add_timer(handle, timestamp, period, tid)
def _handle_rclcpp_timer_callback_added( def _handle_rclcpp_timer_callback_added(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
handle = get_field(event, 'timer_handle') handle = get_field(event, 'timer_handle')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -173,7 +185,7 @@ class Ros2Handler(EventHandler):
self.data.add_callback_object(handle, timestamp, callback_object) self.data.add_callback_object(handle, timestamp, callback_object)
def _handle_rclcpp_callback_register( def _handle_rclcpp_callback_register(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
callback_object = get_field(event, 'callback') callback_object = get_field(event, 'callback')
timestamp = metadata.timestamp timestamp = metadata.timestamp
@ -181,14 +193,14 @@ class Ros2Handler(EventHandler):
self.data.add_callback_symbol(callback_object, timestamp, symbol) self.data.add_callback_symbol(callback_object, timestamp, symbol)
def _handle_callback_start( def _handle_callback_start(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
# Add to dict # Add to dict
callback_addr = get_field(event, 'callback') callback_addr = get_field(event, 'callback')
self._callback_instances[callback_addr] = (event, metadata) self._callback_instances[callback_addr] = (event, metadata)
def _handle_callback_end( def _handle_callback_end(
self, event: Dict, metadata: EventMetadata self, event: Dict, metadata: EventMetadata,
) -> None: ) -> None:
# Fetch from dict # Fetch from dict
callback_object = get_field(event, 'callback') callback_object = get_field(event, 'callback')

View file

@ -38,7 +38,10 @@ class DataModelUtil():
This class provides basic util functions. This class provides basic util functions.
""" """
def __init__(self, data_model: DataModel) -> None: def __init__(
self,
data_model: DataModel,
) -> None:
""" """
Constructor. Constructor.
@ -105,7 +108,10 @@ class DataModelUtil():
class ProfileDataModelUtil(DataModelUtil): class ProfileDataModelUtil(DataModelUtil):
"""Profiling data model utility class.""" """Profiling data model utility class."""
def __init__(self, data_model: ProfileDataModel) -> None: def __init__(
self,
data_model: ProfileDataModel,
) -> None:
""" """
Constructor. Constructor.
@ -113,14 +119,20 @@ class ProfileDataModelUtil(DataModelUtil):
""" """
super().__init__(data_model) super().__init__(data_model)
def with_tid(self, tid: int) -> DataFrame: def with_tid(
self,
tid: int,
) -> DataFrame:
return self.data.times.loc[self.data.times['tid'] == tid] return self.data.times.loc[self.data.times['tid'] == tid]
def get_tids(self) -> Set[int]: def get_tids(self) -> Set[int]:
"""Get the TIDs in the data model.""" """Get the TIDs in the data model."""
return set(self.data.times['tid']) return set(self.data.times['tid'])
def get_call_tree(self, tid: int) -> Dict[str, List[str]]: def get_call_tree(
self,
tid: int,
) -> Dict[str, List[str]]:
depth_names = self.with_tid(tid)[ depth_names = self.with_tid(tid)[
['depth', 'function_name', 'parent_name'] ['depth', 'function_name', 'parent_name']
].drop_duplicates() ].drop_duplicates()
@ -136,7 +148,10 @@ class ProfileDataModelUtil(DataModelUtil):
tree[parent].add(name) tree[parent].add(name)
return dict(tree) return dict(tree)
def get_function_duration_data(self, tid: int) -> List[Dict[str, Union[int, str, DataFrame]]]: def get_function_duration_data(
self,
tid: int,
) -> List[Dict[str, Union[int, str, DataFrame]]]:
"""Get duration data for each function.""" """Get duration data for each function."""
tid_df = self.with_tid(tid) tid_df = self.with_tid(tid)
depth_names = tid_df[['depth', 'function_name', 'parent_name']].drop_duplicates() depth_names = tid_df[['depth', 'function_name', 'parent_name']].drop_duplicates()
@ -167,7 +182,10 @@ class ProfileDataModelUtil(DataModelUtil):
class CpuTimeDataModelUtil(DataModelUtil): class CpuTimeDataModelUtil(DataModelUtil):
"""CPU time data model utility class.""" """CPU time data model utility class."""
def __init__(self, data_model: CpuTimeDataModel) -> None: def __init__(
self,
data_model: CpuTimeDataModel,
) -> None:
""" """
Constructor. Constructor.
@ -183,7 +201,10 @@ class CpuTimeDataModelUtil(DataModelUtil):
class RosDataModelUtil(DataModelUtil): class RosDataModelUtil(DataModelUtil):
"""ROS data model utility class.""" """ROS data model utility class."""
def __init__(self, data_model: RosDataModel) -> None: def __init__(
self,
data_model: RosDataModel,
) -> None:
""" """
Constructor. Constructor.
@ -191,7 +212,10 @@ class RosDataModelUtil(DataModelUtil):
""" """
super().__init__(data_model) super().__init__(data_model)
def _prettify(self, original: str) -> str: def _prettify(
self,
original: str,
) -> str:
""" """
Process symbol to make it more readable. Process symbol to make it more readable.
@ -263,7 +287,8 @@ class RosDataModelUtil(DataModelUtil):
} }
def get_callback_durations( def get_callback_durations(
self, callback_obj: int self,
callback_obj: int,
) -> DataFrame: ) -> DataFrame:
""" """
Get durations of callback instances for a given callback object. Get durations of callback instances for a given callback object.
@ -280,7 +305,8 @@ class RosDataModelUtil(DataModelUtil):
return self.convert_time_columns(data, ['duration'], ['timestamp']) return self.convert_time_columns(data, ['duration'], ['timestamp'])
def get_node_tid_from_name( def get_node_tid_from_name(
self, node_name: str self,
node_name: str,
) -> Union[int, None]: ) -> Union[int, None]:
""" """
Get the tid corresponding to a node. Get the tid corresponding to a node.
@ -296,7 +322,8 @@ class RosDataModelUtil(DataModelUtil):
return node_row.iloc[0]['tid'] if not node_row.empty else None return node_row.iloc[0]['tid'] if not node_row.empty else None
def get_node_names_from_tid( def get_node_names_from_tid(
self, tid: str self,
tid: str,
) -> Union[List[str], None]: ) -> Union[List[str], None]:
""" """
Get the list of node names corresponding to a tid. Get the list of node names corresponding to a tid.
@ -309,7 +336,8 @@ class RosDataModelUtil(DataModelUtil):
]['name'].tolist() ]['name'].tolist()
def get_callback_owner_info( def get_callback_owner_info(
self, callback_obj: int self,
callback_obj: int,
) -> Union[str, None]: ) -> Union[str, None]:
""" """
Get information about the owner of a callback. Get information about the owner of a callback.
@ -322,36 +350,37 @@ class RosDataModelUtil(DataModelUtil):
:param callback_obj: the callback object value :param callback_obj: the callback object value
:return: information about the owner of the callback, or `None` if it fails :return: information about the owner of the callback, or `None` if it fails
""" """
# Get handle corresponding to callback object # Get reference corresponding to callback object
handle = self.data.callback_objects.loc[ reference = self.data.callback_objects.loc[
self.data.callback_objects['callback_object'] == callback_obj self.data.callback_objects['callback_object'] == callback_obj
].index.values.astype(int)[0] ].index.values.astype(int)[0]
type_name = None type_name = None
info = None info = None
# Check if it's a timer first (since it's slightly different than the others) # Check if it's a timer first (since it's slightly different than the others)
if handle in self.data.timers.index: if reference in self.data.timers.index:
type_name = 'Timer' type_name = 'Timer'
info = self.get_timer_handle_info(handle) info = self.get_timer_handle_info(reference)
elif handle in self.data.publishers.index: elif reference in self.data.publishers.index:
type_name = 'Publisher' type_name = 'Publisher'
info = self.get_publisher_handle_info(handle) info = self.get_publisher_handle_info(reference)
elif handle in self.data.subscriptions.index: elif reference in self.data.subscription_objects.index:
type_name = 'Subscription' type_name = 'Subscription'
info = self.get_subscription_handle_info(handle) info = self.get_subscription_reference_info(reference)
elif handle in self.data.services.index: elif reference in self.data.services.index:
type_name = 'Service' type_name = 'Service'
info = self.get_subscription_handle_info(handle) info = self.get_service_handle_info(reference)
elif handle in self.data.clients.index: elif reference in self.data.clients.index:
type_name = 'Client' type_name = 'Client'
info = self.get_client_handle_info(handle) info = self.get_client_handle_info(reference)
if info is not None: if info is not None:
info = f'{type_name} -- {self.format_info_dict(info)}' info = f'{type_name} -- {self.format_info_dict(info)}'
return info return info
def get_timer_handle_info( def get_timer_handle_info(
self, timer_handle: int self,
timer_handle: int,
) -> Union[Mapping[str, Any], None]: ) -> Union[Mapping[str, Any], None]:
""" """
Get information about the owner of a timer. Get information about the owner of a timer.
@ -369,7 +398,8 @@ class RosDataModelUtil(DataModelUtil):
return {'tid': tid, 'period': f'{period_ms:.0f} ms'} return {'tid': tid, 'period': f'{period_ms:.0f} ms'}
def get_publisher_handle_info( def get_publisher_handle_info(
self, publisher_handle: int self,
publisher_handle: int,
) -> Union[Mapping[str, Any], None]: ) -> Union[Mapping[str, Any], None]:
""" """
Get information about a publisher handle. Get information about a publisher handle.
@ -386,30 +416,61 @@ class RosDataModelUtil(DataModelUtil):
publisher_info = {'topic': topic_name} publisher_info = {'topic': topic_name}
return {**node_handle_info, **publisher_info} return {**node_handle_info, **publisher_info}
def get_subscription_handle_info( def get_subscription_reference_info(
self, subscription_handle: int self,
subscription_reference: int,
) -> Union[Mapping[str, Any], None]: ) -> Union[Mapping[str, Any], None]:
""" """
Get information about a subscription handle. Get information about a subscription handle.
:param subscription_handle: the subscription handle value :param subscription_reference: the subscription reference value
:return: a dictionary with name:value info, or `None` if it fails :return: a dictionary with name:value info, or `None` if it fails
""" """
subscriptions_info = self.data.subscriptions.merge( # First check that the subscription reference exists
self.data.nodes, if subscription_reference not in self.data.subscription_objects.index:
left_on='node_handle',
right_index=True)
if subscription_handle not in self.data.subscriptions.index:
return None return None
node_handle = subscriptions_info.loc[subscription_handle, 'node_handle'] # To get information about a subscription reference, we need 3 dataframes
# * subscription_objects
# * subscription (reference) <--> subscription_handle
# * subscriptions
# * subscription_handle <--> topic_name
# * subscription_handle <--> node_handle
# * nodes
# * node_handle <--> (node info)
# First, drop unnecessary common columns for debugging simplicity
subscription_objects_simple = self.data.subscription_objects.drop(
columns=['timestamp'],
axis=1,
)
subscriptions_simple = self.data.subscriptions.drop(
columns=['timestamp', 'rmw_handle'],
inplace=False,
)
nodes_simple = self.data.nodes.drop(
columns=['timestamp', 'rmw_handle'],
inplace=False,
)
# Then merge the 3 dataframes
subscriptions_info = subscription_objects_simple.merge(
subscriptions_simple,
left_on='subscription_handle',
right_index=True,
).merge(
nodes_simple,
left_on='node_handle',
right_index=True,
)
node_handle = subscriptions_info.loc[subscription_reference, 'node_handle']
node_handle_info = self.get_node_handle_info(node_handle) node_handle_info = self.get_node_handle_info(node_handle)
topic_name = subscriptions_info.loc[subscription_handle, 'topic_name'] topic_name = subscriptions_info.loc[subscription_reference, 'topic_name']
subscription_info = {'topic': topic_name} subscription_info = {'topic': topic_name}
return {**node_handle_info, **subscription_info} return {**node_handle_info, **subscription_info}
def get_service_handle_info( def get_service_handle_info(
self, service_handle: int self,
service_handle: int,
) -> Union[Mapping[str, Any], None]: ) -> Union[Mapping[str, Any], None]:
""" """
Get information about a service handle. Get information about a service handle.
@ -427,7 +488,8 @@ class RosDataModelUtil(DataModelUtil):
return {**node_handle_info, **service_info} return {**node_handle_info, **service_info}
def get_client_handle_info( def get_client_handle_info(
self, client_handle: int self,
client_handle: int,
) -> Union[Mapping[str, Any], None]: ) -> Union[Mapping[str, Any], None]:
""" """
Get information about a client handle. Get information about a client handle.
@ -445,7 +507,8 @@ class RosDataModelUtil(DataModelUtil):
return {**node_handle_info, **service_info} return {**node_handle_info, **service_info}
def get_node_handle_info( def get_node_handle_info(
self, node_handle: int self,
node_handle: int,
) -> Union[Mapping[str, Any], None]: ) -> Union[Mapping[str, Any], None]:
""" """
Get information about a node handle. Get information about a node handle.
@ -460,5 +523,8 @@ class RosDataModelUtil(DataModelUtil):
tid = self.data.nodes.loc[node_handle, 'tid'] tid = self.data.nodes.loc[node_handle, 'tid']
return {'node': node_name, 'tid': tid} return {'node': node_name, 'tid': tid}
def format_info_dict(self, info_dict: Mapping[str, Any]) -> str: def format_info_dict(
self,
info_dict: Mapping[str, Any],
) -> str:
return ', '.join([f'{key}: {val}' for key, val in info_dict.items()]) return ', '.join([f'{key}: {val}' for key, val in info_dict.items()])