Skip to content

Commit

Permalink
Memfault Firmware SDK 1.13.0 (Build 10617)
Browse files Browse the repository at this point in the history
  • Loading branch information
Memfault Inc committed Oct 8, 2024
1 parent 47d69c5 commit 3b0fd7c
Show file tree
Hide file tree
Showing 15 changed files with 196 additions and 425 deletions.
71 changes: 60 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,55 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.13.0] - 2024-10-07

### 📈 Added

- FreeRTOS:

- The SDK now has a config to control whether to split CPU usage per core when
building for a multi-core device. Enable this setting by adding
`#define MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT 1` to
`memfault_platform_config.h`. This setting is disabled by default.

- ESP-IDF:
- Added a Kconfig, `CONFIG_MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT`,
to control `MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT`. This Kconfig
is enabled by default for multi-core devices.

### 🛠️ Changed

- FreeRTOS:

- Changed previous idle task run time percent metrics to measure CPU usage
(i.e. the complement of the idle task run time)
- Renamed the following metrics:
- Single-Core + Multi-Core Default:
- `idle_task_run_time_percent` -> `cpu_usage_pct`
- Multi-Core Split:
- `idle0_task_run_time_percent` -> `cpu_usage_pct`
- `idle1_task_run_time_percent` -> `cpu1_usage_pct`

- ESP-IDF:

- Unknown or unclassified reboot reason codes returned by `get_reset_reason()`
are now correctly recorded as `kMfltRebootReason_Unknown` instead of
`kMfltRebootReason_UnknownError` (`UnknownError` is reserved for an
"unexpected" reboot path, where `Unknown` is used when the reboot reason
cannot be determined).

### 🚩 Deprecated

Support for the following vendor platform versions is deprecated in this
release, and will be removed in the following release:

- ESP-IDF < `v4.4` (Jan 26, 2022)
- Zephyr < `v2.7.0` (Oct 16, 2021)
- nRF-Connect SDK < `v1.9.2` (Jul 14, 2022)

Please [contact us]([email protected]) if you need support for earlier
versions!

## [1.12.0] - 2024-09-25

### 📈 Added
Expand Down Expand Up @@ -79,6 +128,17 @@ and this project adheres to

### 📈 Added

- General:

- Add two new core metrics, `MemfaultSdkMetric_os_name` and
`MemfaultSdkMetric_os_version`. These are string metrics that map to the OS
/ platform name and version string respectively, and are included in the the
ELF at build time, and processed out-of-band by Memfault for NCS, Zephyr,
and ESP-IDF. They **are not** ever transmitted over the air (no bandwidth
impact). For example, for Zephyr these metric string values would be
`zephyr` and `3.7.0` for a project on Zephyr v3.7.0. These metrics are
attributes by default and will not be counted towards attribute quotas.

- Zephyr:

- Add the following built-in WiFi metrics for Zephyr devices, enabled by
Expand Down Expand Up @@ -162,17 +222,6 @@ and this project adheres to

### 📈 Improvements

- General:

- Add two new core metrics, `MemfaultSdkMetric_os_name` and
`MemfaultSdkMetric_os_version`. These are string metrics that map to the OS
/ platform name and version string respectively, and are included in the the
ELF at build time, and processed out-of-band by Memfault for NCS, Zephyr,
and ESP-IDF. They **are not** ever transmitted over the air (no bandwidth
impact). For example, for Zephyr these metric string values would be
`zephyr` and `3.7.0` for a project on Zephyr v3.7.0. These metrics are
attributes by default and will not be counted towards attribute quotas.

- ESP-IDF:

- Fix a minor issue where MEMFAULT_LOG_x() macros (MEMFAULT_LOG_ERROR() etc )
Expand Down
6 changes: 3 additions & 3 deletions VERSION
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
BUILD ID: 10351
GIT COMMIT: 1d3c51cd48
VERSION: 1.12.0
BUILD ID: 10617
GIT COMMIT: edc2beda42
VERSION: 1.13.0
4 changes: 2 additions & 2 deletions components/include/memfault/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ typedef struct {
} sMfltSdkVersion;

#define MEMFAULT_SDK_VERSION \
{ .major = 1, .minor = 12, .patch = 0 }
#define MEMFAULT_SDK_VERSION_STR "1.12.0"
{ .major = 1, .minor = 13, .patch = 0 }
#define MEMFAULT_SDK_VERSION_STR "1.13.0"

#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions examples/freertos/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ CFLAGS += \
-Og \
-MD \
-fdebug-prefix-map="$(shell pwd)"=. \
-DBOARD=\"$(BOARD)\" \
$(ARCH_CFLAGS) \

