diff --git a/CHANGELOG.md b/CHANGELOG.md index 26fe37f80..3742c1d20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ 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.8.0] - 2024-04-17 + +### :chart_with_upwards_trend: Improvements + +- General: + - Scale values now fully supported. This metric metadata will scale down + metric values by the specified factor. Metrics with no specified scale value + will not be scaled + - Fix a :bug: that would reset metric values after running a session start + callback. This prevented setting metrics at the start of a session. +- Zephyr: + - Add a metric using a scale value to our Zephyr QEMU example. This metric + measures CPU usage (in permille, 0.0-100.0%) of the main thread. + ## [1.7.7] - 2024-04-15 - FreeRTOS: diff --git a/VERSION b/VERSION index 7c19bf46b..313bab04b 100644 --- a/VERSION +++ b/VERSION @@ -1,3 +1,3 @@ -BUILD ID: 7396 -GIT COMMIT: 4b94654821 -VERSION: 1.7.7 +BUILD ID: 7456 +GIT COMMIT: 69ebc0be9a +VERSION: 1.8.0 diff --git a/components/include/memfault/version.h b/components/include/memfault/version.h index e194b7a1d..414ec5151 100644 --- a/components/include/memfault/version.h +++ b/components/include/memfault/version.h @@ -19,8 +19,8 @@ typedef struct { uint8_t patch; } sMfltSdkVersion; -#define MEMFAULT_SDK_VERSION { .major = 1, .minor = 7, .patch = 7 } -#define MEMFAULT_SDK_VERSION_STR "1.7.7" +#define MEMFAULT_SDK_VERSION { .major = 1, .minor = 8, .patch = 0 } +#define MEMFAULT_SDK_VERSION_STR "1.8.0" #ifdef __cplusplus } diff --git a/components/metrics/src/memfault_metrics.c b/components/metrics/src/memfault_metrics.c index 89d0de375..346d4f5a6 100644 --- a/components/metrics/src/memfault_metrics.c +++ b/components/metrics/src/memfault_metrics.c @@ -1017,11 +1017,6 @@ int memfault_metrics_heartbeat_read_string(MemfaultMetricId key, char *read_val, } int memfault_metrics_session_start(eMfltMetricsSessionIndex session_key) { - MemfaultMetricsSessionStartCb session_start_cb = s_session_start_cbs[session_key]; - if (session_start_cb != NULL) { - session_start_cb(); - } - int rv; memfault_lock(); { @@ -1034,6 +1029,11 @@ int memfault_metrics_session_start(eMfltMetricsSessionIndex session_key) { } memfault_unlock(); + MemfaultMetricsSessionStartCb session_start_cb = s_session_start_cbs[session_key]; + if (session_start_cb != NULL) { + session_start_cb(); + } + return rv; } diff --git a/examples/zephyr/qemu/qemu-app/Kconfig b/examples/zephyr/qemu/qemu-app/Kconfig index 1605d03cd..5835b3ec4 100644 --- a/examples/zephyr/qemu/qemu-app/Kconfig +++ b/examples/zephyr/qemu/qemu-app/Kconfig @@ -10,10 +10,12 @@ config ZEPHYR_MEMFAULT_EXAMPLE_THREAD_TOGGLE Enables creating and aborting an example thread every 10 seconds in the example app -config ZEPHYR_MEMFAULT_EXAMPLE_MEMORY_METRICS - bool "Use metrics to monitor memory usage in the example app" +config ZEPHYR_MEMFAULT_EXAMPLE_METRICS + bool "Use metrics to monitor memory usage and execution time in the example app" default y - depends on MEMFAULT && MEMFAULT_HEAP_STATS && THREAD_STACK_INFO + depends on MEMFAULT && MEMFAULT_HEAP_STATS + depends on THREAD_STACK_INFO + depends on THREAD_RUNTIME_STATS config ZEPHYR_MEMFAULT_EXAMPLE_SOFTWARE_VERSION string "Software Version" diff --git a/examples/zephyr/qemu/qemu-app/config/memfault_metrics_heartbeat_config.def b/examples/zephyr/qemu/qemu-app/config/memfault_metrics_heartbeat_config.def index 4e81da86d..21d7edc44 100644 --- a/examples/zephyr/qemu/qemu-app/config/memfault_metrics_heartbeat_config.def +++ b/examples/zephyr/qemu/qemu-app/config/memfault_metrics_heartbeat_config.def @@ -1 +1,4 @@ MEMFAULT_METRICS_KEY_DEFINE(MainStack_MinBytesFree, kMemfaultMetricType_Unsigned) +// This example metric demonstrates a permille integer with a range of 0-1000 to be translated into +// a percentage 0.0-100.0% +MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(main_thread_cpu_time_permille, kMemfaultMetricType_Unsigned, 10) diff --git a/examples/zephyr/qemu/qemu-app/prj.conf b/examples/zephyr/qemu/qemu-app/prj.conf index b9c053e5c..65a6e8cd6 100644 --- a/examples/zephyr/qemu/qemu-app/prj.conf +++ b/examples/zephyr/qemu/qemu-app/prj.conf @@ -57,3 +57,6 @@ CONFIG_MEMFAULT_SHELL_SELF_TEST_COREDUMP_STORAGE=y # Enable warnings as errors CONFIG_COMPILER_WARNINGS_AS_ERRORS=y + +# Enable Thread run-time stats +CONFIG_THREAD_RUNTIME_STATS=y diff --git a/examples/zephyr/qemu/qemu-app/src/main.c b/examples/zephyr/qemu/qemu-app/src/main.c index 2e306a6cc..9a535bdbe 100644 --- a/examples/zephyr/qemu/qemu-app/src/main.c +++ b/examples/zephyr/qemu/qemu-app/src/main.c @@ -22,7 +22,7 @@ LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); -#if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_MEMORY_METRICS +#if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS //! Size of memory to allocate on main stack //! This requires a larger allocation due to the method used to measure stack usage #define STACK_ALLOCATION_SIZE (CONFIG_MAIN_STACK_SIZE >> 2) @@ -36,7 +36,7 @@ static void *heap_ptrs[4] = { NULL }; //! Keep a reference to the main thread for stack info static struct k_thread *s_main_thread = NULL; -#endif // CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_MEMORY_METRICS +#endif // CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS // Blink code taken from the zephyr/samples/basic/blinky example. static void blink_forever(void) { @@ -122,7 +122,7 @@ static void prv_init_test_thread_timer(void) { static void prv_init_test_thread_timer(void) { } #endif // CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_THREAD_TOGGLE -#if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_MEMORY_METRICS +#if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS //! Helper function to collect metric value on main thread stack usage. static void prv_collect_main_thread_stack_free(void) { @@ -142,6 +142,35 @@ static void prv_collect_main_thread_stack_free(void) { } } +static void prv_collect_main_thread_run_stats(void) { + static uint64_t s_prev_main_thread_cycles = 0; + static uint64_t s_prev_cpu_all_cycles = 0; + + if (s_main_thread == NULL) { + return; + } + + k_thread_runtime_stats_t rt_stats_main = { 0 }; + k_thread_runtime_stats_t rt_stats_all = { 0 }; + + k_thread_runtime_stats_get(s_main_thread, &rt_stats_main); + k_thread_runtime_stats_all_get(&rt_stats_all); + + // Calculate difference since last heartbeat + uint64_t current_main_thread_cycles = rt_stats_main.execution_cycles - s_prev_main_thread_cycles; + // Note: execution_cycles = idle + non-idle, total_cycles = non-idle + uint64_t current_cpu_total_cycles = rt_stats_all.execution_cycles - s_prev_cpu_all_cycles; + + // Calculate permille of main thread execution vs total CPU time + // Multiply permille factor first to avoid truncating lower bits after integer division + uint32_t main_thread_cpu_time = (current_main_thread_cycles * 1000) / current_cpu_total_cycles; + MEMFAULT_METRIC_SET_UNSIGNED(main_thread_cpu_time_permille, main_thread_cpu_time); + + // Update previous values + s_prev_main_thread_cycles = current_main_thread_cycles; + s_prev_cpu_all_cycles = current_cpu_total_cycles; +} + //! Shell function to exercise example memory metrics //! //! This function demonstrates the change in stack and heap memory metrics @@ -185,6 +214,7 @@ SHELL_CMD_REGISTER(memory_metrics, NULL, "Collects runtime memory metrics from a // and print current metric values void memfault_metrics_heartbeat_collect_data(void) { prv_collect_main_thread_stack_free(); + prv_collect_main_thread_run_stats(); memfault_metrics_heartbeat_debug_print(); } @@ -193,7 +223,7 @@ static void prv_run_stack_metrics_example(void) { volatile uint8_t stack_array[STACK_ALLOCATION_SIZE]; memset((uint8_t *)stack_array, 0, STACK_ALLOCATION_SIZE); } -#endif // CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_MEMORY_METRICS +#endif // CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS #if defined(CONFIG_MEMFAULT_FAULT_HANDLER_RETURN) #include MEMFAULT_ZEPHYR_INCLUDE(fatal.h) @@ -213,7 +243,7 @@ int main(void) { memfault_zephyr_collect_reset_info(); #endif -#if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_MEMORY_METRICS +#if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS s_main_thread = k_current_get(); // @warning This code uses `memfault_metrics_heartbeat_debug_trigger` which is not intended diff --git a/scripts/memfault_gdb.py b/scripts/memfault_gdb.py index b275c854e..4b0fbb750 100644 --- a/scripts/memfault_gdb.py +++ b/scripts/memfault_gdb.py @@ -808,7 +808,7 @@ def _create_http_connection(base_uri): else: conn_class = HTTPSConnection default_port = 443 - port = url.port if url.port else default_port + port = url.port or default_port return conn_class(url.hostname, port=port) diff --git a/tests/src/test_memfault_session_metrics.cpp b/tests/src/test_memfault_session_metrics.cpp index 298cc49c0..e8a51b622 100644 --- a/tests/src/test_memfault_session_metrics.cpp +++ b/tests/src/test_memfault_session_metrics.cpp @@ -56,6 +56,8 @@ static void session_end_cb(void) { } static void session_start_cb(void) { + // Set a metric value to ensure that metrics are reset before session_start_cb + memfault_metrics_heartbeat_set_unsigned(MEMFAULT_METRICS_KEY(test_session_key_unsigned), 1); mock().actualCall(__func__); } @@ -141,10 +143,34 @@ TEST(MemfaultSessionMetrics, Test_RegisterSessionStartCb) { mock().expectOneCall("memfault_metrics_session_serialize"); mock().expectOneCall("session_start_cb"); + // Check that a session metric is unset before the session starts + uint32_t val = 0; + int rv = + memfault_metrics_heartbeat_read_unsigned(MEMFAULT_METRICS_KEY(test_session_key_unsigned), &val); + + LONGS_EQUAL(-7, rv); + LONGS_EQUAL(0, val); + memfault_metrics_session_register_start_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), session_start_cb); MEMFAULT_METRICS_SESSION_START(test_key_session); + + // Check that the metric is set after the session starts. Metric is set within the start callback + rv = + memfault_metrics_heartbeat_read_unsigned(MEMFAULT_METRICS_KEY(test_session_key_unsigned), &val); + + LONGS_EQUAL(0, rv); + LONGS_EQUAL(1, val); + MEMFAULT_METRICS_SESSION_END(test_key_session); + + // Check that the metric is set after the session ends. Metrics are reset at session start, not + // end + rv = + memfault_metrics_heartbeat_read_unsigned(MEMFAULT_METRICS_KEY(test_session_key_unsigned), &val); + + LONGS_EQUAL(0, rv); + LONGS_EQUAL(1, val); } TEST(MemfaultSessionMetrics, Test_RegisterSessionEndCb) {