Merge branch 'tracetools' into 'master'
See merge request micro-ROS/ros2_tracing!1 Signed-off-by: Ingo Luetkebohle <ingo.luetkebohle@de.bosch.com>
This commit is contained in:
commit
cc3c142f9f
37 changed files with 2981 additions and 0 deletions
32
.gitlab-ci.yml
Normal file
32
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,32 @@
|
|||
image: ros:dashing-ros-base
|
||||
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay2
|
||||
|
||||
before_script:
|
||||
- apt update
|
||||
- rosdep update
|
||||
- rosdep install -y --from-paths . -i .
|
||||
- ln -s src
|
||||
|
||||
build:
|
||||
script:
|
||||
- colcon build
|
||||
- colcon test
|
||||
artifacts:
|
||||
paths:
|
||||
- install
|
||||
- build
|
||||
reports:
|
||||
junit: build/**/Test.xml
|
||||
|
||||
build_enabled:
|
||||
script:
|
||||
- colcon build --cmake-args -DWITH_LTTNG=ON
|
||||
- colcon test
|
||||
artifacts:
|
||||
paths:
|
||||
- install
|
||||
- build
|
||||
reports:
|
||||
junit: build/**/Test.xml
|
202
LICENSE
Normal file
202
LICENSE
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
518
doc/design_ros_2.md
Normal file
518
doc/design_ros_2.md
Normal file
|
@ -0,0 +1,518 @@
|
|||
# ROS 2 tracing
|
||||
|
||||
Design document for ROS 2 tracing, instrumentation, and analysis effort.
|
||||
|
||||
## Introduction
|
||||
|
||||
Tracing allows to record run-time data from a system, both for system data (e.g., when a process
|
||||
is being scheduled, or when I/O occurs) and for user-defined data. This package helps with
|
||||
user-defined trace data within the ROS2 framework, e.g. to trace when messages arrive,
|
||||
when timers fire, when callbacks are being run, etc.
|
||||
|
||||
## Goals and requirements
|
||||
|
||||
### Goals
|
||||
|
||||
1. Provide low-overhead tools and resources for robotics software development based on ROS 2.
|
||||
2. Make tracing easier to use with ROS.
|
||||
|
||||
### Requirements: instrumentation
|
||||
|
||||
Instrumentation should be built around the main uses of ROS 2, and should include relevant information:
|
||||
|
||||
1. Overall
|
||||
1. When creating a publisher/subscriber/service/client/etc., appropriate references should be kept in order to correlate with other tracepoints related to the same instance.
|
||||
1. Publishers & subscriptions
|
||||
1. When creating a publisher/subscription, the effective topic name should be included (i.e. including namespace and after remapping).
|
||||
2. When publishing a message, some sort of message identifier should be included in the tracepoint so it can be tracked through DDS up to the subscriber's side.
|
||||
3. Callbacks (subscription, service, client, timer)
|
||||
1. Callback function symbol should be included, whenever possible.
|
||||
2. Information about callback execution (e.g. start & end) should be available.
|
||||
4. Timers
|
||||
1. Information about the period should be available.
|
||||
5. Executors
|
||||
1. Information about spin cycles & periods should be available.
|
||||
6. Others
|
||||
1. Provide generic tracepoints for user code.
|
||||
|
||||
### Requirements: analysis & visualization
|
||||
|
||||
Analyses process trace data. They should be general enough to be useful for different use-cases, e.g.:
|
||||
|
||||
* Callback duration
|
||||
* Time between callbacks (between two callback starts and/or a callback end and a start)
|
||||
* Message age (as the difference between processing time and message timestamp)
|
||||
* Message size
|
||||
* Memory usage
|
||||
* Execution time/proportion accross a process' nodes/components
|
||||
* Interruptions (noting that these may be more useful as time-based metrics instead of overall statistics):
|
||||
* scheduling events during a callback
|
||||
* delay between the moment a thread becomes ready and when it's actually scheduled
|
||||
* CPU cycles
|
||||
|
||||
with mean, stdev, etc. when applicable.
|
||||
|
||||
Generic tracepoints for ROS 2 user code could be applied to a user-provided model for higher-level behaviour statistics and visualization.
|
||||
|
||||
### Tools/accessibility
|
||||
|
||||
To make tracing ROS 2 more accessible and easier to adopt, we can put effort into integrating LTTng session setup & recording into the ROS 2 launch system.
|
||||
|
||||
This might include converting existing `tracetools` scripts to more flexible Python scripts, and then plugging that into the launch system.
|
||||
|
||||
## Instrumentation design
|
||||
|
||||
This section includes information about ROS 2's design & architecture through descriptions of the main execution flows. The instrumentation can then be built around that.
|
||||
|
||||
### Flow description
|
||||
|
||||
#### Process creation
|
||||
|
||||
In the call to `rclcpp::init()`, a process-specific `rclcpp::Context` object is fetched and CLI arguments are parsed. Much of the work is actually done by `rcl` through a call to `rcl_init()`. This call processes the `rcl_context_t` handle, which is wrapped by the `Context` object. Also, inside this call, `rcl` calls `rmw_init()` to process the `rmw` context (`rmw_context_t`) as well. This `rmw` handle is itself part of the `rcl_context_t` handle.
|
||||
|
||||
This has to be done once per process, and usually at the very beginning. The components that are then instanciated share this context.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant process
|
||||
participant rclcpp
|
||||
participant Context
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
process->>rclcpp: rclcpp::init(argc, argv)
|
||||
Note over rclcpp: fetches process-specific Context object
|
||||
rclcpp->>Context: init(argc, argv)
|
||||
Note over Context: allocates rcl_context_t handle
|
||||
Context->>rcl: rcl_init(out rcl_context_t)
|
||||
Note over rcl: validates & processes rcl_context_t handle
|
||||
rcl->>rmw: rmw_init(out rmw_context_t)
|
||||
Note over rmw: validates & processes rmw_context_t handle
|
||||
|
||||
rcl-->>tracetools: TP(rcl_init, rcl_context_t *)
|
||||
```
|
||||
|
||||
#### Node/component creation
|
||||
|
||||
In ROS 2, a process can contain multiple nodes. These are sometimes referred to as "components."
|
||||
|
||||
These components are instanciated by the containing process. They are usually classes that extend `rclcpp::Node`, so that the node initialization work is done by the parent constructor.
|
||||
|
||||
This parent constructor will allocate its own `rcl_node_t` handle and call `rcl_node_init()`, which will validate the node name/namespace. `rcl` will also call `rmw_create_node()` to get the node's `rmw` handle (`rmw_node_t`). This will be used later by publishers and subscriptions.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant process
|
||||
participant Component
|
||||
participant rclcpp
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
process->>Component: Component()
|
||||
Component->>rclcpp: : Node(node_name, namespace)
|
||||
Note over rclcpp: allocates rcl_node_t handle
|
||||
rclcpp->>rcl: rcl_node_init(out rcl_node_t, node_name, namespace)
|
||||
Note over rcl: validates node name/namespace
|
||||
Note over rcl: populates rcl_note_t
|
||||
rcl->>rmw: rmw_create_node(node_name, local_namespace) : rmw_node_t
|
||||
Note over rmw: creates rmw_node_t handle
|
||||
|
||||
rcl-->>tracetools: TP(rcl_node_init, rcl_node_t *, rmw_node_t *, node_name, namespace)
|
||||
```
|
||||
|
||||
#### Publisher creation
|
||||
|
||||
The component calls `create_publisher()`, a `rclcpp::Node` method for convenience. That ends up creating an `rclcpp::Publisher` object which extends `rclcpp::PublisherBase`. The latter allocates an `rcl_publisher_t` handle, fetches the corresponding `rcl_node_t` handle, and calls `rcl_publisher_init()` in its constructor. `rcl` does topic name expansion/remapping/validation. It creates an `rmw_publisher_t` handle by calling `rmw_create_publisher()` of the given `rmw` implementation and associates with the node's `rmw_node_t` handle and the publisher's `rcl_publisher_t` handle.
|
||||
|
||||
If intra-process publishing/subscription is enabled, it will be set up after creating the publisher object, through a call to `PublisherBase::setup_intra_process()`, which calls `rcl_publisher_init()`.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Component
|
||||
participant rclcpp
|
||||
participant Publisher
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
Component->>rclcpp: create_publisher(topic_name, options, use_intra_process)
|
||||
Note over rclcpp: (...)
|
||||
rclcpp->>Publisher: Publisher(topic_name, options)
|
||||
Note over Publisher: allocates rcl_publisher_t handle
|
||||
Publisher->>rcl: rcl_publisher_init(out rcl_publisher_t, rcl_node_t, topic_name, options)
|
||||
Note over rcl: populates rcl_publisher_t
|
||||
rcl->>rmw: rmw_create_publisher(rmw_node_t, topic_name, qos_options) : rmw_publisher_t
|
||||
Note over rmw: creates rmw_publisher_t handle
|
||||
|
||||
rcl-->>tracetools: TP(rcl_publisher_init, rcl_node_t *, rcl_publisher_t *, rmw_publisher_t *, topic_name, depth)
|
||||
|
||||
opt use_intra_process
|
||||
rclcpp->>Publisher: setup_intra_process()
|
||||
Publisher->>rcl: rcl_publisher_init(...)
|
||||
end
|
||||
```
|
||||
|
||||
#### Subscription creation
|
||||
|
||||
Subscription creation is done in a very similar manner.
|
||||
|
||||
The componenent calls `create_publisher()`, which ends up creating an `rclcpp::Subscription` object which extends `rclcpp::SubscriptionBase`. The latter allocates an `rcl_subscription_t` handle, fetches its `rcl_node_t` handle, and calls `rcl_subscription_init()` in its constructor. `rcl` does topic name expansion/remapping/validation. It creates an `rmw_subscription_t` handle by calling `rmw_create_subscription()` of the given `rmw` implementation and associates it with the node's `rmw_node_t` handle and the subscription's `rcl_subscription_t` handle.
|
||||
|
||||
If intra-process publishing/subscription is enabled, it will be set up after creating the subscription object, through a call to `Subscription::setup_intra_process()`, which calls `rcl_subscription_init()`. This is very similar to a normal (inter-process) subscription, but it sets some flags for later.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Component
|
||||
participant rclcpp
|
||||
participant Subscription
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
Component->>rclcpp: create_subscription(topic_name, callback, options, use_intra_process)
|
||||
Note over rclcpp: (...)
|
||||
rclcpp->>Subscription: Subscription(topic_name, callback, options)
|
||||
Note over Subscription: allocates rcl_subscription_t handle
|
||||
Subscription->>rcl: rcl_subscription_init(out rcl_subscription_t, rcl_node_t, topic_name, options)
|
||||
Note over rcl: populates rcl_subscription_t
|
||||
rcl->>rmw: rmw_create_subscription(rmw_node_t, topic_name, qos_options) : rmw_subscription_t
|
||||
Note over rmw: creates rmw_subscription_t handle
|
||||
|
||||
rcl-->>tracetools: TP(rcl_subscription_init, rcl_node_t *, rcl_subscription_t *, rmw_subscription_t *, topic_name, depth)
|
||||
|
||||
opt use_intra_process
|
||||
rclcpp->>Subscription: setup_intra_process()
|
||||
Subscription->>rcl: rcl_subscription_init(...)
|
||||
end
|
||||
|
||||
rclcpp-->>tracetools: TP(rclcpp_subscription_callback_added, rcl_subscription_t *, &any_callback)
|
||||
```
|
||||
|
||||
#### Executors
|
||||
|
||||
An `rclcpp::executor::Executor` object is created for a given process. It can be a `SingleThreadedExecutor` or a `MultiThreadedExecutor`.
|
||||
|
||||
Components are instanciated, usually as a `shared_ptr` through `std::make_shared<Component>()`, then added to the executor with `Executor::add_node()`.
|
||||
|
||||
After all the components have been added, `Executor::spin()` is called. `SingleThreadedExecutor::spin()` simply loops forever until the process' context isn't valid anymore. It fetches the next `rclcpp::AnyExecutable` (e.g. subscription, timer, service, client), and calls `Executor::execute_any_executable()` with it. This then calls the relevant `execute*()` method (e.g. `execute_timer()`, `execute_subscription()`, `execute_intra_process_subscription()`, `execute_service()`, `execute_client()`).
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant process
|
||||
participant Executor
|
||||
participant tracetools
|
||||
|
||||
process->>Executor: Executor()
|
||||
Note over process: instanciates components
|
||||
process->>Executor: add_node(component)
|
||||
process->>Executor: spin()
|
||||
loop until shutdown
|
||||
Executor-->>tracetools: TP(?)
|
||||
|
||||
Note over Executor: get_next_executable()
|
||||
Note over Executor: execute_any_executable()
|
||||
Note over Executor: execute_*()
|
||||
end
|
||||
```
|
||||
|
||||
#### Subscription callbacks
|
||||
|
||||
Subscriptions are handled in the `rclcpp` layer. Callbacks are wrapped by an `rclcpp::AnySubscriptionCallback` object, which is registered when creating the `rclcpp::Subscription` object.
|
||||
|
||||
In `execute_*subscription()`, the `Executor` asks the `Subscription` to allocate a message though `Subscription::create_message()`. It then calls `rcl_take*()`, which calls `rmw_take_with_info()`. If that is successful, the `Executor` then passes that on to the subscription through `rclcpp::SubscriptionBase::handle_message()`. This checks if it's the right type of subscription (i.e. inter vs. intra process), then it calls `dispatch()` on the `rclcpp::AnySubscriptionCallback` object with the message (cast to the actual type). This calls the actual `std::function` with the right signature.
|
||||
|
||||
Finally, it returns the message object through `Subscription::return_message()`.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Executor
|
||||
participant Subscription
|
||||
participant AnySubscriptionCallback
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
Note over Executor: execute_subscription()
|
||||
Executor->>Subscription: create_message(): std::shared_ptr<void>
|
||||
Executor->>rcl: rcl_take*(rcl_subscription_t, out msg) : ret
|
||||
rcl->>rmw: rmw_take_with_info(rmw_subscription_t, out msg, out taken)
|
||||
Note over rmw: copies available message to msg if there is one
|
||||
opt RCL_RET_OK == ret
|
||||
Executor->>Subscription: handle_message(msg)
|
||||
Note over Subscription: casts msg to its actual type
|
||||
Subscription->>AnySubscriptionCallback: dispatch(typed_msg)
|
||||
AnySubscriptionCallback-->>tracetools: TP(callback_start, this, is_intra_process)
|
||||
Note over AnySubscriptionCallback: std::function(...)
|
||||
AnySubscriptionCallback-->>tracetools: TP(callback_end, this)
|
||||
end
|
||||
Executor->>Subscription: return_message(msg)
|
||||
```
|
||||
|
||||
#### Message publishing
|
||||
|
||||
To publish a message, an object is first allocated and then populated by the `Component` (or equivalent). Then, the message is sent to the `Publisher` through `publish()`. This then passes that on to `rcl`, which itself passes it to `rmw`.
|
||||
|
||||
TODO add inter- vs. intra-process execution flow
|
||||
TODO talk about IntraProcessManager stuff?
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Component
|
||||
participant Publisher
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
Note over Component: creates a msg
|
||||
Component->>Publisher: publish(msg)
|
||||
Note over Publisher: ...
|
||||
Publisher->>rcl: rcl_publish(rcl_publisher_t, msg)
|
||||
rcl->>rmw: rmw_publish(rmw_publisher_t, msg)
|
||||
rmw-->>tracetools: TP(?)
|
||||
```
|
||||
|
||||
#### Service creation
|
||||
|
||||
Service server creation is similar to subscription creation. The `Component` calls `create_service()` which ends up creating a `rclcpp::Service`. In its constructor, it allocates a `rcl_service_t` handle, then calls `rcl_service_init()`. This processes the handle and validates the service name. It calls `rmw_create_service()` to get the corresponding `rmw_service_t` handle.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Component
|
||||
participant rclcpp
|
||||
participant Service
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
Component->>rclcpp: create_service(service_name, callback)
|
||||
Note over rclcpp: (...)
|
||||
rclcpp->>Service: Service(rcl_node_t, service_name, callback, options)
|
||||
Note over Service: allocates a rcl_service_t handle
|
||||
Service->>rcl: rcl_service_init(out rcl_service_t, rcl_node_t, service_name, options)
|
||||
Note over rcl: validates & processes service handle
|
||||
rcl->>rmw: rmw_create_service(rmw_node_t, service_name, qos_options) : rmw_service_t
|
||||
Note over rmw: creates rmw_service_t handle
|
||||
rcl-->>tracetools: TP(rcl_service_init, rcl_node_t *, rcl_service_t *, rmw_service_t *, service_name)
|
||||
Service-->>tracetools: TP(rclcpp_service_callback_added, rcl_service_t *, &any_callback)
|
||||
```
|
||||
|
||||
#### Service callbacks
|
||||
|
||||
Service callbacks are similar to subscription callbacks. In `execute_service()`, the `Executor` allocates request header and request objects. It then calls `rcl_take_request()` and passes them along with the service handle.
|
||||
|
||||
`rcl` calls `rmw_take_request()`. If those are successful, then the `Executor` calls `handle_request()` on the `Service`. This casts the request to its actual type, allocates a response object, and calls `dispatch()` on its `AnyServiceCallback` object, which calls the actual `std::function` with the right signature.
|
||||
|
||||
For the service response, `Service` calls `rcl_send_response()` which calls `rmw_send_response()`.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Executor
|
||||
participant Service
|
||||
participant AnyServiceCallback
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
Note over Executor: execute_service()
|
||||
Note over Executor: allocates request header and request
|
||||
Executor->>rcl: rcl_take_request(rcl_service, out request_header, out request) : ret
|
||||
rcl->>rmw: rmw_take_request(rmw_service_t, out request_header, out request, out taken)
|
||||
opt RCL_RET_OK == ret
|
||||
Executor->>Service: handle_request(request_header, request)
|
||||
Note over Service: casts request to its actual type
|
||||
Note over Service: allocates a response object
|
||||
Service->>AnyServiceCallback: dispatch(request_header, typed_request, response)
|
||||
AnyServiceCallback-->>tracetools: TP(callback_start, this)
|
||||
Note over AnyServiceCallback: std::function(...)
|
||||
AnyServiceCallback-->>tracetools: TP(callback_end, this)
|
||||
Service->>rcl: rcl_send_response(rcl_service_t, request_header, response)
|
||||
rcl->>rmw: rmw_send_response(rmw_service_t, request_header, response)
|
||||
end
|
||||
```
|
||||
|
||||
#### Client creation
|
||||
|
||||
Client creation is similar to publisher creation. The `Component` calls `create_client()` which ends up creating a `rclcpp::Client`. In its constructor, it allocates a `rcl_client_t` handle, then calls `rcl_client_init()`. This validates and processes the handle. It also calls `rmw_create_client()` which creates the `rmw_client_t` handle.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Component
|
||||
participant Node
|
||||
participant Client
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
Component->>Node: create_client(service_name, options)
|
||||
Node->>Client: Client(service_name, options)
|
||||
Note over Client: allocates a rcl_client_t handle
|
||||
Client->>rcl: rcl_client_init(out rcl_client_t, rcl_node_t, service_name, options)
|
||||
Note over rcl: validates and processes rcl_client_t handle
|
||||
rcl->>rmw: rmw_create_client(rmw_node_t, service_name, qos_options) : rmw_client_t
|
||||
Note over rmw: creates rmw_client_t handle
|
||||
rcl-->>tracetools: TP(rcl_client_init, rcl_node_t *, rcl_client_t *, rmw_client_t *, service_name)
|
||||
```
|
||||
|
||||
#### Client request
|
||||
|
||||
A client request has multiple steps. The `Component` (or the owner of the `Client`) first creates a request object. It then calls `Client::async_send_request()` with the request. It can also provide a callback, but it's optional. The `Client` passes that on to `rcl` by calling `rcl_send_request()`. `rcl` generates a sequence number and assigns it to the request, then calls `rmw_send_request()`. Once this is done, the `Client` puts this sequence number in an internal map along with the created promise and future objects, and the callback (which might simply be empty).
|
||||
|
||||
At this point, the `Client` could simply let its callback be called. It can also use the future object returned by `async_send_request()`, and call `rclcpp::spin_until_future_complete()`. This waits until the future object is ready, or until timeout, and returns.
|
||||
|
||||
If this last call was successful, then the `Component` can get the result and do something with it.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Component
|
||||
participant Executor
|
||||
participant Client
|
||||
participant rclcpp
|
||||
participant rcl
|
||||
participant rmw
|
||||
participant tracetools
|
||||
|
||||
Note over rmw: (implementation)
|
||||
|
||||
Note over Component: creates request
|
||||
Component->>Client: async_send_request(request[, callback]) : result_future
|
||||
Client->>rcl: rcl_send_request(rcl_client_t, request, out sequence_number)
|
||||
Note over rcl: assigns sequence_number
|
||||
rcl-->>tracetools: TP?(rcl_send_request, rcl_client_t *, sequence_number)
|
||||
rcl->>rmw: rmw_send_request(rmw_client_t, request, sequence_number)
|
||||
Note over Client: puts sequence_number in a map with promise+callback+future
|
||||
|
||||
Component->>rclcpp: spin_until_future_complete(result_future) : result_status
|
||||
|
||||
Note over Executor: execute_client()
|
||||
Note over Executor: creates request_header and response objects
|
||||
Executor->>rcl: rcl_take_response(rcl_client_t, out request_header, out response) : ret
|
||||
rcl-->>tracetools: TP?()
|
||||
rcl->>rmw: rmw_take_response(rmw_client_t, out request_header, out response, out taken)
|
||||
opt RCL_RET_OK == ret
|
||||
Executor->>Client: handle_response(request_header, response)
|
||||
Note over Client: gets sequence_number from request_header
|
||||
Client-->>tracetools: TP?()
|
||||
Note over Client: gets promise+callback+future from its map
|
||||
Note over Client: callback(future)
|
||||
end
|
||||
|
||||
rclcpp->>Component: ready or timeout
|
||||
opt SUCCESS == result_status
|
||||
Note over Component: result_future.get() : result
|
||||
Note over Component: do something with result
|
||||
end
|
||||
```
|
||||
|
||||
#### Timer creation
|
||||
|
||||
Timer creation is similar to subscription creation. The `Component` calls `create_service()` which ends up creating a `rclcpp::WallTimer`. In its constructor, it creates a `rclcpp::Clock` object, which (for a `WallTimer`) is simply a nanosecond clock. It then allocates a `rcl_timer_t` handle, then calls `rcl_timer_init()`. This processes the handle and validates the period.
|
||||
|
||||
Note that `rcl_timer_init()` can take a callback as a parameter, but right now that feature is not used anywhere (`nullptr` is given), and callbacks are instead handled in the `rclcpp` layer.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Component
|
||||
participant Node
|
||||
participant WallTimer
|
||||
participant rcl
|
||||
participant tracetools
|
||||
|
||||
Component->>Node: create_wall_timer(period, callback)
|
||||
Node->>WallTimer: WallTimer(period, callback, Context)
|
||||
Note over WallTimer: creates a Clock object
|
||||
Note over WallTimer: allocates a rcl_timer_t handle
|
||||
WallTimer->>rcl: rcl_timer_init(out rcl_timer_t, Clock, rcl_context_t, period)
|
||||
Note over rcl: validates and processes rcl_timer_t handle
|
||||
rcl-->>tracetools: TP(rcl_timer_init, rcl_timer_t *, period)
|
||||
WallTimer-->>tracetools: TP(rclcpp_timer_callback_added, rcl_timer_t *, &callback)
|
||||
```
|
||||
|
||||
#### Timer callbacks
|
||||
|
||||
Timer callbacks are similar to susbcription callbacks. In `execute_timer()`, the `Executor` calls `execute_callback()` on the `WallTimer`. The timer then calls `rcl_timer_call()` with its `rcl_timer_t` handle and checks if the callback should be called.
|
||||
|
||||
If it that is the case, then the timer will call the actual `std::function`. Depending on the `std::function` that was given when creating the timer, it will either call the callback without any parameters or it will pass a reference of itself.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Executor
|
||||
participant WallTimer
|
||||
participant rcl
|
||||
participant tracetools
|
||||
|
||||
Note over Executor: execute_timer()
|
||||
Executor->>WallTimer: execute_callback()
|
||||
WallTimer->>rcl: rcl_timer_call(rcl_timer_t) : ret
|
||||
Note over rcl: validates and updates timer
|
||||
opt RCL_RET_TIMER_CANCELED != ret && RCL_RET_OK == ret
|
||||
WallTimer-->>tracetools: TP(callback_start, &callback)
|
||||
Note over WallTimer: std::function(...)
|
||||
WallTimer-->>tracetools: TP(callback_end, &callback)
|
||||
end
|
||||
```
|
||||
|
||||
## Design & implementation notes
|
||||
|
||||
### Targeted tools/dependencies
|
||||
|
||||
The targeted tools or dependencies are:
|
||||
|
||||
* LTTng for tracing
|
||||
* pandas and Jupyter for analysis & visualization
|
||||
|
||||
### Design
|
||||
|
||||
The plan is to use LTTng with a ROS wrapper package like `tracetools` for ROS 1. The suggested setup is:
|
||||
|
||||
* a tracing package (e.g. `tracetools`) wraps calls to LTTng
|
||||
* ROS 2 is instrumented with calls to the tracing package, therefore it becomes a dependency and ships with the core stack
|
||||
* by default, the tracing package's functions are empty -- they do not do anything
|
||||
* if users wants to enable tracing, they need to
|
||||
* install LTTng
|
||||
* compile the tracing package from source, setting the right compile flag(s)
|
||||
* overlay it on top of their ROS 2 installation
|
||||
* use other package(s) for analysis and visualization
|
||||
|
||||
## Architecture
|
||||
|
||||

|
||||
|
||||
### Timeline
|
||||
|
||||
The first goal is to statically instrument ROS 2, aiming for it to be in the ROS 2 E-turtle release (Nov 2019).
|
||||
|
||||
This includes transposing the existing ROS 1 instrumentation to ROS 2, wherever applicable. This step may not include instrumenting DDS implementations, and thus may be limited to the layer(s) right before `rmw`.
|
||||
|
||||
### Notes on client libraries
|
||||
|
||||
ROS offer a client library (`rcl`) written in C as the base for any language-specific implementation, such as `rclcpp` and `rclpy`.
|
||||
|
||||
However, `rcl` is obviously fairly basic, and still does leave a fair amount of implementation work up to the client libraries. For example, callbacks are not handled in `rcl`, and are left to the client library implementations.
|
||||
|
||||
This means that some instrumentation work will have to be re-done for every client library that we want to trace. We cannot simply instrument `rcl`, nor can we only instrument the base `rmw` interface if we want to dig into that.
|
||||
|
||||
This effort should first focus on `rcl` and `rclcpp` , but `rclpy` should eventually be added and supported.
|
||||
|
||||
### ROS 1/2 compatibility
|
||||
|
||||
We could look into making analyses work on both ROS 1 and ROS 2, through a common instrumentation interface (or other abstraction).
|
BIN
doc/img/tracing_architecture.png
Normal file
BIN
doc/img/tracing_architecture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 107 KiB |
23
doc/public_post_plan.md
Normal file
23
doc/public_post_plan.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
Plan for a first public post/announcement.
|
||||
This week.
|
||||
|
||||
Simple example:
|
||||
* Jupyter notebook to plot:
|
||||
* subscription callback duration
|
||||
* CPU cycles/callback
|
||||
* and resolve/demangle symbols
|
||||
* Include all files/scripts needed to get the result, but keep it simple
|
||||
* To be hosted somewhere
|
||||
* With some documentation/guide on how to try it out
|
||||
|
||||
Post:
|
||||
* Mention that we're working on this
|
||||
* Talk about goals in general
|
||||
* Target = next release
|
||||
* Show off example
|
||||
* Invite people to ask questions/discuss
|
||||
|
||||
Architecture considerations (right now but also later):
|
||||
* Try to use a dataframe as a ROS model abstraction, instead of first going through a layer with python data structures
|
||||
*
|
96
tracetools/CMakeLists.txt
Normal file
96
tracetools/CMakeLists.txt
Normal file
|
@ -0,0 +1,96 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
project(tracetools)
|
||||
|
||||
# Default to C++14
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic -fPIC)
|
||||
endif()
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
|
||||
option(WITH_LTTNG "Include support for tracing with LTTng" OFF)
|
||||
if(WITH_LTTNG)
|
||||
# Try to find LTTng
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LTTNG REQUIRED lttng-ust)
|
||||
endif()
|
||||
if(LTTNG_FOUND)
|
||||
set(LTTNG_TP_FILES
|
||||
include/tracetools/tp_call.h
|
||||
src/tp_call.c
|
||||
)
|
||||
set(TRACING_ENABLED TRUE)
|
||||
message("LTTng found: tracing enabled")
|
||||
elseif(WITH_LTTNG)
|
||||
message("LTTng NOT found: tracing disabled")
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
|
||||
# Status checking tool
|
||||
add_executable(status
|
||||
src/status.c
|
||||
src/tracetools.c
|
||||
src/utils.cpp
|
||||
)
|
||||
target_link_libraries(status
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
ament_target_dependencies(status
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
install(TARGETS
|
||||
status
|
||||
DESTINATION lib/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
# Tracetools lib
|
||||
set(SOURCES
|
||||
src/tracetools.c
|
||||
src/utils.cpp
|
||||
)
|
||||
if(TRACING_ENABLED)
|
||||
list(APPEND SOURCES ${LTTNG_TP_FILES})
|
||||
endif()
|
||||
|
||||
add_library(${PROJECT_NAME} ${SOURCES})
|
||||
if(TRACING_ENABLED)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC TRACETOOLS_LTTNG_ENABLED)
|
||||
target_link_libraries(${PROJECT_NAME} ${LTTNG_LIBRARIES} -ldl)
|
||||
else()
|
||||
target_link_libraries(${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_export_interfaces(${PROJECT_NAME}_export HAS_LIBRARY_TARGET)
|
||||
install(
|
||||
DIRECTORY include/
|
||||
DESTINATION include
|
||||
)
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}_export
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
INCLUDES DESTINATION include
|
||||
)
|
||||
|
||||
ament_export_include_directories(include)
|
||||
if(TRACING_ENABLED)
|
||||
ament_export_libraries(${PROJECT_NAME} ${LTTNG_LIBRARIES})
|
||||
else()
|
||||
ament_export_libraries(${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
|
||||
add_definitions("-D${PROJECT_NAME}_VERSION=\"${${PROJECT_NAME}_VERSION}\"")
|
26
tracetools/README.md
Normal file
26
tracetools/README.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
# tracetools
|
||||
|
||||
## Building
|
||||
|
||||
If tracing is not enabled when building, or if LTTng is not found, then this package will not do anything.
|
||||
|
||||
To enable tracing:
|
||||
|
||||
1. Install [LTTng](https://lttng.org/docs/v2.10/#doc-ubuntu):
|
||||
```
|
||||
$ sudo apt-get install lttng-tools lttng-modules-dkms liblttng-ust-dev
|
||||
```
|
||||
2. Build with the `WITH_LTTNG` flag:
|
||||
```
|
||||
$ colcon build --cmake-args " -DWITH_LTTNG=ON"
|
||||
```
|
||||
3. Check if tracing is enabled (after sourcing):
|
||||
```
|
||||
$ ros2 run tracetools tracetools_status
|
||||
```
|
||||
|
||||
## Tracing
|
||||
|
||||
By default, the steps above will not lead to trace data being generated, and thus they will have no impact on execution.
|
||||
|
||||
LTTng has to be enabled: TODO mention scripts
|
228
tracetools/include/tracetools/tp_call.h
Normal file
228
tracetools/include/tracetools/tp_call.h
Normal file
|
@ -0,0 +1,228 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Provide fake header guard for cpplint
|
||||
#undef TRACETOOLS__TP_CALL_H_
|
||||
#ifndef TRACETOOLS__TP_CALL_H_
|
||||
#define TRACETOOLS__TP_CALL_H_
|
||||
|
||||
#undef TRACEPOINT_PROVIDER
|
||||
#define TRACEPOINT_PROVIDER ros2
|
||||
|
||||
#undef TRACEPOINT_INCLUDE
|
||||
#define TRACEPOINT_INCLUDE "tracetools/tp_call.h"
|
||||
|
||||
#if !defined(_TRACETOOLS__TP_CALL_H_) || defined(TRACEPOINT_HEADER_MULTI_READ)
|
||||
#define _TRACETOOLS__TP_CALL_H_
|
||||
|
||||
#include <lttng/tracepoint.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rcl_init,
|
||||
TP_ARGS(
|
||||
const void *, context_handle_arg,
|
||||
const char *, version_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, context_handle, context_handle_arg)
|
||||
ctf_string(version, version_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rcl_node_init,
|
||||
TP_ARGS(
|
||||
const void *, node_handle_arg,
|
||||
const void *, rmw_handle_arg,
|
||||
const char *, node_name_arg,
|
||||
const char *, namespace_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, node_handle, node_handle_arg)
|
||||
ctf_integer_hex(const void *, rmw_handle, rmw_handle_arg)
|
||||
ctf_string(node_name, node_name_arg)
|
||||
ctf_string(namespace, namespace_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rcl_publisher_init,
|
||||
TP_ARGS(
|
||||
const void *, publisher_handle_arg,
|
||||
const void *, node_handle_arg,
|
||||
const void *, rmw_publisher_handle_arg,
|
||||
const char *, topic_name_arg,
|
||||
const size_t, queue_depth_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, publisher_handle, publisher_handle_arg)
|
||||
ctf_integer_hex(const void *, node_handle, node_handle_arg)
|
||||
ctf_integer_hex(const void *, rmw_publisher_handle, rmw_publisher_handle_arg)
|
||||
ctf_string(topic_name, topic_name_arg)
|
||||
ctf_integer(const size_t, queue_depth, queue_depth_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rcl_subscription_init,
|
||||
TP_ARGS(
|
||||
const void *, subscription_handle_arg,
|
||||
const void *, node_handle_arg,
|
||||
const void *, rmw_subscription_handle_arg,
|
||||
const char *, topic_name_arg,
|
||||
const size_t, queue_depth_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, subscription_handle, subscription_handle_arg)
|
||||
ctf_integer_hex(const void *, node_handle, node_handle_arg)
|
||||
ctf_integer_hex(const void *, rmw_subscription_handle, rmw_subscription_handle_arg)
|
||||
ctf_string(topic_name, topic_name_arg)
|
||||
ctf_integer(const size_t, queue_depth, queue_depth_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rclcpp_subscription_callback_added,
|
||||
TP_ARGS(
|
||||
const void *, subscription_handle_arg,
|
||||
const void *, callback_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, subscription_handle, subscription_handle_arg)
|
||||
ctf_integer_hex(const void *, callback, callback_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rcl_service_init,
|
||||
TP_ARGS(
|
||||
const void *, service_handle_arg,
|
||||
const void *, node_handle_arg,
|
||||
const void *, rmw_service_handle_arg,
|
||||
const char *, service_name_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, service_handle, service_handle_arg)
|
||||
ctf_integer_hex(const void *, node_handle, node_handle_arg)
|
||||
ctf_integer_hex(const void *, rmw_service_handle, rmw_service_handle_arg)
|
||||
ctf_string(service_name, service_name_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rclcpp_service_callback_added,
|
||||
TP_ARGS(
|
||||
const void *, service_handle_arg,
|
||||
const void *, callback_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, service_handle, service_handle_arg)
|
||||
ctf_integer_hex(const void *, callback, callback_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rcl_client_init,
|
||||
TP_ARGS(
|
||||
const void *, client_handle_arg,
|
||||
const void *, node_handle_arg,
|
||||
const void *, rmw_client_handle_arg,
|
||||
const char *, service_name_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, client_handle, client_handle_arg)
|
||||
ctf_integer_hex(const void *, node_handle, node_handle_arg)
|
||||
ctf_integer_hex(const void *, rmw_client_handle, rmw_client_handle_arg)
|
||||
ctf_string(service_name, service_name_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rcl_timer_init,
|
||||
TP_ARGS(
|
||||
const void *, timer_handle_arg,
|
||||
int64_t, period_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, timer_handle, timer_handle_arg)
|
||||
ctf_integer(int64_t, period, period_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rclcpp_timer_callback_added,
|
||||
TP_ARGS(
|
||||
const void *, timer_handle_arg,
|
||||
const void *, callback_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, timer_handle, timer_handle_arg)
|
||||
ctf_integer_hex(const void *, callback, callback_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
rclcpp_callback_register,
|
||||
TP_ARGS(
|
||||
const void *, callback_arg,
|
||||
const char *, symbol_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, callback, callback_arg)
|
||||
ctf_string(symbol, symbol_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
callback_start,
|
||||
TP_ARGS(
|
||||
const void *, callback_arg,
|
||||
int, is_intra_process_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, callback, callback_arg)
|
||||
ctf_integer(int, is_intra_process, is_intra_process_arg)
|
||||
)
|
||||
)
|
||||
|
||||
TRACEPOINT_EVENT(
|
||||
TRACEPOINT_PROVIDER,
|
||||
callback_end,
|
||||
TP_ARGS(
|
||||
const void *, callback_arg
|
||||
),
|
||||
TP_FIELDS(
|
||||
ctf_integer_hex(const void *, callback, callback_arg)
|
||||
)
|
||||
)
|
||||
|
||||
#endif // _TRACETOOLS__TP_CALL_H_
|
||||
|
||||
#include <lttng/tracepoint-event.h>
|
||||
|
||||
#endif // TRACETOOLS__TP_CALL_H_
|
153
tracetools/include/tracetools/tracetools.h
Normal file
153
tracetools/include/tracetools/tracetools.h
Normal file
|
@ -0,0 +1,153 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef TRACETOOLS__TRACETOOLS_H_
|
||||
#define TRACETOOLS__TRACETOOLS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define TRACEPOINT(event_name, ...) \
|
||||
(ros_trace_ ## event_name)(__VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Report whether tracing is compiled in
|
||||
*/
|
||||
bool ros_trace_compile_status();
|
||||
|
||||
/**
|
||||
* tp: rcl_init
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rcl_init,
|
||||
const void * context_handle);
|
||||
|
||||
/**
|
||||
* tp: rcl_node_init
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rcl_node_init,
|
||||
const void * node_handle,
|
||||
const void * rmw_handle,
|
||||
const char * node_name,
|
||||
const char * node_namespace);
|
||||
|
||||
/**
|
||||
* tp: rcl_publisher_init
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rcl_publisher_init,
|
||||
const void * publisher_handle,
|
||||
const void * node_handle,
|
||||
const void * rmw_publisher_handle,
|
||||
const char * topic_name,
|
||||
const size_t queue_depth);
|
||||
|
||||
/**
|
||||
* tp: rcl_subscription_init
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rcl_subscription_init,
|
||||
const void * subscription_handle,
|
||||
const void * node_handle,
|
||||
const void * rmw_subscription_handle,
|
||||
const char * topic_name,
|
||||
const size_t queue_depth);
|
||||
|
||||
/**
|
||||
* tp: rclcpp_subscription_callback_added
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rclcpp_subscription_callback_added,
|
||||
const void * subscription_handle,
|
||||
const void * callback);
|
||||
|
||||
/**
|
||||
* tp: rcl_service_init
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rcl_service_init,
|
||||
const void * service_handle,
|
||||
const void * node_handle,
|
||||
const void * rmw_service_handle,
|
||||
const char * service_name);
|
||||
|
||||
/**
|
||||
* tp: rclcpp_service_callback_added
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
const void * service_handle,
|
||||
const void * callback);
|
||||
|
||||
/**
|
||||
* tp: rcl_client_init
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rcl_client_init,
|
||||
const void * client_handle,
|
||||
const void * node_handle,
|
||||
const void * rmw_client_handle,
|
||||
const char * service_name);
|
||||
|
||||
/**
|
||||
* tp: rcl_timer_init
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rcl_timer_init,
|
||||
const void * timer_handle,
|
||||
int64_t period);
|
||||
|
||||
/**
|
||||
* tp: rclcpp_timer_callback_added
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rclcpp_timer_callback_added,
|
||||
const void * timer_handle,
|
||||
const void * callback);
|
||||
|
||||
/**
|
||||
* tp: rclcpp_callback_register
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
const void * callback,
|
||||
const char * function_symbol);
|
||||
|
||||
/**
|
||||
* tp: callback_start
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
callback_start,
|
||||
const void * callback,
|
||||
const bool is_intra_process);
|
||||
|
||||
/**
|
||||
* tp: callback_end
|
||||
*/
|
||||
void TRACEPOINT(
|
||||
callback_end,
|
||||
const void * callback);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TRACETOOLS__TRACETOOLS_H_
|
35
tracetools/include/tracetools/utils.hpp
Normal file
35
tracetools/include/tracetools/utils.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef TRACETOOLS__UTILS_HPP_
|
||||
#define TRACETOOLS__UTILS_HPP_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <functional>
|
||||
|
||||
template<typename T, typename ... U>
|
||||
void * get_address(std::function<T(U...)> f)
|
||||
{
|
||||
typedef T (fnType)(U...);
|
||||
fnType ** fnPointer = f.template target<fnType *>();
|
||||
// Might be a lambda
|
||||
if (fnPointer == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return reinterpret_cast<void *>(*fnPointer);
|
||||
}
|
||||
|
||||
const char * get_symbol(void * funptr);
|
||||
|
||||
#endif // TRACETOOLS__UTILS_HPP_
|
24
tracetools/package.xml
Normal file
24
tracetools/package.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?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</name>
|
||||
<version>0.0.1</version>
|
||||
<description>ROS 2 wrapper for instrumentation</description>
|
||||
<maintainer email="fixed-term.christophe.bourquebedard@de.bosch.com">Christophe Bedard</maintainer>
|
||||
<maintainer email="ingo.luetkebohle@de.bosch.com">Ingo Luetkebohle</maintainer>
|
||||
<license>Apache Software License 2.0</license>
|
||||
<author email="ingo.luetkebohle@de.bosch.com">Ingo Luetkebohle</author>
|
||||
<author email="fixed-term.christophe.bourquebedard@de.bosch.com">Christophe Bedard</author>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
<buildtool_depend>pkg-config</buildtool_depend>
|
||||
|
||||
<depend>liblttng-ust-dev</depend>
|
||||
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
63
tracetools/scripts/setup-lttng.sh
Executable file
63
tracetools/scripts/setup-lttng.sh
Executable file
|
@ -0,0 +1,63 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
# set up ust ros2 events
|
||||
for event in \
|
||||
ros2:rcl_init \
|
||||
ros2:rcl_node_init \
|
||||
ros2:rcl_publisher_init \
|
||||
ros2:rcl_subscription_init \
|
||||
ros2:rclcpp_subscription_callback_added \
|
||||
ros2:rcl_service_init \
|
||||
ros2:rclcpp_service_callback_added \
|
||||
ros2:rcl_client_init \
|
||||
ros2:rcl_timer_init \
|
||||
ros2:rclcpp_timer_callback_added \
|
||||
ros2:rclcpp_register_callback \
|
||||
ros2:callback_start \
|
||||
ros2:callback_end
|
||||
do
|
||||
lttng enable-event -c ros2 -u $event
|
||||
done
|
||||
|
||||
# process context
|
||||
lttng add-context -c ros2 -u \
|
||||
-t vpid -t procname \
|
||||
-t vtid -t perf:thread:instructions \
|
||||
-t perf:thread:cycles -t perf:thread:cpu-cycles
|
||||
|
||||
#kernel
|
||||
lttng enable-channel --kernel kchan --subbuf-size=8M
|
||||
|
||||
# # network
|
||||
# for event in net_dev_queue netif_receive_skb net_if_receive_skb
|
||||
# do
|
||||
# lttng enable-event --kernel --channel=kchan $event
|
||||
# done
|
||||
|
||||
# other kernel stuff
|
||||
for event in sched_switch sched_waking sched_pi_setprio sched_process_fork sched_process_exit sched_process_free sched_wakeup\
|
||||
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\
|
||||
block_rq_complete block_rq_insert block_rq_issue\
|
||||
block_bio_frontmerge sched_migrate sched_migrate_task power_cpu_frequency\
|
||||
net_dev_queue netif_receive_skb net_if_receive_skb\
|
||||
timer_hrtimer_start timer_hrtimer_cancel timer_hrtimer_expire_entry timer_hrtimer_expire_exit
|
||||
do
|
||||
lttng enable-event --kernel --channel=kchan $event
|
||||
done
|
||||
|
||||
# lttng enable-event -k --syscall --all
|
103
tracetools/scripts/trace.sh
Executable file
103
tracetools/scripts/trace.sh
Executable file
|
@ -0,0 +1,103 @@
|
|||
#!/bin/bash
|
||||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
## Helper script for ROS tracing
|
||||
##
|
||||
## Call this by providing these arguments:
|
||||
## 1. a session name [optional; 'ros2' will be used]
|
||||
## 2. a wait time before killing and stopping (in seconds)
|
||||
## 3. a roslaunch/rosrun command
|
||||
## ex: ./trace.sh 3 roslaunch tracecompass_ros_testcases pub_sub.launch
|
||||
|
||||
source ./${BASH_SOURCE%/*}/../../../../install/local_setup.bash
|
||||
|
||||
## Parameters
|
||||
|
||||
# if no parameters were given, exit with error
|
||||
if [ -z "$1" ] ; then
|
||||
echo "Error: no parameters were given!"
|
||||
exit 1
|
||||
elif [ "$1" == "-h" ] ; then
|
||||
echo -e "ROS tracing helper script.\n" \
|
||||
"Provide 3 arguments:\n" \
|
||||
"1. the lttng session name [optional; 'ros2' will be used]\n" \
|
||||
"2. the wait time before killing and stopping (in seconds)\n" \
|
||||
"3. the ros2 run command\n" \
|
||||
"Example: ./trace.sh ros-trace 3 ros2 run tracetools tracetools_status"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# session name
|
||||
session_name="$1"
|
||||
case "$session_name" in
|
||||
''|*[!0-9]*) # not a number: good
|
||||
shift
|
||||
;;
|
||||
*) # number: so use a default session name
|
||||
session_name="ros2"
|
||||
;;
|
||||
esac
|
||||
|
||||
# wait time (seconds) before killing and stopping
|
||||
sleep_time="$1"
|
||||
case "$sleep_time" in
|
||||
''|*[!0-9]*) # not a number: error!
|
||||
echo "Error: not a valid sleep time!"
|
||||
exit 1
|
||||
;;
|
||||
*) # number: good
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
|
||||
# command from remaining arguments
|
||||
if [ -z "$1" ] ; then
|
||||
echo "Error: no command was given!"
|
||||
exit 1
|
||||
fi
|
||||
launch_cmd="$@"
|
||||
launch_cmd+=" &"
|
||||
|
||||
## Trace
|
||||
|
||||
# create lttng session (and set output to traces/ directory)
|
||||
lttng create $session_name --output=./${BASH_SOURCE%/*}/../traces/$session_name/
|
||||
|
||||
# enable events
|
||||
./${BASH_SOURCE%/*}/setup-lttng.sh
|
||||
|
||||
# start
|
||||
lttng start
|
||||
|
||||
# preload UST library
|
||||
export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/liblttng-ust-cyg-profile.so
|
||||
|
||||
# launch
|
||||
eval "$launch_cmd"
|
||||
|
||||
# wait a bit and kill
|
||||
echo "waiting $sleep_time..."
|
||||
sleep $sleep_time
|
||||
echo "killing"
|
||||
kill $!
|
||||
|
||||
# wait again for everything to shutdown
|
||||
echo "waiting for shutdown..."
|
||||
sleep 2
|
||||
echo "stopping"
|
||||
|
||||
# stop & destroy
|
||||
lttng stop
|
||||
lttng destroy
|
28
tracetools/src/status.c
Normal file
28
tracetools/src/status.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Tracing ");
|
||||
if (ros_trace_compile_status()) {
|
||||
printf("enabled\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("disabled\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
18
tracetools/src/tp_call.c
Normal file
18
tracetools/src/tp_call.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#define TRACEPOINT_CREATE_PROBES
|
||||
|
||||
#define TRACEPOINT_DEFINE
|
||||
#include "tracetools/tp_call.h"
|
218
tracetools/src/tracetools.c
Normal file
218
tracetools/src/tracetools.c
Normal file
|
@ -0,0 +1,218 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
#if defined(TRACETOOLS_LTTNG_ENABLED) && !defined(_WIN32)
|
||||
# include "tracetools/tp_call.h"
|
||||
# define CONDITIONAL_TP(...) \
|
||||
tracepoint(__VA_ARGS__)
|
||||
#else
|
||||
# define CONDITIONAL_TP(...)
|
||||
#endif
|
||||
|
||||
bool ros_trace_compile_status()
|
||||
{
|
||||
#if defined(TRACETOOLS_LTTNG_ENABLED) && !defined(_WIN32)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
void TRACEPOINT(
|
||||
rcl_init,
|
||||
const void * context_handle)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rcl_init,
|
||||
context_handle,
|
||||
tracetools_VERSION);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rcl_node_init,
|
||||
const void * node_handle,
|
||||
const void * rmw_handle,
|
||||
const char * node_name,
|
||||
const char * node_namespace)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rcl_node_init,
|
||||
node_handle,
|
||||
rmw_handle,
|
||||
node_name,
|
||||
node_namespace);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rcl_publisher_init,
|
||||
const void * publisher_handle,
|
||||
const void * node_handle,
|
||||
const void * rmw_publisher_handle,
|
||||
const char * topic_name,
|
||||
const size_t queue_depth)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rcl_publisher_init,
|
||||
publisher_handle,
|
||||
node_handle,
|
||||
rmw_publisher_handle,
|
||||
topic_name,
|
||||
queue_depth);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rcl_subscription_init,
|
||||
const void * subscription_handle,
|
||||
const void * node_handle,
|
||||
const void * rmw_subscription_handle,
|
||||
const char * topic_name,
|
||||
const size_t queue_depth)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rcl_subscription_init,
|
||||
subscription_handle,
|
||||
node_handle,
|
||||
rmw_subscription_handle,
|
||||
topic_name,
|
||||
queue_depth);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rclcpp_subscription_callback_added,
|
||||
const void * subscription_handle,
|
||||
const void * callback)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rclcpp_subscription_callback_added,
|
||||
subscription_handle,
|
||||
callback);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rcl_service_init,
|
||||
const void * service_handle,
|
||||
const void * node_handle,
|
||||
const void * rmw_service_handle,
|
||||
const char * service_name)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rcl_service_init,
|
||||
service_handle,
|
||||
node_handle,
|
||||
rmw_service_handle,
|
||||
service_name);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
const void * service_handle,
|
||||
const void * callback)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rclcpp_service_callback_added,
|
||||
service_handle,
|
||||
callback);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rcl_client_init,
|
||||
const void * client_handle,
|
||||
const void * node_handle,
|
||||
const void * rmw_client_handle,
|
||||
const char * service_name)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rcl_client_init,
|
||||
client_handle,
|
||||
node_handle,
|
||||
rmw_client_handle,
|
||||
service_name);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rcl_timer_init,
|
||||
const void * timer_handle,
|
||||
int64_t period)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rcl_timer_init,
|
||||
timer_handle,
|
||||
period);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rclcpp_timer_callback_added,
|
||||
const void * timer_handle,
|
||||
const void * callback)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rclcpp_timer_callback_added,
|
||||
timer_handle,
|
||||
callback);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
const void * callback,
|
||||
const char * function_symbol)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
rclcpp_callback_register,
|
||||
callback,
|
||||
function_symbol);
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
callback_start,
|
||||
const void * callback,
|
||||
const bool is_intra_process)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
callback_start,
|
||||
callback,
|
||||
(is_intra_process ? 1 : 0));
|
||||
}
|
||||
|
||||
void TRACEPOINT(
|
||||
callback_end,
|
||||
const void * callback)
|
||||
{
|
||||
CONDITIONAL_TP(
|
||||
ros2,
|
||||
callback_end,
|
||||
callback);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
45
tracetools/src/utils.cpp
Normal file
45
tracetools/src/utils.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#if defined(TRACETOOLS_LTTNG_ENABLED) && !defined(_WIN32)
|
||||
#include <dlfcn.h>
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
#include "tracetools/utils.hpp"
|
||||
|
||||
const char * get_symbol(void * funptr)
|
||||
{
|
||||
#define SYMBOL_UNKNOWN "UNKNOWN"
|
||||
#if defined(TRACETOOLS_LTTNG_ENABLED) && !defined(_WIN32)
|
||||
#define SYMBOL_LAMBDA "[lambda]"
|
||||
if (funptr == 0) {
|
||||
return SYMBOL_LAMBDA;
|
||||
}
|
||||
|
||||
Dl_info info;
|
||||
if (dladdr(funptr, &info) == 0) {
|
||||
return SYMBOL_UNKNOWN;
|
||||
}
|
||||
|
||||
char * demangled = nullptr;
|
||||
int status;
|
||||
demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
|
||||
// Use demangled symbol if possible
|
||||
const char * demangled_val = (status == 0 ? demangled : info.dli_sname);
|
||||
return demangled_val != 0 ? demangled_val : SYMBOL_UNKNOWN;
|
||||
#else
|
||||
(void)funptr;
|
||||
return SYMBOL_UNKNOWN;
|
||||
#endif
|
||||
}
|
2
tracetools_test/.gitignore
vendored
Normal file
2
tracetools_test/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*~
|
||||
*.pyc
|
117
tracetools_test/CMakeLists.txt
Normal file
117
tracetools_test/CMakeLists.txt
Normal file
|
@ -0,0 +1,117 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
project(tracetools_test)
|
||||
|
||||
# Default to C++14
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic -fPIC)
|
||||
endif()
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
|
||||
# Tests
|
||||
if(BUILD_TESTING)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
find_package(std_srvs REQUIRED)
|
||||
find_package(tracetools REQUIRED)
|
||||
|
||||
add_executable(test_publisher
|
||||
src/test_publisher.cpp
|
||||
)
|
||||
ament_target_dependencies(test_publisher
|
||||
rclcpp
|
||||
std_msgs
|
||||
)
|
||||
add_executable(test_subscription
|
||||
src/test_subscription.cpp
|
||||
)
|
||||
ament_target_dependencies(test_subscription
|
||||
rclcpp
|
||||
std_msgs
|
||||
)
|
||||
add_executable(test_ping
|
||||
src/test_ping.cpp
|
||||
)
|
||||
ament_target_dependencies(test_ping
|
||||
rclcpp
|
||||
std_msgs
|
||||
)
|
||||
add_executable(test_pong
|
||||
src/test_pong.cpp
|
||||
)
|
||||
ament_target_dependencies(test_pong
|
||||
rclcpp
|
||||
std_msgs
|
||||
)
|
||||
add_executable(test_timer
|
||||
src/test_timer.cpp
|
||||
)
|
||||
ament_target_dependencies(test_timer
|
||||
rclcpp
|
||||
)
|
||||
add_executable(test_service
|
||||
src/test_service.cpp
|
||||
)
|
||||
ament_target_dependencies(test_service
|
||||
rclcpp
|
||||
std_srvs
|
||||
)
|
||||
add_executable(test_service_ping
|
||||
src/test_service_ping.cpp
|
||||
)
|
||||
ament_target_dependencies(test_service_ping
|
||||
rclcpp
|
||||
std_srvs
|
||||
)
|
||||
add_executable(test_service_pong
|
||||
src/test_service_pong.cpp
|
||||
)
|
||||
ament_target_dependencies(test_service_pong
|
||||
rclcpp
|
||||
std_srvs
|
||||
)
|
||||
|
||||
install(TARGETS
|
||||
test_publisher
|
||||
test_subscription
|
||||
test_ping
|
||||
test_pong
|
||||
test_timer
|
||||
test_service
|
||||
test_service_ping
|
||||
test_service_pong
|
||||
DESTINATION lib/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
find_package(ament_cmake_pytest REQUIRED)
|
||||
|
||||
# Run each test in its own pytest invocation
|
||||
set(_tracetools_test_pytest_tests
|
||||
test/test_node.py
|
||||
test/test_publisher.py
|
||||
test/test_subscription.py
|
||||
test/test_subscription_callback.py
|
||||
test/test_timer.py
|
||||
test/test_service.py
|
||||
test/test_service_callback.py
|
||||
)
|
||||
|
||||
foreach(_test_path ${_tracetools_test_pytest_tests})
|
||||
get_filename_component(_test_name ${_test_path} NAME_WE)
|
||||
ament_add_pytest_test(${_test_name} ${_test_path}
|
||||
PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE}"
|
||||
APPEND_ENV AMENT_PREFIX_PATH=${ament_index_build_path}
|
||||
PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 60
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
ament_package()
|
33
tracetools_test/package.xml
Normal file
33
tracetools_test/package.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?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_test</name>
|
||||
<version>0.0.1</version>
|
||||
<description>Separate test package for tracetools</description>
|
||||
<maintainer email="fixed-term.christophe.bourquebedard@de.bosch.com">Christophe Bedard</maintainer>
|
||||
<maintainer email="ingo.luetkebohle@de.bosch.com">Ingo Luetkebohle</maintainer>
|
||||
<license>Apache Software License 2.0</license>
|
||||
<author email="fixed-term.christophe.bourquebedard@de.bosch.com">Christophe Bedard</author>
|
||||
|
||||
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||
<buildtool_depend>pkg-config</buildtool_depend>
|
||||
|
||||
<build_depend>rclcpp</build_depend>
|
||||
<build_depend>std_msgs</build_depend>
|
||||
<build_depend>std_srvs</build_depend>
|
||||
|
||||
<exec_depend>rclcpp</exec_depend>
|
||||
<exec_depend>std_msgs</exec_depend>
|
||||
<exec_depend>std_srvs</exec_depend>
|
||||
|
||||
<test_depend>ament_cmake_pytest</test_depend>
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>launch_ros</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
<test_depend>tracetools</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
74
tracetools_test/src/test_ping.cpp
Normal file
74
tracetools_test/src/test_ping.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/string.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class PingNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit PingNode(rclcpp::NodeOptions options)
|
||||
: Node("ping_node", options)
|
||||
{
|
||||
sub_ = this->create_subscription<std_msgs::msg::String>(
|
||||
"pong",
|
||||
rclcpp::QoS(10),
|
||||
std::bind(&PingNode::callback, this, std::placeholders::_1));
|
||||
pub_ = this->create_publisher<std_msgs::msg::String>(
|
||||
"ping",
|
||||
rclcpp::QoS(10));
|
||||
timer_ = this->create_wall_timer(
|
||||
500ms,
|
||||
std::bind(&PingNode::timer_callback, this));
|
||||
}
|
||||
|
||||
private:
|
||||
void callback(const std_msgs::msg::String::SharedPtr msg)
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "[output] %s", msg->data.c_str());
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void timer_callback()
|
||||
{
|
||||
auto msg = std::make_shared<std_msgs::msg::String>();
|
||||
msg->data = "some random ping string";
|
||||
pub_->publish(*msg);
|
||||
}
|
||||
|
||||
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_;
|
||||
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
auto ping_node = std::make_shared<PingNode>(rclcpp::NodeOptions());
|
||||
exec.add_node(ping_node);
|
||||
|
||||
printf("spinning\n");
|
||||
exec.spin();
|
||||
|
||||
// Will actually be called inside the node's callback
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
63
tracetools_test/src/test_pong.cpp
Normal file
63
tracetools_test/src/test_pong.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/string.hpp"
|
||||
|
||||
class PongNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit PongNode(rclcpp::NodeOptions options)
|
||||
: Node("pong_node", options)
|
||||
{
|
||||
sub_ = this->create_subscription<std_msgs::msg::String>(
|
||||
"ping",
|
||||
rclcpp::QoS(10),
|
||||
std::bind(&PongNode::callback, this, std::placeholders::_1));
|
||||
pub_ = this->create_publisher<std_msgs::msg::String>(
|
||||
"pong",
|
||||
rclcpp::QoS(10));
|
||||
}
|
||||
|
||||
private:
|
||||
void callback(const std_msgs::msg::String::SharedPtr msg)
|
||||
{
|
||||
RCLCPP_INFO(this->get_logger(), "[output] %s", msg->data.c_str());
|
||||
auto next_msg = std::make_shared<std_msgs::msg::String>();
|
||||
next_msg->data = "some random pong string";
|
||||
pub_->publish(*next_msg);
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_;
|
||||
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
auto pong_node = std::make_shared<PongNode>(rclcpp::NodeOptions());
|
||||
exec.add_node(pong_node);
|
||||
|
||||
printf("spinning\n");
|
||||
exec.spin();
|
||||
|
||||
// Will actually be called inside the node's callback
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
48
tracetools_test/src/test_publisher.cpp
Normal file
48
tracetools_test/src/test_publisher.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/string.hpp"
|
||||
|
||||
class PubNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit PubNode(rclcpp::NodeOptions options)
|
||||
: Node("pub_node", options)
|
||||
{
|
||||
pub_ = this->create_publisher<std_msgs::msg::String>(
|
||||
"the_topic",
|
||||
rclcpp::QoS(10));
|
||||
}
|
||||
|
||||
private:
|
||||
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
auto pub_node = std::make_shared<PubNode>(rclcpp::NodeOptions());
|
||||
exec.add_node(pub_node);
|
||||
|
||||
printf("spinning once\n");
|
||||
exec.spin_once();
|
||||
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
64
tracetools_test/src/test_service.cpp
Normal file
64
tracetools_test/src/test_service.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_srvs/srv/empty.hpp"
|
||||
|
||||
class ServiceNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit ServiceNode(rclcpp::NodeOptions options)
|
||||
: Node("service_node", options)
|
||||
{
|
||||
srv_ = this->create_service<std_srvs::srv::Empty>(
|
||||
"service",
|
||||
std::bind(
|
||||
&ServiceNode::service_callback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3));
|
||||
}
|
||||
|
||||
private:
|
||||
void service_callback(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<std_srvs::srv::Empty::Request> request,
|
||||
const std::shared_ptr<std_srvs::srv::Empty::Response> response)
|
||||
{
|
||||
// Nothing
|
||||
(void)request_header;
|
||||
(void)request;
|
||||
(void)response;
|
||||
}
|
||||
|
||||
rclcpp::Service<std_srvs::srv::Empty>::SharedPtr srv_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
auto service_node = std::make_shared<ServiceNode>(rclcpp::NodeOptions());
|
||||
exec.add_node(service_node);
|
||||
|
||||
printf("spinning once\n");
|
||||
exec.spin_once();
|
||||
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
82
tracetools_test/src/test_service_ping.cpp
Normal file
82
tracetools_test/src/test_service_ping.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_srvs/srv/empty.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class PingNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit PingNode(rclcpp::NodeOptions options)
|
||||
: Node("ping_node", options)
|
||||
{
|
||||
srv_ = this->create_service<std_srvs::srv::Empty>(
|
||||
"pong",
|
||||
std::bind(
|
||||
&PingNode::service_callback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3));
|
||||
client_ = this->create_client<std_srvs::srv::Empty>(
|
||||
"ping");
|
||||
timer_ = this->create_wall_timer(
|
||||
500ms,
|
||||
std::bind(&PingNode::timer_callback, this));
|
||||
}
|
||||
|
||||
private:
|
||||
void service_callback(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<std_srvs::srv::Empty::Request> request,
|
||||
const std::shared_ptr<std_srvs::srv::Empty::Response> response)
|
||||
{
|
||||
(void)request_header;
|
||||
(void)request;
|
||||
(void)response;
|
||||
RCLCPP_INFO(this->get_logger(), "got request");
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void timer_callback()
|
||||
{
|
||||
auto req = std::make_shared<std_srvs::srv::Empty::Request>();
|
||||
client_->async_send_request(req);
|
||||
}
|
||||
|
||||
rclcpp::Service<std_srvs::srv::Empty>::SharedPtr srv_;
|
||||
rclcpp::Client<std_srvs::srv::Empty>::SharedPtr client_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
auto ping_node = std::make_shared<PingNode>(rclcpp::NodeOptions());
|
||||
exec.add_node(ping_node);
|
||||
|
||||
printf("spinning\n");
|
||||
exec.spin();
|
||||
|
||||
// Will actually be called inside the node's service callback
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
70
tracetools_test/src/test_service_pong.cpp
Normal file
70
tracetools_test/src/test_service_pong.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_srvs/srv/empty.hpp"
|
||||
|
||||
class PongNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit PongNode(rclcpp::NodeOptions options)
|
||||
: Node("pong_node", options)
|
||||
{
|
||||
srv_ = this->create_service<std_srvs::srv::Empty>(
|
||||
"ping",
|
||||
std::bind(
|
||||
&PongNode::service_callback,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3));
|
||||
client_ = this->create_client<std_srvs::srv::Empty>("pong");
|
||||
}
|
||||
|
||||
private:
|
||||
void service_callback(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<std_srvs::srv::Empty::Request> request,
|
||||
const std::shared_ptr<std_srvs::srv::Empty::Response> response)
|
||||
{
|
||||
(void)request_header;
|
||||
(void)request;
|
||||
(void)response;
|
||||
RCLCPP_INFO(this->get_logger(), "got request");
|
||||
auto req = std::make_shared<std_srvs::srv::Empty::Request>();
|
||||
client_->async_send_request(req);
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::Service<std_srvs::srv::Empty>::SharedPtr srv_;
|
||||
rclcpp::Client<std_srvs::srv::Empty>::SharedPtr client_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
auto pong_node = std::make_shared<PongNode>(rclcpp::NodeOptions());
|
||||
exec.add_node(pong_node);
|
||||
|
||||
printf("spinning\n");
|
||||
exec.spin();
|
||||
|
||||
// Will actually be called inside the node's service callback
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
55
tracetools_test/src/test_subscription.cpp
Normal file
55
tracetools_test/src/test_subscription.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/string.hpp"
|
||||
|
||||
class SubNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit SubNode(rclcpp::NodeOptions options)
|
||||
: Node("sub_node", options)
|
||||
{
|
||||
sub_ = this->create_subscription<std_msgs::msg::String>(
|
||||
"the_topic",
|
||||
rclcpp::QoS(10),
|
||||
std::bind(&SubNode::callback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
private:
|
||||
void callback(const std_msgs::msg::String::SharedPtr msg)
|
||||
{
|
||||
// Nothing
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
auto sub_node = std::make_shared<SubNode>(rclcpp::NodeOptions());
|
||||
exec.add_node(sub_node);
|
||||
|
||||
printf("spinning once\n");
|
||||
exec.spin_once();
|
||||
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
62
tracetools_test/src/test_timer.cpp
Normal file
62
tracetools_test/src/test_timer.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2019 Robert Bosch GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class TimerNode : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
explicit TimerNode(rclcpp::NodeOptions options)
|
||||
: Node("timer_node", options)
|
||||
{
|
||||
is_done_ = false;
|
||||
timer_ = this->create_wall_timer(
|
||||
1ms,
|
||||
std::bind(&TimerNode::timer_callback, this));
|
||||
}
|
||||
|
||||
private:
|
||||
void timer_callback()
|
||||
{
|
||||
if (is_done_) {
|
||||
rclcpp::shutdown();
|
||||
} else {
|
||||
is_done_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
bool is_done_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
auto timer_node = std::make_shared<TimerNode>(rclcpp::NodeOptions());
|
||||
exec.add_node(timer_node);
|
||||
|
||||
printf("spinning\n");
|
||||
exec.spin();
|
||||
|
||||
// Will actually be called inside the timer's callback
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
54
tracetools_test/test/test_node.py
Normal file
54
tracetools_test/test/test_node.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from tracetools_test.utils import (
|
||||
cleanup_trace,
|
||||
get_trace_event_names,
|
||||
run_and_trace,
|
||||
)
|
||||
|
||||
BASE_PATH = '/tmp'
|
||||
PKG = 'tracetools_test'
|
||||
node_creation_events = [
|
||||
'ros2:rcl_init',
|
||||
'ros2:rcl_node_init',
|
||||
]
|
||||
|
||||
|
||||
class TestNode(unittest.TestCase):
|
||||
|
||||
def test_creation(self):
|
||||
session_name_prefix = 'session-test-node-creation'
|
||||
test_node = ['test_publisher']
|
||||
|
||||
exit_code, full_path = run_and_trace(
|
||||
BASE_PATH,
|
||||
session_name_prefix,
|
||||
node_creation_events,
|
||||
None,
|
||||
PKG,
|
||||
test_node)
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
trace_events = get_trace_event_names(full_path)
|
||||
print(f'trace_events: {trace_events}')
|
||||
self.assertSetEqual(set(node_creation_events), trace_events)
|
||||
|
||||
cleanup_trace(full_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
53
tracetools_test/test/test_publisher.py
Normal file
53
tracetools_test/test/test_publisher.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from tracetools_test.utils import (
|
||||
cleanup_trace,
|
||||
get_trace_event_names,
|
||||
run_and_trace,
|
||||
)
|
||||
|
||||
BASE_PATH = '/tmp'
|
||||
PKG = 'tracetools_test'
|
||||
publisher_creation_events = [
|
||||
'ros2:rcl_publisher_init',
|
||||
]
|
||||
|
||||
|
||||
class TestPublisher(unittest.TestCase):
|
||||
|
||||
def test_creation(self):
|
||||
session_name_prefix = 'session-test-publisher-creation'
|
||||
test_node = ['test_publisher']
|
||||
|
||||
exit_code, full_path = run_and_trace(
|
||||
BASE_PATH,
|
||||
session_name_prefix,
|
||||
publisher_creation_events,
|
||||
None,
|
||||
PKG,
|
||||
test_node)
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
trace_events = get_trace_event_names(full_path)
|
||||
print(f'trace_events: {trace_events}')
|
||||
self.assertSetEqual(set(publisher_creation_events), trace_events)
|
||||
|
||||
cleanup_trace(full_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
54
tracetools_test/test/test_service.py
Normal file
54
tracetools_test/test/test_service.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from tracetools_test.utils import (
|
||||
cleanup_trace,
|
||||
get_trace_event_names,
|
||||
run_and_trace,
|
||||
)
|
||||
|
||||
BASE_PATH = '/tmp'
|
||||
PKG = 'tracetools_test'
|
||||
service_creation_events = [
|
||||
'ros2:rcl_service_init',
|
||||
'ros2:rclcpp_service_callback_added',
|
||||
]
|
||||
|
||||
|
||||
class TestService(unittest.TestCase):
|
||||
|
||||
def test_creation(self):
|
||||
session_name_prefix = 'session-test-service-creation'
|
||||
test_nodes = ['test_service']
|
||||
|
||||
exit_code, full_path = run_and_trace(
|
||||
BASE_PATH,
|
||||
session_name_prefix,
|
||||
service_creation_events,
|
||||
None,
|
||||
PKG,
|
||||
test_nodes)
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
trace_events = get_trace_event_names(full_path)
|
||||
print(f'trace_events: {trace_events}')
|
||||
self.assertSetEqual(set(service_creation_events), trace_events)
|
||||
|
||||
cleanup_trace(full_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
54
tracetools_test/test/test_service_callback.py
Normal file
54
tracetools_test/test/test_service_callback.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from tracetools_test.utils import (
|
||||
cleanup_trace,
|
||||
get_trace_event_names,
|
||||
run_and_trace,
|
||||
)
|
||||
|
||||
BASE_PATH = '/tmp'
|
||||
PKG = 'tracetools_test'
|
||||
service_callback_events = [
|
||||
'ros2:callback_start',
|
||||
'ros2:callback_end',
|
||||
]
|
||||
|
||||
|
||||
class TestServiceCallback(unittest.TestCase):
|
||||
|
||||
def test_callback(self):
|
||||
session_name_prefix = 'session-test-service-callback'
|
||||
test_nodes = ['test_service_ping', 'test_service_pong']
|
||||
|
||||
exit_code, full_path = run_and_trace(
|
||||
BASE_PATH,
|
||||
session_name_prefix,
|
||||
service_callback_events,
|
||||
None,
|
||||
PKG,
|
||||
test_nodes)
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
trace_events = get_trace_event_names(full_path)
|
||||
print(f'trace_events: {trace_events}')
|
||||
self.assertSetEqual(set(service_callback_events), trace_events)
|
||||
|
||||
cleanup_trace(full_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
54
tracetools_test/test/test_subscription.py
Normal file
54
tracetools_test/test/test_subscription.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from tracetools_test.utils import (
|
||||
cleanup_trace,
|
||||
get_trace_event_names,
|
||||
run_and_trace,
|
||||
)
|
||||
|
||||
BASE_PATH = '/tmp'
|
||||
PKG = 'tracetools_test'
|
||||
subscription_creation_events = [
|
||||
'ros2:rcl_subscription_init',
|
||||
'ros2:rclcpp_subscription_callback_added',
|
||||
]
|
||||
|
||||
|
||||
class TestSubscription(unittest.TestCase):
|
||||
|
||||
def test_creation(self):
|
||||
session_name_prefix = 'session-test-subscription-creation'
|
||||
test_node = ['test_subscription']
|
||||
|
||||
exit_code, full_path = run_and_trace(
|
||||
BASE_PATH,
|
||||
session_name_prefix,
|
||||
subscription_creation_events,
|
||||
None,
|
||||
PKG,
|
||||
test_node)
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
trace_events = get_trace_event_names(full_path)
|
||||
print(f'trace_events: {trace_events}')
|
||||
self.assertSetEqual(set(subscription_creation_events), trace_events)
|
||||
|
||||
cleanup_trace(full_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
54
tracetools_test/test/test_subscription_callback.py
Normal file
54
tracetools_test/test/test_subscription_callback.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from tracetools_test.utils import (
|
||||
cleanup_trace,
|
||||
get_trace_event_names,
|
||||
run_and_trace,
|
||||
)
|
||||
|
||||
BASE_PATH = '/tmp'
|
||||
PKG = 'tracetools_test'
|
||||
subscription_callback_events = [
|
||||
'ros2:callback_start',
|
||||
'ros2:callback_end',
|
||||
]
|
||||
|
||||
|
||||
class TestSubscriptionCallback(unittest.TestCase):
|
||||
|
||||
def test_callback(self):
|
||||
session_name_prefix = 'session-test-subscription-callback'
|
||||
test_nodes = ['test_ping', 'test_pong']
|
||||
|
||||
exit_code, full_path = run_and_trace(
|
||||
BASE_PATH,
|
||||
session_name_prefix,
|
||||
subscription_callback_events,
|
||||
None,
|
||||
PKG,
|
||||
test_nodes)
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
trace_events = get_trace_event_names(full_path)
|
||||
print(f'trace_events: {trace_events}')
|
||||
self.assertSetEqual(set(subscription_callback_events), trace_events)
|
||||
|
||||
cleanup_trace(full_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
56
tracetools_test/test/test_timer.py
Normal file
56
tracetools_test/test/test_timer.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
|
||||
from tracetools_test.utils import (
|
||||
cleanup_trace,
|
||||
get_trace_event_names,
|
||||
run_and_trace,
|
||||
)
|
||||
|
||||
BASE_PATH = '/tmp'
|
||||
PKG = 'tracetools_test'
|
||||
timer_events = [
|
||||
'ros2:rcl_timer_init',
|
||||
'ros2:rclcpp_timer_callback_added',
|
||||
'ros2:callback_start',
|
||||
'ros2:callback_end',
|
||||
]
|
||||
|
||||
|
||||
class TestTimer(unittest.TestCase):
|
||||
|
||||
def test_all(self):
|
||||
session_name_prefix = 'session-test-timer-all'
|
||||
test_nodes = ['test_timer']
|
||||
|
||||
exit_code, full_path = run_and_trace(
|
||||
BASE_PATH,
|
||||
session_name_prefix,
|
||||
timer_events,
|
||||
None,
|
||||
PKG,
|
||||
test_nodes)
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
trace_events = get_trace_event_names(full_path)
|
||||
print(f'trace_events: {trace_events}')
|
||||
self.assertSetEqual(set(timer_events), trace_events)
|
||||
|
||||
cleanup_trace(full_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
13
tracetools_test/tracetools_test/__init__.py
Normal file
13
tracetools_test/tracetools_test/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
107
tracetools_test/tracetools_test/utils.py
Normal file
107
tracetools_test/tracetools_test/utils.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
# Copyright 2019 Robert Bosch GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Utils for tracetools_test."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
from typing import List
|
||||
from typing import Set
|
||||
from typing import Tuple
|
||||
|
||||
import babeltrace
|
||||
from launch import LaunchDescription
|
||||
from launch import LaunchService
|
||||
from launch_ros import get_default_launch_description
|
||||
import launch_ros.actions
|
||||
from tracetools_trace.tools.lttng import (
|
||||
lttng_destroy,
|
||||
lttng_setup,
|
||||
lttng_start,
|
||||
lttng_stop,
|
||||
)
|
||||
|
||||
|
||||
def run_and_trace(
|
||||
base_path: str,
|
||||
session_name_prefix: str,
|
||||
ros_events: List[str],
|
||||
kernel_events: List[str],
|
||||
package_name: str,
|
||||
node_names: List[str]
|
||||
) -> Tuple[int, str]:
|
||||
"""
|
||||
Run a node while tracing.
|
||||
|
||||
:param base_path: the base path where to put the trace directory
|
||||
:param session_name_prefix: the session name prefix for the trace directory
|
||||
:param ros_events: the list of ROS UST events to enable
|
||||
:param kernel_events: the list of kernel events to enable
|
||||
:param package_name: the name of the package to use
|
||||
:param node_names: the names of the nodes to execute
|
||||
:return: exit code, full generated path
|
||||
"""
|
||||
session_name = f'{session_name_prefix}-{time.strftime("%Y%m%d%H%M%S")}'
|
||||
full_path = os.path.join(base_path, session_name)
|
||||
print(f'trace directory: {full_path}')
|
||||
|
||||
lttng_setup(session_name, full_path, ros_events=ros_events, kernel_events=kernel_events)
|
||||
lttng_start(session_name)
|
||||
|
||||
nodes = []
|
||||
for node_name in node_names:
|
||||
n = launch_ros.actions.Node(
|
||||
package=package_name,
|
||||
node_executable=node_name,
|
||||
output='screen')
|
||||
nodes.append(n)
|
||||
ld = LaunchDescription(nodes)
|
||||
ls = LaunchService()
|
||||
ls.include_launch_description(get_default_launch_description())
|
||||
ls.include_launch_description(ld)
|
||||
|
||||
exit_code = ls.run()
|
||||
|
||||
lttng_stop(session_name)
|
||||
lttng_destroy(session_name)
|
||||
|
||||
return exit_code, full_path
|
||||
|
||||
|
||||
def cleanup_trace(full_path: str) -> None:
|
||||
"""
|
||||
Cleanup trace data.
|
||||
|
||||
:param full_path: the full path to the main trace directory
|
||||
"""
|
||||
shutil.rmtree(full_path)
|
||||
|
||||
|
||||
def get_trace_event_names(trace_directory: str) -> Set[str]:
|
||||
"""
|
||||
Get a set of event names in a trace.
|
||||
|
||||
:param trace_directory: the path to the main/top trace directory
|
||||
:return: event names
|
||||
"""
|
||||
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
|
Loading…
Add table
Add a link
Reference in a new issue