Skip to main content

Demo CLI for Other Platforms

The demo CLI lets you explore features of the SDK components before integrating the SDK into your product. You might also find it helpful to adapt the CLI to your product and use it to exercise your own Memfault implementation.

This guide explains the commands available in the demo CLI for projects not on integrated platforms, such as FreeRTOS and bare metal platforms, and how to interpret their output.

Enabling the Demo CLI

Some platforms enable the demo CLI automatically through their respective build systems. These platforms include nRF Connect SDK (NCS), Zephyr, and ESP-IDF. To include the Demo component in your project if you are not using an integrated platform, you will need to add the demo sources to your build.

Include the demo component in the MEMFAULT_COMPONENTS list:

MEMFAULT_COMPONENTS := core util panics metrics demo

The Demo CLI requires a few changes to your application to connct to your console:

  1. Implement memfault_platform_log_raw(), a function to send a log message to the console output.

    Example:

    #include <stdarg.h>
    #include <stdio.h>

    void memfault_platform_log_raw(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    printf("\n");
    va_end(args);
    }
  2. Implement a function to send a char out of your console backend. This function will be called by the Demo component to send output.

    Example:

    static int prv_send_char(char c) {
    int sent = putchar(c);
    // This step only needed for buffered stdout
    fflush(stdout);
    return sent;
    }
  3. Implement a task or add code to a loop which periodically reads from console input and passes the next char to memfault_demo_shell_receive_char().

    Example:

    static void prv_console_input_task(void) {
    while (true) {
    char c;
    if (read(0, &c, sizeof(c))) {
    memfault_demo_shell_receive_char((char)c);
    } else {
    // sleep a bit if we didn't just receive a character, to prevent 100% CPU
    // utilization in this thread.
    usleep(10 * 1000);
    }
    }
    }
  4. Finally, initialize a sMemfaultShellImpl struct to point to your function from step 2.

    Example:

    int main(void) {
    memfault_platform_boot();

    // Other init code
    const sMemfaultShellImpl impl = {
    .send_char = prv_send_char,
    };
    memfault_demo_shell_boot(&impl);
    }

For a more complete example, check out our FreeRTOS with QEMU example

Running a Command

The CLI displays a prompt to indicate it is ready to accept a command. To see if the CLI is running, press enter and the prompt should be printed:

mflt>

To run a command, type its name and press enter. Try the help command to get started.

Command Reference

help

The help command shows all available commands. Type help and press enter:

mflt> help
clear_core: Clear an existing coredump
drain_chunks: Flushes queued Memfault data. To upload data see https://mflt.io/posting-chunks-with-gdb
export: Export base64-encoded chunks. To upload data see https://mflt.io/chunk-data-export
...
mflt>

Some reference implementations have more commands than others. For example, some targets have network connectivity and can send data directly to the Memfault cloud via the post_chunks command. Devices without this ability will not have that command.

get_device_info

The get_device_info command shows the information configured in the reference implementation's memfault_platform_get_device_info():

mflt> get_device_info
MFLT: [INFO] S/N: 123456789012345678901234
MFLT: [INFO] SW type: NAME-main
MFLT: [INFO] SW version: 1.0.0
MFLT: [INFO] HW version: NAME-proto
mflt>

This information is sent to Memfault with any communications to identify the device and the firmware it is running.

test_* for crashes

Most test_* commands exercise Memfault through crashing:

  • test_assert - trigger a Memfault assert
  • test_cassert - trigger a C assert
  • test_busfault - trigger a busfault
  • test_hardfault - trigger a hardfault
  • test_memmanage - trigger a memory management fault
  • test_usagefault - trigger a usage fault

The crash will be intercepted by the fault handler in the Memfault panics component, a coredump will be generated and stored, and then the device reboots. Typically, build id and device info will be printed on boot, making it easy to tell that we have rebooted. For example:

mflt> test_assert
MFLT:[INFO] GNU Build ID: 24f4b7d8b2670ca871d72ab8941033a65be09d26
MFLT:[INFO] S/N: freertos-example
MFLT:[INFO] SW type: qemu-app
MFLT:[INFO] SW version: 1.0.0
MFLT:[INFO] HW version: qemu-mps2-an385
MFLT:[INFO] Memfault Initialized!
mflt>

