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',
'depth'])
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',
'timestamp',
'node_handle',
@ -74,10 +78,10 @@ class RosDataModel(DataModel):
'tid'])
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',
'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',
'timestamp',
'symbol'])
@ -104,11 +108,16 @@ class RosDataModel(DataModel):
) -> None:
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
) -> None:
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(
self, handle, timestamp, node_handle, rmw_handle, service_name
) -> None:
@ -125,9 +134,9 @@ class RosDataModel(DataModel):
self.timers.loc[handle] = [timestamp, period, tid]
def add_callback_object(
self, handle, timestamp, callback_object
self, reference, timestamp, callback_object
) -> None:
self.callback_objects.loc[handle] = [timestamp, callback_object]
self.callback_objects.loc[reference] = [timestamp, callback_object]
def add_callback_symbol(
self, callback_object, timestamp, symbol
@ -156,6 +165,8 @@ class RosDataModel(DataModel):
print()
print(f'Subscriptions:\n{self.subscriptions.to_string()}')
print()
print(f'Subscription objects:\n{self.subscription_objects.to_string()}')
print()
print(f'Services:\n{self.services.to_string()}')
print()
print(f'Clients:\n{self.clients.to_string()}')

View file

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

View file

@ -38,7 +38,10 @@ class DataModelUtil():
This class provides basic util functions.
"""
def __init__(self, data_model: DataModel) -> None:
def __init__(
self,
data_model: DataModel,
) -> None:
"""
Constructor.
@ -105,7 +108,10 @@ class DataModelUtil():
class ProfileDataModelUtil(DataModelUtil):
"""Profiling data model utility class."""
def __init__(self, data_model: ProfileDataModel) -> None:
def __init__(
self,
data_model: ProfileDataModel,
) -> None:
"""
Constructor.
@ -113,14 +119,20 @@ class ProfileDataModelUtil(DataModelUtil):
"""
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]
def get_tids(self) -> Set[int]:
"""Get the TIDs in the data model."""
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', 'function_name', 'parent_name']
].drop_duplicates()
@ -136,7 +148,10 @@ class ProfileDataModelUtil(DataModelUtil):
tree[parent].add(name)
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."""
tid_df = self.with_tid(tid)
depth_names = tid_df[['depth', 'function_name', 'parent_name']].drop_duplicates()
@ -167,7 +182,10 @@ class ProfileDataModelUtil(DataModelUtil):
class CpuTimeDataModelUtil(DataModelUtil):
"""CPU time data model utility class."""
def __init__(self, data_model: CpuTimeDataModel) -> None:
def __init__(
self,
data_model: CpuTimeDataModel,
) -> None:
"""
Constructor.
@ -183,7 +201,10 @@ class CpuTimeDataModelUtil(DataModelUtil):
class RosDataModelUtil(DataModelUtil):
"""ROS data model utility class."""
def __init__(self, data_model: RosDataModel) -> None:
def __init__(
self,
data_model: RosDataModel,
) -> None:
"""
Constructor.
@ -191,7 +212,10 @@ class RosDataModelUtil(DataModelUtil):
"""
super().__init__(data_model)
def _prettify(self, original: str) -> str:
def _prettify(
self,
original: str,
) -> str:
"""
Process symbol to make it more readable.
@ -263,7 +287,8 @@ class RosDataModelUtil(DataModelUtil):
}
def get_callback_durations(
self, callback_obj: int
self,
callback_obj: int,
) -> DataFrame:
"""
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'])
def get_node_tid_from_name(
self, node_name: str
self,
node_name: str,
) -> Union[int, None]:
"""
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
def get_node_names_from_tid(
self, tid: str
self,
tid: str,
) -> Union[List[str], None]:
"""
Get the list of node names corresponding to a tid.
@ -309,7 +336,8 @@ class RosDataModelUtil(DataModelUtil):
]['name'].tolist()
def get_callback_owner_info(
self, callback_obj: int
self,
callback_obj: int,
) -> Union[str, None]:
"""
Get information about the owner of a callback.
@ -322,36 +350,37 @@ class RosDataModelUtil(DataModelUtil):
:param callback_obj: the callback object value
:return: information about the owner of the callback, or `None` if it fails
"""
# Get handle corresponding to callback object
handle = self.data.callback_objects.loc[
# Get reference corresponding to callback object
reference = self.data.callback_objects.loc[
self.data.callback_objects['callback_object'] == callback_obj
].index.values.astype(int)[0]
type_name = None
info = None
# 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'
info = self.get_timer_handle_info(handle)
elif handle in self.data.publishers.index:
info = self.get_timer_handle_info(reference)
elif reference in self.data.publishers.index:
type_name = 'Publisher'
info = self.get_publisher_handle_info(handle)
elif handle in self.data.subscriptions.index:
info = self.get_publisher_handle_info(reference)
elif reference in self.data.subscription_objects.index:
type_name = 'Subscription'
info = self.get_subscription_handle_info(handle)
elif handle in self.data.services.index:
info = self.get_subscription_reference_info(reference)
elif reference in self.data.services.index:
type_name = 'Service'
info = self.get_subscription_handle_info(handle)
elif handle in self.data.clients.index:
info = self.get_service_handle_info(reference)
elif reference in self.data.clients.index:
type_name = 'Client'
info = self.get_client_handle_info(handle)
info = self.get_client_handle_info(reference)
if info is not None:
info = f'{type_name} -- {self.format_info_dict(info)}'
return info
def get_timer_handle_info(
self, timer_handle: int
self,
timer_handle: int,
) -> Union[Mapping[str, Any], None]:
"""
Get information about the owner of a timer.
@ -369,7 +398,8 @@ class RosDataModelUtil(DataModelUtil):
return {'tid': tid, 'period': f'{period_ms:.0f} ms'}
def get_publisher_handle_info(
self, publisher_handle: int
self,
publisher_handle: int,
) -> Union[Mapping[str, Any], None]:
"""
Get information about a publisher handle.
@ -386,30 +416,61 @@ class RosDataModelUtil(DataModelUtil):
publisher_info = {'topic': topic_name}
return {**node_handle_info, **publisher_info}
def get_subscription_handle_info(
self, subscription_handle: int
def get_subscription_reference_info(
self,
subscription_reference: int,
) -> Union[Mapping[str, Any], None]:
"""
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
"""
subscriptions_info = self.data.subscriptions.merge(
self.data.nodes,
left_on='node_handle',
right_index=True)
if subscription_handle not in self.data.subscriptions.index:
# First check that the subscription reference exists
if subscription_reference not in self.data.subscription_objects.index:
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)
topic_name = subscriptions_info.loc[subscription_handle, 'topic_name']
topic_name = subscriptions_info.loc[subscription_reference, 'topic_name']
subscription_info = {'topic': topic_name}
return {**node_handle_info, **subscription_info}
def get_service_handle_info(
self, service_handle: int
self,
service_handle: int,
) -> Union[Mapping[str, Any], None]:
"""
Get information about a service handle.
@ -427,7 +488,8 @@ class RosDataModelUtil(DataModelUtil):
return {**node_handle_info, **service_info}
def get_client_handle_info(
self, client_handle: int
self,
client_handle: int,
) -> Union[Mapping[str, Any], None]:
"""
Get information about a client handle.
@ -445,7 +507,8 @@ class RosDataModelUtil(DataModelUtil):
return {**node_handle_info, **service_info}
def get_node_handle_info(
self, node_handle: int
self,
node_handle: int,
) -> Union[Mapping[str, Any], None]:
"""
Get information about a node handle.
@ -460,5 +523,8 @@ class RosDataModelUtil(DataModelUtil):
tid = self.data.nodes.loc[node_handle, '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()])