Awesome
Postform
Postform
(short for "Postponed formatting") is a deferred-formatting logging system in C++ for 32 bit microcontrollers. This project is inspired/based on the defmt rust crate from Knurling-rs. They are doing a splendid job that motivated me to create a C++ alternative following similar principles of operation, still leveraging rust for the host-side implementation of this logger.
Table of Contents
- About the Project
- Project Status
- Getting Started
- Release Process
- How to Get Help
- Contributing
- License
- Authors
- Acknowledgments
About the Project
String formatting can be quite expensive in embedded devices. Logging is one of the main tools used by developers to debug and analyze the performance of their devices. To provide an efficient logging mechanism suitable for embedded devices, Postform
doesn't perform on-device formatting. Instead it is postponed and performed by a host computer.
Postform serializes the raw data (format string + all arguments) and sends it to the host device over a given transport. The host is responsible of deserializing this data and perform the log formatting and displaying it for the user.
- The format string.
- All arguments needed for the format string.
On the other hand, there is no point in storing and transmitting the format, since these are known at compile-time. Generally speaking, it is more efficient to simply store an identifier to the format string and let the host find the corresponding string for the given ID. This reduces both the size of the binary and optimizes for runtime performance as usually transmitting an ID is faster than transmitting a large string.
Overall, postponed formatting offers some benefits:
- Smaller memory footprint, since format strings are not stored in the binary.
- Faster logging, since only the raw binary data is transmitted, which is typically smaller than the actual formatted string.
- Having no strings in the binary makes reverse engineering harder.
And, as you could expect, some drawbacks:
- There is some tooling required on the host side to read and interpret the logs.
- The metadata for the logs is stored separately, and they need to be in sync both on the FW side and host side.
Project Status
Postform
is under active development at this point in time. All contributions are really appreciated and encouraged. Feel free to open issues with suggestions for improvements, bugs that you've encountered, etc. If you'd like to contribute, please checkout the contribution guidelines.
Currently the default build targets an Cortex-M
processor, however Postform
aims to be a platform-independent logging library for 32-bit microcontrollers, we aim for portable code as much as possible. In fact, it is also built for the host for testing purposes, showing how portable the code actually is.
At this point the API is still volatile and might change in the future. Postform
uses Semantic Versioning for its releases, so you will know when a breaking API feature has been added when a major number changes. However, until we reach 1.0.0
we may still make breaking API/ABI changes even if the minor is incremented.
Getting Started
Components
Postform
is composed of the following components:
libpostform
, which is a C++ library that can be linked against in embedded code. It handles the formatting and serialization of messages, providing some example transport layers for the log data.postform_decoder
, which is a Rust library that can parse and format log messages. Some other Rust binaries use this library to provide convenient usage and log parsing depending on the transport and format.postform_rtt
, which is a Rust binary that connects through RTT to the target using a debugger connection and reads the logs in runtime through the RTT transport, printing them to the console.postform_serial
, which is a Rust binary that uses a TTY device instead of RTT as a transport and displays log messages on the console.postform_persist
, which is a Rust binary that reads the log data generated bylibpostform
from a file and prints the messages to the console.
Dependencies
libpostform
can be built and used with clang
and GCC
. The build is currently supported on Ubuntu 20.04
and the default toolchain configuration uses clang
as a compiler and the newlib
library from the GCC ARM Embedded toolchain
. libpostform
is tested with clang-12
, but it is known to work with clang-10
as well.
Get the latest GNU ARM Embedded toolchain
here. Make sure to add the bin folder to your path.
The build system requires both CMake
, Ninja
and Cargo
and uses Cargo xtasks to automate and script the build process.
To install the dependencies from the Ubuntu apt repositories run:
sudo apt install cmake ninja-build clang
In addition, to get cargo
and rustc
you can use rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
All the Rust code for the host has its dependencies indicated in the Cargo.toml
file. Some dependencies require some shared libraries, which you can install with:
sudo apt install -y libusb-1.0-0-dev libftdi1-dev libudev-dev
It is worth mentioning that it is possible to avoid installing these dependencies as the build can be run within a docker container which already has all these dependencies available. Go to the running in a docker container section for more details on how to run the build within docker.
Getting the Source
This project is hosted on GitHub.
You can get the repository by cloning this repository:
git clone https://github.com/Javier-varez/Postform postform
Building
As mentioned before, cargo xtasks are used to automate build and testing processes. The following commands are available:
cargo xtask build-firmware cortex_m3
- Builds an example app using libpostform for a Cortex-M3 MCU. Will be available underfw_build/m3/app/postform_format
.cargo xtask build-firmware cortex_m0
- Builds an example app using libpostform for a Cortex-M0 MCU. Will be available underfw_build/m0/app/postform_format
.cargo xtask test
- Runs a all Postform tests.cargo xtask build --release
- Builds all the host binaries likepostform_rtt
,postform_persist
andpostform_serial
.cargo xtask clean
- Cleans all target build folders.cargo xtask run-example-app
- Runs the example application on an STM32F103C8 microcontroller. Should be connected using an ST-Link or compatible SWD debugger.
For more information about all available commands run cargo xtask --help
.
Usage
In order to run the example application run the following command:
cargo xtask run-example-app
This will build the example firmware application for the cortex-m3 and then immediately trigger postform_rtt
(building it if needed). Then postform_rtt
will connect to the target MCU via RTT (using an SWD connection) and load all debugging information from the FW ELF file. The example app is built for an STM32F103C8
microcontrollers connected via a debugger compatible with probe-rs
, like an ST-Link
or a J-Link
.
Running in a docker container
Any of the xtask
commands can be run within a docker container with all dependencies already installed. This is great if you don't want to go through the process of installing all dependencies listed in previous sections and just want a fast way to get started.
To run an xtask
command in a docker container just insert docker
after xtask
in the command invocation. For instance, to run the tests in docker use:
cargo xtask docker test
Release Process
Versioning
This project uses Semantic Versioning. Releases are tagged accordingly. Versions below 1.0.0
can have breaking API/ABI changes at any time.
How to Get Help
Feel free to reach out and open an issue if you need any help getting up and running with Postform
.
Contributing
Public contributions are very welcome and appreciated. Please have a look at CONTRIBUTING.md for details on the development process and kinds of contributions.
License
This project is licensed under the MIT License - see LICENSE.md file for details.
Authors
- Javier Alvarez - Initial work - AllThingsEmbedded
See also the list of contributors who participated in this project.
Acknowledgments
I would like to sincerely thank the good people at knurling-rs that inspired this work. This project is, after all, an alternative implementation of defmt for usage with C++ code.