Need help with probe-run?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

285 Stars 36 Forks Other 543 Commits 41 Opened issues


Run embedded programs just like native ones

Services available


Need anything else?

Contributors list


Runs embedded programs just like native ones

is a custom Cargo runner that transparently runs Rust firmware on an embedded device.

is powered by
and thus supports all the devices and probes supported by


  • Acts as a Cargo runner, integrating into
    cargo run
  • Displays program output streamed from the device via RTT.
  • Exits the firmware and prints a stack backtrace on breakpoints.


To install

, use
cargo install probe-run

On Linux, you might have to install

from your package manager before installing
# ubuntu
$ sudo apt install -y libusb-1.0-0-dev libudev-dev


$ sudo dnf install -y libusbx-devel systemd-devel


1. Set the Cargo runner

The recommend way to use

is to set as the Cargo runner of your application.

Add these two lines to your Cargo configuration file (

) and set the particular
value for your target. In this case it is
for the nRF52840:
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-run --chip nRF52840_xxAA"
#                          ^^^^^^^^^^^^^

To list all supported chips run

probe-run --list-chips

1.1 Env variable

To support multiple devices, or permit overriding default behavior, you may prefer to: 1. set the

environment variable, and 2. set
) to
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-run"

1.2 Multiple probes

If you have several probes connected, you can specify which one to use by adding the

option to the
or setting the
environment variable with a value containing either
// --probe
$ probe-run --probe '0483:3748' --chip ${PROBE_RUN_CHIP}

// PROBE_RUN_PROBE $ PROBE_RUN_PROBE='1366:0101:123456' cargo run

To list all connected probes, run

probe-run --list-probes

2. Enable debug info

Next check that debug info is enabled for all profiles. If you are using the

template then this is already the case. If not check or add these lines to
panic-probe = { version = "0.2", features = ["print-rtt"] }


[] debug = 1 # default is true; not needed if not already overridden

[profile.release] debug = 1 # default is false; using true is also OK as symbols reside on the host, not the target

3. Look out for old dependencies


dependency must be version 0.6.3 or newer. Older versions are not supported. Check your
for old versions. Run
cargo update
to update the
dependency if an older one appears in

4. Run

You are all set. You can now run your firmware using

cargo run
. For example,
use cortex_m::asm;
use cortex_m_rt::entry;
use panic_probe as _;
use rtt_target::rprintln;

#[entry] fn main() -> ! { rtt_init_print!(); // You may prefer to initialize another way
rprintln!("Hello, world!"); loop { asm::bkpt() } }

would output

$ cargo run --bin hello
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
     Running `probe-run --chip nRF52840_xxAA target/thumbv7em-none-eabihf/debug/hello`
  (HOST) INFO  flashing program (30.22 KiB)
  (HOST) INFO  success!
INFO:hello -- Hello, world!
  (HOST) INFO  exiting because the device halted.
To see the backtrace at the exit point repeat this run with
`probe-run --chip nRF52840_xxAA target/thumbv7em-none-eabihf/debug/hello --force-backtrace`

Stack backtraces

When the device raises a hard fault exception, indicating e.g. a panic or a stack overflow,

will print a backtrace and exit with a non-zero exit code.

This backtrace follows the format of the

backtraces you get from
but includes
 lines to indicate where an exception/interrupt occurred.

use cortex_m::asm; #[entry] fn main() -> ! { // trigger a hard fault exception with the UDF instruction. asm::udf() }

    Finished dev [optimized + debuginfo] target(s) in 0.04s
     Running probe-run --chip nRF52840_xxAA target/thumbv7em-none-eabihf/debug/hard-fault
  (HOST) INFO  flashing program (30.08 KiB)
  (HOST) INFO  success!
stack backtrace:
   0: HardFaultTrampoline
   1: __udf
   2: cortex_m::asm::udf
        at /<...>/cortex-m-0.6.4/src/
   3: panic::__cortex_m_rt_main
        at src/bin/
   4: main
        at src/bin/
   5: ResetTrampoline
        at /<...>3/cortex-m-rt-0.6.13/src/
   6: Reset
        at /<...>/cortex-m-rt-0.6.13/src/

If we look at the return code emitted by this

cargo run
, we'll see that it is non-0:
$ echo $?

