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.
- Make
- CMake
- Other/IDE
Include the demo
component in the MEMFAULT_COMPONENTS
list:
MEMFAULT_COMPONENTS := core util panics metrics demo
Include the demo
component in the MEMFAULT_COMPONENTS
list:
include(${MEMFAULT_SDK_ROOT}/cmake/Memfault.cmake)
list(APPEND MEMFAULT_COMPONENTS core util panics metrics demo)
memfault_library(${MEMFAULT_SDK_ROOT} MEMFAULT_COMPONENTS
MEMFAULT_COMPONENTS_SRCS MEMFAULT_COMPONENTS_INC_FOLDERS)
The demo
component files are located here:
- sources:
memfault-firmware-sdk/components/demo/
- includes:
memfault-firmware-sdk/components/include/memfault/demo/
Add the source files to the project, and add the include directory to the preprocessor search directories.
The Demo CLI requires a few changes to your application to connct to your console:
-
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);
} -
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;
} -
Implement a task or add code to a loop which periodically reads from console input and passes the next
char
tomemfault_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);
}
}
} -
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 asserttest_cassert
- trigger a C asserttest_busfault
- trigger a busfaulttest_hardfault
- trigger a hardfaulttest_memmanage
- trigger a memory management faulttest_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
.
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.
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>
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.
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:
- Core
- Panics
test_*
for crashesget_core
clear_core
- Logs
- Trace Events
- Metrics
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 inmemfault_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 callmemfault_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.
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>