If you do not see these messages and the prompt return, nor any other indications that the system reset, then something may have gone wrong in the fault handler. Another possibility is that a debugger is attached and it is sitting on a breakpoint. The fault handler in most reference implementations will break into the debugger if one is attached. In GDB, you should be able to use stepi to resume, but see the README for the specific reference implementation you are using in case it has other instructions.

Once the prompt has returned, the storage can be checked with the get_core command. It can then be cleared with clear_core or read out of the coredump storage with export.

note

Once a coredump has been stored in the coredump storage, it will remain there until either storage is cleared (clear_core command) or read out (export command). If you crash again without doing one of these, saving the coredump will fail silently. The device will still crash and reset but the existing coredump will be overwritten.

get_core

The get_core command displays the state of the coredump storage.

If a coredump is present in the coredump storage:

mflt> get_core
MFLT: [INFO] Has coredump with size: 20224
mflt>

If a coredump is not present:

mflt> get_core
MFLT: [INFO] No coredump present!
mflt>

Running get_core only displays information. It does not affect the state of the coredump storage.

clear_core

The clear_core command unconditionally invalidates the coredump storage so that a new coredump can be stored:

mflt> clear_core
MFLT: [INFO] Invalidating coredump
mflt>

The clear_core command will show the same output regardless of whether a coredump was present in the coredump storage or not.

heartbeat_dump

The heartbeat_dump command will print out the current values of the heartbeat metrics. If a value has not been written to a metric in the current heartbeat window, it's value will be shown as null.

mflt> heartbeat_dump
MFLT:[INFO] Heartbeat keys/values:
MFLT:[INFO] MemfaultSdkMetric_IntervalMs: 5433
MFLT:[INFO] MemfaultSdkMetric_UnexpectedRebootCount: null
MFLT:[INFO] operational_hours: null
MFLT:[INFO] operational_crashfree_hours: null
MFLT:[INFO] Example_HeapFreeBytes: 2032
MFLT:[INFO] Example_HeapMinFreeBytes: 184
MFLT:[INFO] idle_task_run_time_percent: null
mflt>

The heartbeat metrics will only reset after a heartbeat interval has expired or by running the heartbeat command.

heartbeat

The heartbeat command captures the current heartbeat, writes it to event storage, and resets the running heartbeat metrics.

mflt> heartbeat
MFLT:[INFO] Heartbeat keys/values:
MFLT:[INFO] MemfaultSdkMetric_IntervalMs: 10742
MFLT:[INFO] MemfaultSdkMetric_UnexpectedRebootCount: null
MFLT:[INFO] operational_hours: null
MFLT:[INFO] operational_crashfree_hours: null
MFLT:[INFO] Example_HeapFreeBytes: 1360
MFLT:[INFO] Example_HeapMinFreeBytes: 184
MFLT:[INFO] idle_task_run_time_percent: 100
mflt>

The current values of the heartbeat metrics may or may not get printed like above depending on if memfault_metrics_heartbeat_debug_print() is being called in memfault_metrics_heartbeat_collect_data(). memfault_metrics_heartbeat_collect_data() is a function that can be overridden with a user definition if they want to set certain metric values at the end of a heartbeat right before collection.

test_reboot

The test_reboot command records a reboot reason, the current PC and LR, and then forces a system reboot. This command does not cause a coredump capture since the reboot recorded is considered an expected reboot. Unexpected reboot reboots can also be recorded, and are typically paired with a coredump to aid with debugging.

mflt> test_reboot
MFLT:[INFO] GNU Build ID: 3c92e3313fe1c9d00ac8687ce966d5f527a05ed3
MFLT:[INFO] S/N: freertos-example
MFLT:[INFO] SW type: qemu-app
MFLT:[INFO] SW version: 1.0.0
MFLT:[INFO] HW version: qemu-mps2-an385
MFLT:[INFO] Memfault Initialized!

mflt>

test_log

The test_log command writes test logs to Memfault's logging buffer.

mflt> test_log
Raw log!
MFLT:[INFO] Info log!
MFLT:[WARN] Warning log!
MFLT:[ERRO] Error log!
mflt>

test_log_capture

The test_log_capture command captures the current log buffer contents, so they will be ready once data is exported.

test_trace

The test_trace command captures a trace event and writes it to the event storage.

