Skip to content

Integrate Rust -> BSW logger bridge#480

Merged
rolandreichweinbmw merged 5 commits into
eclipse-openbsw:mainfrom
esrlabs:cr-624756
Jun 30, 2026
Merged

Integrate Rust -> BSW logger bridge#480
rolandreichweinbmw merged 5 commits into
eclipse-openbsw:mainfrom
esrlabs:cr-624756

Conversation

@felixm-acc

@felixm-acc felixm-acc commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

This PR improves Rust support and offline/containerized build reliability:

  • Rust logging bridge: adds a bridge so Rust code can log through the existing C++ logger.
  • Offline builds: vendors first external Rust dependencies so builds work without network access.
  • Toolchain: bumps the default Rust version to 1.96.0.
  • Container UID handling: makes the cargo and rustup homes accessible to the runtime UID injected into
    the dev container.
  • Repo hygiene: adds a .gitattributes file (for exclusion from vendored 3rdparty code from SLOC stats on main page and Github diff view)

2. The Rust compiler

Install Rust 1.90.0 to be compatible with the CI builds:
Install Rust 1.96.0 to be compatible with the CI builds:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ubuntu version is defined only once. Probably we should use the same approach for rust:

:prop:`tool:ubuntu_version`

@felixm-acc felixm-acc Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. There's even a nice mechanism already in place in doc/dev/conf.py to replace x.x in code blocks with the correct version number depending on which marker string is nearby.

@ThoFrankACN ThoFrankACN left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice work!

I only have some minor feedback. In my opinion the vendoring approach is good. We have everything available for an offline build and can track changes, but using dependencies from crates.io is still simple.

if it is unset the message is dropped. The crate name is prepended to the
message so the originating module is still visible.

.. warning::

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also warn about the performance implications of double-buffering here?

And not sure how cpp behaves, but the formatting overhead for rust messages is still there even if the cpp logger component is disabled.

@felixm-acc felixm-acc Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points, I'll add the two caveats.
I'll also check how it's done in C++, maybe some performance gains hidden there by disabling serialization if it's not going to get printed anyway.

@felixm-acc felixm-acc Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the bsw logger it's gated through if (isEnabled(componentIndex, _level)) in Logger.h. I can also (double) check isEnabled via FFI from Rust before doing the serialization. Maybe with some profiling of some format heavy logs to see whether that optimization is worth it.

@felixm-acc felixm-acc Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test log line with some formatting.

"rx id={:#05x} dlc={} ts={:>8}ms seq={:05} crc={:#06x}"
-> "rx id=0x1a4 dlc=8 ts= 123456ms seq=00042 crc=0xbeef"

Results (200k iterations/run, 3 runs)

gated naive (always format) delta
disabled component ~6.5 ns/call ~160 ns/call saving ~155 ns (~24x)
enabled component ~160 ns/call ~160 ns/call overhead ~ 0

definitely seems to be worth it to add the additional is_enabled FFI call also to Rust to skip serializing entirely when the logging component is disabled.

Comment thread libs/bsw/logger/rust/src/log_buf.rs Outdated
Comment on lines +28 to +40
for &b in s {
self.buffer[self.bytes_used] = b;
self.bytes_used += 1;
}
Ok(())
} else {
// not enough space
// only copy what fits
let s = &s[0..(LOG_BUF_SIZE - self.bytes_used - 1)];
for &b in s {
self.buffer[self.bytes_used] = b;
self.bytes_used += 1;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the loops should be replaced by copy_from_slice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

pub use component::LoggerComponent;

#[doc(hidden)]
pub use paste as __paste;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there the need to re-export paste?

@felixm-acc felixm-acc Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used in the expansion of the log macro. If we don't re-export then it's not available at the callsite of the macro and every caller of our logging macros would need to add a dependency to paste in their Cargo.toml. Similar to https://stackoverflow.com/questions/65164156/how-to-set-dependencies-for-a-macro-in-rust#:~:text=lib%2Ers%2E-,Library,attribute

/opt/cargo and /opt/rustup are filled as root during the image build
(rustup setup and cargo install cbindgen), but the container runs under
the UID injected via DOCKER_UID, which then cannot read or lock the
cargo registry. This caused the following CI error:

  error: failed to open `/opt/cargo/registry/cache/.../equivalent-1.0.2.crate`
  Caused by: Permission denied (os error 13)

chmod the cargo and rustup homes world-accessible after install, the
same fix already applied to /home/build/.cache.

Change-Id: I6cd61820097a6f56d030c0972ca25b0e2dd88320
Introduce the openbsw-logger crate (libs/bsw/logger/rust) so Rust code
can emit log messages through the existing C++ ::util::logger::Logger.

First-party Rust code uses per-component macros:
declare_logger_component! binds a component to the extern "C" getter
that DECLARE/DEFINE_LOGGER_COMPONENT now emit on the C++ side, and
bsw_debug!/info!/warn!/error!/critical! format into a stack buffer and
forward to bsw_cpp_logger_log. The component index is fetched from C++
on every call rather than cached, so there is no Rust-side init step and
no dependency on logger::init() ordering (the symbols hold
COMPONENT_NONE until applyMapping() runs).

Messages routed through the `log` facade (e.g. third-party crates) fall
back to a single component set via set_default_component, with the crate
name prepended; they are dropped if no default is configured.

bsw_cpp_logger_log passes the already-formatted message through a
literal "%s" so stray '%' characters are not interpreted as printf
specifiers against an empty va_list. Trace maps to DEBUG, since a trace
level does not exist in the BSW logger.

Use in referenceApp: rustHelloWorld logs under the DEMO component and
app.cpp installs the logger and default component under BUILD_RUST.
Document the Rust API in the logger module docs.

Change-Id: Iab2756f03f322b5e5354c3ee94e41c8f4f00eac9
This will exclude 3rdparty code from the GitHub language stats bars.

Change-Id: Ied6c8add46a2a7ec819ed4762c3966242a9593dd
@felixm-acc felixm-acc changed the title bump default Rust version to 1.96.0 Integrate Rust -> BSW logger bridge Jun 18, 2026
Vendor the workspace's crate dependencies into
libs/3rdparty/cargo-vendor alongside the other third-party imports.

Remove cbindgen from the build so the vendored tree stays small.
cbindgen only generates the logger's C header (include/BswLogger.h,
checked in), and made cargo vendor 88 MB large across 71 crates by
dragging in unneeded windows deps. Dropping it leaves just log and paste
(576 KB, 2 crates).

The header is now a checked-in artifact regenerated outside of the build
with the cbindgen CLI. .ci/cbindgen.py regenerates it and fails on drift
via git diff --exit-code, mirroring .ci/format.py, and a matching
cbindgen.yml workflow runs it in the development container.

Bump the cbindgen CLI already installed in that image from 0.27.0 to
0.29.2 so it matches the version that produced the committed header.

Remove the logger crate's build.rs, which existed only to run cbindgen

Change-Id: I2de261e9b4e4e5cc1c3504051874578b701087f7
Also adapt the .treefmt.toml script and Dockerfile because on the CI
rustfmt wasn't available for the new Rust version.

Change-Id: Ie7b52473792e1d5f0a74dd595952d568368c8394

@ThoFrankACN ThoFrankACN left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@rolandreichweinbmw rolandreichweinbmw merged commit 8bcfb3d into eclipse-openbsw:main Jun 30, 2026
131 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants