Support traces with multiple callbacks for same pointer (#13) (#15)

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
This commit is contained in:
Christophe Bedard 2024-04-23 09:23:47 -07:00 committed by GitHub
parent d9250ce036
commit fada2d0fc2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 13 deletions

View file

@ -77,7 +77,9 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {}, "metadata": {
"scrolled": false
},
"outputs": [], "outputs": [],
"source": [ "source": [
"data_util = Ros2DataModelUtil(handler.data)\n", "data_util = Ros2DataModelUtil(handler.data)\n",
@ -86,13 +88,17 @@
"\n", "\n",
"output_notebook()\n", "output_notebook()\n",
"psize = 450\n", "psize = 450\n",
"# If the trace contains more callbacks, add colours here\n",
"# or use: https://docs.bokeh.org/en/3.2.2/docs/reference/palettes.html\n",
"colours = ['#29788E', '#DD4968', '#410967']" "colours = ['#29788E', '#DD4968', '#410967']"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {}, "metadata": {
"scrolled": false
},
"outputs": [], "outputs": [],
"source": [ "source": [
"# Plot durations separately\n", "# Plot durations separately\n",
@ -153,6 +159,7 @@
" )\n", " )\n",
"\n", "\n",
" colour_i += 1\n", " colour_i += 1\n",
" colour_i %= len(colours)\n",
" show(row(duration, hist))" " show(row(duration, hist))"
] ]
}, },
@ -197,6 +204,7 @@
" line_color=colours[colour_i],\n", " line_color=colours[colour_i],\n",
" )\n", " )\n",
" colour_i += 1\n", " colour_i += 1\n",
" colour_i %= len(colours)\n",
" duration.legend.label_text_font_size = '11px'\n", " duration.legend.label_text_font_size = '11px'\n",
" duration.xaxis[0].formatter = DatetimeTickFormatter(seconds='%Ss')\n", " duration.xaxis[0].formatter = DatetimeTickFormatter(seconds='%Ss')\n",
"\n", "\n",
@ -227,7 +235,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.10.6" "version": "3.10.12"
} }
}, },
"nbformat": 4, "nbformat": 4,

View file

@ -23,6 +23,7 @@ from typing import Optional
from typing import Union from typing import Union
import numpy as np import numpy as np
import pandas as pd
from pandas import concat from pandas import concat
from pandas import DataFrame from pandas import DataFrame
@ -119,9 +120,15 @@ class Ros2DataModelUtil(DataModelUtil):
# Get a list of callback objects # Get a list of callback objects
callback_objects = set(callback_instances['callback_object']) callback_objects = set(callback_instances['callback_object'])
# Get their symbol # Get their symbol
return { pretty_symbols = {}
obj: self._prettify(callback_symbols.loc[obj, 'symbol']) for obj in callback_objects for obj in callback_objects:
} # There could be multiple callback symbols for the same callback object (pointer), e.g.,
# if we create and destroy subscriptions dynamically
symbols = callback_symbols.loc[obj, 'symbol']
symbols = symbols if isinstance(symbols, pd.Series) else [symbols]
# In that case, just combine the symbols
pretty_symbols[obj] = ' and '.join(self._prettify(symbol) for symbol in symbols)
return pretty_symbols
def get_tids(self) -> List[str]: def get_tids(self) -> List[str]:
"""Get a list of thread ids corresponding to the nodes.""" """Get a list of thread ids corresponding to the nodes."""
@ -303,7 +310,8 @@ class Ros2DataModelUtil(DataModelUtil):
if info is None: if info is None:
return None return None
return f'{type_name} -- {self.format_info_dict(info)}' info_str = self.format_info_dict(info, sep='\n')
return f'{type_name}\n{info_str}'
def get_timer_handle_info( def get_timer_handle_info(
self, self,
@ -395,12 +403,27 @@ class Ros2DataModelUtil(DataModelUtil):
right_index=True, right_index=True,
) )
node_handle = subscriptions_info.loc[subscription_reference, 'node_handle'] # There could be multiple subscriptions for the same subscription object pointer, e.g., if
node_handle_info = self.get_node_handle_info(node_handle) # we create and destroy subscriptions dynamically, so this subscription could belong to more
if node_handle_info is None: # than one node
return None # In that case, just combine the information
topic_name = subscriptions_info.loc[subscription_reference, 'topic_name'] node_handles = subscriptions_info.loc[subscription_reference, 'node_handle']
node_handles = node_handles if isinstance(node_handles, pd.Series) else [node_handles]
topic_names = subscriptions_info.loc[subscription_reference, 'topic_name']
topic_names = topic_names if isinstance(topic_names, pd.Series) else [topic_names]
nodes_handle_info = []
for node_handle in node_handles:
node_handle_info = self.get_node_handle_info(node_handle)
if node_handle_info is None:
return None
nodes_handle_info.append(node_handle_info)
topic_name = ' and '.join(topic_names)
subscription_info = {'topic': topic_name} subscription_info = {'topic': topic_name}
# Turn list of dicts into dict of combined values
node_handle_info = {
key: ' and '.join(set(str(info[key]) for info in nodes_handle_info))
for key in nodes_handle_info[0]
}
return {**node_handle_info, **subscription_info} return {**node_handle_info, **subscription_info}
def get_service_handle_info( def get_service_handle_info(
@ -540,5 +563,6 @@ class Ros2DataModelUtil(DataModelUtil):
def format_info_dict( def format_info_dict(
self, self,
info_dict: Mapping[str, Any], info_dict: Mapping[str, Any],
sep: str = ', ',
) -> str: ) -> str:
return ', '.join([f'{key}: {val}' for key, val in info_dict.items()]) return sep.join(f'{key}: {val}' for key, val in info_dict.items())