Once a trace has been captured, it can be read out of the event storage with the export command.

note

The event storage is completely separate from the coredump storage. The commands get_core and clear_core work only on the coredump storage. There are no equivalents for the event storage.

export

The export command reads out all data from both the coredump storage and the event storage. It then formats this data into a base64-encoded chunk that can be sent to the Memfault cloud using one of the strategies described here.

# example export command output
mflt> export
MC:CAKnAgIDAQpqemVwaHlyLWFwcAltMS4wLjArZGJkZWI0OAZucWVtdV9jb3J0ZXhfbTMLRvknGqz1fwSiAQAFAMQ5:
MC:SE8CpwIBAwEKanplcGh5ci1hcHAJbTEuMC4wK2RiZGViNDgGbnFlbXVfY29ydGV4X20zC0b5Jxqs9X8EoQGIAAEBGQyIGefcGfWnGQ+0GQw=:
mflt>
note

The export command has the side effect of clearing the data. After the packetizer consumes the coredump storage and the event storage, they will both be cleared and ready for new data.

If the upload succeeds but you don't see the data processed on Memfault, go to the Processing Log under the “Integration Hub” sub-menu. The Processing Log contains details on what data has been received and processed by Memfault, as well as any error that may have occurred. Note that you need to upload the symbols to get a complete analysis of uploaded coredumps.

post_chunks

The post_chunks command is available on targets with direct network connectivity:

mflt> post_chunks
Posting Memfault Data
mflt>

It will read out all data from both the coredump storage and event storage and then send the data directly to the Memfault cloud. The target must already be configured on a network and be able to make an HTTPS connection to the Memfault cloud servers. A valid Memfault project key must also be configured. See the instructions in the reference implementation for how to do this.

note

The post_chunks command has the side effect of clearing the data. After the packetizer consumes the coredump storage and the event storage, they will both be cleared and ready for new data.

If the upload succeeds but you don't see the data processed on Memfault, you probably need to upload the symbols.

drain_chunks

The drain_chunks command is available to read out all coredump data and event storage similar to post_chunks such that all data is cleared. By default, there is no mechanism to upload chunks via drain_chunks to the Memfault cloud, but you can add one with user_transport_send_chunk_data() if you prefer to test a method other than the one used by post_chunks, or post_chunks is not available on your reference implementation.

self_test

The self_test command runs tests on the most important subsystems of Memfault to validate proper integration of the SDK.

SDK Components

This list summarizes which demo CLI commands are used to test each SDK component:

Data can be exported and cleared with:

Implementation Notes

The demo CLI itself is implemented as two components:

  • demo/memfault_demo_shell implements the console. It handles character input and output, displaying the prompt, and dispatching the command to a C function in memfault_demo_cli that performs it. The demo shell is not used on all reference implementations. Some target platforms have a similar shell toolkit built-in. For those platforms, the built-in shell maybe used to call memfault_demo_cli functions directly.

  • demo/memfault_demo_cli implements the actual demo. It has one function for each command on the CLI. Each function typically calls one function in the SDK, checks the result, and formats the output.

Using Invoke Commands in the SDK

The CLI runs on the embedded device itself and communicates with a host computer over a communications interface. The interface varies depending on what is available on the target but it will commonly be serial (UART), Segger RTT, or semihosting. The README file for the example implementations explains the interface and any steps needed to wire it up. The console works the same regardless of the interface used.

The CLI is started with an Invoke command similar to this:

$ invoke NAME.console

Replace NAME with the name of the reference implementation (eg mbed). The README file for the implementation will give the exact command. If the invoke command does not halt with an error message, the console is active and can accept commands.

note

The invoke command does more than just pass through characters as a terminal would. It actively listens to the connection and provides a special feature used with the export command.

If you are running the demo CLI via the Invoke wrapper,you should see this prompt:

Invoke CLI wrapper detected 'export' call
Would you like to run the command displayed above? [y/n]

The above prompt comes from your computer, not from the demo CLI running on the target. The Invoke wrapper monitors all output from the demo CLI and when it detects the output from the export command, it interrupts the session and offers to run the curl command automatically.

After you accept (or decline) to run the curl command, the Invoke wrapper will return you to your session with the demo CLI. If you don't see the mflt> prompt, press enter and it should appear:

mflt>