diff --git a/.codecov.yml b/.codecov.yml index ff6e1e81d..c7c3b7b4c 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -3,6 +3,7 @@ coverage: round: down range: "70...100" status: + patch: off project: default: target: 70% diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d24341f..6780959b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,79 @@ 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.16.0] - 2024-10-24 + +### 🔥 Removed + +- Removed support for Zephyr < 2.7.0 +- Removed support for nRF-Connect SDK < 1.9.2 +- Removed support for ESP-IDF < 4.4.0 + +Please [contact us](https://mflt.io/contact-support) if you need support for +earlier versions! + +### 🐛 Fixed + +- General: + + - Correct an issue where `eMemfaultRebootReason` is expressed as a 4-byte type + instead of 2-bytes when compiling with Clang with high optimization, when + targeting ARM. This results in Coredumps tagged as `Unknown` instead of the + correct reason code. + +### 📈 Added + +- General: + + - Add a pair of optional user-provided functions, + `memfault_reboot_tracking_load()` / `memfault_reboot_tracking_save()`, to + allow users to provide their own implementations for saving and loading + reboot tracking data. This is useful when the default implementation is not + suitable for the platform or when the user wants to store the data in a + different location. + + - The + [Stable Sessions Device Vital](https://docs.memfault.com/docs/platform/memfault-core-metrics#stable-sessions) + added in SDK version `1.15.0` is fully available and no longer considered + experimental. + + - Add an optional `memfault_port_coredump_save_begin()` callback, for use by + Memfault ports. This allows `memfault_platform_coredump_save_begin()` to be + implemented by the platform instead, for custom pre-coredump operations. + Thanks to @finger563 for reporting this issue in + [#77](https://github.com/memfault/memfault-firmware-sdk/issues/77)! + + - Improved API docs for events and data packetizer components by noting + restrictions for use in ISR contexts + +- Zephyr: + + - Update the Qemu app to support the `nucleo_l496zg` board, with support for + the Zephyr `bbram` subsystem, and implement the new + `memfault_reboot_tracking_load()` / `memfault_reboot_tracking_save()` + functions to demonstrate the functionality. + +- ESP-IDF: + + - New Kconfig setting, `CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP`, to print the + ESP-IDF reboot reason code on system boot, for debugging purposes. This + feature is disabled by default. + +### 🛠️ Changed + +- General: + + - Update support links to refer to the preferred site + instead of the Memfault support email. + This link will redirect to a form where questions can be sent to the + Memfault support team. + +- nRF-Connect SDK: + + - Changed the Kconfig symbol `MEMFAULT_REBOOT_REASON_GET_CUSTOM` to be `imply` + instead of `select` when the nRF-Connect SDK is enabled. This permits users + to disable the `nrfx`-based reboot reason tracking if needed. + ## [1.15.0] - 2024-10-13 ### 📈 Added @@ -1361,8 +1434,9 @@ earlier versions! - Improve FOTA support for nRF-Connect SDK 2.4+, by improving the technique used to find the correct Memfault server root cert. Memfault uses a fast CDN to improve OTA payload delivery, which uses a different root cert than the - Memfault device server. Please contact immediately if - you encounter any cert-related issues. + Memfault device server. Please + [contact support](https://mflt.io/contact-support) immediately if you + encounter any cert-related issues. ### 💥 Breaking Changes diff --git a/VERSION b/VERSION index 7214a797c..79afd4263 100644 --- a/VERSION +++ b/VERSION @@ -1,3 +1,3 @@ -BUILD ID: 10752 -GIT COMMIT: 1904cdb3cb -VERSION: 1.15.0 +BUILD ID: 10952 +GIT COMMIT: c690f5abc6 +VERSION: 1.16.0 diff --git a/components/core/src/memfault_ram_reboot_info_tracking.c b/components/core/src/memfault_ram_reboot_info_tracking.c index 11a2bb663..65842b719 100644 --- a/components/core/src/memfault_ram_reboot_info_tracking.c +++ b/components/core/src/memfault_ram_reboot_info_tracking.c @@ -160,6 +160,14 @@ static void prv_record_reboot_event(eMemfaultRebootReason reboot_reason, s_mflt_reboot_info->lr = reg->lr; } +MEMFAULT_WEAK void memfault_reboot_tracking_load(sMemfaultRebootTrackingStorage *dst) { + (void)dst; +} + +MEMFAULT_WEAK void memfault_reboot_tracking_save(const sMemfaultRebootTrackingStorage *src) { + (void)src; +} + void memfault_reboot_tracking_boot(void *start_addr, const sResetBootupInfo *bootup_info) { s_mflt_reboot_info = start_addr; @@ -167,6 +175,8 @@ void memfault_reboot_tracking_boot(void *start_addr, const sResetBootupInfo *boo return; } + memfault_reboot_tracking_load((sMemfaultRebootTrackingStorage *)s_mflt_reboot_info); + if (!prv_check_or_init_struct()) { return; } @@ -191,6 +201,8 @@ void memfault_reboot_tracking_mark_reset_imminent(eMemfaultRebootReason reboot_r } prv_record_reboot_event(reboot_reason, reg); + + memfault_reboot_tracking_save((const sMemfaultRebootTrackingStorage *)s_mflt_reboot_info); } bool memfault_reboot_tracking_read_reset_info(sMfltResetReasonInfo *info) { diff --git a/components/include/memfault/core/data_packetizer.h b/components/include/memfault/core/data_packetizer.h index 44e5d69af..bb56d0838 100644 --- a/components/include/memfault/core/data_packetizer.h +++ b/components/include/memfault/core/data_packetizer.h @@ -20,11 +20,12 @@ extern "C" { #endif -//! Fills buffer with a chunk when there is data available +//! Fill buffer with a chunk when there is data available //! //! NOTE: This is the simplest way to interact with the packetizer. The API call returns a single //! "chunk" to be forwarded out over the transport topology to the Memfault cloud. For more //! advanced control over chunking, the lower level APIs exposed below in this module can be used. +//! @note This function must not be called from an ISR context. //! //! @param[out] buf The buffer to copy data to be sent into //! @param[in,out] buf_len The size of the buffer to copy data into. On return, populated @@ -54,10 +55,13 @@ typedef enum { //! transport path. #define MEMFAULT_PACKETIZER_MIN_BUF_LEN 9 -//! @return true if there is data available to send, false otherwise +//! Check if there is data available to send +//! +//! @note This can be used prior to opening a connection over the underlying transport to the +//! internet +//! @note This function must not be called from an ISR context. //! -//! This can be used to check if there is any data to send prior to opening a connection over the -//! underlying transport to the internet +//! @return true if there is data available to send, false otherwise bool memfault_packetizer_data_available(void); typedef struct { @@ -87,6 +91,10 @@ typedef struct { uint32_t single_chunk_message_length; } sPacketizerMetadata; +//! Initialize the packetizer and get metadata about the message to be sent +//! +//! @note This function must not be called from an ISR context. +//! //! @return true if there is data available to send, false otherwise. bool memfault_packetizer_begin(const sPacketizerConfig *cfg, sPacketizerMetadata *metadata_out); @@ -106,6 +114,7 @@ bool memfault_packetizer_begin(const sPacketizerConfig *cfg, sPacketizerMetadata //! this API up to the cloud _reliably_ and _in-order_. //! @note The api is not threadsafe. The expectation is that a user will drain data from a single //! thread or otherwise wrap the call with a mutex +//! @note This function must not be called from an ISR context. //! //! @param[out] buf The buffer to copy data to be sent into //! @param[in,out] buf_len The size of the buffer to copy data into. On return, populated diff --git a/components/include/memfault/core/event_storage.h b/components/include/memfault/core/event_storage.h index 027aa878b..aabd2b513 100644 --- a/components/include/memfault/core/event_storage.h +++ b/components/include/memfault/core/event_storage.h @@ -102,16 +102,25 @@ int memfault_event_storage_persist(void); #endif -//! Simple API call to retrieve the number of bytes used in the allocated event storage buffer. -//! Returns zero if the storage has not been allocated. +//! Retrieve the number of bytes used in the allocated event storage buffer. +//! +//! @note This function must not be called from an ISR context. +//! +//! @return zero if the storage has not been allocated. size_t memfault_event_storage_bytes_used(void); -//! Simple API call to retrieve the number of bytes free (unused) in the allocated event storage -//! buffer. Returns zero if the storage has not been allocated. +//! Retrieve the number of bytes free (unused) in the allocated event storage +//! buffer. +//! +//! @note This function must not be called from an ISR context. +//! +//! @return zero if the storage has not been allocated. size_t memfault_event_storage_bytes_free(void); //! Check if event storage component has booted //! +//! @note This function must not be called from an ISR context. +//! //! @returns true if event storage booted or false if not bool memfault_event_storage_booted(void); diff --git a/components/include/memfault/core/reboot_reason_types.h b/components/include/memfault/core/reboot_reason_types.h index c0cee7b02..94d89c962 100644 --- a/components/include/memfault/core/reboot_reason_types.h +++ b/components/include/memfault/core/reboot_reason_types.h @@ -24,7 +24,9 @@ extern "C" { #undef MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE #undef MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE -typedef enum MfltResetReason { +//! This enum must be packed to prevent compiler optimizations in instructions which load an +//! eMemfaultRebootReason. +typedef enum MEMFAULT_PACKED MfltResetReason { // A reboot reason was not determined either by hardware or a previously marked reboot reason // This reason is classified as a crash when calculating the operational_crashfree_hours metric kMfltRebootReason_Unknown = 0x0000, @@ -135,6 +137,8 @@ typedef enum MfltResetReason { } eMemfaultRebootReason; +MEMFAULT_STATIC_ASSERT(sizeof(eMemfaultRebootReason) == 2, "Reboot reason enum must be 2 bytes"); + #if MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE == 1 // Ensure that the custom reboot reasons are within the expected range #define MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(name) \ diff --git a/components/include/memfault/core/reboot_tracking.h b/components/include/memfault/core/reboot_tracking.h index 86da302f0..a707087dd 100644 --- a/components/include/memfault/core/reboot_tracking.h +++ b/components/include/memfault/core/reboot_tracking.h @@ -230,6 +230,40 @@ bool memfault_reboot_tracking_metrics_session_was_active(uint32_t index); //! @param name The name of the custom reboot reason #define MEMFAULT_REBOOT_REASON_KEY(name) kMfltRebootReason_##name +//! The below pair of functions, memfault_reboot_tracking_load and +//! memfault_reboot_tracking_save, are used when the reboot tracking RAM storage +//! cannot be safely persisted across reboots. In this case, the user can +//! provide their own implementation to load and save the reboot tracking data +//! to a backing store (e.g. battery-backed ram, non-memory-mapped backup +//! registers, etc). +//! +//! memfault_reboot_tracking_load() is called from +//! memfault_reboot_tracking_boot(), and is used to retrieve the initial value +//! of the reboot tracking data from the backing store. +//! +//! memfault_reboot_tracking_save() is called from +//! memfault_reboot_tracking_mark_reset_imminent(), and is used to persist the +//! reboot tracking data to the backing store. + +typedef MEMFAULT_PACKED_STRUCT MemfaultRebootTrackingStorage { + uint8_t data[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; +} +sMemfaultRebootTrackingStorage; + +//! Optional callback issued to load reboot tracking from the backing store, +//! called during Memfault reboot tracking initialization. +//! +//! @param dst The destination buffer to load into +extern void memfault_reboot_tracking_load(sMemfaultRebootTrackingStorage *dst); + +//! Optional callback issued when reboot tracking data should be saved to the +//! backing store, for persistence across reboots. This function MUST be safe +//! to call from exception context! It is called from the Memfault fault handler +//! before the coredump is saved. +//! +//! @param src The source buffer to save +extern void memfault_reboot_tracking_save(const sMemfaultRebootTrackingStorage *src); + #ifdef __cplusplus } #endif diff --git a/components/include/memfault/panics/platform/coredump.h b/components/include/memfault/panics/platform/coredump.h index 9c97d2d15..058c49706 100644 --- a/components/include/memfault/panics/platform/coredump.h +++ b/components/include/memfault/panics/platform/coredump.h @@ -161,6 +161,14 @@ extern bool memfault_coredump_read(uint32_t offset, void *buf, size_t buf_len); //! @return true if to continue saving the coredump, false to abort extern bool memfault_platform_coredump_save_begin(void); +//! Called prior to invoking any platform_storage_[read/write/erase] calls upon +//! crash +//! +//! @note this is similar to memfault_platform_coredump_save_begin(), but is +//! meant to be implemented by the Memfault port, not the user platform +//! @return true if to continue saving the coredump, false to abort +extern bool memfault_port_coredump_save_begin(void); + #ifdef __cplusplus } #endif diff --git a/components/include/memfault/version.h b/components/include/memfault/version.h index 0c64ff305..653967c55 100644 --- a/components/include/memfault/version.h +++ b/components/include/memfault/version.h @@ -20,8 +20,8 @@ typedef struct { } sMfltSdkVersion; #define MEMFAULT_SDK_VERSION \ - { .major = 1, .minor = 15, .patch = 0 } -#define MEMFAULT_SDK_VERSION_STR "1.15.0" + { .major = 1, .minor = 16, .patch = 0 } +#define MEMFAULT_SDK_VERSION_STR "1.16.0" #ifdef __cplusplus } diff --git a/components/panics/src/memfault_coredump.c b/components/panics/src/memfault_coredump.c index 7cb56146f..23fc93218 100644 --- a/components/panics/src/memfault_coredump.c +++ b/components/panics/src/memfault_coredump.c @@ -440,7 +440,7 @@ static bool prv_write_coredump_sections(const sMemfaultCoredumpSaveInfo *save_in } if (!compute_size_only) { - if (!memfault_platform_coredump_save_begin()) { + if (!memfault_port_coredump_save_begin() || !memfault_platform_coredump_save_begin()) { return false; } @@ -529,6 +529,10 @@ MEMFAULT_WEAK bool memfault_platform_coredump_save_begin(void) { return true; } +MEMFAULT_WEAK bool memfault_port_coredump_save_begin(void) { + return true; +} + size_t memfault_coredump_get_save_size(const sMemfaultCoredumpSaveInfo *save_info) { const bool compute_size_only = true; size_t total_size = 0; diff --git a/components/panics/src/memfault_coredump_storage_debug.c b/components/panics/src/memfault_coredump_storage_debug.c index 258c5e7b6..96bdf88c0 100644 --- a/components/panics/src/memfault_coredump_storage_debug.c +++ b/components/panics/src/memfault_coredump_storage_debug.c @@ -95,7 +95,7 @@ bool memfault_coredump_storage_debug_test_begin(void) { // On some ports there maybe some extra setup that needs to occur // before we can safely use the backing store without interrupts // enabled. Call this setup function now. - if (!memfault_platform_coredump_save_begin()) { + if (!memfault_port_coredump_save_begin() || !memfault_platform_coredump_save_begin()) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Prepare, kMemfaultCoredumpStorageResult_PlatformApiFail, 0, info.size); return false; diff --git a/components/panics/src/memfault_fault_handling_riscv.c b/components/panics/src/memfault_fault_handling_riscv.c index d2d2fd24e..2b17459e2 100644 --- a/components/panics/src/memfault_fault_handling_riscv.c +++ b/components/panics/src/memfault_fault_handling_riscv.c @@ -91,7 +91,7 @@ MEMFAULT_NO_OPT void memfault_fault_handling_assert(void *pc, void *lr) { } #elif !defined(ESP_PLATFORM) - #error "Unsupported RISC-V platform, please contact support@memfault.com" + #error "Unsupported RISC-V platform. Please visit https://mflt.io/contact-support" #endif // !defined(ESP_PLATFORM) && defined(__ZEPHYR__) void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { diff --git a/components/panics/src/memfault_fault_handling_xtensa.c b/components/panics/src/memfault_fault_handling_xtensa.c index ec1229e0c..b5eae7f1a 100644 --- a/components/panics/src/memfault_fault_handling_xtensa.c +++ b/components/panics/src/memfault_fault_handling_xtensa.c @@ -96,7 +96,7 @@ MEMFAULT_NO_OPT void memfault_fault_handling_assert(void *pc, void *lr) { } #elif !defined(ESP_PLATFORM) - #error "Unsupported Xtensa platform, please contact support@memfault.com" + #error "Unsupported Xtensa platform. Please visit https://mflt.io/contact-support" #endif // !defined(ESP_PLATFORM) && defined(__ZEPHYR__) void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { diff --git a/examples/esp32/apps/memfault_demo_app/CMakeLists.txt b/examples/esp32/apps/memfault_demo_app/CMakeLists.txt index 1c312e912..2c5859155 100644 --- a/examples/esp32/apps/memfault_demo_app/CMakeLists.txt +++ b/examples/esp32/apps/memfault_demo_app/CMakeLists.txt @@ -76,34 +76,3 @@ if (INVALID_PARTITION_TABLE) If this error occurs repeatedly run `idf.py fullclean && rm sdkconfig`" ) endif() - -if (CONFIG_MEMFAULT) - # Set MEMFAULT_IDF_VERSION, used for version-specific logic later. - if (IDF_VERSION_MAJOR) - set(MEMFAULT_IDF_VERSION "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}") - else() - # pre-4.0 doesn't have version information available to cmake, so set it to - # the lowest compatible version - set(MEMFAULT_IDF_VERSION "3.3.5") - endif() - - # As of Memfault SDK v1.12.0, adding the Memfault Build ID is done in the - # memfault component for ESP-IDF >=4.2.5. - if(MEMFAULT_IDF_VERSION VERSION_LESS "4.2.5") - # Add the Memfault Build ID so each build can have a unique version. - set(IDF_PROJECT_EXECUTABLE ${PROJECT_NAME}.elf) - add_custom_command(TARGET ${IDF_PROJECT_EXECUTABLE} - POST_BUILD - # Compute and insert the build id - COMMAND python ${memfault_firmware_sdk_dir}/scripts/fw_build_id.py ${IDF_PROJECT_EXECUTABLE} - # Save a copy of the ELF that includes the 'log_fmt' section - BYPRODUCTS ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt - # Compress debug sections; this reduces the elf file size from ~10MB -> ~4.8MB - COMMAND ${CMAKE_OBJCOPY} --compress-debug-sections ${IDF_PROJECT_EXECUTABLE} - COMMAND ${CMAKE_COMMAND} -E copy ${IDF_PROJECT_EXECUTABLE} ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt - COMMAND ${CMAKE_COMMAND} -E echo "*** NOTE: the symbol file to upload to app.memfault.com is ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt ***" - # Remove the 'log_fmt' compact log section, which confuses elf2image - COMMAND ${CMAKE_OBJCOPY} --remove-section log_fmt ${IDF_PROJECT_EXECUTABLE} - ) - endif() -endif() # NOT CONFIG_MEMFAULT_DISABLE diff --git a/examples/esp32/apps/memfault_demo_app/main/cmd_app.c b/examples/esp32/apps/memfault_demo_app/main/cmd_app.c index 055540551..a89dee76d 100644 --- a/examples/esp32/apps/memfault_demo_app/main/cmd_app.c +++ b/examples/esp32/apps/memfault_demo_app/main/cmd_app.c @@ -31,17 +31,16 @@ static int test_task_watchdog(int argc, char **argv) { } #endif -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) - #if defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || \ - !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE) - #define OVERFLOW_TASK_STACK_SIZE 4096 +#if defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || \ + !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE) + #define OVERFLOW_TASK_STACK_SIZE 4096 static StaticTask_t s_overflow_task_tcb; static StackType_t s_overflow_task_stack[OVERFLOW_TASK_STACK_SIZE]; static TaskHandle_t s_overflow_task_handle = NULL; static void prv_trigger_stack_overflow(void) { - #if defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL) + #if defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL) // General idea is to allocate a bunch of memory on the stack but not too much // to hose FreeRTOS Then yield and the FreeRTOS stack overflow check (on task // switch) should kick in due to stack pointer limits @@ -60,11 +59,11 @@ static void prv_trigger_stack_overflow(void) { stack_array[i] = i; taskYIELD(); } - #else + #else // The canary checks only look at the last bytes (lowest addresses) of the // stack Execute a write over the last 32 bytes to trigger the watchpoint memset(s_overflow_task_stack, 0, 32); - #endif // defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTR) + #endif // defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTR) } /** @@ -107,9 +106,8 @@ static void prv_init_stack_overflow_test(void) { }; ESP_ERROR_CHECK(esp_console_cmd_register(&test_stack_overflow_cmd)); } - #endif // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || - // !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE) -#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) +#endif // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || + // !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE) #if defined(CONFIG_HEAP_TASK_TRACKING) // Print out per-task heap allocations. This is lifted from the example here: @@ -173,12 +171,9 @@ void register_app(void) { ESP_ERROR_CHECK(esp_console_cmd_register(&heap_task_stats_cmd)); #endif -// Only support the stack overflow test on esp-idf >= 4.3.0 -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) - #if defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || \ - !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE) +#if defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || \ + !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE) prv_init_stack_overflow_test(); - #endif // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || - // !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE) -#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) +#endif // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) || + // !defined(CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE) } diff --git a/examples/esp32/apps/memfault_demo_app/main/cmd_wifi_legacy.c b/examples/esp32/apps/memfault_demo_app/main/cmd_wifi_legacy.c index cc2df29af..020cab11a 100644 --- a/examples/esp32/apps/memfault_demo_app/main/cmd_wifi_legacy.c +++ b/examples/esp32/apps/memfault_demo_app/main/cmd_wifi_legacy.c @@ -34,12 +34,6 @@ static char s_wifi_pass[WIFI_CREDS_MAX_SIZE]; static EventGroupHandle_t wifi_event_group; const int CONNECTED_BIT = BIT0; -// this type changed in ESP-IDF v4.0, to 'nvs_handle_t'; add a backwards-compat -// typedef -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0) -typedef nvs_handle nvs_handle_t; -#endif - int wifi_get_project_key(char *project_key, size_t project_key_len) { // Configurable project key not supported, project key must be compiled in via // CONFIG_MEMFAULT_PROJECT_KEY diff --git a/examples/esp32/apps/memfault_demo_app/main/main.c b/examples/esp32/apps/memfault_demo_app/main/main.c index 15c38629b..83878df29 100644 --- a/examples/esp32/apps/memfault_demo_app/main/main.c +++ b/examples/esp32/apps/memfault_demo_app/main/main.c @@ -36,11 +36,11 @@ #endif #if defined(CONFIG_MEMFAULT) + #include "esp_idf_version.h" #include "memfault/components.h" #include "memfault/esp_port/cli.h" #include "memfault/esp_port/core.h" #include "memfault/esp_port/http_client.h" - #include "memfault/esp_port/version.h" #include "settings.h" #endif @@ -85,42 +85,32 @@ static void initialize_nvs() { ESP_ERROR_CHECK(err); } -// Name change at version 4.x -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) - #define CONFIG_CONSOLE_UART_NUM CONFIG_ESP_CONSOLE_UART_NUM -#endif - static void initialize_console() { /* Disable buffering on stdin and stdout */ setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); - // These APIs vary depending on ESP-IDF version. +// These APIs vary depending on ESP-IDF version. #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ uart_vfs_dev_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR); /* Move the caret to the beginning of the next line on '\n' */ uart_vfs_dev_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF); -#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) +#else // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR); /* Move the caret to the beginning of the next line on '\n' */ esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF); -#else - /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ - esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR); - /* Move the caret to the beginning of the next line on '\n' */ - esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); -#endif +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) /* Install UART driver for interrupt-driven reads and writes */ - ESP_ERROR_CHECK(uart_driver_install(CONFIG_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0)); + ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0)); - /* Tell VFS to use UART driver */ +/* Tell VFS to use UART driver */ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) - uart_vfs_dev_use_driver(CONFIG_CONSOLE_UART_NUM); + uart_vfs_dev_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); #else - esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM); + esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); #endif /* Initialize the console */ @@ -254,7 +244,7 @@ void memfault_esp_port_wifi_autojoin(void) { #endif // CONFIG_MEMFAULT_APP_WIFI_AUTOJOIN -// // Periodically post any Memfault data that has not yet been posted. +// Periodically post any Memfault data that has not yet been posted. static void prv_ota_task(void *args) { const TickType_t ota_check_interval = pdMS_TO_TICKS(60 * 60 * 1000); @@ -263,7 +253,7 @@ static void prv_ota_task(void *args) { while (true) { // count the number of times this task has run MEMFAULT_METRIC_ADD(PosterTaskNumSchedules, 1); - // attempt to autojoin wifi, if configured + // attempt to autojoin wifi, if configured #if defined(CONFIG_MEMFAULT_APP_WIFI_AUTOJOIN) memfault_esp_port_wifi_autojoin(); #endif @@ -454,9 +444,7 @@ void app_main() { g_mflt_http_client_config.api_key = project_key; } - // Load chunks + device URLs from NVS too, only for esp_idf >=4 - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) - // Need to persist for the lifetime of the program + // Load chunks + device URLs from NVS too. Need to persist for the lifetime of the program static char chunks_url[128] = { 0 }; static char device_url[128] = { 0 }; size_t chunks_url_len = sizeof(chunks_url); @@ -466,7 +454,6 @@ void app_main() { g_mflt_http_client_config.chunks_api.host = (chunks_url_len > 1) ? chunks_url : NULL; g_mflt_http_client_config.device_api.host = (device_url_len > 1) ? device_url : NULL; } - #endif #if MEMFAULT_COMPACT_LOG_ENABLE MEMFAULT_COMPACT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, "This is a compact log example"); diff --git a/examples/esp32/apps/memfault_demo_app/main/ota_session_metrics.c b/examples/esp32/apps/memfault_demo_app/main/ota_session_metrics.c index df12c58f0..4db831034 100644 --- a/examples/esp32/apps/memfault_demo_app/main/ota_session_metrics.c +++ b/examples/esp32/apps/memfault_demo_app/main/ota_session_metrics.c @@ -57,17 +57,6 @@ int __wrap_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len) { return rv; } -// This API was added in ESP-IDF v4.3.6, v4.4.6, v5.0.4, and v5.1.1. Add a stub for versions -// < v4.3.6. For simplicity, projects with versions > v4.3.6 but less than the patch the API was -// added in for the corresponding minor version are not supported with this check. There will be a -// build error due to a missing definition for the API. -// https://github.com/espressif/esp32-wifi-lib/blob/release/v4.4/esp32/libnet80211.a -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 3, 6) -esp_err_t esp_wifi_sta_get_rssi(int *rssi) { - return -1; -} -#endif - void ota_session_metrics_end(int ota_error_code) { // error code MEMFAULT_METRIC_SESSION_SET_SIGNED(ota_error_code, ota, ota_error_code); diff --git a/examples/esp32/apps/memfault_demo_app/main/settings.h b/examples/esp32/apps/memfault_demo_app/main/settings.h index 1a6e6d475..d1c4b4b13 100644 --- a/examples/esp32/apps/memfault_demo_app/main/settings.h +++ b/examples/esp32/apps/memfault_demo_app/main/settings.h @@ -9,7 +9,7 @@ #include -#include "memfault/esp_port/version.h" +#include "esp_idf_version.h" enum settings_key { kSettingsWifiSsid, @@ -21,25 +21,14 @@ enum settings_key { kSettingsDeviceUrl, }; -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) - #if __GNUC__ >= 11 +#if __GNUC__ >= 11 __attribute__((access(write_only, 2))) - #endif +#endif int settings_get(enum settings_key key, void *value, size_t *len); - #if __GNUC__ >= 11 +#if __GNUC__ >= 11 __attribute__((access(read_only, 2, 3))) - #endif +#endif int settings_set(enum settings_key key, const void *value, size_t len); void settings_register_shell_commands(void); -#else // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) -// stub definitions that always fail -static inline int settings_get(enum settings_key key, void *value, size_t *len) { - return -1; -} -static inline int settings_set(enum settings_key key, const void *value, size_t len) { - return -1; -} -static inline void settings_register_shell_commands(void) { } -#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) diff --git a/examples/esp32/apps/memfault_demo_app/sdkconfig.defaults b/examples/esp32/apps/memfault_demo_app/sdkconfig.defaults index 5280129ec..5d8728385 100644 --- a/examples/esp32/apps/memfault_demo_app/sdkconfig.defaults +++ b/examples/esp32/apps/memfault_demo_app/sdkconfig.defaults @@ -53,5 +53,8 @@ CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=y # Upload logs. Not recommended for production. CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS=y +# Pretty-print reboot reason +CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP=y + # Support older ESP32-C3 variants CONFIG_ESP32C3_REV_MIN_2=y diff --git a/examples/freertos/boards/qemu_mps2_an385/startup.c b/examples/freertos/boards/qemu_mps2_an385/startup.c index 414f3e218..b2758fd73 100644 --- a/examples/freertos/boards/qemu_mps2_an385/startup.c +++ b/examples/freertos/boards/qemu_mps2_an385/startup.c @@ -85,7 +85,7 @@ extern uint32_t __bss_start__; // End address for the .bss section extern uint32_t __bss_end__; // End address for stack -extern uint32_t __stack; +extern void __stack(void); // Prevent inlining to avoid persisting any stack allocations __attribute__((noinline)) static void prv_cinit(void) { @@ -129,7 +129,7 @@ extern void UsageFault_Handler(void); // A minimal vector table for a Cortex M. __attribute__((section(".isr_vector"))) void (*const g_pfnVectors[])(void) = { - (void *)(&__stack), // initial stack pointer + __stack, // initial stack pointer Reset_Handler, NMI_Handler, HardFault_Handler, diff --git a/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/CMakeLists.txt b/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/CMakeLists.txt index aacd284de..5449cf5ce 100644 --- a/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/CMakeLists.txt +++ b/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/CMakeLists.txt @@ -14,12 +14,9 @@ endif() # which is not something that can be achieved with Zephyr today find_package(Ncs HINTS $ENV{ZEPHYR_BASE}/../nrf) -if (NOT NCS_VERSION_MAJOR) - # Note: If we cannot resolve the NCS version, assume we are using 1.2.x which is the oldest - # version Memfault has been tested against - set(NCS_VERSION_MAJOR 1) - set(NCS_VERSION_MINOR 2) - set(NCS_VERSION_PATCH 0) +# Confirm NCS_VERSION_MAJOR is defined +if (NOT DEFINED NCS_VERSION_MAJOR) + message(FATAL_ERROR "NCS Version not found, please contact Memfault support!") endif() # Below we conditionally set Kconfig variables based on nRF Connect SDK version @@ -30,76 +27,30 @@ endif() # so we set the ones we need here using that: # https://docs.zephyrproject.org/latest/build/kconfig/setting.html#the-initial-configuration -if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 1.3) -else() # nRF Connect SDK Version >= 1.3.0 - # We cannot set for NCS <= 1.2 because on older versions of zephyr the argument was not - # directly user-configurable (no prompt in Kconfig) - set(CONFIG_SHELL_LOG_BACKEND n CACHE INTERNAL "") -endif() +# Enable Memfault FOTA Support +set(CONFIG_MEMFAULT_FOTA y CACHE INTERNAL "") +set(CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE 400 CACHE INTERNAL "") +set(CONFIG_DOWNLOAD_CLIENT_STACK_SIZE 1600 CACHE INTERNAL "") -if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 1.5) - # Legacy name for CONFIG_DEBUG_THREAD_INFO needed for nRF Connect version <= 1.5 - # due to the version of Zephyr included (pre v2.5) - set(CONFIG_OPENOCD_SUPPORT y CACHE INTERNAL "") -else() # nRF Connect SDK Version >= 1.5.0 - # Enable Memfault FOTA Support - # - # We enable conditionally because prior to NCS < 1.5 a small patch (https://mflt.io/nrf-fota) was - # needed in order for things to work - set(CONFIG_MEMFAULT_FOTA y CACHE INTERNAL "") - set(CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE 400 CACHE INTERNAL "") - set(CONFIG_DOWNLOAD_CLIENT_STACK_SIZE 1600 CACHE INTERNAL "") - - # Note: Can optionally be changed to =y to implement - # a custom event handler for FOTA events - # CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM=n -endif() +# Note: Can optionally be changed to =y to implement +# a custom event handler for FOTA events +# CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM=n -if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 1.6.0) - # Required for logging to work from crash but deprecated in Zephyr included in NCS 1.6 - set(CONFIG_LOG_IMMEDIATE y CACHE INTERNAL "") - - # NCS 1.6 was the first one which introduced setting the project key via - # Kconfig. Look for this argument and remap to a regular compiler definition - # that gets picked up in memfault_demo_app/src/main.c - if (CONFIG_MEMFAULT_NCS_PROJECT_KEY) - add_compile_definitions(MEMFAULT_NCS_PROJECT_KEY=${CONFIG_MEMFAULT_NCS_PROJECT_KEY}) - unset(CONFIG_MEMFAULT_NCS_PROJECT_KEY CACHE) - endif() -else() # nRF Connect SDK Version >= 1.6.0 - set(CONFIG_MEMFAULT_NCS_PROVISION_CERTIFICATES n CACHE INTERNAL "") - set(CONFIG_MEMFAULT_NCS_DEVICE_ID_RUNTIME y CACHE INTERNAL "") - set(CONFIG_NEWLIB_LIBC y CACHE INTERNAL "") -endif() +set(CONFIG_MEMFAULT_NCS_PROVISION_CERTIFICATES n CACHE INTERNAL "") +set(CONFIG_MEMFAULT_NCS_DEVICE_ID_RUNTIME y CACHE INTERNAL "") +set(CONFIG_NEWLIB_LIBC y CACHE INTERNAL "") -if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 1.7.99) - # These were removed in NCS 1.8.0, but are needed prior to that. Enable them - # if NCS version <1.7.99 (.99 patch version is used for the next revision's - # development series) - set(CONFIG_BSD_LIBRARY y CACHE INTERNAL "") - set(CONFIG_BSD_LIBRARY_SYS_INIT n CACHE INTERNAL "") - # ^ Note: CONFIG_BSD_ were renamed to _NRF_MODEM_ in - # nRF Connect SDK v1.5. We use the legacy names here - # so the app can continue to compile on older targets - # - # CONFIG_NRF_MODEM_LIB - # CONFIG_NRF_MODEM_LIB_SYS_INIT - - # For nRF Connect SDK v1.5 default value changed here - set(CONFIG_AT_CMD_SYS_INIT n CACHE INTERNAL "") -else() # nRF Connect SDK Version >= 1.8.0 - # in v1.8+, use the modem_info library to get the IMEI for device_serial - set(CONFIG_MODEM_INFO y CACHE INTERNAL "") - - # pre-2.3.0, SPM (Secure Partition Manager) can be used instead of the default - # TFM, and results in nicer backtraces in certain cases, so select it here. - if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 2.2.99) - # Use SPM instead of TFM for Secure Firmware as TFM does not (yet) support forwarding of fault handlers - set(CONFIG_BUILD_WITH_TFM n CACHE INTERNAL "") - # Explicitly set SPM=y because as of nRF Connect >= 2.1.0 it is marked as deprecated and no longer enabled by default - set(CONFIG_SPM y CACHE INTERNAL "") - endif() +# in v1.8+, use the modem_info library to get the IMEI for device_serial +set(CONFIG_MODEM_INFO y CACHE INTERNAL "") + +# pre-2.3.0, SPM (Secure Partition Manager) can be used instead of the default +# TFM, and results in nicer backtraces in certain cases, so select it here. +if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 2.2.99) + # Use SPM instead of TFM for Secure Firmware as TFM does not (yet) support forwarding of fault handlers + set(CONFIG_BUILD_WITH_TFM n CACHE INTERNAL "") + # Explicitly set SPM=y because as of nRF Connect >= 2.1.0 it is marked as deprecated and no longer enabled by default + set(CONFIG_SPM y CACHE INTERNAL "") endif() if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} GREATER_EQUAL 2.4.0) @@ -120,13 +71,6 @@ else() set(CONFIG_LTE_AUTO_INIT_AND_CONNECT n CACHE INTERNAL "") endif() -# Required for app to compile against nRF Connect SDK <= v1.2 -# Must be included after updating Kconfig settings in CMake Cache above -if(DEFINED ENV{MEMFAULT_INCLUDE_ZEPHYR_BOILERPLATE}) - include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) - include($ENV{ZEPHYR_BASE}/../nrf/cmake/boilerplate.cmake) -endif() - find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) project(hello_world) diff --git a/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/main.c b/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/main.c index 7c2c9255a..b89d8b81d 100644 --- a/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/main.c +++ b/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/main.c @@ -27,50 +27,13 @@ #include "memfault/ports/ncs/version.h" #include "memfault_demo_app.h" -// nRF Connect SDK < 1.3 -#if (NCS_VERSION_MAJOR == 1) && (NCS_VERSION_MINOR < 3) - -#include -#include -#include -#include -#include -#include - -// nRF Connect SDK < 1.9 -#elif (NCS_VERSION_MAJOR == 1) && (NCS_VERSION_MINOR < 9) && (NCS_PATCHLEVEL < 99) - -#include -#include -#include -#include -#include - -// nRF Connect SDK >= 1.9 -#else #include #include #include -#endif -#if (NCS_VERSION_MAJOR == 1) && (NCS_VERSION_MINOR <= 2) -#include -static int prv_init_modem_lib(void) { - return bsdlib_init(); -} -#elif (NCS_VERSION_MAJOR == 1) && (NCS_VERSION_MINOR <= 4) -#include -static int prv_init_modem_lib(void) { - return bsdlib_init(); -} -#elif (NCS_VERSION_MAJOR == 1) && (NCS_VERSION_MINOR <= 5) -#include -static int prv_init_modem_lib(void) { - return nrf_modem_lib_init(NORMAL_MODE); -} -#elif !MEMFAULT_NCS_VERSION_GT(2, 3) -/* nRF Connect SDK >= 1.6 || <= 2.3 */ +#if !MEMFAULT_NCS_VERSION_GT(2, 3) +/* nRF Connect SDK >= 1.9.2 || <= 2.3 */ #include "memfault_ncs.h" #include static int prv_init_modem_lib(void) { @@ -95,54 +58,7 @@ static int prv_init_modem_lib(void) { #define IMEI_LEN 15 static char s_device_serial[IMEI_LEN + 1 /* '\0' */] = "unknown"; -// A direct Memfault integration was added in 1.6 -#if (NCS_VERSION_MAJOR == 1) && (NCS_VERSION_MINOR < 6) - -// Note: Starting with nRF Connect SDK 1.6, there is a direct integration of Memfault -// and these dependencies are no longer needed! -// See https://mflt.io/nrf-connect-sdk-lib for more details - -static char s_fw_version[16] = "1.0.0-dev"; - - #ifndef MEMFAULT_NCS_PROJECT_KEY - #define MEMFAULT_NCS_PROJECT_KEY "" - #endif - -sMfltHttpClientConfig g_mflt_http_client_config = { - .api_key = MEMFAULT_NCS_PROJECT_KEY, -}; - -void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { - // platform specific version information - *info = (sMemfaultDeviceInfo){ - .device_serial = s_device_serial, - .software_type = "nrf91ns-fw", - .software_version = s_fw_version, - .hardware_version = "nrf9160dk", - }; -} - -// stub for function implemented in nRF Connect SDK >= 1.6 -static int memfault_ncs_device_id_set(const char *device_id, size_t len) { - return 0; -} - -#endif - -#if !MEMFAULT_NCS_VERSION_GT(1, 8) -static int query_modem(const char *cmd, char *buf, size_t buf_len) { - enum at_cmd_state at_state; - int ret = at_cmd_write(cmd, buf, buf_len, &at_state); - if (ret != 0) { - printk("at_cmd_write [%s] error: %d, at_state: %d\n", cmd, ret, at_state); - } - - return ret; -} -#endif - static int prv_get_imei(char *buf, size_t buf_len) { -#if MEMFAULT_NCS_VERSION_GT(1, 8) // use the cached modem info to fetch the IMEI int err = modem_info_init(); if (err != 0) { @@ -151,10 +67,6 @@ static int prv_get_imei(char *buf, size_t buf_len) { modem_info_string_get(MODEM_INFO_IMEI, buf, buf_len); } return err; -#else - // use an AT command to read IMEI - return query_modem("AT+CGSN", buf, buf_len); -#endif } static void prv_init_device_info(void) { @@ -193,21 +105,6 @@ int main(void) { goto cleanup; } - // These libraries were removed in ncs 1.9 -#if !MEMFAULT_NCS_VERSION_GT(1, 8) - err = at_cmd_init(); - if (err) { - printk("Failed to initialize AT commands, err %d\n", err); - goto cleanup; - } - - err = at_notif_init(); - if (err) { - printk("Failed to initialize AT notifications, err %d\n", err); - goto cleanup; - } -#endif - // requires AT modem interface to be up prv_init_device_info(); diff --git a/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/watchdog.c b/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/watchdog.c index 312b90b1c..3b2368d58 100644 --- a/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/watchdog.c +++ b/examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/watchdog.c @@ -14,7 +14,6 @@ #include MEMFAULT_ZEPHYR_INCLUDE(device.h) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/watchdog.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) -#include #include "memfault/components.h" #include "memfault/ports/zephyr/version.h" @@ -38,30 +37,24 @@ struct k_thread my_thread_data; // properties. The new preferred approach is accessing resources via the DEVICE_DT_GET macro static const struct device *s_wdt = DEVICE_DT_GET(DT_NODELABEL(wdt)); -#elif MEMFAULT_ZEPHYR_VERSION_GT(2, 1) +#else static const struct device *s_wdt = NULL; -#else // Zephyr Kernel <= 2.1 -static struct device *s_wdt = NULL; #endif -#if KERNEL_VERSION_MAJOR == 2 && KERNEL_VERSION_MINOR < 3 - #define WDT_DEV_NAME DT_WDT_0_NAME +//! Watchdog device tree name changed in NCS v2.0.0 : +//! https://github.com/nrfconnect/sdk-zephyr/blob/12ee4d5f4b99acef542ce3977cb9078fcbb36d82/dts/arm/nordic/nrf9160_common.dtsi#L368 +//! Pick the one that's available in the current SDK version. +#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_wdt) + #define WDT_NODE_NAME nordic_nrf_wdt +#elif DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_watchdog) + #define WDT_NODE_NAME nordic_nrf_watchdog #else - //! Watchdog device tree name changed in NCS v2.0.0 : - //! https://github.com/nrfconnect/sdk-zephyr/blob/12ee4d5f4b99acef542ce3977cb9078fcbb36d82/dts/arm/nordic/nrf9160_common.dtsi#L368 - //! Pick the one that's available in the current SDK version. - #if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_wdt) - #define WDT_NODE_NAME nordic_nrf_wdt - #elif DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_watchdog) - #define WDT_NODE_NAME nordic_nrf_watchdog - #else - #error "No compatible watchdog instance for this configuration!" - #endif - - #define WDT_NODE DT_INST(0, WDT_NODE_NAME) - #define WDT_DEV_NAME DT_LABEL(WDT_NODE) + #error "No compatible watchdog instance for this configuration!" #endif +#define WDT_NODE DT_INST(0, WDT_NODE_NAME) +#define WDT_DEV_NAME DT_LABEL(WDT_NODE) + static int s_wdt_channel_id = -1; void memfault_demo_app_watchdog_feed(void) { diff --git a/examples/zephyr/qemu/qemu-app/boards/nucleo_l496zg.conf b/examples/zephyr/qemu/qemu-app/boards/nucleo_l496zg.conf new file mode 100644 index 000000000..0272030c1 --- /dev/null +++ b/examples/zephyr/qemu/qemu-app/boards/nucleo_l496zg.conf @@ -0,0 +1,3 @@ +CONFIG_BBRAM=y +CONFIG_BBRAM_SHELL=y +CONFIG_RTC=y diff --git a/examples/zephyr/qemu/qemu-app/boards/nucleo_l496zg.overlay b/examples/zephyr/qemu/qemu-app/boards/nucleo_l496zg.overlay new file mode 100644 index 000000000..81e09aa8c --- /dev/null +++ b/examples/zephyr/qemu/qemu-app/boards/nucleo_l496zg.overlay @@ -0,0 +1,9 @@ +/ { + aliases { + backup-register = &bbram; + }; +}; + +&bbram { + status = "okay"; +}; diff --git a/examples/zephyr/qemu/qemu-app/src/main.c b/examples/zephyr/qemu/qemu-app/src/main.c index 40979cbf9..dfff87cf6 100644 --- a/examples/zephyr/qemu/qemu-app/src/main.c +++ b/examples/zephyr/qemu/qemu-app/src/main.c @@ -295,6 +295,31 @@ static int prv_session_crash(const struct shell *shell, size_t argc, char **argv SHELL_CMD_REGISTER(session_crash, NULL, "session crash", prv_session_crash); +#if defined(CONFIG_BBRAM) + #include + +void memfault_reboot_tracking_load(sMemfaultRebootTrackingStorage *dst) { + // restore reboot tracking from bbram_read + + // clear the destination buffer- if the bbram restore doesn't work, we won't + // have reboot tracking data. + memset(dst, 0, sizeof(*dst)); + + const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(bbram)); + for (size_t i = 0; i < sizeof(*dst) / 4; i++) { + bbram_read(dev, 4 * i, 4, &dst->data[4 * i]); + } +} + +void memfault_reboot_tracking_save(const sMemfaultRebootTrackingStorage *src) { + // now back up reboot tracking to RTC backup regs + const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(bbram)); + for (size_t i = 0; i < sizeof(*src) / 4; i++) { + bbram_write(dev, 4 * i, 4, &src->data[4 * i]); + } +} +#endif // defined(CONFIG_BBRAM) + int main(void) { LOG_INF("👋 Memfault Demo App! Board %s\n", CONFIG_BOARD); memfault_device_info_dump(); diff --git a/ports/dialog/da1468x/qspi_coredump_storage.c b/ports/dialog/da1468x/qspi_coredump_storage.c index 413410095..5ab5a3998 100644 --- a/ports/dialog/da1468x/qspi_coredump_storage.c +++ b/ports/dialog/da1468x/qspi_coredump_storage.c @@ -145,10 +145,10 @@ bool memfault_platform_saving_coredump(void) { return s_memfault_using_qspi; } -// We need to override the weak nop version of memfault_platform_coredump_save_begin() +// We need to override the weak nop version of memfault_port_coredump_save_begin() // to check if it's safe to use the flash with interrupts disabled. We just need to call // our checker function added to ad_flash.c in the Dialog SDK. -bool memfault_platform_coredump_save_begin(void) { +bool memfault_port_coredump_save_begin(void) { bool memfault_ad_flash_is_locked(void); // Defined in ad_flash.c, not in a header file bool memfault_ad_nvms_direct_is_locked( void); // Defined in ad_nvms_direct.c, not in a header file diff --git a/ports/esp8266_sdk/memfault/memfault_platform_coredump_storage.c b/ports/esp8266_sdk/memfault/memfault_platform_coredump_storage.c index 45d8a5dbf..066a19aaf 100644 --- a/ports/esp8266_sdk/memfault/memfault_platform_coredump_storage.c +++ b/ports/esp8266_sdk/memfault/memfault_platform_coredump_storage.c @@ -113,7 +113,7 @@ void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) }; } -bool memfault_platform_coredump_save_begin(void) { +bool memfault_port_coredump_save_begin(void) { const esp_partition_t *core_part = prv_validate_and_get_core_partition(); if (core_part == NULL) { return false; diff --git a/ports/esp_idf/memfault/CMakeLists.txt b/ports/esp_idf/memfault/CMakeLists.txt index 30b03e246..b61392440 100644 --- a/ports/esp_idf/memfault/CMakeLists.txt +++ b/ports/esp_idf/memfault/CMakeLists.txt @@ -35,13 +35,16 @@ if(NOT DEFINED MEMFAULT_ESP_HTTP_CLIENT_ENABLE) endif() endif() -# Set MEMFAULT_IDF_VERSION, used for version-specific logic later. -if (IDF_VERSION_MAJOR) - set(MEMFAULT_IDF_VERSION "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}") -else() - # pre-4.0 doesn't have version information available to cmake, so set it to - # the lowest compatible version - set(MEMFAULT_IDF_VERSION "3.3.5") +# Set variables used for version-specific checks later +set (MEMFAULT_IDF_VERSION_MINIMUM "4.4.0") +set(MEMFAULT_IDF_VERSION "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}") +set(MEMFAULT_IDF_VERSION_ERROR_MSG "Memfault SDK requires ESP-IDF version ${MEMFAULT_IDF_VERSION_MINIMUM} or later. \ + Please contact mflt.io/contact-support for assistance.") + +# Check that we are working with a supported version. If IDF_VERSION_MAJOR etc +# are unset (IDF versions before 4.0), this will catch that too. +if (MEMFAULT_IDF_VERSION VERSION_LESS MEMFAULT_IDF_VERSION_MINIMUM) + message(FATAL_ERROR "${MEMFAULT_IDF_VERSION_ERROR_MSG}") endif() # Select RISCV or XTENSA architecture, depending on target chip @@ -65,9 +68,6 @@ list(APPEND MEMFAULT_COMPONENTS core util panics demo http metrics) memfault_library(${MEMFAULT_SDK_ROOT} MEMFAULT_COMPONENTS MEMFAULT_COMPONENTS_SRCS MEMFAULT_COMPONENTS_INC_FOLDERS ${ESP_ARCH}) -# v4.0 and greater expose the IDF_VERSION in cmake. If we can't find -# it, we assume the end user is on v3.x - include($ENV{IDF_PATH}/tools/cmake/version.cmake OPTIONAL) # Select version-specific porting files @@ -75,10 +75,8 @@ if(MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL 5) set(MEMFAULT_ESP_IDF_PORT v5.x) elseif(MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL 4) set(MEMFAULT_ESP_IDF_PORT v4.x) -elseif(MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL 3) - set(MEMFAULT_ESP_IDF_PORT v3.x) else() - message(FATAL_ERROR "IDF_VERSION=${MEMFAULT_IDF_VERSION} is not currently supported") + message(FATAL_ERROR "${MEMFAULT_IDF_VERSION_ERROR_MSG}") endif() # esp-idf version specific porting files @@ -99,6 +97,7 @@ list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_device_info.c ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_http_client_buffer.c ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_http_client.c + ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_metrics.c ) if (CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD) @@ -107,18 +106,6 @@ if (CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD) ) endif() -# Add a legacy metrics file for now. To be removed in the near future when v3.x -# support is dropped. -if(IDF_VERSION_MAJOR VERSION_EQUAL 3) - list(APPEND MEMFAULT_COMPONENTS_SRCS - ${MEMFAULT_ESP_IDF_PORT}/memfault_platform_metrics_legacy.c - ) -else() - list(APPEND MEMFAULT_COMPONENTS_SRCS - ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_metrics.c - ) -endif() - list(APPEND MEMFAULT_COMPONENTS_INC_FOLDERS include include/${MEMFAULT_ESP_IDF_PORT} @@ -244,19 +231,13 @@ component_compile_options( if (MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "5.0.0") # Empty for now, reserving for the future -elseif (MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "4.4.0") - # ESP-IDF v4.4 forces the FreeRTOS config +else() + # ESP-IDF < 5.0.0 forces the FreeRTOS config # INCLUDE_xTimerGetTimerDaemonTaskHandle=0, which is used to record timer task # stack in the SDK. Just disable the usage unconditionally. list(APPEND MEMFAULT_EXTRA_COMPILE_OPTIONS "-DMEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES=0" ) -else() - # ESP-IDF < 4.4 don't force this setting, so set it ourselves to the correct - # value. - list(APPEND MEMFAULT_EXTRA_COMPILE_OPTIONS - "-DINCLUDE_xTimerGetTimerDaemonTaskHandle=1" - ) endif() @@ -266,39 +247,21 @@ endif() # keys are used in the application. # # Set the ESP-IDF specific Memfault platform config header as well. -if (MEMFAULT_IDF_VERSION VERSION_LESS 4) - # On pre-v4 of ESP-IDF, insert the compiler flag to the global CMAKE_C_FLAGS - set( - memfault_c_flags - "-DMEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE=\\\"memfault_esp_metrics_heartbeat_config.def\\\"" - "-DMEMFAULT_PLATFORM_CONFIG_FILE=\\\"memfault_esp_idf_port_config.h\\\"" - "-DMEMFAULT_TRACE_REASON_USER_DEFS_FILE=\\\"memfault_trace_reason_esp_idf_port_config.def\\\"" - ${MEMFAULT_EXTRA_COMPILE_OPTIONS} - ) - # convert from list to string before setting - list(JOIN memfault_c_flags " " memfault_c_flags) - set(CMAKE_C_FLAGS - "${CMAKE_C_FLAGS} ${memfault_c_flags}" - CACHE STRING - "Global C Flags" - FORCE - ) -else() - # set these as compile_options, not compile_definitions; pre-v5 of ESP-IDF - # required the -D prefix, post-v5 auto strips it, but for safety, set them as - # plain options not "definitions" - list(APPEND compile_options - "-DMEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE=\"memfault_esp_metrics_heartbeat_config.def\"" - "-DMEMFAULT_PLATFORM_CONFIG_FILE=\"memfault_esp_idf_port_config.h\"" - "-DMEMFAULT_TRACE_REASON_USER_DEFS_FILE=\"memfault_trace_reason_esp_idf_port_config.def\"" - ${MEMFAULT_EXTRA_COMPILE_OPTIONS} - ) - idf_build_set_property( - COMPILE_OPTIONS - "${compile_options}" - APPEND - ) -endif() +# +# Set these as compile_options, not compile_definitions; pre-v5 of ESP-IDF +# required the -D prefix, post-v5 auto strips it, but for safety, set them as +# plain options not "definitions" +list(APPEND compile_options + "-DMEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE=\"memfault_esp_metrics_heartbeat_config.def\"" + "-DMEMFAULT_PLATFORM_CONFIG_FILE=\"memfault_esp_idf_port_config.h\"" + "-DMEMFAULT_TRACE_REASON_USER_DEFS_FILE=\"memfault_trace_reason_esp_idf_port_config.def\"" + ${MEMFAULT_EXTRA_COMPILE_OPTIONS} +) +idf_build_set_property( + COMPILE_OPTIONS + "${compile_options}" + APPEND +) # We will intercept the panic handlers enabled by CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH # and run the Memfault Fault Handler instead. @@ -368,34 +331,30 @@ target_link_libraries( # compatible with more recent ESP-IDF SDK versions, which have the 'app' target # where the custom command is inserted. if(CONFIG_MEMFAULT_USE_MEMFAULT_BUILD_ID) - if(MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "4.2.5") - # We cannot use the 'EXECUTABLE'/'EXECUTABLE_NAME' build properties, because they are set - # after component processing in the build process. Reconstruct it following the - # same pattern, which works for typical idf.py builds. - - idf_build_get_property(build_dir BUILD_DIR) - set(IDF_PROJECT_EXECUTABLE "${build_dir}/${CMAKE_PROJECT_NAME}.elf") - - add_custom_command(OUTPUT "${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt" - # Compute and insert the build id - COMMAND python ${MEMFAULT_SDK_ROOT}/scripts/fw_build_id.py ${IDF_PROJECT_EXECUTABLE} - # Compress debug sections; this reduces the elf file size from ~10MB -> ~4.8MB - COMMAND ${CMAKE_OBJCOPY} --compress-debug-sections ${IDF_PROJECT_EXECUTABLE} - # Save a copy of the ELF that includes the 'log_fmt' section - COMMAND ${CMAKE_COMMAND} -E copy ${IDF_PROJECT_EXECUTABLE} ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt - COMMAND ${CMAKE_COMMAND} -E echo "*** NOTE: the symbol file to upload to app.memfault.com is ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt ***" - # Remove the 'log_fmt' compact log section, which confuses elf2image - COMMAND ${CMAKE_OBJCOPY} --remove-section log_fmt ${IDF_PROJECT_EXECUTABLE} - DEPENDS "${IDF_PROJECT_EXECUTABLE}" - ) + # We cannot use the 'EXECUTABLE'/'EXECUTABLE_NAME' build properties, because they are set + # after component processing in the build process. Reconstruct it following the + # same pattern, which works for typical idf.py builds. + + idf_build_get_property(build_dir BUILD_DIR) + set(IDF_PROJECT_EXECUTABLE "${build_dir}/${CMAKE_PROJECT_NAME}.elf") + + add_custom_command(OUTPUT "${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt" + # Compute and insert the build id + COMMAND python ${MEMFAULT_SDK_ROOT}/scripts/fw_build_id.py ${IDF_PROJECT_EXECUTABLE} + # Compress debug sections; this reduces the elf file size from ~10MB -> ~4.8MB + COMMAND ${CMAKE_OBJCOPY} --compress-debug-sections ${IDF_PROJECT_EXECUTABLE} + # Save a copy of the ELF that includes the 'log_fmt' section + COMMAND ${CMAKE_COMMAND} -E copy ${IDF_PROJECT_EXECUTABLE} ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt + COMMAND ${CMAKE_COMMAND} -E echo "*** NOTE: the symbol file to upload to app.memfault.com is ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt ***" + # Remove the 'log_fmt' compact log section, which confuses elf2image + COMMAND ${CMAKE_OBJCOPY} --remove-section log_fmt ${IDF_PROJECT_EXECUTABLE} + DEPENDS "${IDF_PROJECT_EXECUTABLE}" + ) - # Dependency chain: - # app -> gen_project_binary -> memfault_build_id -> ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt -> ${IDF_PROJECT_EXECUTABLE} - add_custom_target(memfault_build_id DEPENDS "${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt") - add_dependencies(gen_project_binary memfault_build_id) - else() - message(WARNING "CONFIG_MEMFAULT_USE_MEMFAULT_BUILD_ID requires ESP-IDF v4.2.5 or newer") - endif() + # Dependency chain: + # app -> gen_project_binary -> memfault_build_id -> ${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt -> ${IDF_PROJECT_EXECUTABLE} + add_custom_target(memfault_build_id DEPENDS "${IDF_PROJECT_EXECUTABLE}.memfault_log_fmt") + add_dependencies(gen_project_binary memfault_build_id) endif() # Link required libraries and add compiler flags needed for FreeRTOS region collection diff --git a/ports/esp_idf/memfault/Kconfig b/ports/esp_idf/memfault/Kconfig index 380103ee0..3160cc668 100644 --- a/ports/esp_idf/memfault/Kconfig +++ b/ports/esp_idf/memfault/Kconfig @@ -333,6 +333,13 @@ endif help Automatically insert the Memfault Build ID during the build process. + config MEMFAULT_ENABLE_REBOOT_DIAG_DUMP + bool "Pretty print reboot reason on boot" + default n + help + Print out the reboot reason, eg "Watchdog", on boot. Adds a few + hundred bytes of code space. + # HTTPS Periodic Upload menuconfig MEMFAULT_HTTP_PERIODIC_UPLOAD bool "Enable a dedicated thread to periodically upload Memfault data over HTTPS" diff --git a/ports/esp_idf/memfault/common/memfault_fault_handler.c b/ports/esp_idf/memfault/common/memfault_fault_handler.c index 6b40e765d..d9696f093 100644 --- a/ports/esp_idf/memfault/common/memfault_fault_handler.c +++ b/ports/esp_idf/memfault/common/memfault_fault_handler.c @@ -7,12 +7,13 @@ #include -#include "memfault/esp_port/version.h" -// keep the version.h include above, to support ESP-IDF < v4 +#include "esp_idf_version.h" +// keep the esp_idf_version.h include above for version-specific support #include "esp_attr.h" #include "esp_core_dump.h" #include "esp_err.h" +#include "esp_private/panic_internal.h" #ifdef __XTENSA__ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) #include "xtensa_api.h" @@ -31,13 +32,10 @@ #error "The port assumes the esp-idf is in use!" #endif -// Header refactor and watchpoint definitions added in 4.3.0 // Only include if watchpoint stack overflow detection enabled -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) && \ - defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) +#if defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) #include "soc/soc_caps.h" -#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) && - // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) +#endif // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) // Note: The esp-idf implements abort which will invoke the esp-idf coredump handler as well as a // chip reboot so we just piggyback off of that @@ -67,14 +65,10 @@ __wrap_panic_abort(const char *details) { __real_panic_abort(details); } -// This header is only available for ESP-IDF >= 4.2 -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) - #include "esp_private/panic_internal.h" // Ensure the substituted function signature matches the original function _Static_assert(__builtin_types_compatible_p(__typeof__(&panic_abort), __typeof__(&__wrap_panic_abort)), "Error: esp panic_abort wrapper is not compatible with esp-idf implementation"); -#endif void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { memfault_arch_fault_handling_assert(pc, lr, extra_info->assert_reason); @@ -91,35 +85,25 @@ void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInf //! The default implementation is replaced by leveraging GCCs --wrap feature //! https://github.com/espressif/esp-idf/blob/v4.0/components/esp32/panic.c#L620 //! -//! @note The signature for the wrapped function changed in esp-idf v4.3+, then -//! later backported to the 4.2 branch in v4.2.3. Support that change with a -//! version check (see static assert below for verifying the signature is -//! correct). -//! The signature changed in esp-idf v5.3.0, back ported to v5.1.4 + v5.2.2. -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 3) - #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)) || \ - ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4)) && \ - (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0))) +//! @note The signature changed in esp-idf v5.3.0, back ported to v5.1.4 + v5.2.2. +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)) || \ + ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4)) && \ + (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0))) void __wrap_esp_core_dump_write(panic_info_t *info) { - #else +#else // ESP_IDF_VERSION void __wrap_esp_core_dump_to_flash(panic_info_t *info) { - #endif - #ifdef __XTENSA__ +#endif // ESP_IDF_VERSION +#ifdef __XTENSA__ XtExcFrame *fp = (void *)info->frame; - #elif __riscv +#elif __riscv RvExcFrame *fp = (void *)info->frame; - #endif -#else -void __wrap_esp_core_dump_to_flash(XtExcFrame *fp) { -#endif +#endif // __XTENSA__ eMemfaultRebootReason reason; -/* - * To better classify the exception, we need panic_info_t provided by - * esp-idf v4.2.3+. For older versions just assume kMfltRebootReason_HardFault - */ -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 3) + /* + * To better classify the exception, we need panic_info_t + */ switch (info->exception) { case PANIC_EXCEPTION_DEBUG: reason = kMfltRebootReason_DebuggerHalted; @@ -129,9 +113,6 @@ void __wrap_esp_core_dump_to_flash(XtExcFrame *fp) { reason = kMfltRebootReason_HardFault; break; } -#else - reason = kMfltRebootReason_HardFault; -#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 3) #ifdef __XTENSA__ // Clear "EXCM" bit so we don't have to correct PS.OWB to get a good unwind This will also be @@ -173,8 +154,7 @@ void __wrap_esp_core_dump_to_flash(XtExcFrame *fp) { }; // If enabled, check if exception was triggered by stackoverflow type - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) && \ - defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) + #if defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) /* * See Xtensa ISA Debug Cause Register section: * https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/ip/tensilica-ip/isa-summary.pdf#_OPENTOPIC_TOC_PROCESSING_d61e48262 @@ -200,8 +180,7 @@ void __wrap_esp_core_dump_to_flash(XtExcFrame *fp) { reason = kMfltRebootReason_StackOverflow; } } - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) && - // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) + #endif // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) #elif __riscv sMfltRegState regs = { .mepc = fp->mepc, @@ -248,7 +227,7 @@ void __wrap_esp_core_dump_to_flash(XtExcFrame *fp) { .mtval = fp->mtval, .mhartid = fp->mhartid, }; -#endif +#endif // __XTENSA__ memfault_fault_handler(®s, reason); } diff --git a/ports/esp_idf/memfault/common/memfault_platform_core.c b/ports/esp_idf/memfault/common/memfault_platform_core.c index 6e8e0b764..e1584b887 100644 --- a/ports/esp_idf/memfault/common/memfault_platform_core.c +++ b/ports/esp_idf/memfault/common/memfault_platform_core.c @@ -8,10 +8,10 @@ #include #include +#include "esp_idf_version.h" #include "memfault/components.h" #include "memfault/esp_port/cli.h" #include "memfault/esp_port/http_client.h" -#include "memfault/esp_port/version.h" #include "memfault/metrics/metrics.h" #ifndef ESP_PLATFORM @@ -75,12 +75,7 @@ void memfault_platform_halt_if_debugging(void) { static void prv_record_reboot_reason(void) { eMemfaultRebootReason reboot_reason = kMfltRebootReason_Unknown; - int esp_reset_cause = 0; - - // esp_reset_reason is not implemented for 3.x builds -#if defined(ESP_IDF_VERSION) - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) - esp_reset_cause = (int)esp_reset_reason(); + int esp_reset_cause = (int)esp_reset_reason(); switch (esp_reset_cause) { case ESP_RST_POWERON: reboot_reason = kMfltRebootReason_PowerOnReset; @@ -107,8 +102,31 @@ static void prv_record_reboot_reason(void) { reboot_reason = kMfltRebootReason_Unknown; break; } - #endif -#endif + +#if defined(CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP) + // pretty-print the reset reason + struct esp_rst_reason_to_string { + esp_reset_reason_t reason; + const char *str; + } esp_rst_reasons[] = { + { ESP_RST_POWERON, "Power On" }, + { ESP_RST_SW, "Software" }, + { ESP_RST_INT_WDT, "Interrupt Watchdog" }, + { ESP_RST_TASK_WDT, "Task Watchdog" }, + { ESP_RST_WDT, "Watchdog" }, + { ESP_RST_DEEPSLEEP, "Deep Sleep" }, + { ESP_RST_BROWNOUT, "Brownout" }, + { ESP_RST_PANIC, "Panic" }, + { ESP_RST_UNKNOWN, "Unknown" }, + }; + + for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(esp_rst_reasons); i++) { + if (esp_rst_reasons[i].reason == esp_reset_cause) { + MEMFAULT_LOG_INFO("Reboot reason: %s", esp_rst_reasons[i].str); + break; + } + } +#endif // MEMFAULT_ENABLE_REBOOT_DIAG_DUMP const sResetBootupInfo reset_info = { .reset_reason_reg = esp_reset_cause, @@ -162,8 +180,7 @@ static int prv_memfault_log_wrapper(const char *fmt, va_list args) { } #endif // defined(CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK) -#if defined(CONFIG_MEMFAULT_ASSERT_ON_ALLOC_FAILURE) && \ - (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)) +#if defined(CONFIG_MEMFAULT_ASSERT_ON_ALLOC_FAILURE) // Crash if an allocation fails. If the application is running correctly, // without memory leaks, there should be zero allocation failures. If any occur, // it's an error and we would like a nice crash report at the point of failure. @@ -202,8 +219,7 @@ void memfault_boot(void) { memfault_register_cli(); #endif -#if defined(CONFIG_MEMFAULT_ASSERT_ON_ALLOC_FAILURE) && \ - (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)) +#if defined(CONFIG_MEMFAULT_ASSERT_ON_ALLOC_FAILURE) heap_caps_register_failed_alloc_callback(prv_alloc_failed_callback); #endif diff --git a/ports/esp_idf/memfault/common/memfault_platform_coredump.c b/ports/esp_idf/memfault/common/memfault_platform_coredump.c index 4900251ac..26caefb5d 100644 --- a/ports/esp_idf/memfault/common/memfault_platform_coredump.c +++ b/ports/esp_idf/memfault/common/memfault_platform_coredump.c @@ -14,9 +14,9 @@ #include #include +#include "esp_idf_version.h" #include "esp_intr_alloc.h" #include "esp_partition.h" -#include "memfault/esp_port/version.h" // The include order below, especially 'soc/uart_reg.h' and 'soc/soc.h', is // significant to support the various ESP-IDF versions, where the definitions @@ -362,7 +362,7 @@ static void prv_panic_safe_putstr(const char *str) { } } -bool memfault_platform_coredump_save_begin(void) { +bool memfault_port_coredump_save_begin(void) { // Update task watchdog bookkeeping, if it's enabled memfault_task_watchdog_bookkeep(); @@ -376,12 +376,8 @@ bool memfault_platform_coredump_save_begin(void) { #if !defined(ETS_INT_WDT_INUM) #define ETS_INT_WDT_INUM (ETS_T1_WDT_INUM) #endif - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) esp_intr_disable_source(ETS_INT_WDT_INUM); - #else - ESP_INTR_DISABLE(ETS_INT_WDT_INUM); - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) -#endif // defined(CONFIG_ESP_INT_WDT) +#endif // defined(CONFIG_ESP_INT_WDT) prv_panic_safe_putstr("Saving Memfault Coredump!\r\n"); return (memfault_esp_spi_flash_coredump_begin() == 0); diff --git a/ports/esp_idf/memfault/common/memfault_platform_demo_cli_cmds.c b/ports/esp_idf/memfault/common/memfault_platform_demo_cli_cmds.c index c2b25f3cb..31d9da433 100644 --- a/ports/esp_idf/memfault/common/memfault_platform_demo_cli_cmds.c +++ b/ports/esp_idf/memfault/common/memfault_platform_demo_cli_cmds.c @@ -9,9 +9,9 @@ // TODO: Migrate to "driver/gptimer.h" to fix warning #include "esp_console.h" #include "esp_err.h" +#include "esp_idf_version.h" #include "esp_system.h" #include "esp_wifi.h" -#include "memfault/esp_port/version.h" #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) #include "driver/gptimer.h" #include "esp_private/esp_clk.h" @@ -19,11 +19,7 @@ #else #include "driver/periph_ctrl.h" #include "driver/timer.h" - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) - #include "esp32/clk.h" - #else - #include "esp_clk.h" - #endif + #include "esp32/clk.h" #endif #include "memfault/components.h" diff --git a/ports/esp_idf/memfault/common/memfault_platform_device_info.c b/ports/esp_idf/memfault/common/memfault_platform_device_info.c index fe19b2707..1f3815445 100644 --- a/ports/esp_idf/memfault/common/memfault_platform_device_info.c +++ b/ports/esp_idf/memfault/common/memfault_platform_device_info.c @@ -7,16 +7,12 @@ //! Reference implementation of Memfault device info API platform dependencies for the ESP32 #include - -#include "memfault/components.h" -#include "memfault/esp_port/version.h" -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) - #include "esp_mac.h" -#endif - #include +#include "esp_idf_version.h" +#include "esp_mac.h" #include "esp_system.h" +#include "memfault/components.h" #include "memfault/esp_port/device_info.h" static char s_device_serial[32]; diff --git a/ports/esp_idf/memfault/common/memfault_platform_http_client.c b/ports/esp_idf/memfault/common/memfault_platform_http_client.c index 885bda585..e3bd15364 100644 --- a/ports/esp_idf/memfault/common/memfault_platform_http_client.c +++ b/ports/esp_idf/memfault/common/memfault_platform_http_client.c @@ -14,11 +14,11 @@ #include "esp_http_client.h" #include "esp_https_ota.h" + #include "esp_idf_version.h" #include "esp_wifi.h" #include "memfault/components.h" #include "memfault/esp_port/core.h" #include "memfault/esp_port/http_client.h" - #include "memfault/esp_port/version.h" #ifndef MEMFAULT_HTTP_DEBUG #define MEMFAULT_HTTP_DEBUG (0) @@ -425,16 +425,14 @@ bool memfault_esp_port_wifi_connected(void) { } bool memfault_esp_port_netif_connected(void) { - // The netif component was added in ESP-IDF v4.1 - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) // iterate over all netifs and check if any of them are connected esp_netif_t *netif = NULL; while ((netif = - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) esp_netif_next_unsafe - #else + #else esp_netif_next - #endif + #endif (netif)) != NULL) { if (!esp_netif_is_netif_up(netif)) { continue; @@ -448,11 +446,6 @@ bool memfault_esp_port_netif_connected(void) { // didn't find a netif with a non-zero IP address, return not connected return false; - #else // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) - // Previous versions, just check if wifi is connected. This won't work for - // non-wifi devices. - return memfault_esp_port_wifi_connected(); - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) } // Similar to memfault_platform_http_client_post_data() but just posts diff --git a/ports/esp_idf/memfault/common/memfault_platform_http_periodic_upload.c b/ports/esp_idf/memfault/common/memfault_platform_http_periodic_upload.c index 772c9b0fa..bf4b09d9f 100644 --- a/ports/esp_idf/memfault/common/memfault_platform_http_periodic_upload.c +++ b/ports/esp_idf/memfault/common/memfault_platform_http_periodic_upload.c @@ -8,18 +8,13 @@ #include #include +#include "esp_idf_version.h" +#include "esp_random.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/esp_port/http_client.h" -#include "memfault/esp_port/version.h" - -#if !defined(ESP_IDF_VERSION) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0)) - #include "esp_system.h" -#else - #include "esp_random.h" -#endif #define PERIODIC_UPLOAD_TASK_NAME "mflt_periodic_upload" diff --git a/ports/esp_idf/memfault/common/memfault_platform_metrics.c b/ports/esp_idf/memfault/common/memfault_platform_metrics.c index 3c554a14d..dbe18960c 100644 --- a/ports/esp_idf/memfault/common/memfault_platform_metrics.c +++ b/ports/esp_idf/memfault/common/memfault_platform_metrics.c @@ -14,6 +14,7 @@ #include "esp_err.h" #include "esp_event.h" #include "esp_heap_caps.h" +#include "esp_idf_version.h" #include "esp_timer.h" #include "esp_wifi.h" #include "esp_wifi_types.h" @@ -21,7 +22,6 @@ #include "memfault/core/math.h" #include "memfault/core/reboot_tracking.h" #include "memfault/esp_port/metrics.h" -#include "memfault/esp_port/version.h" #include "memfault/metrics/connectivity.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/connectivity.h" @@ -47,17 +47,13 @@ #if defined(CONFIG_MEMFAULT_ESP_WIFI_METRICS) - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) int32_t s_min_rssi; - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) - // This is a practical maximum RSSI value. In reality, the RSSI value is - // will likely be much lower. A value in the mid -60s is considered very good - // for example. An RSSI of -20 would be a device essentially on top of the AP. - #define MAXIMUM_RSSI -10 - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) -#endif // CONFIG_MEMFAULT_ESP_WIFI_METRICS + // This is a practical maximum RSSI value. In reality, the RSSI value is + // will likely be much lower. A value in the mid -60s is considered very good + // for example. An RSSI of -20 would be a device essentially on top of the AP. + #define MAXIMUM_RSSI -10 +#endif // CONFIG_MEMFAULT_ESP_WIFI_METRICS MEMFAULT_WEAK void memfault_esp_metric_timer_dispatch(MemfaultPlatformTimerCallback handler) { if (handler == NULL) { @@ -78,9 +74,7 @@ static void prv_metric_timer_handler(void *arg) { #if defined(CONFIG_MEMFAULT_ESP_WIFI_METRICS) static void metric_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) wifi_event_bss_rssi_low_t *rssi_data; - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) switch (event_id) { case WIFI_EVENT_STA_START: @@ -91,12 +85,10 @@ static void metric_event_handler(void *arg, esp_event_base_t event_base, int32_t break; case WIFI_EVENT_STA_CONNECTED: - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) // The RSSI threshold needs to be set when WIFI is already initialized. // This event is the most reliable way to know that we're already in that // state. ESP_ERROR_CHECK(esp_wifi_set_rssi_threshold(MAXIMUM_RSSI)); - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) MEMFAULT_METRIC_TIMER_START(wifi_connected_time_ms); #if defined(CONFIG_MEMFAULT_ESP_WIFI_CONNECTIVITY_TIME_METRICS) @@ -115,14 +107,12 @@ static void metric_event_handler(void *arg, esp_event_base_t event_base, int32_t #endif break; - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) case WIFI_EVENT_STA_BSS_RSSI_LOW: rssi_data = (wifi_event_bss_rssi_low_t *)event_data; s_min_rssi = MEMFAULT_MIN(rssi_data->rssi, s_min_rssi); esp_wifi_set_rssi_threshold(s_min_rssi); break; - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) default: break; @@ -153,7 +143,6 @@ static void prv_collect_oui(void) { } static void prv_collect_wifi_metrics(void) { - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) if (s_min_rssi <= MAXIMUM_RSSI) { MEMFAULT_METRIC_SET_SIGNED(wifi_sta_min_rssi, s_min_rssi); // Error checking is ignored here, as it's possible that WIFI is not @@ -163,7 +152,6 @@ static void prv_collect_wifi_metrics(void) { (void)esp_wifi_set_rssi_threshold(MAXIMUM_RSSI); s_min_rssi = 0; } - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) // Collect AP OUI prv_collect_oui(); diff --git a/ports/esp_idf/memfault/config/memfault_esp_idf_port_config.h b/ports/esp_idf/memfault/config/memfault_esp_idf_port_config.h index 7b62722ba..4645e71f9 100644 --- a/ports/esp_idf/memfault/config/memfault_esp_idf_port_config.h +++ b/ports/esp_idf/memfault/config/memfault_esp_idf_port_config.h @@ -68,6 +68,10 @@ extern "C" { #define MEMFAULT_IN_USE_BLOCK_COUNT_AUTOMATIC 0 #endif +#if defined(CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP) + #define MEMFAULT_ENABLE_REBOOT_DIAG_DUMP 1 +#endif + // Memfault SDK logs are routed to ESP-IDF logging, which are saved by Memfault, // so it's redundant to save them in the Memfault SDK as well. #define MEMFAULT_SDK_LOG_SAVE_DISABLE 1 diff --git a/ports/esp_idf/memfault/config/memfault_esp_metrics_heartbeat_config.def b/ports/esp_idf/memfault/config/memfault_esp_metrics_heartbeat_config.def index 43a2584e2..6913da8bb 100644 --- a/ports/esp_idf/memfault/config/memfault_esp_metrics_heartbeat_config.def +++ b/ports/esp_idf/memfault/config/memfault_esp_metrics_heartbeat_config.def @@ -25,9 +25,7 @@ MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_pct_max, kMemfaultMetricType #if defined(CONFIG_MEMFAULT_ESP_WIFI_METRICS) MEMFAULT_METRICS_KEY_DEFINE(wifi_connected_time_ms, kMemfaultMetricType_Timer) - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) MEMFAULT_METRICS_KEY_DEFINE(wifi_sta_min_rssi, kMemfaultMetricType_Signed) - #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) MEMFAULT_METRICS_KEY_DEFINE(wifi_disconnect_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_STRING_KEY_DEFINE(wifi_ap_oui, sizeof("00:00:00") - 1) #endif // CONFIG_MEMFAULT_ESP_WIFI_METRICS diff --git a/ports/esp_idf/memfault/include/memfault/esp_port/version.h b/ports/esp_idf/memfault/include/memfault/esp_port/version.h deleted file mode 100644 index a2557658a..000000000 --- a/ports/esp_idf/memfault/include/memfault/esp_port/version.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -//! @file -//! -//! Copyright (c) Memfault, Inc. -//! See LICENSE for details -//! -//! @brief -//! Provide some esp-idf version identifiers that work across esp-idf versions. -//! - -#include "esp_idf_version.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// These version macros were added in esp-idf v4.0. If they don't exist, it's an -// earlier release. Define the macros so they can be used, and set a fallback -// version of 3.99.99 for comparison purposes. These macros can't be used to -// compare for earlier versions. - -#if !defined(ESP_IDF_VERSION) - - #define ESP_IDF_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) - - #define ESP_IDF_VERSION ESP_IDF_VERSION_VAL(3, 99, 99) - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/ports/esp_idf/memfault/include/v3.x/memfault/esp_port/uart.h b/ports/esp_idf/memfault/include/v3.x/memfault/esp_port/uart.h deleted file mode 100644 index bca762176..000000000 --- a/ports/esp_idf/memfault/include/v3.x/memfault/esp_port/uart.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -//! @file -//! -//! Copyright (c) Memfault, Inc. -//! See LICENSE for details -//! -//! @brief -//! Thin wrapper around rom/uart which was refactored between the v3.x and v4.x esp-idf releases - -#include "rom/uart.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define MEMFAULT_ESP32_CONSOLE_UART_NUM CONFIG_CONSOLE_UART_NUM - -#ifdef __cplusplus -} -#endif diff --git a/ports/esp_idf/memfault/v3.x/Memfault-esp-idf-compat.cmake b/ports/esp_idf/memfault/v3.x/Memfault-esp-idf-compat.cmake deleted file mode 100644 index 1cf3968d2..000000000 --- a/ports/esp_idf/memfault/v3.x/Memfault-esp-idf-compat.cmake +++ /dev/null @@ -1,30 +0,0 @@ -if(DEFINED IDF_VERSION_MAJOR) # Introduced in v3.3.2 - set(MEMFAULT_ESP_IDF_VER "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}") -elseif(DEFINED IDF_VER) - # Strip the leading v so we can do a CMAKE version compare - STRING(REGEX REPLACE "^v" "" MEMFAULT_ESP_IDF_VER ${IDF_VER}) -endif() - -set(MEMFAULT_ESP_IDF_VERSION_SPECIFIC_REQUIRES esp32) - -if(DEFINED MEMFAULT_ESP_IDF_VER) - # In v3.3, espcoredump was moved to its own component - if(${MEMFAULT_ESP_IDF_VER} VERSION_GREATER_EQUAL "3.3") - list(APPEND MEMFAULT_ESP_IDF_VERSION_SPECIFIC_REQUIRES espcoredump) - endif() -endif() - -if(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD) - list(APPEND MEMFAULT_ESP_IDF_VERSION_SPECIFIC_REQUIRES esp_hw_support) -endif() - -function(mflt_esp32_component_get_target var component_dir) - if(COMMAND component_get_target) - # esp-idf v3.3 adds a prefix to the target and introduced component_get_target(): - component_get_target(local_var ${component_dir}) - set(${var} ${local_var} PARENT_SCOPE) - else() - # Pre-v3.3, the target is named after the directory: - set(${var} ${component_dir} PARENT_SCOPE) - endif() -endfunction() diff --git a/ports/esp_idf/memfault/v3.x/memfault_esp_spi_flash.c b/ports/esp_idf/memfault/v3.x/memfault_esp_spi_flash.c deleted file mode 100644 index d50718a71..000000000 --- a/ports/esp_idf/memfault/v3.x/memfault_esp_spi_flash.c +++ /dev/null @@ -1,25 +0,0 @@ -//! @file -//! -//! Copyright (c) Memfault, Inc. -//! See LICENSE for details - -#include "esp_spi_flash.h" -#include "memfault/esp_port/spi_flash.h" - -int memfault_esp_spi_flash_coredump_begin(void) { - // re-configure flash driver to be call-able from an Interrupt context - spi_flash_guard_set(&g_flash_guard_no_os_ops); - return 0; -} - -int memfault_esp_spi_flash_erase_range(size_t start_address, size_t size) { - return spi_flash_erase_range(start_address, size); -} - -int memfault_esp_spi_flash_write(size_t dest_addr, const void *src, size_t size) { - return spi_flash_write(dest_addr, src, size); -} - -int memfault_esp_spi_flash_read(size_t src_addr, void *dest, size_t size) { - return spi_flash_read(src_addr, dest, size); -} diff --git a/ports/esp_idf/memfault/v3.x/memfault_platform_metrics_legacy.c b/ports/esp_idf/memfault/v3.x/memfault_platform_metrics_legacy.c deleted file mode 100644 index aa02157e6..000000000 --- a/ports/esp_idf/memfault/v3.x/memfault_platform_metrics_legacy.c +++ /dev/null @@ -1,95 +0,0 @@ -//! @file -//! -//! Copyright (c) Memfault, Inc. -//! See LICENSE for details -//! -//! A port of dependency functions for Memfault metrics subsystem using FreeRTOS. -//! -//! @note For test purposes, the heartbeat interval can be changed to a faster period -//! by using the following CFLAG: -//! -DMEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS=15 - -#include "esp_timer.h" -#include "memfault/core/debug_log.h" -#include "memfault/core/reboot_tracking.h" -#include "memfault/esp_port/metrics.h" -#include "memfault/metrics/metrics.h" -#include "memfault/metrics/platform/connectivity.h" -#include "memfault/metrics/platform/timer.h" -#include "sdkconfig.h" - -#if CONFIG_MEMFAULT_LWIP_METRICS - #include "memfault/ports/lwip/metrics.h" -#endif // CONFIG_MEMFAULT_LWIP_METRICS - -#if CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS - #include "memfault/ports/freertos/metrics.h" -#endif // CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS - -#if CONFIG_MEMFAULT_MBEDTLS_METRICS - #include "memfault/ports/mbedtls/metrics.h" -#endif // CONFIG_MEMFAULT_MBEDTLS_METRICS - -MEMFAULT_WEAK void memfault_esp_metric_timer_dispatch(MemfaultPlatformTimerCallback handler) { - if (handler == NULL) { - return; - } - handler(); -} - -static void prv_metric_timer_handler(void *arg) { - memfault_reboot_tracking_reset_crash_count(); - - // NOTE: This timer runs once per MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS where the default is - // once per hour. - MemfaultPlatformTimerCallback *metric_timer_handler = (MemfaultPlatformTimerCallback *)arg; - memfault_esp_metric_timer_dispatch(metric_timer_handler); -} - -bool memfault_platform_metrics_timer_boot(uint32_t period_sec, - MemfaultPlatformTimerCallback callback) { - const esp_timer_create_args_t periodic_timer_args = { - .callback = &prv_metric_timer_handler, - .arg = callback, - .name = "mflt", - }; - - // Ignore return value; this function should be safe to call multiple times - // during system init, but needs to called before we create any timers. - // See implementation here (may change by esp-idf version!): - // https://github.com/espressif/esp-idf/blob/master/components/esp_timer/src/esp_timer.c#L431-L460 - (void)esp_timer_init(); - - esp_timer_handle_t periodic_timer; - ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer)); - - const int64_t us_per_sec = 1000000; - ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, period_sec * us_per_sec)); - - return true; -} - -void memfault_metrics_heartbeat_collect_sdk_data(void) { -#if CONFIG_MEMFAULT_LWIP_METRICS - memfault_lwip_heartbeat_collect_data(); -#endif // CONFIG_MEMFAULT_LWIP_METRICS - -#ifdef CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS - MEMFAULT_STATIC_ASSERT( - MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS <= (60 * 60), - "Heartbeat must be an hour or less for runtime metrics to mitigate counter overflow"); - - memfault_freertos_port_task_runtime_metrics(); -#endif // CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS - -#if CONFIG_MEMFAULT_MBEDTLS_METRICS - memfault_mbedtls_heartbeat_collect_data(); -#endif // CONFIG_MEMFAULT_MBEDTLS_METRICS -} - -#if defined(CONFIG_MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT) -//! Stub implementation to prevent compilation errors for v3.x -void memfault_platform_metrics_connectivity_boot(void) { - MEMFAULT_LOG_DEBUG("WiFi connectivity connected time is not implemented for ESP-IDF v3.x"); -} -#endif // CONFIG_MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT diff --git a/ports/freertos/src/memfault_sdk_metrics_freertos.c b/ports/freertos/src/memfault_sdk_metrics_freertos.c index d44b8cf65..2babf24ba 100644 --- a/ports/freertos/src/memfault_sdk_metrics_freertos.c +++ b/ports/freertos/src/memfault_sdk_metrics_freertos.c @@ -16,10 +16,10 @@ #endif #ifdef MEMFAULT_USE_ESP32_FREERTOS_INCLUDE + #include "esp_idf_version.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/timers.h" - #include "memfault/esp_port/version.h" #else #include "FreeRTOS.h" #include "task.h" diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 98041d80b..342cfd9db 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -1,12 +1,12 @@ if(CONFIG_MEMFAULT) - set(MEMFAULT_SDK_ROOT ../..) - - if(NOT DEFINED MEMFAULT_ZEPHYR_PORT_TARGET) - # Pick up latest supported target by default. - # Currently works for Zephyr >= 2.4 - set(MEMFAULT_ZEPHYR_PORT_TARGET v2.4) + if(${KERNEL_VERSION_MAJOR}.${KERNEL_VERSION_MINOR}.${KERNEL_PATCHLEVEL} VERSION_LESS "2.7.0") + message(FATAL_ERROR + "Memfault SDK requires Zephyr v2.7.0 or later. Please contact mflt.io/contact-support for assistance." + ) endif() + set(MEMFAULT_SDK_ROOT ../..) + # Collect Memfault SDK dependencies list(APPEND MEMFAULT_COMPONENTS core util panics demo) @@ -60,9 +60,9 @@ if(CONFIG_MEMFAULT) # Remove this line when removing support for < Zephyr v3.2 zephyr_library_compile_options(-imacros ${CMAKE_CURRENT_LIST_DIR}/include/memfault/ports/zephyr/include_compatibility.h) - # Pick up Zephyr specific port files - add_subdirectory(${MEMFAULT_ZEPHYR_PORT_TARGET}) + # Add subdirectories add_subdirectory(common) + add_subdirectory(panics) add_subdirectory_ifdef(CONFIG_MEMFAULT_NRF_CONNECT_SDK ncs) # Link Memfault Library diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index f354938a4..fa22fbf32 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -9,54 +9,30 @@ The instructions below assume you have an environment already setup for building and flashing a Zephyr application. If you do not, see the official [getting started guide](https://docs.zephyrproject.org/2.0.0/getting_started/index.html#build-hello-world). -## Directories +## Directory Structure -The subdirectories within the folder are titled to align with the Zephyr release -the porting files were tested against. If no breaking API changes have been made -within Zephyr and a different release, the port may work there as well - -- [v2.0](https://github.com/zephyrproject-rtos/zephyr/tree/v2.0-branch) - -## Integrating SDK - -1. Depending on Zephyr version, apply .patch in release directory to Zephyr - Kernel. Be sure to use the patch matching the Zephyr version in use. +The Memfault Zephyr port is set up as a Zephyr module. The directory structure +is as follows: +```bash +├── CMakeLists.txt # CMake file for the Memfault Zephyr module +├── common/ # Shared files for Zephyr integration +├── config/ # Configuration files for Memfault SDK +├── include/ # Header files for Zephyr integration +├── Kconfig # Kconfig file for Memfault Zephyr module +├── ncs/ # Files specific to the Zephyr-based Nordic nRF-Connect SDK +├── panics/ # Files for handling panics in Zephyr +└── README.md # This file ``` -$ cd $ZEPHYR_ROOT_DIR/ -$ git apply $MEMFAULT_SDK_ROOT/ports/zephyr/[v2.0]/zephyr-integration.patch -``` - -Note that `v2.2_v2.3/coredump-support.patch` should be applied for Zephyr 2.2 -and 2.3 only. -2. Clone (or symlink) memfault-firmware-sdk in Zephyr Project - -``` -$ cd $ZEPHYR_ROOT_DIR/ext/lib/memfault -$ git clone https://github.com/memfault/memfault-firmware-sdk.git -``` +## Integrating SDK -3. Implement device-specific dependencies. +Follow the guide here for how to integrate the Memfault SDK into a Zephyr +project: -``` -void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { - *info = (sMemfaultDeviceInfo) { - .device_serial = "DEMOSERIAL", - .software_type = "zephyr-main", - .software_version = "1.15.0", - .hardware_version = "disco_l475_iot1", - }; -} -``` - -``` -sMfltHttpClientConfig g_mflt_http_client_config = { - .api_key = "", -}; -``` + ## Demo -An example integration and instructions can be found for the STM32L4 in -`$MEMFAULT_SDK_ROOT/examples/zephyr/README.md` +An example integration and instructions can be found for a QEMU-emulated board +in `$MEMFAULT_SDK_ROOT/examples/zephyr/qemu/README.md` diff --git a/ports/zephyr/common/memfault_demo_cli.c b/ports/zephyr/common/memfault_demo_cli.c index bebb7d4c6..0902799de 100644 --- a/ports/zephyr/common/memfault_demo_cli.c +++ b/ports/zephyr/common/memfault_demo_cli.c @@ -13,7 +13,8 @@ #include "memfault/components.h" #include "memfault/ports/zephyr/http.h" #include "memfault/ports/zephyr/fota.h" -#include "zephyr_release_specific_headers.h" +#include MEMFAULT_ZEPHYR_INCLUDE(sys/__assert.h) +#include MEMFAULT_ZEPHYR_INCLUDE(sys/printk.h) // clang-format on static int prv_clear_core_cmd(const struct shell *shell, size_t argc, char **argv) { diff --git a/ports/zephyr/common/memfault_http_periodic_upload.c b/ports/zephyr/common/memfault_http_periodic_upload.c index 3bc67b717..31b52d079 100644 --- a/ports/zephyr/common/memfault_http_periodic_upload.c +++ b/ports/zephyr/common/memfault_http_periodic_upload.c @@ -7,6 +7,8 @@ #include MEMFAULT_ZEPHYR_INCLUDE(init.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) +#include "memfault/ports/zephyr/version.h" + // for zephyr 3.5.0+, use random.h instead of rand32.h #if MEMFAULT_ZEPHYR_VERSION_GT_STRICT(3, 4) #include MEMFAULT_ZEPHYR_INCLUDE(random/random.h) diff --git a/ports/zephyr/common/memfault_logging_legacy.c b/ports/zephyr/common/memfault_logging_legacy.c index 837b06f52..b4ceb8cea 100644 --- a/ports/zephyr/common/memfault_logging_legacy.c +++ b/ports/zephyr/common/memfault_logging_legacy.c @@ -48,10 +48,7 @@ static void prv_log_put_sync_string(const struct log_backend *const backend, const char *fmt, va_list ap); static void prv_log_panic(struct log_backend const *const backend); -// The function signature for struct log_backend_api init changed between Zephyr 2.5 and Zephyr 2.6 -// and we don't use any of the parameters so we leave the parameter list empty to mitigate -// -Wincompatible-pointer-types between versions -static void prv_log_init(); +static void prv_log_init(const struct log_backend *const backend); static void prv_log_dropped(const struct log_backend *const backend, uint32_t cnt); const struct log_backend_api log_backend_mflt_api = { diff --git a/ports/zephyr/common/memfault_platform_core.c b/ports/zephyr/common/memfault_platform_core.c index 74a377629..94f29c74e 100644 --- a/ports/zephyr/common/memfault_platform_core.c +++ b/ports/zephyr/common/memfault_platform_core.c @@ -21,23 +21,10 @@ #include "memfault/ports/reboot_reason.h" #include "memfault/ports/zephyr/core.h" #include "memfault/ports/zephyr/log_backend.h" -#include "zephyr_release_specific_headers.h" +#include MEMFAULT_ZEPHYR_INCLUDE(sys/__assert.h) +#include "memfault/ports/zephyr/version.h" // clang-format on -#if !MEMFAULT_ZEPHYR_VERSION_GT(2, 5) - -// Note: CONFIG_OPENOCD_SUPPORT was deprecated in Zephyr 2.6 and fully removed in the 3.x line. To -// maintain backward compatibility with older versions of Zephyr we inline the check here. -// -// When support for Zephyr <= 2.5 is removed, we should adopt an approach like the following -// https://github.com/memfault/memfault-firmware-sdk/pull/26 - - #if !CONFIG_OPENOCD_SUPPORT - #error "CONFIG_OPENOCD_SUPPORT=y must be added to your prj.conf" - #endif - -#endif - #if CONFIG_MEMFAULT_METRICS #include "memfault/metrics/metrics.h" #endif diff --git a/ports/zephyr/common/memfault_platform_coredump_regions.c b/ports/zephyr/common/memfault_platform_coredump_regions.c index e589ae5e0..fb2666bdd 100644 --- a/ports/zephyr/common/memfault_platform_coredump_regions.c +++ b/ports/zephyr/common/memfault_platform_coredump_regions.c @@ -18,10 +18,8 @@ #if defined(CONFIG_ARM) #if MEMFAULT_ZEPHYR_VERSION_GT(3, 4) #include -#elif MEMFAULT_ZEPHYR_VERSION_GT(2, 1) - #include MEMFAULT_ZEPHYR_INCLUDE(arch/arm/aarch32/cortex_m/cmsis.h) #else - #include MEMFAULT_ZEPHYR_INCLUDE(arch/arm/cortex_m/cmsis.h) + #include MEMFAULT_ZEPHYR_INCLUDE(arch/arm/aarch32/cortex_m/cmsis.h) #endif #endif diff --git a/ports/zephyr/common/memfault_platform_debug_log.c b/ports/zephyr/common/memfault_platform_debug_log.c index befa68d99..9a28f6b6d 100644 --- a/ports/zephyr/common/memfault_platform_debug_log.c +++ b/ports/zephyr/common/memfault_platform_debug_log.c @@ -15,7 +15,7 @@ #include "memfault/config.h" #include "memfault/ports/zephyr/version.h" -#include "zephyr_release_specific_headers.h" +#include MEMFAULT_ZEPHYR_INCLUDE(sys/printk.h) // clang-format on LOG_MODULE_REGISTER(mflt, CONFIG_MEMFAULT_LOG_LEVEL); @@ -88,13 +88,9 @@ void memfault_platform_log_raw(const char *fmt, ...) { va_list args; va_start(args, fmt); -#define ZEPHYR_VERSION_GTE(major, minor) \ - ((KERNEL_VERSION_MAJOR > (major)) || \ - ((KERNEL_VERSION_MAJOR == (major)) && (KERNEL_VERSION_MINOR >= (minor)))) - char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; vsnprintf(log_buf, sizeof(log_buf), fmt, args); -#if ZEPHYR_VERSION_GTE(3, 0) +#if MEMFAULT_ZEPHYR_VERSION_GTE_STRICT(3, 0) LOG_PRINTK("%s\n", log_buf); #else printk("%s\n", log_buf); diff --git a/ports/zephyr/common/memfault_platform_http.c b/ports/zephyr/common/memfault_platform_http.c index 58b461ea4..1d4d780a1 100644 --- a/ports/zephyr/common/memfault_platform_http.c +++ b/ports/zephyr/common/memfault_platform_http.c @@ -26,6 +26,7 @@ #include "memfault/ports/zephyr/http.h" #include "memfault/ports/zephyr/root_cert_storage.h" #include "memfault/ports/zephyr/deprecated_root_cert.h" +#include "memfault/ports/zephyr/version.h" #if MEMFAULT_ZEPHYR_VERSION_GT_STRICT(3, 6) diff --git a/ports/zephyr/common/memfault_platform_metrics.c b/ports/zephyr/common/memfault_platform_metrics.c index 206d49901..b3d383f35 100644 --- a/ports/zephyr/common/memfault_platform_metrics.c +++ b/ports/zephyr/common/memfault_platform_metrics.c @@ -199,8 +199,7 @@ void memfault_metrics_heartbeat_collect_sdk_data(void) { #if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO) struct k_thread *me = k_current_get(); - #if defined(CONFIG_THREAD_STACK_INFO) && MEMFAULT_ZEPHYR_VERSION_GT(2, 1) - // k_thread_stack_space_get() introduced in v2.2.0 + #if defined(CONFIG_THREAD_STACK_INFO) size_t free_stack_size; k_thread_stack_space_get(me, &free_stack_size); MEMFAULT_METRIC_SET_UNSIGNED(TimerTaskFreeStack, free_stack_size); diff --git a/ports/zephyr/common/memfault_zephyr_ram_regions.c b/ports/zephyr/common/memfault_zephyr_ram_regions.c index e97c126dc..cc3853bff 100644 --- a/ports/zephyr/common/memfault_zephyr_ram_regions.c +++ b/ports/zephyr/common/memfault_zephyr_ram_regions.c @@ -14,7 +14,6 @@ #include "memfault/components.h" #include "memfault/ports/zephyr/coredump.h" -#include "memfault/ports/zephyr/version.h" // Use this config flag to manually select the old data region names #if !defined(MEMFAULT_ZEPHYR_USE_OLD_DATA_REGION_NAMES) @@ -26,7 +25,7 @@ static struct k_thread *s_task_tcbs[CONFIG_MEMFAULT_COREDUMP_MAX_TRACKED_TASKS]; #if CONFIG_THREAD_STACK_INFO #if CONFIG_STACK_GROWS_UP #error \ - "Only full-descending stacks are supported by this implementation. Please contact support@memfault.com." + "Only full-descending stacks are supported by this implementation. Please visit https://mflt.io/contact-support" #endif #if defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) @@ -277,21 +276,17 @@ size_t memfault_zephyr_get_data_regions(sMfltCoredumpRegion *regions, size_t num } // These linker variables are defined in linker.ld in Zephyr RTOS. Note that - // the data region name changed in v2.7 of the kernel, so check the kernel - // version and set the name appropriately. + // the data region name changed in v2.7 of the kernel. // - // Also check for a user override; this is necessary if NCS v1.7.1 is used - // with a Memfault SDK version >=0.27.3, because that NCS release used an - // intermediate Zephyr release, so the version number checking is not - // possible. + // Also check for a user override, in case of non-standard configurations. #if !defined(ZEPHYR_DATA_REGION_START) && !defined(ZEPHYR_DATA_REGION_END) - #if !MEMFAULT_ZEPHYR_USE_OLD_DATA_REGION_NAMES && MEMFAULT_ZEPHYR_VERSION_GT(2, 6) - #define ZEPHYR_DATA_REGION_START __data_region_start - #define ZEPHYR_DATA_REGION_END __data_region_end - #else + #if MEMFAULT_ZEPHYR_USE_OLD_DATA_REGION_NAMES // The old names are used in previous Zephyr versions (<=2.6) #define ZEPHYR_DATA_REGION_START __data_ram_start #define ZEPHYR_DATA_REGION_END __data_ram_end + #else + #define ZEPHYR_DATA_REGION_START __data_region_start + #define ZEPHYR_DATA_REGION_END __data_region_end #endif #endif diff --git a/ports/zephyr/config/memfault_metrics_heartbeat_zephyr_port_config.def b/ports/zephyr/config/memfault_metrics_heartbeat_zephyr_port_config.def index b433b8580..64fbd0ddd 100644 --- a/ports/zephyr/config/memfault_metrics_heartbeat_zephyr_port_config.def +++ b/ports/zephyr/config/memfault_metrics_heartbeat_zephyr_port_config.def @@ -4,7 +4,7 @@ #if defined(CONFIG_MEMFAULT_METRICS_DEFAULT_SET_ENABLE) - #if defined(CONFIG_THREAD_STACK_INFO) && MEMFAULT_ZEPHYR_VERSION_GT(2, 1) + #if defined(CONFIG_THREAD_STACK_INFO) MEMFAULT_METRICS_KEY_DEFINE(TimerTaskFreeStack, kMemfaultMetricType_Unsigned) #endif diff --git a/ports/zephyr/config/memfault_zephyr_platform_config.h b/ports/zephyr/config/memfault_zephyr_platform_config.h index 73418c71a..7a5e854a6 100644 --- a/ports/zephyr/config/memfault_zephyr_platform_config.h +++ b/ports/zephyr/config/memfault_zephyr_platform_config.h @@ -6,15 +6,12 @@ //! See LICENSE for details //! //! Zephyr port overrides for the default configuration settings in the memfault-firmware-sdk. -#include #ifdef __cplusplus extern "C" { #endif -// Note that pre-v2.0 Zephyr did not create the section allocation needed to support -// our Gnu build ID usage. -#if KERNEL_VERSION_MAJOR >= 2 && defined(CONFIG_MEMFAULT_USE_GNU_BUILD_ID) +#if defined(CONFIG_MEMFAULT_USE_GNU_BUILD_ID) // Add a unique identifier to the firmware build // // It is very common, especially during development, to not change the firmware diff --git a/ports/zephyr/include/memfault/ports/ncs/version.h b/ports/zephyr/include/memfault/ports/ncs/version.h index e0936deaf..40963dfdf 100644 --- a/ports/zephyr/include/memfault/ports/ncs/version.h +++ b/ports/zephyr/include/memfault/ports/ncs/version.h @@ -13,25 +13,7 @@ extern "C" { #endif //! NCS Version was introduced in nRF Connect SDK >= 1.4 -#if __has_include("ncs_version.h") - - #include "ncs_version.h" - -//! modem/bsdlib.h was introduced in nRF Connect SDK 1.3 -#elif __has_include("modem/bsdlib.h") - - #define NCS_VERSION_MAJOR 1 - #define NCS_VERSION_MINOR 3 - #define NCS_PATCHLEVEL 0 - -#else - - //! The lowest version the memfault-firmware-sdk has been ported to - #define NCS_VERSION_MAJOR 1 - #define NCS_VERSION_MINOR 2 - #define NCS_PATCHLEVEL 0 - -#endif +#include "ncs_version.h" //! Returns true if current nRF Connect Version is greater than the one specified //! diff --git a/ports/zephyr/include/memfault/ports/zephyr/version.h b/ports/zephyr/include/memfault/ports/zephyr/version.h index 39b81f076..9bb705fb9 100644 --- a/ports/zephyr/include/memfault/ports/zephyr/version.h +++ b/ports/zephyr/include/memfault/ports/zephyr/version.h @@ -40,6 +40,12 @@ extern "C" { ((KERNEL_VERSION_MAJOR > (major)) || \ ((KERNEL_VERSION_MAJOR == (major)) && (KERNEL_VERSION_MINOR > (minor)))) +//! Returns true if current Zephyr Kernel Version is greater than or equal to the one specified. +//! Does not support "development" versions. +#define MEMFAULT_ZEPHYR_VERSION_GTE_STRICT(major, minor) \ + ((KERNEL_VERSION_MAJOR > (major)) || \ + ((KERNEL_VERSION_MAJOR == (major)) && (KERNEL_VERSION_MINOR >= (minor)))) + #ifdef __cplusplus } #endif diff --git a/ports/zephyr/ncs/CMakeLists.txt b/ports/zephyr/ncs/CMakeLists.txt index 0783cb138..c1edab5d3 100644 --- a/ports/zephyr/ncs/CMakeLists.txt +++ b/ports/zephyr/ncs/CMakeLists.txt @@ -1,12 +1,9 @@ if(CONFIG_MEMFAULT_NRF_CONNECT_SDK) - if (${KERNEL_VERSION_MAJOR} EQUAL 2) - if (${KERNEL_VERSION_MINOR} LESS_EQUAL 3) - if (CONFIG_MEMFAULT_HTTP_MAX_POST_SIZE EQUAL 0) - message(FATAL_ERROR "CONFIG_MEMFAULT_HTTP_MAX_POST_SIZE=256 must be added to your prj.conf" - " when using an nRF Connect SDK version older than v1.4") - endif() - endif() + if(${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS "1.9.2") + message(FATAL_ERROR + "Memfault SDK requires nRF Connect SDK version 1.9.2 or newer. Please contact mflt.io/contact-support for assistance." + ) endif() add_subdirectory(src) diff --git a/ports/zephyr/ncs/Kconfig b/ports/zephyr/ncs/Kconfig index 2e5051834..c91b54509 100644 --- a/ports/zephyr/ncs/Kconfig +++ b/ports/zephyr/ncs/Kconfig @@ -1,7 +1,7 @@ config MEMFAULT_NRF_CONNECT_SDK bool "nRF Connect SDK extensions" default y if SOC_SERIES_NRF52X || SOC_SERIES_NRF53X || SOC_SERIES_NRF91X - select MEMFAULT_REBOOT_REASON_GET_CUSTOM + imply MEMFAULT_REBOOT_REASON_GET_CUSTOM if MEMFAULT_NRF_CONNECT_SDK diff --git a/ports/zephyr/ncs/src/memfault_fota.c b/ports/zephyr/ncs/src/memfault_fota.c index 8e4ef206a..35610f9e3 100644 --- a/ports/zephyr/ncs/src/memfault_fota.c +++ b/ports/zephyr/ncs/src/memfault_fota.c @@ -159,18 +159,8 @@ int memfault_fota_start(void) { // Note: The nordic FOTA API only supports passing one root CA today. So we cycle through the // list of required Root CAs in use by Memfault to find the appropriate one for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_memfault_fota_certs); i++) { - #if MEMFAULT_NCS_VERSION_GT(1, 7) - // In NCS 1.8 signature was changed "to accept an integer parameter specifying the PDN ID, - // which replaces the parameter used to specify the APN" - // - // https://github.com/nrfconnect/sdk-nrf/blob/v1.8.0/include/net/fota_download.h#L88-L106 rv = fota_download_start(s_download_url, s_download_url, s_memfault_fota_certs[i], 0 /* pdn_id */, 0); - #else // NCS <= 1.7 - // https://github.com/nrfconnect/sdk-nrf/blob/v1.4.1/include/net/fota_download.h#L88-L106 - rv = fota_download_start(s_download_url, s_download_url, s_memfault_fota_certs[i], - NULL /* apn */, 0); - #endif if (rv == 0) { // success -- we are ready to start the FOTA download! break; diff --git a/ports/zephyr/ncs/src/memfault_nrf91_root_cert_storage.c b/ports/zephyr/ncs/src/memfault_nrf91_root_cert_storage.c index 07020cf09..3e33f617c 100644 --- a/ports/zephyr/ncs/src/memfault_nrf91_root_cert_storage.c +++ b/ports/zephyr/ncs/src/memfault_nrf91_root_cert_storage.c @@ -9,36 +9,16 @@ #include -#include "memfault/ports/zephyr/root_cert_storage.h" - -//! Between nrf-connect-sdk v1.2.1 and v1.4 the locations of some headers changed. -//! We use __has_include() to support both paths so the port works with either SDK -//! version. -#if __has_include("zephyr/types.h") - #include "zephyr/types.h" -#endif - -#if __has_include("modem/modem_key_mgmt.h") - #include "modem/modem_key_mgmt.h" -#else - #include -#endif - #include "memfault/core/debug_log.h" -#include "memfault/ports/ncs/version.h" +#include "memfault/ports/zephyr/root_cert_storage.h" +#include "modem/modem_key_mgmt.h" +#include "zephyr/types.h" int memfault_root_cert_storage_add(eMemfaultRootCert cert_id, const char *cert, size_t cert_length) { bool exists; -// Note: modem_key_mgmt_exists() signature changed between nRF Connect SDK 1.7 & 1.8 -// https://github.com/nrfconnect/sdk-nrf/pull/5631 -#if MEMFAULT_NCS_VERSION_GT(1, 7) int err = modem_key_mgmt_exists(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); -#else - uint8_t unused; - int err = modem_key_mgmt_exists(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists, &unused); -#endif if (err != 0) { MEMFAULT_LOG_ERROR("Failed to check if cert %d exists in storage, rv=%d", cert_id, err); @@ -66,14 +46,7 @@ int memfault_root_cert_storage_add(eMemfaultRootCert cert_id, const char *cert, int memfault_root_cert_storage_remove(eMemfaultRootCert cert_id) { bool exists; -// Note: modem_key_mgmt_exists() signature changed between nRF Connect SDK 1.7 & 1.8 -// https://github.com/nrfconnect/sdk-nrf/pull/5631 -#if MEMFAULT_NCS_VERSION_GT(1, 7) int err = modem_key_mgmt_exists(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); -#else - uint8_t unused; - int err = modem_key_mgmt_exists(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists, &unused); -#endif if (err != 0) { MEMFAULT_LOG_ERROR("Failed to check if cert %d exists in storage, rv=%d", cert_id, err); diff --git a/ports/zephyr/ncs/v1.3-zephyr-coredump-support.patch b/ports/zephyr/ncs/v1.3-zephyr-coredump-support.patch deleted file mode 100644 index b6f091e2a..000000000 --- a/ports/zephyr/ncs/v1.3-zephyr-coredump-support.patch +++ /dev/null @@ -1,251 +0,0 @@ -From 2a43ce9e4ec74947d5d868e309b99fc6307ad90b Mon Sep 17 00:00:00 2001 -From: Memfault Inc -Date: Tue, 26 Jan 2021 00:01:13 -0500 -Subject: [PATCH] Backport of "arch: arm: Collect full register state in - Cortex-M Exception Stack Frame" - -This patch is intended for use on Zephyr 2.2 & 2.3 - -See the following commit for more details: -https://github.com/zephyrproject-rtos/zephyr/pull/27496/commits/5c1ea3f96b496a82cea89053eb4d2aedef1cde88 ---- - arch/Kconfig | 12 ++++++++ - arch/arm/core/aarch32/Kconfig | 1 + - arch/arm/core/aarch32/cortex_m/fault.c | 17 ++++++++++- - arch/arm/core/aarch32/cortex_m/fault_s.S | 36 ++++++++++++++++++++---- - arch/arm/core/aarch32/fatal.c | 25 ++++++++++++++++ - arch/nios2/Kconfig | 1 + - include/arch/arm/aarch32/exc.h | 17 +++++++++++ - 7 files changed, 102 insertions(+), 7 deletions(-) - -diff --git a/arch/Kconfig b/arch/Kconfig -index 2b1c9cbf2a..c8b93b7155 100644 ---- a/arch/Kconfig -+++ b/arch/Kconfig -@@ -359,6 +359,15 @@ config IRQ_OFFLOAD - run in interrupt context. Only useful for test cases that need - to validate the correctness of kernel objects in IRQ context. - -+ -+config EXTRA_EXCEPTION_INFO -+ bool "Collect extra exception info" -+ depends on ARCH_HAS_EXTRA_EXCEPTION_INFO -+ help -+ This option enables the collection of extra information, such as -+ register state, when a fault occurs. This information can be useful -+ to collect for post-mortem analysis and debug of issues. -+ - endmenu # Interrupt configuration - - endmenu -@@ -388,6 +397,9 @@ config ARCH_HAS_RAMFUNC_SUPPORT - config ARCH_HAS_NESTED_EXCEPTION_DETECTION - bool - -+config ARCH_HAS_EXTRA_EXCEPTION_INFO -+ bool -+ - # - # Other architecture related options - # -diff --git a/arch/arm/core/aarch32/Kconfig b/arch/arm/core/aarch32/Kconfig -index b996b7ec5f..b3aabe857e 100644 ---- a/arch/arm/core/aarch32/Kconfig -+++ b/arch/arm/core/aarch32/Kconfig -@@ -17,6 +17,7 @@ config CPU_CORTEX_M - select ARCH_HAS_RAMFUNC_SUPPORT - select ARCH_HAS_NESTED_EXCEPTION_DETECTION - select SWAP_NONATOMIC -+ select ARCH_HAS_EXTRA_EXCEPTION_INFO - help - This option signifies the use of a CPU of the Cortex-M family. - -diff --git a/arch/arm/core/aarch32/cortex_m/fault.c b/arch/arm/core/aarch32/cortex_m/fault.c -index 38748f387d..d16a169db4 100644 ---- a/arch/arm/core/aarch32/cortex_m/fault.c -+++ b/arch/arm/core/aarch32/cortex_m/fault.c -@@ -921,9 +921,11 @@ static inline z_arch_esf_t *get_esf(u32_t msp, u32_t psp, u32_t exc_return, - * @param msp MSP value immediately after the exception occurred - * @param psp PSP value immediately after the exception occurred - * @param exc_return EXC_RETURN value present in LR after exception entry. -+ * @param callee_regs Callee-saved registers (R4-R11, PSP) - * - */ --void z_arm_fault(u32_t msp, u32_t psp, u32_t exc_return) -+void z_arm_fault(u32_t msp, u32_t psp, u32_t exc_return, -+ _callee_saved_t *callee_regs) - { - u32_t reason = K_ERR_CPU_EXCEPTION; - int fault = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk; -@@ -951,7 +953,20 @@ void z_arm_fault(u32_t msp, u32_t psp, u32_t exc_return) - } - - /* Copy ESF */ -+#if !defined(CONFIG_EXTRA_EXCEPTION_INFO) - memcpy(&esf_copy, esf, sizeof(z_arch_esf_t)); -+ ARG_UNUSED(callee_regs); -+#else -+ /* the extra exception info is not present in the original esf -+ * so we only copy the fields before those. -+ */ -+ memcpy(&esf_copy, esf, offsetof(z_arch_esf_t, extra_info)); -+ esf_copy.extra_info = (struct __extra_esf_info) { -+ .callee = callee_regs, -+ .exc_return = exc_return, -+ .msp = msp -+ }; -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ - - /* Overwrite stacked IPSR to mark a nested exception, - * or a return to Thread mode. Note that this may be -diff --git a/arch/arm/core/aarch32/cortex_m/fault_s.S b/arch/arm/core/aarch32/cortex_m/fault_s.S -index b691807ad1..e4a1b24775 100644 ---- a/arch/arm/core/aarch32/cortex_m/fault_s.S -+++ b/arch/arm/core/aarch32/cortex_m/fault_s.S -@@ -46,9 +46,10 @@ GTEXT(z_arm_exc_spurious) - * - the MSP - * - the PSP - * - the EXC_RETURN value -+ * - callee saved register state (r4-r11, psp) - * as parameters to the z_arm_fault() C function that will perform the -- * rest of the fault handling (i.e. z_arm_fault(MSP, PSP, EXC_RETURN)). -- -+ * rest of the fault handling: -+ * (i.e. z_arm_fault(MSP, PSP, EXC_RETURN, CALLEE_REGS)). - * Provides these symbols: - * - * z_arm_hard_fault -@@ -78,12 +79,35 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,z_arm_exc_spurious) - - mrs r0, MSP - mrs r1, PSP -- mov r2, lr /* EXC_RETURN */ -- - push {r0, lr} -- -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ /* Build _callee_saved_t. To match the struct -+ * definition we push the psp & then r11-r4 -+ */ -+ push { r1, r2 } -+#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) -+ mov r3, r11 -+ mov r2, r10 -+ push {r2, r3} -+ mov r3, r9 -+ mov r2, r8 -+ push {r2, r3} -+ push {r4-r7} -+#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) -+ push {r4-r11} -+#endif -+ mov r3, sp /* pointer to _callee_saved_t */ -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ -+ mov r2, lr /* EXC_RETURN */ - bl z_arm_fault -- -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ /* We do not need to restore any register state here -+ * because we did not use any callee-saved registers -+ * in this routine. Therefore, we can just reset -+ * the MSP to its value prior to entering the function -+ */ -+ add sp, #40 -+#endif - pop {r0, pc} - - .end -diff --git a/arch/arm/core/aarch32/fatal.c b/arch/arm/core/aarch32/fatal.c -index 849a4ee69c..73d2888b29 100644 ---- a/arch/arm/core/aarch32/fatal.c -+++ b/arch/arm/core/aarch32/fatal.c -@@ -34,6 +34,18 @@ static void esf_dump(const z_arch_esf_t *esf) - } - LOG_ERR("fpscr: 0x%08x", esf->fpscr); - #endif -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ const struct _callee_saved *callee = esf->extra_info.callee; -+ -+ if (callee != NULL) { -+ LOG_ERR("r4/v1: 0x%08x r5/v2: 0x%08x r6/v3: 0x%08x", -+ callee->v1, callee->v2, callee->v3); -+ LOG_ERR("r7/v4: 0x%08x r8/v5: 0x%08x r9/v6: 0x%08x", -+ callee->v4, callee->v5, callee->v6); -+ LOG_ERR("r10/v7: 0x%08x r11/v8: 0x%08x psp: 0x%08x", -+ callee->v7, callee->v8, callee->psp); -+ } -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ - LOG_ERR("Faulting instruction address (r15/pc): 0x%08x", - esf->basic.pc); - } -@@ -83,7 +95,20 @@ void z_do_kernel_oops(const z_arch_esf_t *esf) - } - - #endif /* CONFIG_USERSPACE */ -+ -+#if !defined(CONFIG_EXTRA_EXCEPTION_INFO) - z_arm_fatal_error(reason, esf); -+#else -+ /* extra exception info is not collected for kernel oops -+ * path today so we make a copy of the ESF and zero out -+ * that information -+ */ -+ z_arch_esf_t esf_copy; -+ -+ memcpy(&esf_copy, esf, offsetof(z_arch_esf_t, extra_info)); -+ esf_copy.extra_info = (struct __extra_esf_info) { 0 }; -+ z_arm_fatal_error(reason, &esf_copy); -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ - } - - FUNC_NORETURN void arch_syscall_oops(void *ssf_ptr) -diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig -index f64fd7ad71..3528bc24cb 100644 ---- a/arch/nios2/Kconfig -+++ b/arch/nios2/Kconfig -@@ -14,6 +14,7 @@ config CPU_NIOS2_GEN2 - bool - default y - select BUILD_OUTPUT_HEX -+ select ARCH_HAS_EXTRA_EXCEPTION_INFO - help - This option signifies the use of a Nios II Gen 2 CPU - -diff --git a/include/arch/arm/aarch32/exc.h b/include/arch/arm/aarch32/exc.h -index 75050d17f1..028b79e20d 100644 ---- a/include/arch/arm/aarch32/exc.h -+++ b/include/arch/arm/aarch32/exc.h -@@ -73,6 +73,20 @@ GTEXT(z_arm_exc_exit); - extern "C" { - #endif - -+/* Additional register state that is not stacked by hardware on exception -+ * entry. -+ * -+ * These fields are ONLY valid in the ESF copy passed into z_arm_fatal_error(). -+ * When information for a member is unavailable, the field is set to zero. -+ */ -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+struct __extra_esf_info { -+ _callee_saved_t *callee; -+ uint32_t msp; -+ uint32_t exc_return; -+}; -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ -+ - struct __esf { - struct __basic_sf { - sys_define_gpr_with_alias(a1, r0); -@@ -89,6 +103,9 @@ struct __esf { - u32_t fpscr; - u32_t undefined; - #endif -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ struct __extra_esf_info extra_info; -+#endif - }; - - typedef struct __esf z_arch_esf_t; --- -2.28.0 - diff --git a/ports/zephyr/v2.4/CMakeLists.txt b/ports/zephyr/panics/CMakeLists.txt similarity index 100% rename from ports/zephyr/v2.4/CMakeLists.txt rename to ports/zephyr/panics/CMakeLists.txt diff --git a/ports/zephyr/v2.4/memfault_fault_handler.c b/ports/zephyr/panics/memfault_fault_handler.c similarity index 98% rename from ports/zephyr/v2.4/memfault_fault_handler.c rename to ports/zephyr/panics/memfault_fault_handler.c index 50cf92ecd..c4c10e223 100644 --- a/ports/zephyr/v2.4/memfault_fault_handler.c +++ b/ports/zephyr/panics/memfault_fault_handler.c @@ -19,7 +19,6 @@ #include "memfault/panics/arch/arm/cortex_m.h" #include "memfault/panics/coredump.h" #include "memfault/panics/fault_handling.h" -#include "memfault/ports/ncs/version.h" #include "memfault/ports/zephyr/version.h" // Starting in v3.4, the handler set function was renamed and the declaration @@ -137,8 +136,8 @@ static eMemfaultRebootReason prv_zephyr_to_memfault_fault_reason(unsigned int re case K_ERR_ARM_SECURE_LAZY_STATE_ERROR: return kMfltRebootReason_SecurityViolation; - // Zephyr + Cortex A/R is currently unsupported, please contact support@memfault.com for - // assistance! + // Zephyr + Cortex A/R is currently unsupported. Please visit https://mflt.io/contact-support + // for assistance! // // Cortex-A/R exceptions // K_ERR_ARM_UNDEFINED_INSTRUCTION // K_ERR_ARM_ALIGNMENT_FAULT @@ -161,6 +160,7 @@ extern void sys_arch_reboot(int type); // Intercept zephyr/kernel/fatal.c:z_fatal_error(). Note that the signature // changed in zephyr 3.7. #if defined(CONFIG_MEMFAULT_NRF_CONNECT_SDK) + #include "memfault/ports/ncs/version.h" #define MEMFAULT_NEW_ARCH_ESF_STRUCT MEMFAULT_NCS_VERSION_GT(2, 7) #else #define MEMFAULT_NEW_ARCH_ESF_STRUCT MEMFAULT_ZEPHYR_VERSION_GT_STRICT(3, 6) diff --git a/ports/zephyr/v2.4/memfault_fault_handler_riscv.c b/ports/zephyr/panics/memfault_fault_handler_riscv.c similarity index 100% rename from ports/zephyr/v2.4/memfault_fault_handler_riscv.c rename to ports/zephyr/panics/memfault_fault_handler_riscv.c diff --git a/ports/zephyr/v2.4/memfault_fault_handler_xtensa.c b/ports/zephyr/panics/memfault_fault_handler_xtensa.c similarity index 100% rename from ports/zephyr/v2.4/memfault_fault_handler_xtensa.c rename to ports/zephyr/panics/memfault_fault_handler_xtensa.c diff --git a/ports/zephyr/v2.0/CMakeLists.txt b/ports/zephyr/v2.0/CMakeLists.txt deleted file mode 100644 index 3be4ee9cf..000000000 --- a/ports/zephyr/v2.0/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -zephyr_library_sources(memfault_fault_handler.c) -zephyr_include_directories(.) diff --git a/ports/zephyr/v2.0/memfault_fault_handler.c b/ports/zephyr/v2.0/memfault_fault_handler.c deleted file mode 100644 index 2bc3b007c..000000000 --- a/ports/zephyr/v2.0/memfault_fault_handler.c +++ /dev/null @@ -1,50 +0,0 @@ -//! @file -//! -//! Copyright (c) Memfault, Inc. -//! See LICENSE for details -//! -//! @brief -//! Glue between Zephyr Fault Handler and Memfault Fault Handler for ARM: -//! https://github.com/zephyrproject-rtos/zephyr/blob/v2.0-branch/arch/arm/core/cortex_m/fault.c#L807 - -#include -#include - -#include "memfault/core/compiler.h" -#include "memfault/core/reboot_reason_types.h" -#include "memfault/panics/arch/arm/cortex_m.h" -#include "memfault/panics/coredump.h" -#include "zephyr_release_specific_headers.h" - -void memfault_platform_reboot(void) { - const int reboot_type_unused = 0; // ignored for ARM - sys_reboot(reboot_type_unused); - MEMFAULT_UNREACHABLE; -} - -// By default, the Zephyr NMI handler is an infinite loop. Instead -// let's register the Memfault Exception Handler -static int prv_install_nmi_handler(struct device *dev) { - extern void z_NmiHandlerSet(void (*pHandler)(void)); - extern void NMI_Handler(void); - z_NmiHandlerSet(NMI_Handler); - return 0; -} - -SYS_INIT(prv_install_nmi_handler, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); - -void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { - sMfltRegState reg = { - .exception_frame = esf->exception_frame_addr, - .r4 = esf->callee_regs->r4, - .r5 = esf->callee_regs->r5, - .r6 = esf->callee_regs->r6, - .r7 = esf->callee_regs->r7, - .r8 = esf->callee_regs->r8, - .r9 = esf->callee_regs->r9, - .r10 = esf->callee_regs->r10, - .r11 = esf->callee_regs->r11, - .exc_return = esf->callee_regs->exc_return, - }; - memfault_fault_handler(®, kMfltRebootReason_HardFault); -} diff --git a/ports/zephyr/v2.0/zephyr-integration.patch b/ports/zephyr/v2.0/zephyr-integration.patch deleted file mode 100644 index cbb000429..000000000 --- a/ports/zephyr/v2.0/zephyr-integration.patch +++ /dev/null @@ -1,238 +0,0 @@ -From 3cdc23e13aea352856de5f0a794aeabda2c60870 Mon Sep 17 00:00:00 2001 -From: Memfault Inc -Date: Tue, 3 Dec 2019 23:35:57 -0500 -Subject: [PATCH] Integrate Memfault Firmware SDK into Zephyr RTOS - -* Add 'memfault' module to 'ext/lib/memfault'. To add the SDK, create - a 'memfault-firmware-sdk' directory to the folder & copy or - symlink a clone of https://github.com/memfault/memfault-firmware-sdk.git -* Extend Zephyr Fault Handler to store extra register as the exception - stack frame (using the CONFIG_EXTRA_EXCEPTION_INFO option) -* Add an option to skip clearing of fault status register information - during exception handling (CONFIG_PERSIST_CFSR_STATE). This information - can be useful for post-mortem fault analysis ---- - arch/arm/core/cortex_m/Kconfig | 17 +++++++++++- - arch/arm/core/cortex_m/fault.c | 26 ++++++++++++----- - arch/arm/core/fault_s.S | 13 ++++++++- - ext/Kconfig | 2 ++ - include/arch/arm/exc.h | 49 ++++++++++++++++++++++++++------- - 7 files changed, 91 insertions(+), 19 deletions(-) - -diff --git a/arch/arm/core/cortex_m/Kconfig b/arch/arm/core/cortex_m/Kconfig -index bd3516ecd0..410bda5fcc 100644 ---- a/arch/arm/core/cortex_m/Kconfig -+++ b/arch/arm/core/cortex_m/Kconfig -@@ -75,6 +75,21 @@ config CPU_CORTEX_M7 - - if CPU_CORTEX_M - -+config EXTRA_EXCEPTION_INFO -+ bool "Enable collection of callee saved register state" -+ help -+ Enables preserving callee saved registers as part of the -+ Exception Stack Frame (z_arch_esf_t) provided to fault handlers. -+ This can be helpful information to collect in order to recover -+ accurate backtraces for post-mortem analysis. -+ -+config PERSIST_CFSR_STATE -+ bool -+ help -+ Don't clear the Configurable Fault Status Register (CFSR) as -+ part of the kernel fault handling. This information can be useful -+ can be helpful to collect for post-mortem analysis. -+ - config CPU_CORTEX_M_HAS_SYSTICK - bool - # Omit prompt to signify "hidden" option -@@ -122,7 +137,7 @@ config CPU_CORTEX_M_HAS_SPLIM - - In an ARMv8-M Mainline implementation with the Security Extension - the MSPLIM, PSPLIM registers have additional Secure instances. -- In an ARMv8-M Baseline implementation with the Security Extension -+ In an ARMv8-M Baseline implementation with the Security Extension - the MSPLIM, PSPLIM registers have only Secure instances. - - config CPU_CORTEX_M_HAS_PROGRAMMABLE_FAULT_PRIOS -diff --git a/arch/arm/core/cortex_m/fault.c b/arch/arm/core/cortex_m/fault.c -index 7943248368..74d8d433eb 100644 ---- a/arch/arm/core/cortex_m/fault.c -+++ b/arch/arm/core/cortex_m/fault.c -@@ -239,8 +239,10 @@ static u32_t MpuFault(z_arch_esf_t *esf, int fromHardFault, bool *recoverable) - if ((SCB->CFSR & SCB_CFSR_MMARVALID_Msk) != 0) { - PR_EXC(" MMFAR Address: 0x%x", mmfar); - if (fromHardFault) { -+#if !defined(CONFIG_PERSIST_CFSR_STATE) - /* clear SCB_MMAR[VALID] to reset */ - SCB->CFSR &= ~SCB_CFSR_MMARVALID_Msk; -+#endif - } - } - } -@@ -315,10 +317,10 @@ static u32_t MpuFault(z_arch_esf_t *esf, int fromHardFault, bool *recoverable) - "Stacking error without stack guard / User-mode support\n"); - #endif /* CONFIG_MPU_STACK_GUARD || CONFIG_USERSPACE */ - } -- -+#if !defined(CONFIG_PERSIST_CFSR_STATE) - /* clear MMFSR sticky bits */ - SCB->CFSR |= SCB_CFSR_MEMFAULTSR_Msk; -- -+#endif - /* Assess whether system shall ignore/recover from this MPU fault. */ - *recoverable = memory_fault_recoverable(esf); - -@@ -361,7 +363,9 @@ static int BusFault(z_arch_esf_t *esf, int fromHardFault, bool *recoverable) - PR_EXC(" BFAR Address: 0x%x", bfar); - if (fromHardFault) { - /* clear SCB_CFSR_BFAR[VALID] to reset */ -+#if !defined(CONFIG_PERSIST_CFSR_STATE) - SCB->CFSR &= ~SCB_CFSR_BFARVALID_Msk; -+#endif - } - } - } -@@ -470,10 +474,10 @@ static int BusFault(z_arch_esf_t *esf, int fromHardFault, bool *recoverable) - SYSMPU->CESR &= ~sperr; - } - #endif /* defined(CONFIG_ARM_MPU) && defined(CONFIG_CPU_HAS_NXP_MPU) */ -- -+#if !defined(CONFIG_PERSIST_CFSR_STATE) - /* clear BFSR sticky bits */ - SCB->CFSR |= SCB_CFSR_BUSFAULTSR_Msk; -- -+#endif - *recoverable = memory_fault_recoverable(esf); - - return reason; -@@ -527,10 +531,10 @@ static u32_t UsageFault(const z_arch_esf_t *esf) - if ((SCB->CFSR & SCB_CFSR_UNDEFINSTR_Msk) != 0) { - PR_FAULT_INFO(" Attempt to execute undefined instruction"); - } -- -+#if !defined(CONFIG_PERSIST_CFSR_STATE) - /* clear UFSR sticky bits */ - SCB->CFSR |= SCB_CFSR_USGFAULTSR_Msk; -- -+#endif - return reason; - } - -@@ -804,12 +808,20 @@ static void SecureStackDump(const z_arch_esf_t *secure_esf) - * Note: exc_return argument shall only be used by the Fault handler if we are - * running a Secure Firmware. - */ --void _Fault(z_arch_esf_t *esf, u32_t exc_return) -+void _Fault(struct __esf *esfp, u32_t exc_return, struct __callee_saved_esf *callee_regs) - { - u32_t reason = K_ERR_CPU_EXCEPTION; - int fault = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk; - bool recoverable; - -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ z_arch_esf_t esf_copy = { 0 }; -+ memcpy(&esf_copy, esfp, sizeof(*esfp)); -+ z_arch_esf_t *esf = &esf_copy; -+ esf->exception_frame_addr = esfp; -+ esf->callee_regs = callee_regs; -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ -+ - #if defined(CONFIG_ARM_SECURE_FIRMWARE) - if ((exc_return & EXC_RETURN_INDICATOR_PREFIX) != - EXC_RETURN_INDICATOR_PREFIX) { -diff --git a/arch/arm/core/fault_s.S b/arch/arm/core/fault_s.S -index 785f49d01a..607b3c27d2 100644 ---- a/arch/arm/core/fault_s.S -+++ b/arch/arm/core/fault_s.S -@@ -158,7 +158,18 @@ _s_stack_frame_endif: - mov r1, lr - #endif /* CONFIG_ARM_SECURE_FIRMWARE || CONFIG_ARM_NONSECURE_FIRMWARE */ - push {r0, lr} -- bl _Fault -+ -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ /* push the callee-saved registers on the stack and pass them to _Fault -+ * as part of argument 2 -+ */ -+ push {r4-r11, lr} -+ mov r2, sp -+#endif -+ bl _Fault -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ pop {r4-r11, lr} -+#endif - - #if defined(CONFIG_CPU_CORTEX_M) - pop {r0, pc} -diff --git a/include/arch/arm/exc.h b/include/arch/arm/exc.h -index dfa1ad8e4a..79da2b2f0f 100644 ---- a/include/arch/arm/exc.h -+++ b/include/arch/arm/exc.h -@@ -44,17 +44,19 @@ GTEXT(z_ExcExit); - extern "C" { - #endif - -+struct __basic_sf { -+ sys_define_gpr_with_alias(a1, r0); -+ sys_define_gpr_with_alias(a2, r1); -+ sys_define_gpr_with_alias(a3, r2); -+ sys_define_gpr_with_alias(a4, r3); -+ sys_define_gpr_with_alias(ip, r12); -+ sys_define_gpr_with_alias(lr, r14); -+ sys_define_gpr_with_alias(pc, r15); -+ u32_t xpsr; -+}; -+ - struct __esf { -- struct __basic_sf { -- sys_define_gpr_with_alias(a1, r0); -- sys_define_gpr_with_alias(a2, r1); -- sys_define_gpr_with_alias(a3, r2); -- sys_define_gpr_with_alias(a4, r3); -- sys_define_gpr_with_alias(ip, r12); -- sys_define_gpr_with_alias(lr, r14); -- sys_define_gpr_with_alias(pc, r15); -- u32_t xpsr; -- } basic; -+ struct __basic_sf basic; - #if defined(CONFIG_FLOAT) && defined(CONFIG_FP_SHARING) - float s[16]; - u32_t fpscr; -@@ -62,7 +64,34 @@ struct __esf { - #endif - }; - -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+struct __callee_saved_esf { -+ uint32_t r4; -+ uint32_t r5; -+ uint32_t r6; -+ uint32_t r7; -+ uint32_t r8; -+ uint32_t r9; -+ uint32_t r10; -+ uint32_t r11; -+ uint32_t exc_return; -+}; -+ -+struct __extended_esf { -+ struct __basic_sf basic; -+#if defined(CONFIG_FLOAT) && defined(CONFIG_FP_SHARING) -+ float s[16]; -+ u32_t fpscr; -+ u32_t undefined; -+#endif -+ void *exception_frame_addr; -+ struct __callee_saved_esf *callee_regs; -+}; -+ -+typedef struct __extended_esf z_arch_esf_t; -+#else - typedef struct __esf z_arch_esf_t; -+#endif - - extern void z_ExcExit(void); - --- -2.21.0 - diff --git a/ports/zephyr/v2.0/zephyr_release_specific_headers.h b/ports/zephyr/v2.0/zephyr_release_specific_headers.h deleted file mode 100644 index 3a119132f..000000000 --- a/ports/zephyr/v2.0/zephyr_release_specific_headers.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -//! @file -//! -//! Copyright (c) Memfault, Inc. -//! See LICENSE for details -//! -//! @brief -//! Headers used in Zephyr porting files that have been relocated between releases - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -#ifdef __cplusplus -} -#endif diff --git a/ports/zephyr/v2.2_v2.3/coredump-support.patch b/ports/zephyr/v2.2_v2.3/coredump-support.patch deleted file mode 100644 index 7ce7ba9bb..000000000 --- a/ports/zephyr/v2.2_v2.3/coredump-support.patch +++ /dev/null @@ -1,268 +0,0 @@ -From 2a997541cc6f1d4bb231816cc23ebb820f2448a5 Mon Sep 17 00:00:00 2001 -From: Memfault Inc -Date: Thu, 21 Jan 2021 00:03:55 -0500 -Subject: [PATCH] Backport of "arch: arm: Collect full register state in - Cortex-M Exception Stack Frame" - -This patch is intended for use on Zephyr 2.2 & 2.3 - -See the following commit for more details: -https://github.com/zephyrproject-rtos/zephyr/pull/27496/commits/5c1ea3f96b496a82cea89053eb4d2aedef1cde88 ---- - arch/Kconfig | 15 ++++++++ - arch/arm/core/aarch32/Kconfig | 2 ++ - arch/arm/core/aarch32/cortex_m/fault.c | 17 +++++++++- - arch/arm/core/aarch32/fatal.c | 25 ++++++++++++++ - arch/arm/core/aarch32/fault_s.S | 47 +++++++++++++++----------- - arch/nios2/Kconfig | 1 + - include/arch/arm/aarch32/exc.h | 17 ++++++++++ - 7 files changed, 103 insertions(+), 21 deletions(-) - -diff --git a/arch/Kconfig b/arch/Kconfig -index 049053ba3b..c7c4012be0 100644 ---- a/arch/Kconfig -+++ b/arch/Kconfig -@@ -321,6 +321,15 @@ config IRQ_OFFLOAD - run in interrupt context. Only useful for test cases that need - to validate the correctness of kernel objects in IRQ context. - -+ -+config EXTRA_EXCEPTION_INFO -+ bool "Collect extra exception info" -+ depends on ARCH_HAS_EXTRA_EXCEPTION_INFO -+ help -+ This option enables the collection of extra information, such as -+ register state, when a fault occurs. This information can be useful -+ to collect for post-mortem analysis and debug of issues. -+ - endmenu # Interrupt configuration - - endmenu -@@ -350,6 +359,12 @@ config ARCH_HAS_RAMFUNC_SUPPORT - config ARCH_HAS_NESTED_EXCEPTION_DETECTION - bool - -+config ARCH_SUPPORTS_COREDUMP -+ bool -+ -+config ARCH_HAS_EXTRA_EXCEPTION_INFO -+ bool -+ - # - # Other architecture related options - # -diff --git a/arch/arm/core/aarch32/Kconfig b/arch/arm/core/aarch32/Kconfig -index 18368038f3..e3e428c9c7 100644 ---- a/arch/arm/core/aarch32/Kconfig -+++ b/arch/arm/core/aarch32/Kconfig -@@ -22,6 +22,8 @@ config CPU_CORTEX_M - select ARCH_HAS_RAMFUNC_SUPPORT - select ARCH_HAS_NESTED_EXCEPTION_DETECTION - select SWAP_NONATOMIC -+ select ARCH_HAS_EXTRA_EXCEPTION_INFO -+ imply XIP - help - This option signifies the use of a CPU of the Cortex-M family. - -diff --git a/arch/arm/core/aarch32/cortex_m/fault.c b/arch/arm/core/aarch32/cortex_m/fault.c -index 117eb0257c..31822114ae 100644 ---- a/arch/arm/core/aarch32/cortex_m/fault.c -+++ b/arch/arm/core/aarch32/cortex_m/fault.c -@@ -920,9 +920,11 @@ static inline z_arch_esf_t *get_esf(u32_t msp, u32_t psp, u32_t exc_return, - * @param msp MSP value immediately after the exception occurred - * @param psp PSP value immediately after the exception occurred - * @param exc_return EXC_RETURN value present in LR after exception entry. -+ * @param callee_regs Callee-saved registers (R4-R11, PSP) - * - */ --void z_arm_fault(u32_t msp, u32_t psp, u32_t exc_return) -+void z_arm_fault(u32_t msp, u32_t psp, u32_t exc_return, -+ _callee_saved_t *callee_regs) - { - u32_t reason = K_ERR_CPU_EXCEPTION; - int fault = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk; -@@ -950,7 +952,20 @@ void z_arm_fault(u32_t msp, u32_t psp, u32_t exc_return) - } - - /* Copy ESF */ -+#if !defined(CONFIG_EXTRA_EXCEPTION_INFO) - memcpy(&esf_copy, esf, sizeof(z_arch_esf_t)); -+ ARG_UNUSED(callee_regs); -+#else -+ /* the extra exception info is not present in the original esf -+ * so we only copy the fields before those. -+ */ -+ memcpy(&esf_copy, esf, offsetof(z_arch_esf_t, extra_info)); -+ esf_copy.extra_info = (struct __extra_esf_info) { -+ .callee = callee_regs, -+ .exc_return = exc_return, -+ .msp = msp -+ }; -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ - - /* Overwrite stacked IPSR to mark a nested exception, - * or a return to Thread mode. Note that this may be -diff --git a/arch/arm/core/aarch32/fatal.c b/arch/arm/core/aarch32/fatal.c -index dde9efe6f6..297a383b90 100644 ---- a/arch/arm/core/aarch32/fatal.c -+++ b/arch/arm/core/aarch32/fatal.c -@@ -34,6 +34,18 @@ static void esf_dump(const z_arch_esf_t *esf) - } - LOG_ERR("fpscr: 0x%08x", esf->fpscr); - #endif -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ const struct _callee_saved *callee = esf->extra_info.callee; -+ -+ if (callee != NULL) { -+ LOG_ERR("r4/v1: 0x%08x r5/v2: 0x%08x r6/v3: 0x%08x", -+ callee->v1, callee->v2, callee->v3); -+ LOG_ERR("r7/v4: 0x%08x r8/v5: 0x%08x r9/v6: 0x%08x", -+ callee->v4, callee->v5, callee->v6); -+ LOG_ERR("r10/v7: 0x%08x r11/v8: 0x%08x psp: 0x%08x", -+ callee->v7, callee->v8, callee->psp); -+ } -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ - LOG_ERR("Faulting instruction address (r15/pc): 0x%08x", - esf->basic.pc); - } -@@ -83,7 +95,20 @@ void z_do_kernel_oops(const z_arch_esf_t *esf) - } - - #endif /* CONFIG_USERSPACE */ -+ -+#if !defined(CONFIG_EXTRA_EXCEPTION_INFO) - z_arm_fatal_error(reason, esf); -+#else -+ /* extra exception info is not collected for kernel oops -+ * path today so we make a copy of the ESF and zero out -+ * that information -+ */ -+ z_arch_esf_t esf_copy; -+ -+ memcpy(&esf_copy, esf, offsetof(z_arch_esf_t, extra_info)); -+ esf_copy.extra_info = (struct __extra_esf_info) { 0 }; -+ z_arm_fatal_error(reason, &esf_copy); -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ - } - - FUNC_NORETURN void arch_syscall_oops(void *ssf_ptr) -diff --git a/arch/arm/core/aarch32/fault_s.S b/arch/arm/core/aarch32/fault_s.S -index 4bdd766f67..45fabe06f0 100644 ---- a/arch/arm/core/aarch32/fault_s.S -+++ b/arch/arm/core/aarch32/fault_s.S -@@ -50,6 +50,7 @@ GTEXT(z_arm_reserved) - * - the MSP - * - the PSP - * - the EXC_RETURN value -+ * - callee saved register state (r4-r11, psp) - * as parameters to the z_arm_fault() C function that will perform the - * rest of the fault handling (i.e. z_arm_fault(MSP, PSP, EXC_RETURN)). - * -@@ -87,31 +88,37 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,z_arm_data_abort) - #endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ - SECTION_SUBSEC_FUNC(TEXT,__fault,z_arm_reserved) - --#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) || \ -- defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) - mrs r0, MSP - mrs r1, PSP -- mov r2, lr /* EXC_RETURN */ -- - push {r0, lr} --#elif defined(CONFIG_ARMV7_R) -- /* -- * Pass null for the esf to z_arm_fault for now. A future PR will add -- * better exception debug for Cortex-R that subsumes what esf -- * provides. -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ /* Build _callee_saved_t. To match the struct -+ * definition we push the psp & then r11-r4 - */ -- mov r0, #0 --#else --#error Unknown ARM architecture --#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE || CONFIG_ARMv7_M_ARMV8_M_MAINLINE */ -- -+ push { r1, r2 } -+#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) -+ mov r3, r11 -+ mov r2, r10 -+ push {r2, r3} -+ mov r3, r9 -+ mov r2, r8 -+ push {r2, r3} -+ push {r4-r7} -+#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) -+ push {r4-r11} -+#endif -+ mov r3, sp /* pointer to _callee_saved_t */ -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ -+ mov r2, lr /* EXC_RETURN */ - bl z_arm_fault -- --#if defined(CONFIG_CPU_CORTEX_M) -- pop {r0, pc} --#elif defined(CONFIG_CPU_CORTEX_R) -- pop {r0, lr} -- subs pc, lr, #8 -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ /* We do not need to restore any register state here -+ * because we did not use any callee-saved registers -+ * in this routine. Therefore, we can just reset -+ * the MSP to its value prior to entering the function -+ */ -+ add sp, #40 - #endif -+ pop {r0, pc} - - .end -diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig -index f64fd7ad71..3528bc24cb 100644 ---- a/arch/nios2/Kconfig -+++ b/arch/nios2/Kconfig -@@ -14,6 +14,7 @@ config CPU_NIOS2_GEN2 - bool - default y - select BUILD_OUTPUT_HEX -+ select ARCH_HAS_EXTRA_EXCEPTION_INFO - help - This option signifies the use of a Nios II Gen 2 CPU - -diff --git a/include/arch/arm/aarch32/exc.h b/include/arch/arm/aarch32/exc.h -index 821654fede..7e39bd091e 100644 ---- a/include/arch/arm/aarch32/exc.h -+++ b/include/arch/arm/aarch32/exc.h -@@ -44,6 +44,20 @@ GTEXT(z_arm_exc_exit); - extern "C" { - #endif - -+/* Additional register state that is not stacked by hardware on exception -+ * entry. -+ * -+ * These fields are ONLY valid in the ESF copy passed into z_arm_fatal_error(). -+ * When information for a member is unavailable, the field is set to zero. -+ */ -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+struct __extra_esf_info { -+ _callee_saved_t *callee; -+ uint32_t msp; -+ uint32_t exc_return; -+}; -+#endif /* CONFIG_EXTRA_EXCEPTION_INFO */ -+ - struct __esf { - struct __basic_sf { - sys_define_gpr_with_alias(a1, r0); -@@ -60,6 +74,9 @@ struct __esf { - u32_t fpscr; - u32_t undefined; - #endif -+#if defined(CONFIG_EXTRA_EXCEPTION_INFO) -+ struct __extra_esf_info extra_info; -+#endif - }; - - typedef struct __esf z_arch_esf_t; --- -2.28.0 - diff --git a/ports/zephyr/v2.4/zephyr_release_specific_headers.h b/ports/zephyr/v2.4/zephyr_release_specific_headers.h deleted file mode 100644 index 34c442108..000000000 --- a/ports/zephyr/v2.4/zephyr_release_specific_headers.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -//! @file -//! -//! Copyright (c) Memfault, Inc. -//! See LICENSE for details -//! -//! @brief -//! Headers used in Zephyr porting files that have been relocated between releases - -#ifdef __cplusplus -extern "C" { -#endif - -// clang-format off -#include MEMFAULT_ZEPHYR_INCLUDE(sys/__assert.h) -#include MEMFAULT_ZEPHYR_INCLUDE(sys/printk.h) -// clang-format on - -#ifdef __cplusplus -} -#endif diff --git a/scripts/mflt-build-id/Dockerfile.py27 b/scripts/mflt-build-id/Dockerfile.py27 deleted file mode 100644 index 3e23be37a..000000000 --- a/scripts/mflt-build-id/Dockerfile.py27 +++ /dev/null @@ -1,8 +0,0 @@ -FROM python:2.7-slim AS base - -RUN pip install enum34==1.1.10 -RUN pip install snapshottest==0.5.0 -RUN pip install pyelftools==0.27 -RUN pip install pytest==4.6.11 - -COPY ./ /mflt-build-id/ diff --git a/scripts/mflt-build-id/pyproject.toml b/scripts/mflt-build-id/pyproject.toml index 03ed55641..dc6e9778b 100644 --- a/scripts/mflt-build-id/pyproject.toml +++ b/scripts/mflt-build-id/pyproject.toml @@ -4,7 +4,7 @@ version = "1.0.2" dependencies = [ "pyelftools >=0.31,<0.32", ] -requires-python = ">=3.6,<4.0" +requires-python = ">=3.8,<4.0" [tool.uv] dev-dependencies = [ diff --git a/scripts/mflt-build-id/ruff.toml b/scripts/mflt-build-id/ruff.toml index fdfddb9af..55e050beb 100644 --- a/scripts/mflt-build-id/ruff.toml +++ b/scripts/mflt-build-id/ruff.toml @@ -1,3 +1,2 @@ extend = "../../ruff.toml" -target-version = "py37" # ruff does not support 3.6 -lint.extend-ignore = ["UP"] +target-version = "py38" diff --git a/scripts/mflt-build-id/src/mflt_build_id/__init__.py b/scripts/mflt-build-id/src/mflt_build_id/__init__.py index 599131904..1d38dbb65 100644 --- a/scripts/mflt-build-id/src/mflt_build_id/__init__.py +++ b/scripts/mflt-build-id/src/mflt_build_id/__init__.py @@ -3,8 +3,6 @@ # See LICENSE for details # -# pyright: reportTypeCommentUsage=false -# This file needs to be Python 2.7 compatible (i.e. type annotations must remain in comments). # Note: The snippet should be kept in sync with the "Example Usage" section of the README """Inspects provided ELF for a Build ID and when missing adds one if possible. @@ -30,29 +28,22 @@ https://mflt.io//symbol-file-build-ids """ +from __future__ import annotations + import argparse import hashlib import struct -import sys import zlib from collections import defaultdict from enum import Enum - -try: - # Lint rule disabled because it doesn't notice the `type:` comments - from typing import IO, Any, Dict, List, Optional, Tuple # noqa: F401 -except ImportError: - if sys.version_info >= (3, 5): - raise +from typing import IO try: from elftools.elf.constants import SH_FLAGS from elftools.elf.elffile import ELFFile - - # Lint rule disabled because it doesn't notice the `type:` comments - from elftools.elf.sections import NoteSection, Section, Symbol, SymbolTableSection # noqa: F401 + from elftools.elf.sections import NoteSection, Section, Symbol, SymbolTableSection except ImportError: - raise ImportError( # (no raise-from in Python 2.7) + raise ImportError( """ Script depends on pyelftools. Add it to your requirements.txt or run: $ pip install pyelftools @@ -60,17 +51,8 @@ ) -if sys.version_info < (3,): - - def hexlify(data): - # type: (str) -> str - return data.encode("hex") - -else: - - def hexlify(data): - # type: (bytes) -> str - return data.hex() +def hexlify(data: bytes) -> str: + return data.hex() SHA1_BUILD_ID_SIZE_BYTES = 20 @@ -84,24 +66,23 @@ class SectionType(Enum): class ELFFileHelper: - def __init__(self, elf): - # type: (ELFFile) -> None + def __init__(self, elf: ELFFile) -> None: """ :param elf: An elftools.elf.elffile.ELFFile instance """ self.elf = elf - self._symtab = None # type: Optional[SymbolTableSection] + self._symtab: SymbolTableSection | None = None @staticmethod - def section_in_binary(section): - # type: (Section) -> bool + def section_in_binary(section: Section) -> bool: # Only allocated sections make it into the actual binary sh_flags = section["sh_flags"] return sh_flags & SH_FLAGS.SHF_ALLOC != 0 @staticmethod - def build_symbol_by_name_cache(symtab, little_endian): - # type: (SymbolTableSection, bool) -> Dict[Optional[str], List[int]] + def build_symbol_by_name_cache( + symtab: SymbolTableSection, little_endian: bool + ) -> dict[str | None, list[int]]: # An optimized imlementation for building a cache for quick symbol info lookups # # Replacing implementation here: @@ -121,8 +102,7 @@ def build_symbol_by_name_cache(symtab, little_endian): stringtable_data = symtab.stringtable.data() - def _get_string(start_offset): - # type: (int) -> Optional[str] + def _get_string(start_offset: int) -> str | None: end_offset = stringtable_data.find(b"\x00", start_offset) if end_offset == -1: return None @@ -136,15 +116,14 @@ def _get_string(start_offset): symtab_entry_data = symtab_data[entry_offset : entry_offset + 4] endianess_prefix = "<" if little_endian else ">" - st_name = struct.unpack("{}I".format(endianess_prefix), symtab_entry_data)[0] + st_name = struct.unpack(f"{endianess_prefix}I", symtab_entry_data)[0] name = _get_string(st_name) symbol_name_map[name].append(idx) return symbol_name_map @property - def symtab(self): - # type: () -> Optional[SymbolTableSection] + def symtab(self) -> SymbolTableSection | None: # Cache the SymbolTableSection, to avoid re-parsing if self._symtab: return self._symtab @@ -160,8 +139,7 @@ def symtab(self): return self._symtab - def find_symbol_and_section(self, symbol_name): - # type: (str) -> Tuple[Optional[Symbol], Optional[Section]] + def find_symbol_and_section(self, symbol_name: str) -> tuple[Symbol | None, Section | None]: symtab = self.symtab if symtab is None: return None, None @@ -176,11 +154,10 @@ def find_symbol_and_section(self, symbol_name): section = self.find_section_for_address_range((symbol_start, symbol_start + symbol_size)) if section is None: - raise BuildIdError("Could not locate a section with symbol {}".format(symbol_name)) + raise BuildIdError(f"Could not locate a section with symbol {symbol_name}") return symbol, section - def find_section_for_address_range(self, addr_range): - # type: (Tuple[int, int]) -> Optional[NoteSection] + def find_section_for_address_range(self, addr_range: tuple[int, int]) -> NoteSection | None: for section in self.elf.iter_sections(): if not ELFFileHelper.section_in_binary(section): continue @@ -202,18 +179,15 @@ def find_section_for_address_range(self, addr_range): return None @staticmethod - def get_symbol_offset_in_sector(symbol, section): - # type: (Symbol, Section) -> int + def get_symbol_offset_in_sector(symbol: Symbol, section: Section) -> int: return symbol["st_value"] - section["sh_addr"] - def get_symbol_data(self, symbol, section): - # type: (Any, Section) -> bytes + def get_symbol_data(self, symbol: Symbol, section: Section) -> bytes: offset_in_section = self.get_symbol_offset_in_sector(symbol, section) symbol_size = symbol["st_size"] return section.data()[offset_in_section : offset_in_section + symbol_size] - def get_section_type(self, section): - # type: (Section) -> SectionType + def get_section_type(self, section: Section) -> SectionType: if not self.section_in_binary(section): return SectionType.UNALLOCATED @@ -239,8 +213,12 @@ class BuildIdError(Exception): class BuildIdInspectorAndPatcher: - def __init__(self, elf_file, elf=None, elf_helper=None): - # type: (IO[bytes], Optional[ELFFile], Optional[ELFFileHelper]) -> None + def __init__( + self, + elf_file: IO[bytes], + elf: ELFFile | None = None, + elf_helper: ELFFileHelper | None = None, + ) -> None: """ :param elf_file: file object with the ELF to inspect and/or patch :param elf: optional, already instantiated ELFFile @@ -250,8 +228,9 @@ def __init__(self, elf_file, elf=None, elf_helper=None): self.elf = (elf_helper.elf if elf_helper else elf) or ELFFile(elf_file) self._helper = elf_helper or ELFFileHelper(self.elf) - def _generate_build_id(self, sha1_symbol_section=None, sha1_symbol_section_offset=0): - # type: (Optional[Section], int) -> hashlib._Hash + def _generate_build_id( + self, sha1_symbol_section: Section | None = None, sha1_symbol_section_offset: int = 0 + ) -> hashlib._Hash: build_id = hashlib.sha1() for section in self.elf.iter_sections(): if not self._helper.section_in_binary(section): @@ -280,14 +259,12 @@ def _generate_build_id(self, sha1_symbol_section=None, sha1_symbol_section_offse return build_id - def _get_build_id(self): - # type: () -> Optional[str] + def _get_build_id(self) -> str | None: def _get_note_sections(elf): for section in elf.iter_sections(): if not isinstance(section, NoteSection): continue - for note in section.iter_notes(): - yield note + yield from section.iter_notes() for note in _get_note_sections(self.elf): if note.n_type == "NT_GNU_BUILD_ID": @@ -298,12 +275,10 @@ def _get_note_sections(elf): def check_or_update_sha1_build_id(self, sha1_sym_name, dump_only): symbol, section = self._helper.find_symbol_and_section(sha1_sym_name) if symbol is None or section is None: - raise BuildIdError("Could not locate '{}' symbol in provided ELF".format(sha1_sym_name)) + raise BuildIdError(f"Could not locate '{sha1_sym_name}' symbol in provided ELF") if symbol["st_size"] != SHA1_BUILD_ID_SIZE_BYTES: - raise BuildIdError( - "A build ID should be {} bytes in size".format(SHA1_BUILD_ID_SIZE_BYTES) - ) + raise BuildIdError(f"A build ID should be {SHA1_BUILD_ID_SIZE_BYTES} bytes in size") current_sha1_bytes = bytearray(self._helper.get_symbol_data(symbol, section)) @@ -315,15 +290,11 @@ def check_or_update_sha1_build_id(self, sha1_sym_name, dump_only): sha1_hash_bytes = sha1_hash.digest() if current_sha1_bytes == sha1_hash_bytes: - print( - "Memfault Generated SHA1 Build ID at '{}': {}".format( - sha1_sym_name, sha1_hash.hexdigest() - ) - ) + print(f"Memfault Generated SHA1 Build ID at '{sha1_sym_name}': {sha1_hash.hexdigest()}") return sha1_hash if dump_only: - print("Memfault Build ID at '{}' is not written".format(sha1_sym_name)) + print(f"Memfault Build ID at '{sha1_sym_name}' is not written") return sha1_hash with open(self.elf_file.name, "r+b") as fh: @@ -334,19 +305,18 @@ def check_or_update_sha1_build_id(self, sha1_sym_name, dump_only): fh.seek(derived_id_patch_offset) fh.write(sha1_hash_bytes) - print("Added Memfault Generated SHA1 Build ID to ELF: {}".format(sha1_hash.hexdigest())) + print(f"Added Memfault Generated SHA1 Build ID to ELF: {sha1_hash.hexdigest()}") # Return the sha1 computed in case someone wants to run this as a library return sha1_hash - def _write_and_return_build_info(self, dump_only): - # type: (bool) -> Tuple[MemfaultBuildIdTypes, Optional[str], Optional[int]] + def _write_and_return_build_info( + self, dump_only: bool + ) -> tuple[MemfaultBuildIdTypes, str | None, int | None]: sdk_build_id_sym_name = "g_memfault_build_id" symbol, section = self._helper.find_symbol_and_section(sdk_build_id_sym_name) if symbol is None or section is None: - raise BuildIdError( - "Could not locate '{}' symbol in provided ELF".format(sdk_build_id_sym_name) - ) + raise BuildIdError(f"Could not locate '{sdk_build_id_sym_name}' symbol in provided ELF") gnu_build_id = self._get_build_id() @@ -370,9 +340,7 @@ def _write_and_return_build_info(self, dump_only): derived_sym_name = "g_memfault_sdk_derived_build_id" sdk_build_id, sdk_build_id_section = self._helper.find_symbol_and_section(derived_sym_name) if sdk_build_id is None or sdk_build_id_section is None: - raise BuildIdError( - "Could not locate '{}' symbol in provided elf".format(derived_sym_name) - ) + raise BuildIdError(f"Could not locate '{derived_sym_name}' symbol in provided elf") if build_id_type == MemfaultBuildIdTypes.MEMFAULT_BUILD_ID_SHA1.value: build_id_bytes = self._helper.get_symbol_data(sdk_build_id, sdk_build_id_section) @@ -382,7 +350,7 @@ def _write_and_return_build_info(self, dump_only): print("WARNING: Located a GNU build id but it's not being used by the Memfault SDK") if build_id_type != MemfaultBuildIdTypes.NONE.value: - raise BuildIdError("Unrecognized Build Id Type '{}'".format(build_id_type)) + raise BuildIdError(f"Unrecognized Build Id Type '{build_id_type}'") if dump_only: return MemfaultBuildIdTypes.NONE, None, None @@ -405,19 +373,17 @@ def _write_and_return_build_info(self, dump_only): fh.write(build_id_hash.digest()) build_id = build_id_hash.hexdigest() - print("Added Memfault Generated Build ID to ELF: {}".format(build_id)) + print(f"Added Memfault Generated Build ID to ELF: {build_id}") return MemfaultBuildIdTypes.MEMFAULT_BUILD_ID_SHA1, build_id, short_len - def check_or_update_build_id(self): - # type: () -> None + def check_or_update_build_id(self) -> None: build_type, build_id, _ = self._write_and_return_build_info(dump_only=False) if build_type == MemfaultBuildIdTypes.GNU_BUILD_ID_SHA1: - print("Found GNU Build ID: {}".format(build_id)) + print(f"Found GNU Build ID: {build_id}") elif build_type == MemfaultBuildIdTypes.MEMFAULT_BUILD_ID_SHA1: - print("Found Memfault Build Id: {}".format(build_id)) + print(f"Found Memfault Build Id: {build_id}") - def dump_build_info(self, num_chars): - # type: (int) -> None + def dump_build_info(self, num_chars: int) -> None: build_type, build_id, _ = self._write_and_return_build_info(dump_only=True) if build_type is None or build_id is None: raise BuildIdError("No Build ID Found") @@ -425,9 +391,11 @@ def dump_build_info(self, num_chars): print(build_id[:num_chars]) def _generate_crc32_build_id( - self, crc32_symbol_section, crc32_symbol_section_offset, crc32_symbol_length=4 - ): - # type: (Section, int, int) -> int + self, + crc32_symbol_section: Section, + crc32_symbol_section_offset: int, + crc32_symbol_length: int = 4, + ) -> int: crc32 = 0 for section in self.elf.iter_sections(): @@ -458,19 +426,14 @@ def _generate_crc32_build_id( return crc32 - def check_or_update_crc_build_id(self, crc_symbol_name, dump_only=False): - # type: (str, bool) -> None + def check_or_update_crc_build_id(self, crc_symbol_name: str, dump_only: bool = False) -> None: symbol, section = self._helper.find_symbol_and_section(crc_symbol_name) if symbol is None or section is None: - raise BuildIdError( - "Could not locate '{}' CRC symbol in provided ELF".format(crc_symbol_name) - ) + raise BuildIdError(f"Could not locate '{crc_symbol_name}' CRC symbol in provided ELF") sec_type = self._helper.get_section_type(section) if sec_type not in {SectionType.TEXT, SectionType.DATA}: - raise BuildIdError( - "CRC symbol '{}' in invalid Section '{}'".format(crc_symbol_name, sec_type) - ) + raise BuildIdError(f"CRC symbol '{crc_symbol_name}' in invalid Section '{sec_type}'") section_offset = section["sh_offset"] symbol_offset = self._helper.get_symbol_offset_in_sector(symbol, section) @@ -480,23 +443,21 @@ def check_or_update_crc_build_id(self, crc_symbol_name, dump_only=False): endianess_prefix = "<" if self.elf.little_endian else ">" current_crc_build_id = struct.unpack( - "{}I".format(endianess_prefix), + f"{endianess_prefix}I", section.data()[symbol_offset : symbol_offset + symbol_length], )[0] if current_crc_build_id == crc_build_id: print( - "CRC32 Generated Build ID at '{}' to ELF already written: {}".format( - crc_symbol_name, hex(crc_build_id) - ) + f"CRC32 Generated Build ID at '{crc_symbol_name}' to ELF already written: {hex(crc_build_id)}" ) return if dump_only: - print("CRC32 Build ID at '{}' is not written".format(crc_symbol_name)) + print(f"CRC32 Build ID at '{crc_symbol_name}' is not written") return - crc_build_id_bytes = struct.pack("{}I".format(endianess_prefix), crc_build_id) + crc_build_id_bytes = struct.pack(f"{endianess_prefix}I", crc_build_id) with open(self.elf_file.name, "r+b") as fh: derived_id_patch_offset = section_offset + symbol_offset @@ -504,22 +465,16 @@ def check_or_update_crc_build_id(self, crc_symbol_name, dump_only=False): fh.seek(derived_id_patch_offset) fh.write(crc_build_id_bytes) - print( - "Added CRC32 Generated Build ID at '{}' to ELF: {}".format( - crc_symbol_name, hex(crc_build_id) - ) - ) + print(f"Added CRC32 Generated Build ID at '{crc_symbol_name}' to ELF: {hex(crc_build_id)}") - def get_build_info(self): - # type: () -> Tuple[Optional[MemfaultBuildIdTypes], Optional[str], Optional[int]] + def get_build_info(self) -> tuple[MemfaultBuildIdTypes | None, str | None, int | None]: try: return self._write_and_return_build_info(dump_only=True) except BuildIdError: return None, None, None -def main(): - # type: () -> None +def main() -> None: parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter ) diff --git a/scripts/mflt-build-id/tests_mflt_build_id/test_fw_build_id.py b/scripts/mflt-build-id/tests_mflt_build_id/test_fw_build_id.py index 7fb2e6dab..089e5dfaf 100644 --- a/scripts/mflt-build-id/tests_mflt_build_id/test_fw_build_id.py +++ b/scripts/mflt-build-id/tests_mflt_build_id/test_fw_build_id.py @@ -26,12 +26,12 @@ @pytest.fixture() def copy_file(tmp_path): """Copies a file into the tests tmp path""" - idx = [0] + idx = 0 def _copy_file(src): - # NB: Python 2.7 does not support `nonlocal` - tmp_name = str(tmp_path / "file_{}.bin".format(idx[0])) - idx[0] += 1 + nonlocal idx + tmp_name = str(tmp_path / f"file_{idx}.bin") + idx += 1 shutil.copy2(src, tmp_name) return tmp_name diff --git a/scripts/mflt-build-id/uv.lock b/scripts/mflt-build-id/uv.lock index 46982887f..6b05acd50 100644 --- a/scripts/mflt-build-id/uv.lock +++ b/scripts/mflt-build-id/uv.lock @@ -1,5 +1,5 @@ version = 1 -requires-python = ">=3.6, <4.0" +requires-python = ">=3.8, <4.0" [[package]] name = "atomicwrites" @@ -43,24 +43,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c2/c1/7d7c1960b80383f24adb010b5957d8e6847621125dae8d8393172ed3de13/coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76", size = 179500 }, { url = "https://files.pythonhosted.org/packages/d3/e8/af92f858651a3a819e16e6490a11299f3ea03ac16df5f5cae47b54f037ca/coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47", size = 217729 }, { url = "https://files.pythonhosted.org/packages/06/1a/06ebf8430a2d821f022ab3fb57905df075f10e03612b9e2306d306addf92/coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64", size = 183391 }, - { url = "https://files.pythonhosted.org/packages/17/38/14ec6016feaa0202545b133c4bb4a5595af9cffada24dbf7c2761d1529d4/coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9", size = 179365 }, - { url = "https://files.pythonhosted.org/packages/17/ac/2e8792ded5b0b3998bc66b13899404a512d4ce648618b21b0cfdaf2c1cf6/coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d", size = 204640 }, - { url = "https://files.pythonhosted.org/packages/e2/76/5f3341b4ecf72235fe5566633a37e4eb9c89e54ead015f27820d43ea090d/coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48", size = 211420 }, - { url = "https://files.pythonhosted.org/packages/4c/0b/731e558a762ed89478e2badc388443b30ebb0a975c335ae60e24b52d5b00/coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e", size = 212418 }, - { url = "https://files.pythonhosted.org/packages/f9/04/ea1a10745ce27ea7a18dc194fa07f5ffd80af451ed4d8adf8f1a8478cf36/coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d", size = 209355 }, - { url = "https://files.pythonhosted.org/packages/ed/3a/03b1563377c52e05945bc815073991a973dd4229f35cb0ec98e4c3b8e4d7/coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17", size = 207954 }, - { url = "https://files.pythonhosted.org/packages/bd/b1/769cc9481d5ed1b3dc8d92e218d281d90b92591242086daadc379779e2e4/coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781", size = 209005 }, - { url = "https://files.pythonhosted.org/packages/6b/29/fc44d8a5c7283091520d65140bafdfbd12524e07791609bef2b89eb16ecd/coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a", size = 182236 }, - { url = "https://files.pythonhosted.org/packages/3d/5d/c067d19e5ee1c39962d199b43568091d0f66c9913d625dee300c84764fbf/coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0", size = 183241 }, - { url = "https://files.pythonhosted.org/packages/24/c0/8bf5b0419956049211dc1608fc4de8bf961172f5455780a530c658700c43/coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49", size = 179365 }, - { url = "https://files.pythonhosted.org/packages/ff/a0/96d3a58fdb2413b45e3593bf7f57ea0801ae97f6bd4ebc4fe2e013b96241/coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521", size = 204641 }, - { url = "https://files.pythonhosted.org/packages/c9/01/2766873baf557339edd406f17b8c3b3821f42b31cc83ee83b76019337ccd/coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884", size = 212959 }, - { url = "https://files.pythonhosted.org/packages/e2/78/e7f9f4e28c237f3909ed184939cae5bf19e6507459185286afcacb730578/coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa", size = 213925 }, - { url = "https://files.pythonhosted.org/packages/a8/ef/c4bbf88286a230f1ad6e239680639fc49bfe5fe6feea376232ca780d11da/coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64", size = 210811 }, - { url = "https://files.pythonhosted.org/packages/e0/f3/5f32d19a0fffba7b0b40123e96162665cc4470aa718dfa56af59a7763618/coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617", size = 209344 }, - { url = "https://files.pythonhosted.org/packages/22/97/123a4f218f1c809c7b875eb5d91a00a2692cfc4a0c1c5f98a6d943b39e8f/coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8", size = 210429 }, - { url = "https://files.pythonhosted.org/packages/c6/c0/dba91b86fd52388a2c6ede945eea298cd0fc913a2f2d277cd8c35617ec30/coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4", size = 182238 }, - { url = "https://files.pythonhosted.org/packages/87/f5/8cc271f3eac0bed756d8ba4d6fcc1aa9671a40258d72823c17f2f1645c64/coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74", size = 183245 }, { url = "https://files.pythonhosted.org/packages/5c/49/8be88365773b7fd016fd315ac4a76f8296c92840616fc5992b97744d3f0f/coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e", size = 179535 }, { url = "https://files.pythonhosted.org/packages/56/19/284085d9f5283bba6740a83b75f062796fba7641e13cc7958396a0ca5ad8/coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58", size = 209059 }, { url = "https://files.pythonhosted.org/packages/09/76/05ab224f30332fa81942c08510d52ec7d8bcf63b2144184e446b3f0e9cfa/coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc", size = 215770 }, @@ -79,19 +61,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/58/d5/45d0c648099b9a2da324cd9a92f55cd2e93dca1285cec781d86c5610a125/coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de", size = 172056 }, ] -[[package]] -name = "importlib-metadata" -version = "4.8.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.8'" }, - { name = "zipp", marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/ed/e65128cc5cb1580f22ee3009d9187ecdfcc43ffb3b581fe854b24e87d8e7/importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668", size = 41979 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/a1/b153a0a4caf7a7e3f15c2cd56c7702e2cf3d89b1b359d1f1c5e59d68f4ce/importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e", size = 17978 }, -] - [[package]] name = "iniconfig" version = "1.1.1" @@ -144,9 +113,6 @@ wheels = [ name = "pluggy" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, -] sdist = { url = "https://files.pythonhosted.org/packages/a1/16/db2d7de3474b6e37cbb9c008965ee63835bba517e22cdb8c35b5116b5ce1/pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", size = 51510 } wheels = [ { url = "https://files.pythonhosted.org/packages/9e/01/f38e2ff29715251cf25532b9082a1589ab7e4f571ced434f98d0139336dc/pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3", size = 13667 }, @@ -187,7 +153,6 @@ dependencies = [ { name = "atomicwrites", marker = "sys_platform == 'win32'" }, { name = "attrs" }, { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, @@ -234,32 +199,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, ] -[[package]] -name = "typing-extensions" -version = "4.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b1/5a/8b5fbb891ef3f81fc923bf3cb4a578c0abf9471eb50ce0f51c74212182ab/typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42", size = 26694 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/6b/44f7f8f1e110027cf88956b59f2fad776cca7e1704396d043f89effd3a0e/typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2", size = 26844 }, -] - [[package]] name = "wasmer" version = "0.3.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/d1/43eedf0c73e9b8af15f7382ee9dbdfac043f2424187d716f8563808346f1/wasmer-0.3.0-cp36-cp36m-macosx_10_7_x86_64.whl", hash = "sha256:4fe592b764fc09d535757682d0ced6da1037976a7eb97986fce3523779a89682", size = 994318 }, - { url = "https://files.pythonhosted.org/packages/a7/89/739e34035f355ab4aa0801f5cdda07ce50247d7feed38a6075b8962514ed/wasmer-0.3.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e547b1074e52c10f0581de415b509aa61e577f5248340a68b356938393d773c8", size = 1123467 }, - { url = "https://files.pythonhosted.org/packages/02/61/8cd89aec2149cf81c005532157471bb1e0c4837a2b9332e619454ec99ded/wasmer-0.3.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:2edb87608daa3b46bd2520e0b5b90580fde9c805be4d92eeb98c22b29a21abc6", size = 994014 }, - { url = "https://files.pythonhosted.org/packages/ee/5f/93eac0a1c307ab97df5fd91cbb91c23dc464df604f1af62b520031abd8d7/wasmer-0.3.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcfe2c7a9fbf323f3520ef9766b82e80cd433d7f8c87ff084b18bcde716923af", size = 1123064 }, { url = "https://files.pythonhosted.org/packages/c8/91/03f6a73ebce72f0bc208314403d74bf6b11082f689e9693a3dfafd75b105/wasmer-0.3.0-py3-none-any.whl", hash = "sha256:1d2c337425721fd9ac6c6b17698ef8a9795b236a38b0e3c85872a5845ffb0d90", size = 1338 }, ] - -[[package]] -name = "zipp" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/bf/0d03dbdedb83afec081fefe86cae3a2447250ef1a81ac601a9a56e785401/zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832", size = 13047 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/df/d4a4974a3e3957fd1c1fa3082366d7fff6e428ddb55f074bf64876f8e8ad/zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc", size = 5313 }, -] diff --git a/tests/src/test_memfault_coredump_storage_debug.cpp b/tests/src/test_memfault_coredump_storage_debug.cpp index 9890d1059..07e2f2298 100644 --- a/tests/src/test_memfault_coredump_storage_debug.cpp +++ b/tests/src/test_memfault_coredump_storage_debug.cpp @@ -9,8 +9,13 @@ #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" -static bool s_inject_prepare_failure = false; +// Stub bool memfault_platform_coredump_save_begin(void) { + return true; +} + +static bool s_inject_prepare_failure = false; +bool memfault_port_coredump_save_begin(void) { if (s_inject_prepare_failure) { return false; } diff --git a/tests/src/test_memfault_metrics_reliability.cpp b/tests/src/test_memfault_metrics_reliability.cpp index f83522eb3..777245c71 100644 --- a/tests/src/test_memfault_metrics_reliability.cpp +++ b/tests/src/test_memfault_metrics_reliability.cpp @@ -42,6 +42,8 @@ TEST_GROUP(MemfaultMetricsReliability){ }; // clang-format on +// Note: this is kept in one big test case, because the internal state of the +// function under test is not touched. TEST(MemfaultMetricsReliability, Test_OperationalHours) { // 1 ms less than 1 hr prv_fake_time_incr(60 * 60 * 1000 - 1); @@ -88,4 +90,77 @@ TEST(MemfaultMetricsReliability, Test_OperationalHours) { prv_fake_time_incr(1); // no mocks should be called memfault_metrics_reliability_collect(); + + // Now test rollover scenarios + // advance to UINT32_MAX - 1; this represents a little over 49 days long + // heartbeat, so we won't track uptime + const uint32_t elapsed_hours = (UINT32_MAX - 1 - s_fake_time_ms) / 1000 / 3600; + prv_fake_time_incr(UINT32_MAX - 1 - s_fake_time_ms); + // plenty of crashfree hours! + mock() + .expectOneCall("memfault_metrics_heartbeat_add") + .withParameterOfType("MemfaultMetricId", "key", &operational_hours_key) + .withParameter("amount", elapsed_hours) + .andReturnValue(0); + mock() + .expectOneCall("memfault_metrics_heartbeat_add") + .withParameterOfType("MemfaultMetricId", "key", &operational_crashfree_hours_key) + .withParameter("amount", elapsed_hours) + .andReturnValue(0); + memfault_metrics_reliability_collect(); + + // advance 1 ms (UINT32_MAX total) + prv_fake_time_incr(1); + // no mocks should be called + memfault_metrics_reliability_collect(); + + // 1 ms more (UINT32_MAX + 1 total) + prv_fake_time_incr(1); + // no mocks should be called + memfault_metrics_reliability_collect(); + + // advance exactly 1 hour + prv_fake_time_incr(60 * 60 * 1000); + mock() + .expectOneCall("memfault_metrics_heartbeat_add") + .withParameterOfType("MemfaultMetricId", "key", &operational_hours_key) + .withParameter("amount", 1) + .andReturnValue(0); + mock() + .expectOneCall("memfault_metrics_heartbeat_add") + .withParameterOfType("MemfaultMetricId", "key", &operational_crashfree_hours_key) + .withParameter("amount", 1) + .andReturnValue(0); + + memfault_metrics_reliability_collect(); + + // advance to UINT64_MAX - 1. the amount of accumulated hours wraps at + // UINT32_MAX due to the implementation only using uint32_t for tracking + // heartbeat_ms duration- this should be ok, 49 days is a long heartbeat. + const uint32_t elapsed_hours_2 = (uint32_t)((UINT32_MAX - 1 - s_fake_time_ms)) / 1000 / 3600; + + prv_fake_time_incr(UINT64_MAX - 1 - s_fake_time_ms); + + // plenty of crashfree hours! + mock() + .expectOneCall("memfault_metrics_heartbeat_add") + .withParameterOfType("MemfaultMetricId", "key", &operational_hours_key) + .withParameter("amount", elapsed_hours_2) + .andReturnValue(0); + mock() + .expectOneCall("memfault_metrics_heartbeat_add") + .withParameterOfType("MemfaultMetricId", "key", &operational_crashfree_hours_key) + .withParameter("amount", elapsed_hours_2) + .andReturnValue(0); + memfault_metrics_reliability_collect(); + + // advance 1 ms (UINT64_MAX total) + prv_fake_time_incr(1); + // no mocks should be called + memfault_metrics_reliability_collect(); + + // 1 ms more (UINT64_MAX + 1 total) + prv_fake_time_incr(1); + // no mocks should be called + memfault_metrics_reliability_collect(); }