⚠️ NOTE when you run your application with

, the
handler (default or user-defined) will NOT be executed.

Backtrace options



flag is optional and can get passed the following values:
  • --backtrace=always
    - forced backtrace (if you'd like to see a backtrace at the end of successful program run)
  • --backtrace=never
    - suppresed backtrace
  • --backtrace=auto
    - default, shows a backtrace if the program panics or the stack overflows

Run it like this (example for a forced backtrace):

$ cargo run --bin hello --backtrace=always



flag is optional and defaults to 50. It is possible to set any number.

is accepted and means "no limit".

To show a shortened backtrace showing 5 frames, run:

$ cargo run --bin panic --backtrace-limit=5

Note: if

is set, setting
has no effect.


"Error: no probe was found."

First, check your data cable:

  • make sure that it is connected to the right port on your development board
  • make sure that you are using a data cable– some cables are built for charging only! When in doubt, try using a different cable.

If this doesn't resolve the issue, try the following:

[Linux only] udev rules haven't been set

Check if your device shows up in

$ lsusb
Bus 001 Device 008: ID 1366:1015 SEGGER J-Link

If your device shows up like in the example, skip to the next troubleshooting section

If it doesn't show up, you need to give your system permission to access the device as a non-root user so that

can find your device.

In order to grant these permissions, you'll need to add a new set of udev rules.

To learn how to do this for the nRF52840 Development Kit, check out the installation instructions in our embedded training materials.

afterwards, your device should show up in

probe-run --list-probes
similar to this:
$ probe-run --list-probes
The following devices were found:
[0]: J-Link (J-Link) (VID: 1366, PID: 1015, Serial: , JLink)

No external or on-board debugger present

To use

you need a "probe" (also known as "debugger") that sits between your PC and the microcontroller.

Most development boards, especially the bigger ones, have a probe "on-board": If the product description of your board mentions something like a J-Link or ST-Link on-board debugger you're good to go. With these boards, all you need to do is connect your PC to the dev board using a USB cable you are all set to use


If this is not the case for your board, check in the datasheet if it exposes exposes SWD or JTAG pins. If they are exposed, you can connect a "stand alone" probe device to the microcontroller and then connect the probe to your PC via USB. Some examples of stand alone probes are: the ST-Link and the J-Link.

Note that this may involve some soldering if your board does not come with a pre-attached header to plug your debugger into.

Error: RTT up channel 0 not found

This may instead present as

Error: RTT control block not found in target memory.

Your code, or a library you're using (e.g. RTIC) might be putting your CPU to sleep when idle. You can verify that this is the problem by busy looping instead of sleeping. When using RTIC, this can be achieved by adding an idle handler to your app:

fn idle(_ctx: idle::Context) -> ! {
     loop {}

Assuming you'd like to still sleep in order to save power, you need to configure your microcontroller so that RTT can still be handled even when the CPU is sleeping. How to do this varies between microcontrollers.

On an STM32G0 running RTIC it can be done by amending your init function to set the

bit on
. e.g.:
fn init(ctx: init::Context) -> init::LateResources {
     ctx.device.RCC.ahbenr.write(|w| w.dmaen().set_bit());

defmt version mismatch


Follow the instructions in the error message to resolve the mismatch.


If you are hacking around with

, you can disable the version check by setting the
environment variable to
at runtime.

Developer Information

running your locally modified

For easier copy-paste-ability, here's an example how to try out your local

$ cd probe-run/
$ PROBE_RUN_IGNORE_VERSION=1 cargo run -- --chip nRF52840_xxAA --backtrace-limit=10 hello
  ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ                                   ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ ˆˆˆˆˆ
  environment variables                                        extra flags             binary to be
  (optional)                                                   (optional)              flashed & run

running snapshot tests

To check whether your change has altered probe-run in unexpected ways, please run the snapshot tests in

before opening a PR if at all possible.

You can do so by connecting a nrf52840 Development Kit and running

$ cargo test -- --ignored

Support Us

is part of the Knurling project, Ferrous Systems' effort at improving tooling used to develop for embedded systems.

If you think that our work is useful, consider sponsoring it via GitHub Sponsors.


Licensed under either of

  • Apache License, Version 2.0 (LICENSE-APACHE or

  • MIT license (LICENSE-MIT or

at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.