# Enable the self test by default
Expand Down
23 changes: 23 additions & 0 deletions examples/freertos/src/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! See LICENSE for details

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "FreeRTOS.h"
Expand Down Expand Up @@ -108,6 +109,23 @@ static int prv_session(int argc, char *argv[]) {
return 0;
}

static int prv_spin_cpu(int argc, char *argv[]) {
uint32_t cycle_count = UINT32_MAX;
if (argc > 1) {
char *end = NULL;
cycle_count = strtoul(argv[1], &end, 10);
if (end == argv[1]) {
MEMFAULT_LOG_RAW("Invalid cycle count");
cycle_count = UINT32_MAX;
}
}

MEMFAULT_LOG_RAW("Spinning CPU for %lu cycles", cycle_count);
for (uint32_t i = 0; i < cycle_count; i++) { }
MEMFAULT_LOG_RAW("Spinning completed, check cpu_usage_pct");
return 0;
}

static const sMemfaultShellCommand s_freertos_example_shell_extension_list[] = {
{
.command = "freertos_vassert",
Expand All @@ -129,6 +147,11 @@ static const sMemfaultShellCommand s_freertos_example_shell_extension_list[] = {
.handler = prv_session,
.help = "Execute a test metrics session named 'cli'",
},
{
.command = "spin_cpu",
.handler = prv_spin_cpu,
.help = "Spin CPU to increase cpu_usage_pct metric",
},
};
#endif

Expand Down
2 changes: 1 addition & 1 deletion examples/freertos/src/memfault/memfault_platform_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static const char *s_log_prefix = "MFLT";
void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) {
*info = (sMemfaultDeviceInfo){
.device_serial = "freertos-example",
.hardware_version = "qemu-mps2-an385",
.hardware_version = BOARD,
.software_type = "qemu-app",
.software_version = "1.0.0",
};
Expand Down
11 changes: 10 additions & 1 deletion ports/esp_idf/memfault/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,20 @@ endif
default y
select FREERTOS_GENERATE_RUN_TIME_STATS
help
Collects the idle thread runtime metric as a percentage of total runtime.
Collects the CPU usage as a percentage of total runtime.
This metric is only available when using esp_timer for runtime stats to
avoid issues with counter overflow. Note for FreeRTOS versions < 10.2.0
this will be a no-op.

config MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT
bool "Enables per-core cpu_usage_pct metrics for multi-core targets"
default y
depends on MEMFAULT_FREERTOS_TASK_RUNTIME_STATS
depends on SOC_CPU_CORES_NUM > 1
help
When enabled, collects a separate measure of each CPU core usage as
cpu_usage_pct for cpu0 and cpu1_usage_pct for cpu1

config MEMFAULT_MBEDTLS_METRICS
bool "Collect metrics from mbedTLS"
default y
Expand Down
5 changes: 4 additions & 1 deletion ports/esp_idf/memfault/common/memfault_platform_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,12 @@ static void prv_record_reboot_reason(void) {
reboot_reason = kMfltRebootReason_BrownOutReset;
break;
case ESP_RST_PANIC:
default:
reboot_reason = kMfltRebootReason_UnknownError;
break;
case ESP_RST_UNKNOWN:
default:
reboot_reason = kMfltRebootReason_Unknown;
break;
}
#endif
#endif
Expand Down
8 changes: 8 additions & 0 deletions ports/esp_idf/memfault/config/memfault_esp_idf_port_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ extern "C" {
// so it's redundant to save them in the Memfault SDK as well.
#define MEMFAULT_SDK_LOG_SAVE_DISABLE 1

// Kconfiglib will set this symbol to y even if SOC_CPU_CORES_NUM is not defined in older esp-idf
// versions. Instead, use the FreeRTOS-SMP configNUM_CORES to determine support
#if defined(CONFIG_MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT)
#if defined(configNUM_CORES) && configNUM_CORES > 1
#define MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT 1
#endif
#endif

// Pick up any user configuration overrides. This should be kept at the bottom
// of this file
#if CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
// This definition is used to track if this file has been included yet
#define MEMFAULT_METRICS_HEARTBEAT_FREERTOS_CONFIG_DEF 1

#if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE
MEMFAULT_METRICS_KEY_DEFINE(idle0_task_run_time_percent, kMemfaultMetricType_Unsigned)
MEMFAULT_METRICS_KEY_DEFINE(idle1_task_run_time_percent, kMemfaultMetricType_Unsigned)
#elif MEMFAULT_FREERTOS_RUN_TIME_STATS_SINGLE_CORE
MEMFAULT_METRICS_KEY_DEFINE(idle_task_run_time_percent, kMemfaultMetricType_Unsigned)
#define CPU_USAGE_PCT_SCALE_VALUE (100)

MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(cpu_usage_pct, kMemfaultMetricType_Unsigned,
CPU_USAGE_PCT_SCALE_VALUE)
#if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT
MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(cpu1_usage_pct, kMemfaultMetricType_Unsigned,
CPU_USAGE_PCT_SCALE_VALUE)
#endif

#if MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES
Expand Down
90 changes: 58 additions & 32 deletions ports/freertos/src/memfault_sdk_metrics_freertos.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,28 @@
#define MEMFAULT_USE_NEW_FREERTOS_IDLETASK_RUNTIME_API 0
#endif

// Defines the precision used to calculate changes in cpu_usage_pct
// Changes in this value require changing the scale_value used with cpu_usage_pct metrics
// according to this equation: 100 = PRECISION_CONSTANT / scale_value
// See ports/freertos/config/memfault_metrics_heartbeat_freertos_config.def
#define PRECISION_CONSTANT (10000ULL)
// A fudge factor used to improve rounding behavior at low/high end
#define FUDGE_FACTOR (PRECISION_CONSTANT / 2ULL)

static configRUN_TIME_COUNTER_TYPE prv_calculate_delta_runtime(
configRUN_TIME_COUNTER_TYPE prev_runtime_ticks, configRUN_TIME_COUNTER_TYPE curr_runtime_ticks) {
return curr_runtime_ticks - prev_runtime_ticks;
}

static int32_t prv_calc_runtime_percentage(configRUN_TIME_COUNTER_TYPE prev_task_runtime,
configRUN_TIME_COUNTER_TYPE curr_task_runtime,
configRUN_TIME_COUNTER_TYPE curr_total_runtime,
configRUN_TIME_COUNTER_TYPE prev_total_runtime) {
// NB: We calculate a percentage that may need to be scaled according to PRECISION constant:
// - 1 percent: 1000 ticks/s * 60 s/min * 60 min/hr * (1/100)hrs -> 36,000 ticks
// - 1 permyriad: 1000 ticks/s * 60 s/min * 60 min/hr * (1/10000)hrs -> 360 ticks
// The PRECISION_CONSTANT allows handling higher tick rates at higher values (i.e. percent vs
// permyriad)
static int32_t prv_calc_runtime_percent(configRUN_TIME_COUNTER_TYPE prev_task_runtime,
configRUN_TIME_COUNTER_TYPE curr_task_runtime,
configRUN_TIME_COUNTER_TYPE curr_total_runtime,
configRUN_TIME_COUNTER_TYPE prev_total_runtime) {
uint64_t delta_runtime = prv_calculate_delta_runtime(prev_task_runtime, curr_task_runtime);
uint64_t delta_total_runtime =
prv_calculate_delta_runtime(prev_total_runtime, curr_total_runtime);
Expand All @@ -58,16 +71,23 @@ static int32_t prv_calc_runtime_percentage(configRUN_TIME_COUNTER_TYPE prev_task
}

// Make sure to not overflow when computing the percentage below
if (delta_runtime >= UINT64_MAX - (UINT64_MAX / 100)) {
// Scale both parts of the fraction by 100 to avoid overflow.
// Include a fudge factor of 50 to prevent rounding errors.
delta_runtime = ((UINT64_MAX - 50) < delta_runtime) ? (UINT64_MAX) : (delta_runtime + 50);
delta_runtime /= 100;
delta_total_runtime /= 100;
if (delta_runtime >= UINT64_MAX - (UINT64_MAX / PRECISION_CONSTANT)) {
// Scale both parts of the fraction by 10000 to avoid overflow.
// Include a fudge factor of 5000 to prevent rounding errors.
delta_runtime =
((UINT64_MAX - FUDGE_FACTOR) < delta_runtime) ? (UINT64_MAX) : (delta_runtime + FUDGE_FACTOR);
delta_runtime /= PRECISION_CONSTANT;
delta_total_runtime /= PRECISION_CONSTANT;
}

// Clamp delta_runtime to be less than delta_total_runtime
// This can happen due to differences in when the total runtime vs a task's runtime are updated
if (delta_runtime > delta_total_runtime) {
delta_runtime = delta_total_runtime;
}

// Now compute percentage
const int32_t percentage = (int32_t)((delta_runtime * 100ULL) / delta_total_runtime);
const int32_t percentage = (int32_t)((delta_runtime * PRECISION_CONSTANT) / delta_total_runtime);
return percentage;
}

Expand Down Expand Up @@ -125,49 +145,55 @@ static void prv_record_timer_stack_free_bytes(void) {

void memfault_freertos_port_task_runtime_metrics(void) {
#if MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS
static configRUN_TIME_COUNTER_TYPE s_prev_idle0_runtime = 0;
#if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE
static configRUN_TIME_COUNTER_TYPE s_prev_idle_runtime = 0;

// Only collect separate cpu1 runtime if multi-core split
#if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT
static configRUN_TIME_COUNTER_TYPE s_prev_idle1_runtime = 0;
#endif

static configRUN_TIME_COUNTER_TYPE s_prev_total_runtime = 0;

configRUN_TIME_COUNTER_TYPE idle_runtime =
#if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE
configRUN_TIME_COUNTER_TYPE idle0_runtime = prv_get_idle_counter_for_core(0);
configRUN_TIME_COUNTER_TYPE idle1_runtime = prv_get_idle_counter_for_core(1);
prv_get_idle_counter_for_core(0);
#else
configRUN_TIME_COUNTER_TYPE idle0_runtime =
#if MEMFAULT_USE_NEW_FREERTOS_IDLETASK_RUNTIME_API
ulTaskGetIdleRunTimeCounter
#else
xTaskGetIdleRunTimeCounter
#endif
();
#endif

#if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT
configRUN_TIME_COUNTER_TYPE idle1_runtime = prv_get_idle_counter_for_core(1);
#endif

configRUN_TIME_COUNTER_TYPE total_runtime = prv_get_total_runtime();

int32_t idle0_runtime_percentage = prv_calc_runtime_percentage(
s_prev_idle0_runtime, idle0_runtime, total_runtime, s_prev_total_runtime);
#if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE
int32_t idle1_runtime_percentage = prv_calc_runtime_percentage(
s_prev_idle1_runtime, idle1_runtime, total_runtime, s_prev_total_runtime);
int32_t idle_runtime_percent = prv_calc_runtime_percent(s_prev_idle_runtime, idle_runtime,
total_runtime, s_prev_total_runtime);
#if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT
int32_t idle1_runtime_percent = prv_calc_runtime_percent(s_prev_idle1_runtime, idle1_runtime,
total_runtime, s_prev_total_runtime);
#endif

s_prev_idle0_runtime = idle0_runtime;
#if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE
s_prev_idle_runtime = idle_runtime;
#if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT
s_prev_idle1_runtime = idle1_runtime;
#endif
s_prev_total_runtime = total_runtime;

if (idle0_runtime_percentage >= 0) {
#if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE
MEMFAULT_METRIC_SET_UNSIGNED(idle0_task_run_time_percent, (uint32_t)idle0_runtime_percentage);
#else
MEMFAULT_METRIC_SET_UNSIGNED(idle_task_run_time_percent, (uint32_t)idle0_runtime_percentage);
#endif
if (idle_runtime_percent >= 0) {
MEMFAULT_METRIC_SET_UNSIGNED(cpu_usage_pct,
(uint32_t)(PRECISION_CONSTANT - (uint32_t)idle_runtime_percent));
}
#if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE
if (idle1_runtime_percentage >= 0) {
MEMFAULT_METRIC_SET_UNSIGNED(idle1_task_run_time_percent, (uint32_t)idle1_runtime_percentage);

#if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT
if (idle1_runtime_percent >= 0) {
MEMFAULT_METRIC_SET_UNSIGNED(cpu1_usage_pct,
(uint32_t)(PRECISION_CONSTANT - (uint32_t)idle1_runtime_percent));
}
#endif
#endif // MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS
Expand Down
11 changes: 11 additions & 0 deletions ports/include/memfault/ports/freertos/metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@
(MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE || MEMFAULT_FREERTOS_RUN_TIME_STATS_SINGLE_CORE)
#endif

// Disable multi-core split metrics if not defined
#if !defined(MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT)
#define MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT 0
#endif

// Provide build-time error if single core + runtime stats split
#if MEMFAULT_FREERTOS_RUN_TIME_STATS_SINGLE_CORE && MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT
#error \
"Multi-core split stats not supported for single-core devices, disable MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT"
#endif

#if !defined(MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES)
#define MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES 1
#endif
Expand Down
Loading

0 comments on commit 3b0fd7c

Please sign in to comment.