How to define inter-nodes data path#
Latency definition section is empty just after an architecture object is loaded from a set of CTF-based recorded data. If you want to observe data flow on targeted paths, you should add the target paths to the architecture object. As a path is combination of multiple nodes and topics, it may be laborious to list their names. To mitigate such burden, CARET serves a function to search the targeted path. The targeted path is added to the architecture object via Python API.
Basic usage to find and add target path#
Listing nodes and topics by their names is laborious. CARET serve a helpful method,
arch.search_paths, to search candidates that you want to observe.
The following sample code shows usage of
# Architecture object is loaded to variable of arch paths = arch.search_paths('source_node', 'destination_node') type(paths) # list of multiple paths paths.summary.pprint() # shows nodes and topics in paths
In the sample,
paths is a list including all possible paths between
destination_node, whose type is
PathStructValue. If you are satisfied with output from
paths.summary.pprint(), you have to add
paths to the
arch object as below.
arch.add_path('target_path', paths) arch.export('new_architecture_with_path.yaml')
In the above sample,
paths is named as
target_path and registered to the
arch object is exported to a new architecture file for reuse.
If you want to restore the
paths object with
arch.get_path() method will help you to do so.
arch = Architecture('yaml', 'new_architecture_with_path.yaml') path = arch.get_path('target_path') # path object is same as paths in the previous sample
Efficient target path search#
As explained above,
Architecture.search_paths() returns list of multiple paths.
The list size will be too large to find target path if application has large numbers of nodes and distance between source and destination node is long. In worse case,
Architecture.search_paths() keep searching and does not return
paths variable after hours passes.
Architecture.search_paths() method serves four options to narrow down possible paths as below.
- Additional nodes as variable length arguments
- Limiting maximum number of nodes between given nodes with
- Node filter which excludes paths including specific nodes
- Communication filter which excludes paths including specific topics
Architecture.search_paths is defined as follows.
search_paths( *node_names: 'str', max_node_depth: 'Optional[int]' = None, node_filter: 'Optional[Callable[[str], bool]]' = None, communication_filter: 'Optional[Callable[[str], bool]]' = None ) -> 'List[PathStructValue]'
The following sub-sections will explain their roles and usages in details.
In the previous example,
Architecture.search_paths() had two arguments
destination_node. However, the number of nodes given to
Architecture.search_paths() is variable and not always two. You can add other nodes to
Architecture.search_paths() as below, and you will get a list including multiple paths which passes all given nodes.
# Architecture object is loaded to variable of arch paths = arch.search_paths('source_node', 'intermediate_node_1', 'intermediate_node_2', 'destination_node')
paths is a list including multiple paths which pass
destination_node. They are allowed to pass another node, but all chosen nodes are passed in order.
Limiting maximum number of nodes#
Architecture.search_paths() will scan all paths as possible if you don't give
max_node_depth means maximum number between given nodes. The number of candidate paths will be suppressed by this argument.
This argument will be helpful When you waste much time for
The usage is shown as below. This can be used with another approach to filter candidates.
# Architecture object is loaded to variable of arch paths = arch.search_paths('source_node', 'intermediate_node_1', 'destination_node', max_node_depth=10)
max_node_depth does not always limit the maximum number of nodes between source and destination. If you give 3 nodes to
arch.search_paths as shown above,
max_node_depth does not limits the maximum number of nodes between
destination_node. In this example,
max_node_depth limits the number of nodes between
intermediate_node_1, and that between
Node and topic filter#
As node filter is similar to communication filter, they are explained together in this section.
With node filter and communication filter,
Architecture.search_paths() excludes paths which includes selected nodes or topics. They support regular expression.
The following sample code shows usage.
import re # name list of nodes to be excluded node_filters = [ re.compile(r'/_ros2cli_/*'), re.compile(r'/launch_ros_*'), ] # name list of topics to be excluded comm_filters = [ re.compile(r'/tf/*'), ] def comm_filter(topic_name: str) -> bool: can_pass = True for comm_filter in comm_filters: can_pass &= not bool(comm_filter.search(topic_name)) return can_pass def node_filter(node_name: str) -> bool: can_pass = True for node_filter in node_filters: can_pass &= not bool(node_filter.search(node_name)) return can_pass paths = arch.search_paths( '/start_node', '/intermediate_node' '/end_node', max_node_depth=30, node_filter = node_filter, communication_filter = comm_filter)
Architecture.combine_path() combines two paths which are found by
By searching short paths and combining them repeatedly, you can get a target path. It is sometimes more efficient than searching a longer path directly according to "divide-and-conquer" method.
Architecture.combine_path() is as following.
paths_1 = arch.search_paths('source_node', 'intermediate_node') paths_2 = arch.search_paths('intermediate_node', 'destination_node') target_path = arch.combine_path(paths_1, paths_2) arch.add_path('combined_path_name', target_path) arch.export('new_architecture.yaml')
An exception is raised for pairs that cannot be combined.
Considering first/last callback#
In CARET, the path is defined as
[node_name]-[topic_name]-... -[topic_name]-[node_name] (for more information in Path).
In the default configuration, the path analysis does not include the callback processing time either at the first node or at the last node of the above path. The following two processing times are not included by default in path analysis.
- Processing time in the first node; from
- Processing time in the last node; from
These processing times are evaluated using the following options.
True, the processing time in the first node is included in the path analysis. The callback_start in the first node is that of a trace data whose timestamp is the closest to publish and thread id (tid) is the same as publish. Note that a callback object of the callback_start in the first node can vary if the node has several callbacks and any of them runs before publish.
True, the processing time in the last node is included in the path analysis.
Path.include_first_callback/include_last_callback is as following.
app = Application(arch, lttng) path_name = 'target_path' target_path = app.get_path(path_name) target_path.include_first_callback = True # Include first callback in path analysis. target_path.include_last_callback = True # Include last callback in path analysis.