Repository: memfault/memfault-firmware-sdk Branch: master Commit: 84d6c4199ea7 Files: 960 Total size: 4.4 MB Directory structure: gitextract_wb972ssr/ ├── .circleci/ │ ├── Dockerfile │ ├── config.yml │ └── runas.sh ├── .clang-format ├── .codecov.yml ├── .cyignore ├── .git-blame-ignore-revs ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── Kconfig ├── LICENSE ├── README.md ├── VERSION ├── cmake/ │ └── Memfault.cmake ├── components/ │ ├── README.md │ ├── core/ │ │ ├── README.md │ │ └── src/ │ │ ├── .gitkeep │ │ ├── arch_arm_cortex_m.c │ │ ├── memfault_batched_events.c │ │ ├── memfault_build_id.c │ │ ├── memfault_build_id_private.h │ │ ├── memfault_compact_log_serializer.c │ │ ├── memfault_core_utils.c │ │ ├── memfault_custom_data_recording.c │ │ ├── memfault_custom_data_recording_private.h │ │ ├── memfault_data_export.c │ │ ├── memfault_data_packetizer.c │ │ ├── memfault_data_source_rle.c │ │ ├── memfault_event_storage.c │ │ ├── memfault_heap_stats.c │ │ ├── memfault_log.c │ │ ├── memfault_log_data_source.c │ │ ├── memfault_log_data_source_private.h │ │ ├── memfault_log_private.h │ │ ├── memfault_ram_reboot_info_tracking.c │ │ ├── memfault_reboot_tracking_private.h │ │ ├── memfault_reboot_tracking_serializer.c │ │ ├── memfault_sdk_assert.c │ │ ├── memfault_self_test.c │ │ ├── memfault_self_test_private.h │ │ ├── memfault_self_test_utils.c │ │ ├── memfault_serializer_helper.c │ │ ├── memfault_task_watchdog.c │ │ ├── memfault_trace_event.c │ │ └── memfault_trace_event_private.h │ ├── demo/ │ │ ├── README.md │ │ └── src/ │ │ ├── http/ │ │ │ └── memfault_demo_http.c │ │ ├── memfault_demo_cli_drain_chunks.c │ │ ├── memfault_demo_cli_log.c │ │ ├── memfault_demo_cli_trace_event.c │ │ ├── memfault_demo_core.c │ │ ├── memfault_demo_shell.c │ │ ├── memfault_demo_shell_commands.c │ │ ├── memfault_demo_watchdog.c │ │ └── panics/ │ │ ├── memfault_demo_cli_aux.c │ │ ├── memfault_demo_cli_aux_private.h │ │ └── memfault_demo_panics.c │ ├── http/ │ │ ├── README.md │ │ └── src/ │ │ ├── memfault_http_client.c │ │ ├── memfault_http_client_post_chunk.c │ │ ├── memfault_http_utils.c │ │ └── memfault_root_certs_der.c │ ├── include/ │ │ └── memfault/ │ │ ├── components.h │ │ ├── config.h │ │ ├── core/ │ │ │ ├── arch.h │ │ │ ├── batched_events.h │ │ │ ├── build_info.h │ │ │ ├── compact_log_compile_time_checks.h │ │ │ ├── compact_log_helpers.h │ │ │ ├── compact_log_serializer.h │ │ │ ├── compiler.h │ │ │ ├── compiler_armcc.h │ │ │ ├── compiler_gcc.h │ │ │ ├── compiler_iar.h │ │ │ ├── compiler_ti_arm.h │ │ │ ├── custom_data_recording.h │ │ │ ├── data_export.h │ │ │ ├── data_packetizer.h │ │ │ ├── data_packetizer_source.h │ │ │ ├── data_source_rle.h │ │ │ ├── debug_log.h │ │ │ ├── device_info.h │ │ │ ├── errors.h │ │ │ ├── event_storage.h │ │ │ ├── event_storage_implementation.h │ │ │ ├── heap_stats.h │ │ │ ├── heap_stats_impl.h │ │ │ ├── log.h │ │ │ ├── log_impl.h │ │ │ ├── math.h │ │ │ ├── platform/ │ │ │ │ ├── core.h │ │ │ │ ├── crc32.h │ │ │ │ ├── debug_log.h │ │ │ │ ├── device_info.h │ │ │ │ ├── nonvolatile_event_storage.h │ │ │ │ ├── overrides.h │ │ │ │ ├── reboot_tracking.h │ │ │ │ └── system_time.h │ │ │ ├── preprocessor.h │ │ │ ├── reboot_reason_types.h │ │ │ ├── reboot_tracking.h │ │ │ ├── sdk_assert.h │ │ │ ├── self_test.h │ │ │ ├── serializer_helper.h │ │ │ ├── serializer_key_ids.h │ │ │ ├── task_watchdog.h │ │ │ ├── task_watchdog_impl.h │ │ │ ├── trace_event.h │ │ │ ├── trace_event_impl.h │ │ │ └── trace_reason_user.h │ │ ├── default_config.h │ │ ├── demo/ │ │ │ ├── cli.h │ │ │ ├── shell.h │ │ │ ├── shell_commands.h │ │ │ └── util.h │ │ ├── http/ │ │ │ ├── http_client.h │ │ │ ├── platform/ │ │ │ │ └── http_client.h │ │ │ ├── root_certs.h │ │ │ └── utils.h │ │ ├── metrics/ │ │ │ ├── battery.h │ │ │ ├── connectivity.h │ │ │ ├── heartbeat_config.def │ │ │ ├── ids_impl.h │ │ │ ├── metrics.h │ │ │ ├── platform/ │ │ │ │ ├── battery.h │ │ │ │ ├── connectivity.h │ │ │ │ ├── overrides.h │ │ │ │ └── timer.h │ │ │ ├── reliability.h │ │ │ ├── serializer.h │ │ │ └── utils.h │ │ ├── panics/ │ │ │ ├── arch/ │ │ │ │ ├── arm/ │ │ │ │ │ ├── aarch64.h │ │ │ │ │ ├── cortex_m.h │ │ │ │ │ └── v7_a_r.h │ │ │ │ ├── posix/ │ │ │ │ │ └── posix.h │ │ │ │ ├── riscv/ │ │ │ │ │ └── riscv.h │ │ │ │ └── xtensa/ │ │ │ │ └── xtensa.h │ │ │ ├── assert.h │ │ │ ├── coredump.h │ │ │ ├── coredump_impl.h │ │ │ ├── fault_handling.h │ │ │ └── platform/ │ │ │ └── coredump.h │ │ ├── util/ │ │ │ ├── align.h │ │ │ ├── banner.h │ │ │ ├── base64.h │ │ │ ├── cbor.h │ │ │ ├── chunk_transport.h │ │ │ ├── circular_buffer.h │ │ │ ├── crc16.h │ │ │ ├── rle.h │ │ │ └── varint.h │ │ └── version.h │ ├── metrics/ │ │ ├── README.md │ │ └── src/ │ │ ├── memfault_metrics.c │ │ ├── memfault_metrics_battery.c │ │ ├── memfault_metrics_connectivity.c │ │ ├── memfault_metrics_reliability.c │ │ └── memfault_metrics_serializer.c │ ├── panics/ │ │ ├── README.md │ │ └── src/ │ │ ├── memfault_coredump.c │ │ ├── memfault_coredump_regions_armv7.c │ │ ├── memfault_coredump_sdk_regions.c │ │ ├── memfault_coredump_storage_debug.c │ │ ├── memfault_coredump_utils.c │ │ ├── memfault_fault_handling_aarch64.c │ │ ├── memfault_fault_handling_arm.c │ │ ├── memfault_fault_handling_armv7_a_r.c │ │ ├── memfault_fault_handling_posix.c │ │ ├── memfault_fault_handling_riscv.c │ │ ├── memfault_fault_handling_xtensa.c │ │ └── memfault_stdlib_assert.c │ └── util/ │ ├── README.md │ └── src/ │ ├── memfault_base64.c │ ├── memfault_chunk_transport.c │ ├── memfault_circular_buffer.c │ ├── memfault_crc16_ccitt.c │ ├── memfault_minimal_cbor.c │ ├── memfault_rle.c │ └── memfault_varint.c ├── examples/ │ ├── README.md │ ├── cypress/ │ │ └── CY8CKIT-064S0S2-4343W/ │ │ ├── Makefile │ │ ├── README.md │ │ ├── amazon-freertos.patch │ │ └── src/ │ │ ├── README.md │ │ ├── memfault_metrics_heartbeat_config.def │ │ ├── memfault_platform_config.h │ │ ├── memfault_platform_log_config.h │ │ ├── memfault_platform_port.c │ │ ├── memfault_platform_storage.c │ │ ├── memfault_platform_storage.h │ │ ├── memfault_test.c │ │ ├── memfault_test.h │ │ ├── memfault_trace_reason_user_config.def │ │ └── mqtt_demo_memfault.c │ ├── dialog/ │ │ ├── README.md │ │ ├── da145xx/ │ │ │ ├── README.md │ │ │ └── apps/ │ │ │ └── memfault_demo_app/ │ │ │ ├── .gitignore │ │ │ ├── Eclipse/ │ │ │ │ ├── .cproject │ │ │ │ ├── .project │ │ │ │ └── makefile.targets │ │ │ ├── Keil_5/ │ │ │ │ ├── memfault_demo_app.uvoptx │ │ │ │ ├── memfault_demo_app.uvprojx │ │ │ │ ├── unused_531.txt │ │ │ │ ├── unused_585.txt │ │ │ │ └── unused_586.txt │ │ │ └── src/ │ │ │ ├── config/ │ │ │ │ ├── da1458x_config_advanced.h │ │ │ │ ├── da1458x_config_basic.h │ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ │ ├── memfault_platform_config.h │ │ │ │ ├── memfault_trace_reason_user_config.def │ │ │ │ ├── user_callback_config.h │ │ │ │ ├── user_config.h │ │ │ │ ├── user_modules_config.h │ │ │ │ ├── user_periph_setup.h │ │ │ │ └── user_profiles_config.h │ │ │ ├── custom_profile/ │ │ │ │ ├── user_custs1_def.c │ │ │ │ ├── user_custs1_def.h │ │ │ │ ├── user_custs_config.c │ │ │ │ └── user_custs_config.h │ │ │ ├── memfault_platform_device_info.c │ │ │ ├── platform/ │ │ │ │ └── user_periph_setup.c │ │ │ ├── user_app.c │ │ │ └── user_app.h │ │ └── da1469x/ │ │ ├── README.md │ │ └── apps/ │ │ └── memfault_demo_app/ │ │ ├── .cproject │ │ ├── .gitignore │ │ ├── .project │ │ ├── config/ │ │ │ ├── custom_config_qspi.h │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ ├── memfault_platform_config.h │ │ │ └── memfault_trace_reason_user_config.def │ │ ├── main.c │ │ ├── makefile.targets │ │ └── memfault_platform_device_info.c │ ├── esp32/ │ │ ├── README.md │ │ └── apps/ │ │ └── memfault_demo_app/ │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── main/ │ │ │ ├── CMakeLists.txt │ │ │ ├── Kconfig.projbuild │ │ │ ├── app_memfault_transport.h │ │ │ ├── app_memfault_transport_http.c │ │ │ ├── app_memfault_transport_mqtt.c │ │ │ ├── button.c │ │ │ ├── button.h │ │ │ ├── cmd_app.c │ │ │ ├── cmd_decl.h │ │ │ ├── cmd_system.c │ │ │ ├── cmd_wifi.c │ │ │ ├── cmd_wifi_legacy.c │ │ │ ├── config/ │ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ │ ├── memfault_platform_config.h │ │ │ │ ├── memfault_reboot_reason_user_config.def │ │ │ │ ├── memfault_task_watchdog_config.def │ │ │ │ └── memfault_trace_reason_user_config.def │ │ │ ├── deep_sleep.c │ │ │ ├── deep_sleep.h │ │ │ ├── idf_component.yml │ │ │ ├── led.c │ │ │ ├── led.h │ │ │ ├── main.c │ │ │ ├── metrics.c │ │ │ ├── ota_session_metrics.c │ │ │ ├── ota_session_metrics.h │ │ │ ├── settings.c │ │ │ └── settings.h │ │ ├── partitions_example.csv │ │ ├── sdkconfig.defaults │ │ ├── sdkconfig.heaptrace │ │ └── sdkconfig.mqtt │ ├── freertos/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.md │ │ ├── boards/ │ │ │ ├── qemu_mps2_an385/ │ │ │ │ ├── Makefile │ │ │ │ ├── linker.ld │ │ │ │ ├── memfault_platform_impl.c │ │ │ │ └── startup.c │ │ │ ├── qemu_mps2_an386/ │ │ │ │ ├── Makefile │ │ │ │ ├── linker.ld │ │ │ │ ├── memfault_platform_impl.c │ │ │ │ └── startup.c │ │ │ └── qemu_mps2_an505/ │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── linker.ld │ │ │ ├── memfault_platform_impl.c │ │ │ └── startup.c │ │ └── src/ │ │ ├── FreeRTOSConfig.h │ │ ├── compact_log.cpp │ │ ├── compact_log.h │ │ ├── console.c │ │ ├── console.h │ │ ├── heap_task.c │ │ ├── heap_task.h │ │ ├── main.c │ │ ├── memfault/ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ ├── memfault_platform_config.h │ │ │ ├── memfault_platform_log_config.h │ │ │ ├── memfault_platform_port.c │ │ │ └── memfault_trace_reason_user_config.def │ │ ├── metrics.c │ │ ├── metrics.h │ │ ├── mpu.c │ │ └── mpu.h │ ├── libcurl/ │ │ ├── Makefile │ │ ├── README.md │ │ ├── get_latest.c │ │ └── post_chunks.c │ ├── nrf-connect-sdk/ │ │ ├── nrf5/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ └── memfault_demo_app/ │ │ │ ├── 1mbaud_uart.overlay │ │ │ ├── CMakeLists.txt │ │ │ ├── Kconfig │ │ │ ├── Kconfig.sysbuild │ │ │ ├── VERSION │ │ │ ├── boards/ │ │ │ │ ├── nrf54h20dk_nrf54h20_cpuapp.conf │ │ │ │ ├── nrf54h20dk_nrf54h20_cpuapp.overlay │ │ │ │ ├── nrf54l15dk_nrf54l15_cpuapp.conf │ │ │ │ ├── nrf54l15dk_nrf54l15_cpuapp_noinit.ld │ │ │ │ ├── nrf54lm20dk_nrf54lm20a_cpuapp.conf │ │ │ │ ├── nrf54lm20dk_nrf54lm20a_cpuapp.overlay │ │ │ │ ├── nrf54lm20dk_nrf54lm20a_cpuapp.overlay.example │ │ │ │ └── nrf54lm20dk_nrf54lm20a_cpuapp_noinit.ld │ │ │ ├── config/ │ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ │ ├── memfault_platform_config.h │ │ │ │ └── memfault_trace_reason_user_config.def │ │ │ ├── pm_static_nrf54l15dk_nrf54l15_cpuapp.yml │ │ │ ├── pm_static_nrf54lm20dk_nrf54lm20a_cpuapp.yml │ │ │ ├── prj.conf │ │ │ ├── sample.yaml │ │ │ ├── src/ │ │ │ │ ├── main.c │ │ │ │ └── shell_commands.c │ │ │ ├── sysbuild/ │ │ │ │ └── ipc_radio/ │ │ │ │ └── prj.conf │ │ │ ├── sysbuild.conf │ │ │ └── west.yml │ │ └── nrf9160/ │ │ ├── .gitignore │ │ └── memfault_demo_app/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── boards/ │ │ │ └── nrf9160dk_nrf9160_ns_0_14_0.overlay │ │ ├── config/ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ ├── memfault_platform_config.h │ │ │ ├── memfault_reboot_reason_user_config.def │ │ │ └── memfault_trace_reason_user_config.def │ │ ├── overlay-coap.conf │ │ ├── overlays/ │ │ │ ├── ncs-main.conf │ │ │ ├── ncs-pre-v2.0.0.conf │ │ │ ├── ncs-pre-v2.2.99.conf │ │ │ ├── ncs-pre-v2.4.0.conf │ │ │ ├── ncs-pre-v2.8.0.conf │ │ │ └── ncs-pre-v2.9.99.conf │ │ ├── prj.conf │ │ ├── sample.yaml │ │ ├── src/ │ │ │ ├── main.c │ │ │ ├── memfault_demo_app.h │ │ │ └── watchdog.c │ │ ├── submanifests/ │ │ │ └── .gitkeep │ │ ├── sysbuild.conf │ │ └── west.yml │ ├── nrf5/ │ │ ├── README.md │ │ └── apps/ │ │ └── memfault_demo_app/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── config/ │ │ │ └── sdk_config.h │ │ ├── memfault_demo_app_nrf52.ld │ │ ├── src/ │ │ │ ├── cli.c │ │ │ ├── main.c │ │ │ └── mflt_cli.h │ │ └── third_party/ │ │ └── memfault/ │ │ ├── memfault_metrics_heartbeat_config.def │ │ ├── memfault_platform_config.h │ │ ├── memfault_platform_log_config.h │ │ ├── memfault_platform_port.c │ │ ├── memfault_trace_reason_user_config.def │ │ └── sdk_overrides/ │ │ └── app_error.h │ ├── qp/ │ │ ├── README.md │ │ └── apps/ │ │ └── memfault_demo_app/ │ │ ├── Makefile │ │ ├── config/ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ ├── memfault_platform_config.h │ │ │ └── memfault_trace_reason_user_config.def │ │ └── src/ │ │ ├── arm-generic.ld │ │ ├── bsp.c │ │ ├── bsp.h │ │ ├── main.c │ │ ├── platform_reference_impl/ │ │ │ ├── memfault_platform_core.c │ │ │ └── memfault_platform_log.c │ │ └── startup_stm32f4xx.c │ ├── stm32/ │ │ ├── README.md │ │ └── stm32h743i/ │ │ ├── Makefile_test.mk │ │ ├── README.md │ │ ├── chibios-memfault-integration.patch │ │ ├── memfault_sdk.mk │ │ └── platform_reference_impl/ │ │ ├── memfault_platform_config.h │ │ ├── memfault_platform_core.c │ │ ├── memfault_platform_coredump.c │ │ ├── memfault_platform_device_info.c │ │ ├── memfault_trace_reason_user_config.def │ │ ├── stm32h7xx_hal_conf.h │ │ └── stm32h7xx_hal_stubs.c │ ├── wiced/ │ │ ├── README.md │ │ ├── apps/ │ │ │ └── memfault_demo_app/ │ │ │ ├── config/ │ │ │ │ └── memfault_platform_config.h │ │ │ ├── memfault_demo_app.c │ │ │ └── memfault_demo_app.mk │ │ └── libraries/ │ │ └── memfault/ │ │ ├── core/ │ │ │ └── core.mk │ │ ├── demo/ │ │ │ └── demo.mk │ │ ├── http/ │ │ │ └── http.mk │ │ ├── panics/ │ │ │ └── panics.mk │ │ ├── platform_reference_impl/ │ │ │ ├── memfault_platform_coredump.c │ │ │ ├── memfault_platform_coredump.ld │ │ │ ├── memfault_platform_crc32.c │ │ │ ├── memfault_platform_debug_log.c │ │ │ ├── memfault_platform_device_info.c │ │ │ ├── memfault_platform_fault_handling_arm_gcc.c │ │ │ ├── memfault_platform_http_client.c │ │ │ ├── memfault_platform_impl.c │ │ │ ├── memfault_platform_wiced.h │ │ │ └── platform_reference_impl.mk │ │ └── util/ │ │ └── util.mk │ └── zephyr/ │ ├── nucleo_wba55cg/ │ │ ├── .gitignore │ │ ├── README.md │ │ └── memfault_demo_app/ │ │ ├── CMakeLists.txt │ │ ├── VERSION │ │ ├── config/ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ ├── memfault_platform_config.h │ │ │ └── memfault_trace_reason_user_config.def │ │ ├── prj.conf │ │ ├── src/ │ │ │ └── main.c │ │ └── west.yml │ ├── qemu/ │ │ ├── .gitignore │ │ ├── README.md │ │ └── qemu-app/ │ │ ├── CMakeLists.txt │ │ ├── Kconfig │ │ ├── LICENSE │ │ ├── boards/ │ │ │ ├── apollo4p_blue_kxr_evb.conf │ │ │ ├── apollo4p_blue_kxr_evb.overlay │ │ │ ├── apollo510_evb.conf │ │ │ ├── apollo510_evb.overlay │ │ │ ├── b_u585i_iot02a.conf │ │ │ ├── b_u585i_iot02a.overlay │ │ │ ├── mps2_an385.conf │ │ │ ├── mps2_an385.overlay │ │ │ ├── nucleo_f756zg.conf │ │ │ ├── nucleo_f756zg.overlay │ │ │ ├── nucleo_l496zg.conf │ │ │ ├── nucleo_l496zg.overlay │ │ │ ├── qemu_cortex_m3.conf │ │ │ └── qemu_cortex_m3.overlay │ │ ├── config/ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ └── memfault_platform_config.h │ │ ├── prj.conf │ │ ├── src/ │ │ │ ├── cdr.c │ │ │ ├── cdr.h │ │ │ ├── main.c │ │ │ └── metrics.c │ │ └── west.yml │ └── stm32l4_disco/ │ ├── .ci-project-setup.json │ ├── .gitignore │ ├── README.md │ ├── apps/ │ │ └── memfault_demo_app/ │ │ ├── CMakeLists.txt │ │ ├── config/ │ │ │ ├── memfault_metrics_heartbeat_config.def │ │ │ ├── memfault_platform_config.h │ │ │ └── memfault_trace_reason_user_config.def │ │ ├── prj.conf │ │ ├── sample.yaml │ │ └── src/ │ │ └── main.c │ └── stm32l4_disco_zephyr2.5_wifi.patch ├── idf_component.yml ├── makefiles/ │ └── MemfaultWorker.mk ├── ports/ │ ├── README.md │ ├── atmel/ │ │ └── saml1x/ │ │ └── rcause_reboot_tracking.c │ ├── cypress/ │ │ └── psoc6/ │ │ ├── README.md │ │ ├── configs/ │ │ │ ├── memfault_metrics_mtb_heartbeat_config.def │ │ │ ├── memfault_mtb_platform_config.h │ │ │ └── memfault_platform_log_config.h │ │ ├── memfault_bss.ld │ │ ├── memfault_platform_core.c │ │ ├── memfault_platform_coredump_regions.c │ │ ├── memfault_platform_http.c │ │ ├── memfault_psoc6_port.h │ │ ├── psoc6_default_config.h │ │ └── res_cause_reboot_tracking.c │ ├── dialog/ │ │ ├── da145xx/ │ │ │ ├── armcc-fault-handler.patch │ │ │ ├── gcc-hardfault.patch │ │ │ ├── gnu-build-id.patch │ │ │ ├── memfault_platform_core.c │ │ │ ├── memfault_platform_coredump_regions.c │ │ │ ├── memfault_platform_coredump_storage.c │ │ │ ├── memfault_platform_debug_log.c │ │ │ ├── memfault_platform_metrics.c │ │ │ └── reset_reboot_tracking.c │ │ ├── da1468x/ │ │ │ ├── gnu-build-id.patch │ │ │ ├── memfault-qspi-coredump-storage.patch │ │ │ ├── qspi_coredump_storage.c │ │ │ ├── reset_stat_reboot_tracking.c │ │ │ └── wdog_software_watchdog.c │ │ └── da1469x/ │ │ ├── fault-handlers.patch │ │ ├── freertos-config.patch │ │ ├── gnu-build-id.patch │ │ ├── memfault_diagnostic_service.c │ │ ├── memfault_platform_core.c │ │ ├── memfault_platform_coredump_regions.c │ │ ├── memfault_platform_coredump_storage.c │ │ ├── memfault_platform_debug_log.c │ │ └── reset_reboot_tracking.c │ ├── emlib/ │ │ ├── README.md │ │ ├── memfault_demo_cli.c │ │ ├── msc_coredump_storage.c │ │ ├── rmu_reboot_tracking.c │ │ └── wdog_software_watchdog.c │ ├── esp_idf/ │ │ ├── README.md │ │ ├── memfault/ │ │ │ ├── CMakeLists.txt │ │ │ ├── Kconfig │ │ │ ├── common/ │ │ │ │ ├── memfault_compact_log.ld │ │ │ │ ├── memfault_esp_freertos.lf │ │ │ │ ├── memfault_fault_handler.c │ │ │ │ ├── memfault_platform_core.c │ │ │ │ ├── memfault_platform_coredump.c │ │ │ │ ├── memfault_platform_debug_log.c │ │ │ │ ├── memfault_platform_deep_sleep.c │ │ │ │ ├── memfault_platform_demo_cli_cmds.c │ │ │ │ ├── memfault_platform_device_info.c │ │ │ │ ├── memfault_platform_http_client.c │ │ │ │ ├── memfault_platform_http_client_buffer.c │ │ │ │ ├── memfault_platform_http_periodic_upload.c │ │ │ │ ├── memfault_platform_metrics.c │ │ │ │ ├── memfault_platform_system_time.c │ │ │ │ └── memfault_self_test_platform.c │ │ │ ├── config/ │ │ │ │ ├── memfault_esp_idf_port_config.h │ │ │ │ ├── memfault_esp_metrics_heartbeat_config.def │ │ │ │ ├── memfault_platform_freertos_error_log.h │ │ │ │ └── memfault_trace_reason_esp_idf_port_config.def │ │ │ ├── include/ │ │ │ │ └── memfault/ │ │ │ │ └── esp_port/ │ │ │ │ ├── cli.h │ │ │ │ ├── core.h │ │ │ │ ├── coredump.h │ │ │ │ ├── deep_sleep.h │ │ │ │ ├── device_info.h │ │ │ │ ├── http_client.h │ │ │ │ ├── metrics.h │ │ │ │ └── spi_flash.h │ │ │ ├── v4.x/ │ │ │ │ ├── Memfault-esp-idf-compat.cmake │ │ │ │ └── memfault_esp_spi_flash.c │ │ │ ├── v5.x/ │ │ │ │ ├── Memfault-esp-idf-compat.cmake │ │ │ │ └── memfault_esp_spi_flash.c │ │ │ └── v6.x/ │ │ │ ├── Memfault-esp-idf-compat.cmake │ │ │ └── memfault_esp_spi_flash.c │ │ └── memfault.cmake │ ├── freertos/ │ │ ├── README.md │ │ ├── config/ │ │ │ └── memfault_metrics_heartbeat_freertos_config.def │ │ └── src/ │ │ ├── memfault_core_freertos.c │ │ ├── memfault_freertos_ram_regions.c │ │ ├── memfault_metrics_freertos.c │ │ ├── memfault_panics_freertos.c │ │ ├── memfault_sdk_metrics_freertos.c │ │ ├── memfault_sdk_metrics_thread.c │ │ └── memfault_self_test_platform.c │ ├── include/ │ │ ├── .mtbsearch.h │ │ └── memfault/ │ │ └── ports/ │ │ ├── ble/ │ │ │ └── mds.h │ │ ├── buffered_coredump_storage.h │ │ ├── freertos/ │ │ │ ├── metrics.h │ │ │ └── thread_metrics.h │ │ ├── freertos.h │ │ ├── freertos_coredump.h │ │ ├── freertos_trace.h │ │ ├── lwip/ │ │ │ └── metrics.h │ │ ├── mbedtls/ │ │ │ └── metrics.h │ │ ├── reboot_reason.h │ │ ├── stm32cube/ │ │ │ ├── l4/ │ │ │ │ └── flash.h │ │ │ └── wb/ │ │ │ └── flash.h │ │ ├── threadx_coredump.h │ │ └── watchdog.h │ ├── lwip/ │ │ ├── config/ │ │ │ └── memfault_lwip_metrics_heartbeat_config.def │ │ └── memfault_lwip_metrics.c │ ├── mbedtls/ │ │ ├── config/ │ │ │ └── memfault_mbedtls_metrics_heartbeat_config.def │ │ ├── memfault_mbedtls_metrics.c │ │ └── memfault_platform_http_client.c │ ├── mynewt/ │ │ ├── README.md │ │ ├── include/ │ │ │ ├── memfault_metrics_heartbeat_mynewt_config.def │ │ │ ├── memfault_platform_config.h │ │ │ ├── memfault_platform_log_config.h │ │ │ ├── memfault_shell.h │ │ │ └── memfault_trace_reason_mynewt_config.def │ │ ├── pkg.yml │ │ ├── src/ │ │ │ ├── memfault_platform_flash_backed_coredump.c │ │ │ ├── memfault_platform_port.c │ │ │ └── memfault_shell.c │ │ └── syscfg.yml │ ├── nrf5_sdk/ │ │ ├── memfault_platform_metrics.c │ │ ├── nrf5_coredump_regions.c │ │ ├── nrf5_coredump_storage.c │ │ ├── resetreas_reboot_tracking.c │ │ └── software_watchdog.c │ ├── nxp/ │ │ ├── rt1021/ │ │ │ └── src_reboot_tracking.c │ │ └── rw61x/ │ │ └── pmu_reboot_tracking.c │ ├── panics/ │ │ └── src/ │ │ └── memfault_platform_ram_backed_coredump.c │ ├── particle/ │ │ ├── README.md │ │ ├── examples/ │ │ │ └── memfault_test/ │ │ │ ├── project.properties │ │ │ └── src/ │ │ │ ├── application.cpp │ │ │ ├── memfault_particle_user_config.h │ │ │ └── memfault_trace_reason_user_config.def │ │ └── src/ │ │ ├── memfault.cpp │ │ ├── memfault.h │ │ ├── memfault_particle_metrics_heartbeat_config.def │ │ ├── memfault_particle_trace_reason_user_config.def │ │ ├── memfault_platform_config.h │ │ └── memfault_platform_log_config.h │ ├── qp/ │ │ ├── README.md │ │ ├── qassert.h.patch │ │ ├── qf_pkg.h.patch │ │ └── qf_pkg.hpp.patch │ ├── s32sdk/ │ │ ├── ftfc_flash_coredump_storage.c │ │ ├── lpit_software_watchdog.c │ │ └── rcm_reboot_tracking.c │ ├── silabs/ │ │ └── wiseconnect/ │ │ └── siwx91x/ │ │ └── siwx91x_reboot_tracking.c │ ├── stm32cube/ │ │ ├── README.md │ │ ├── f4/ │ │ │ ├── flash_coredump_storage.c │ │ │ └── rcc_reboot_tracking.c │ │ ├── f7/ │ │ │ └── rcc_reboot_tracking.c │ │ ├── h5/ │ │ │ └── rcc_reboot_tracking.c │ │ ├── h7/ │ │ │ ├── lptim_software_watchdog.c │ │ │ └── rcc_reboot_tracking.c │ │ ├── l4/ │ │ │ ├── flash_coredump_storage.c │ │ │ ├── lptim_software_watchdog.c │ │ │ └── rcc_reboot_tracking.c │ │ ├── u5/ │ │ │ ├── flash_coredump_storage.c │ │ │ └── rcc_reboot_tracking.c │ │ └── wb/ │ │ ├── flash_coredump_storage.c │ │ └── rcc_reboot_tracking.c │ ├── templates/ │ │ ├── README.md │ │ ├── apache-2.0.txt │ │ ├── memfault_metrics_heartbeat_config.def │ │ ├── memfault_platform_config.h │ │ ├── memfault_platform_log_config.h │ │ ├── memfault_platform_port.c │ │ └── memfault_trace_reason_user_config.def │ ├── threadx/ │ │ └── src/ │ │ └── memfault_threadx_ram_regions.c │ └── zephyr/ │ ├── CMakeLists.txt │ ├── Kconfig │ ├── README.md │ ├── common/ │ │ ├── CMakeLists.txt │ │ ├── coredump_storage/ │ │ │ ├── memfault_ambiq_mram_backed_coredump.c │ │ │ ├── memfault_mram_backed_coredump.c │ │ │ ├── memfault_nrf_rram_backed_coredump.c │ │ │ └── memfault_stm32u5_flash_backed_coredump.c │ │ ├── memfault-build-id.ld │ │ ├── memfault-compact-log.ld │ │ ├── memfault-mbedtls.conf │ │ ├── memfault-no-init.ld │ │ ├── memfault-rtc-noinit-region.ld │ │ ├── memfault_demo_cli.c │ │ ├── memfault_logging.c │ │ ├── memfault_logging_legacy.c │ │ ├── memfault_logging_minimal.c │ │ ├── memfault_mcumgr.c │ │ ├── memfault_periodic_upload.c │ │ ├── memfault_platform_core.c │ │ ├── memfault_platform_coredump_regions.c │ │ ├── memfault_platform_debug_log.c │ │ ├── memfault_platform_fota.c │ │ ├── memfault_platform_http.c │ │ ├── memfault_platform_lock.c │ │ ├── memfault_platform_post.c │ │ ├── memfault_platform_ram_backed_coredump.c │ │ ├── memfault_platform_system_time.c │ │ ├── memfault_self_test_platform.c │ │ ├── memfault_software_watchdog.c │ │ ├── memfault_tls_root_cert_storage.c │ │ ├── memfault_zephyr_ram_regions.c │ │ └── metrics/ │ │ ├── CMakeLists.txt │ │ ├── memfault_platform_bluetooth_metrics.c │ │ ├── memfault_platform_metrics.c │ │ ├── memfault_platform_thread_metrics.c │ │ └── memfault_platform_wifi_metrics.c │ ├── config/ │ │ ├── memfault_metrics_heartbeat_zephyr_port_config.def │ │ ├── memfault_platform_log_config.h │ │ ├── memfault_trace_reason_zephyr_port_config.def │ │ └── memfault_zephyr_platform_config.h │ ├── include/ │ │ └── memfault/ │ │ └── ports/ │ │ ├── ncs/ │ │ │ ├── date_time_callback.h │ │ │ └── version.h │ │ └── zephyr/ │ │ ├── bluetooth_metrics.h │ │ ├── core.h │ │ ├── coredump.h │ │ ├── deprecated_root_cert.h │ │ ├── fota.h │ │ ├── http.h │ │ ├── include_compatibility.h │ │ ├── log_backend.h │ │ ├── log_panic.h │ │ ├── memfault_mcumgr.h │ │ ├── periodic_upload.h │ │ ├── root_cert_storage.h │ │ ├── thread_metrics.h │ │ └── version.h │ ├── ncs/ │ │ ├── CMakeLists.txt │ │ ├── Kconfig │ │ ├── README.md │ │ ├── config/ │ │ │ └── memfault_metrics_heartbeat_ncs_port_config.def │ │ ├── include/ │ │ │ └── memfault/ │ │ │ └── nrfconnect_port/ │ │ │ └── coap.h │ │ └── src/ │ │ ├── CMakeLists.txt │ │ ├── memfault_fota.c │ │ ├── memfault_fota_legacy.c │ │ ├── memfault_nrf_modem_root_cert_init.c │ │ ├── memfault_nrf_modem_root_cert_storage.c │ │ ├── memfault_platform_coap.c │ │ ├── memfault_platform_metrics_connectivity_lte.c │ │ ├── memfault_platform_npm13xx_battery.c │ │ └── nrfx_pmu_reboot_tracking.c │ └── panics/ │ ├── CMakeLists.txt │ ├── memfault_fault_handler.c │ ├── memfault_fault_handler_posix.c │ ├── memfault_fault_handler_riscv.c │ └── memfault_fault_handler_xtensa.c ├── repository.yml ├── requirements.txt ├── scripts/ │ ├── cmsis_pack_bundle.py │ ├── create_arduino_library.py │ ├── eclipse_patch.py │ ├── fw_build_id.py │ ├── memfault_gdb.py │ ├── memfault_group.py │ ├── mflt-build-id/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── pyproject.toml │ │ ├── setup.py │ │ ├── src/ │ │ │ └── mflt_build_id/ │ │ │ ├── __init__.py │ │ │ └── py.typed │ │ ├── tasks/ │ │ │ └── __init__.py │ │ └── tests_mflt_build_id/ │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── elf_fixtures/ │ │ │ ├── __init__.py │ │ │ ├── crc32_build_id_populated.elf │ │ │ ├── crc32_build_id_unpopulated.elf │ │ │ ├── gnu_id_present_and_not_used.elf │ │ │ ├── gnu_id_present_and_used.elf │ │ │ ├── gnu_id_with_short_len.elf │ │ │ ├── memfault_build_id_present_and_populated.elf │ │ │ ├── memfault_build_id_present_and_unpopulated.elf │ │ │ ├── memfault_build_id_with_short_len.elf │ │ │ ├── memfault_id_used_gnu_id_present.elf │ │ │ ├── no_memfault_symbols.elf │ │ │ └── no_symtab_no_text_no_data.elf │ │ ├── test_elf_file_helper.py │ │ └── test_fw_build_id.py │ └── tests_embedded_scripts/ │ ├── __init__.py │ ├── gdb_fake.py │ ├── snapshots/ │ │ ├── __init__.py │ │ ├── snap_test_eclipse_patch.py │ │ └── snap_test_memfault_gdb.py │ ├── test_eclipse_patch.py │ ├── test_memfault_gdb.py │ └── testinput/ │ ├── .cproject │ └── .project ├── tasks/ │ ├── __init__.py │ ├── esp32.py │ ├── gdb.py │ ├── macos_ftdi.py │ ├── nrf.py │ ├── print_chunk_watcher.py │ ├── wiced.py │ └── zephyr.py ├── tests/ │ ├── README.md │ └── unit/ │ ├── Makefile │ ├── MakefileWorker.mk │ ├── MakefileWorkerOverrides.mk │ ├── README.md │ ├── comparators/ │ │ ├── comparator_memfault_fault_handling.hpp │ │ └── comparator_memfault_metric_ids.hpp │ ├── fakes/ │ │ ├── fake_memfault_buffered_coredump_storage.h │ │ ├── fake_memfault_build_id.c │ │ ├── fake_memfault_build_id.h │ │ ├── fake_memfault_coredump_utils.c │ │ ├── fake_memfault_event_storage.cpp │ │ ├── fake_memfault_event_storage.h │ │ ├── fake_memfault_metrics_platform_locking.c │ │ ├── fake_memfault_platform_boot_time.c │ │ ├── fake_memfault_platform_coredump_storage.c │ │ ├── fake_memfault_platform_coredump_storage.h │ │ ├── fake_memfault_platform_crc32.c │ │ ├── fake_memfault_platform_debug_log.c │ │ ├── fake_memfault_platform_get_device_info.c │ │ ├── fake_memfault_platform_get_device_info.h │ │ ├── fake_memfault_platform_http_client.c │ │ ├── fake_memfault_platform_locking.c │ │ ├── fake_memfault_platform_metrics_locking.h │ │ ├── fake_memfault_platform_time.c │ │ ├── fake_memfault_platform_time.h │ │ ├── fake_memfault_reboot_tracking.c │ │ └── fake_memfault_sdk_assert.c │ ├── llvm-cov-wrapper.sh │ ├── makefiles/ │ │ ├── Makefile_assert.mk │ │ ├── Makefile_batched_events.mk │ │ ├── Makefile_circular_buffer.mk │ │ ├── Makefile_memfault_base64.mk │ │ ├── Makefile_memfault_buffered_coredump_storage.mk │ │ ├── Makefile_memfault_build_id_gnu.mk │ │ ├── Makefile_memfault_build_id_memfault.mk │ │ ├── Makefile_memfault_cdr_source.mk │ │ ├── Makefile_memfault_chunking_transport.mk │ │ ├── Makefile_memfault_compact_log.mk │ │ ├── Makefile_memfault_compact_log_macros.mk │ │ ├── Makefile_memfault_compact_log_save_truncation.mk │ │ ├── Makefile_memfault_compact_log_serializer.mk │ │ ├── Makefile_memfault_coredump.mk │ │ ├── Makefile_memfault_coredump_sdk_regions.mk │ │ ├── Makefile_memfault_coredump_utils.mk │ │ ├── Makefile_memfault_coredump_with_serial.mk │ │ ├── Makefile_memfault_crc16_ccitt.mk │ │ ├── Makefile_memfault_crc16_ccitt_no_lut.mk │ │ ├── Makefile_memfault_data_export.mk │ │ ├── Makefile_memfault_data_packetizer.mk │ │ ├── Makefile_memfault_data_packetizer_with_project_key.mk │ │ ├── Makefile_memfault_data_source_rle.mk │ │ ├── Makefile_memfault_demo_shell.mk │ │ ├── Makefile_memfault_event_storage.mk │ │ ├── Makefile_memfault_event_storage_batch_read.mk │ │ ├── Makefile_memfault_event_storage_no_persistent_storage.mk │ │ ├── Makefile_memfault_heap_stats.mk │ │ ├── Makefile_memfault_heartbeat_metrics.mk │ │ ├── Makefile_memfault_heartbeat_metrics_debug.mk │ │ ├── Makefile_memfault_heartbeat_metrics_nocustom.mk │ │ ├── Makefile_memfault_http_utils.mk │ │ ├── Makefile_memfault_log.mk │ │ ├── Makefile_memfault_log_data_source.mk │ │ ├── Makefile_memfault_log_data_source_timestamps.mk │ │ ├── Makefile_memfault_log_with_timestamps.mk │ │ ├── Makefile_memfault_metrics_battery.mk │ │ ├── Makefile_memfault_metrics_connectivity.mk │ │ ├── Makefile_memfault_metrics_reliability.mk │ │ ├── Makefile_memfault_metrics_serializer.mk │ │ ├── Makefile_memfault_port_lwip_metrics.mk │ │ ├── Makefile_memfault_port_mbedtls_metrics.mk │ │ ├── Makefile_memfault_port_nrf5_coredump_regions.mk │ │ ├── Makefile_memfault_printf_attribute.mk │ │ ├── Makefile_memfault_ram_backed_coredump_port.mk │ │ ├── Makefile_memfault_reboot_tracking_serializer.mk │ │ ├── Makefile_memfault_rle.mk │ │ ├── Makefile_memfault_root_cert.mk │ │ ├── Makefile_memfault_sdk_assert.mk │ │ ├── Makefile_memfault_sdk_metrics_freertos.mk │ │ ├── Makefile_memfault_self_test.mk │ │ ├── Makefile_memfault_self_test_component_boot_check.mk │ │ ├── Makefile_memfault_self_test_coredump_regions.mk │ │ ├── Makefile_memfault_self_test_coredump_storage.mk │ │ ├── Makefile_memfault_self_test_data_export.mk │ │ ├── Makefile_memfault_self_test_device_info.mk │ │ ├── Makefile_memfault_self_test_reboot_reason.mk │ │ ├── Makefile_memfault_self_test_time.mk │ │ ├── Makefile_memfault_self_test_utils.mk │ │ ├── Makefile_memfault_serializer_helper.mk │ │ ├── Makefile_memfault_serializer_helper_with_device_serial.mk │ │ ├── Makefile_memfault_serializer_helper_without_build_id.mk │ │ ├── Makefile_memfault_session_metrics.mk │ │ ├── Makefile_memfault_session_metrics_debug.mk │ │ ├── Makefile_memfault_session_vitals.mk │ │ ├── Makefile_memfault_task_watchdog.mk │ │ ├── Makefile_memfault_test_coredump_storage_debug.mk │ │ ├── Makefile_memfault_trace_event.mk │ │ ├── Makefile_memfault_trace_event_compact_log.mk │ │ ├── Makefile_memfault_trace_event_no_isr_log.mk │ │ ├── Makefile_memfault_user_reboot_reasons.mk │ │ ├── Makefile_minimal_cbor.mk │ │ ├── Makefile_ram_reboot_tracking.mk │ │ └── Makefile_varint.mk │ ├── mocks/ │ │ ├── mock_memfault_coredump.cpp │ │ ├── mock_memfault_coredump.h │ │ ├── mock_memfault_fault_handling.cpp │ │ ├── mock_memfault_metrics.cpp │ │ ├── mock_memfault_metrics_reliability.cpp │ │ ├── mock_memfault_platform_debug_log.cpp │ │ ├── mock_memfault_platform_debug_log.h │ │ ├── mock_memfault_platform_system_time.cpp │ │ └── mock_memfault_reboot_tracking.cpp │ ├── scripts/ │ │ └── filterGcov.sh │ ├── src/ │ │ ├── AllTests.cpp │ │ ├── memfault_test_compact_log_c.h │ │ ├── test_assert.cpp │ │ ├── test_memfault_base64.cpp │ │ ├── test_memfault_batched_events.cpp │ │ ├── test_memfault_buffered_coredump_storage.cpp │ │ ├── test_memfault_buffered_coredump_storage_impl.c │ │ ├── test_memfault_build_id.cpp │ │ ├── test_memfault_chunk_transport.cpp │ │ ├── test_memfault_circular_buffer.cpp │ │ ├── test_memfault_compact_log_c.c │ │ ├── test_memfault_compact_log_cxx.c │ │ ├── test_memfault_compact_log_macros.cpp │ │ ├── test_memfault_compact_log_save_truncation.cpp │ │ ├── test_memfault_compact_log_serializer.cpp │ │ ├── test_memfault_coredump.cpp │ │ ├── test_memfault_coredump_sdk_regions.cpp │ │ ├── test_memfault_coredump_storage_debug.cpp │ │ ├── test_memfault_coredump_utils.cpp │ │ ├── test_memfault_crc16_ccitt.cpp │ │ ├── test_memfault_custom_data_recording.cpp │ │ ├── test_memfault_data_export.cpp │ │ ├── test_memfault_data_packetizer.cpp │ │ ├── test_memfault_data_source_rle.cpp │ │ ├── test_memfault_demo_shell.cpp │ │ ├── test_memfault_event_storage.cpp │ │ ├── test_memfault_heap_stats.cpp │ │ ├── test_memfault_heartbeat_metrics.cpp │ │ ├── test_memfault_heartbeat_metrics_debug.cpp │ │ ├── test_memfault_heartbeat_metrics_nocustom.cpp │ │ ├── test_memfault_http_utils.cpp │ │ ├── test_memfault_log.cpp │ │ ├── test_memfault_log_data_source.cpp │ │ ├── test_memfault_log_with_timestamps.cpp │ │ ├── test_memfault_metrics_battery.cpp │ │ ├── test_memfault_metrics_connectivity.cpp │ │ ├── test_memfault_metrics_reliability.cpp │ │ ├── test_memfault_metrics_serializer.cpp │ │ ├── test_memfault_minimal_cbor.cpp │ │ ├── test_memfault_port_lwip_metrics.cpp │ │ ├── test_memfault_port_mbedtls_metrics.cpp │ │ ├── test_memfault_port_nrf5_coredump_regions.cpp │ │ ├── test_memfault_printf_attribute.cpp │ │ ├── test_memfault_ram_backed_coredump_port.cpp │ │ ├── test_memfault_ram_reboot_tracking.cpp │ │ ├── test_memfault_reboot_tracking_serializer.cpp │ │ ├── test_memfault_rle.cpp │ │ ├── test_memfault_root_cert.cpp │ │ ├── test_memfault_sdk_assert.cpp │ │ ├── test_memfault_sdk_freertos_metrics.cpp │ │ ├── test_memfault_self_test.cpp │ │ ├── test_memfault_self_test_component_boot_check.cpp │ │ ├── test_memfault_self_test_coredump_regions.cpp │ │ ├── test_memfault_self_test_coredump_storage.cpp │ │ ├── test_memfault_self_test_data_export.cpp │ │ ├── test_memfault_self_test_device_info.cpp │ │ ├── test_memfault_self_test_reboot_reason.cpp │ │ ├── test_memfault_self_test_time.cpp │ │ ├── test_memfault_self_test_utils.cpp │ │ ├── test_memfault_serializer_helper.cpp │ │ ├── test_memfault_session_metrics.cpp │ │ ├── test_memfault_session_metrics_debug.cpp │ │ ├── test_memfault_session_vitals.cpp │ │ ├── test_memfault_task_watchdog.cpp │ │ ├── test_memfault_trace_event.cpp │ │ ├── test_memfault_user_reboot_reasons.cpp │ │ └── test_memfault_varint.cpp │ ├── stub_includes/ │ │ ├── FreeRTOS.h │ │ ├── lwip/ │ │ │ └── stats.h │ │ ├── mbedtls_mem.h │ │ ├── memfault_metrics_heartbeat_config.def │ │ ├── memfault_platform_config.h │ │ ├── memfault_reboot_reason_user_config.def │ │ ├── memfault_task_watchdog_config.def │ │ ├── memfault_trace_reason_user_config.def │ │ ├── sdk_common.h │ │ ├── task.h │ │ └── timers.h │ ├── stubs/ │ │ ├── stub_assert.c │ │ ├── stub_component_booted.c │ │ ├── stub_mbedtls_mem.c │ │ ├── stub_memfault_coredump.c │ │ ├── stub_memfault_coredump_regions.c │ │ ├── stub_memfault_coredump_storage_debug.c │ │ ├── stub_memfault_log.c │ │ ├── stub_memfault_log_save.c │ │ ├── stub_platform.c │ │ └── stub_reboot_tracking.c │ └── test.py └── zephyr/ └── module.yml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/Dockerfile ================================================ # This Docker image is used in CircleCI to build the SDK. It's published to # Docker Hub as "memfault/memfault-sdk-embedded-ci". # # It's built with the following command (assuming cwd is the directory # containing this Dockerfile): # # ❯ DOCKER_BUILDKIT=1 docker build -t memfault/memfault-firmware-sdk-ci: . # # And uploaded to Docker Hub: # ❯ docker push memfault/memfault-firmware-sdk-ci: FROM ubuntu:22.04 # Some details based on this Dockerfile: # https://github.com/CircleCI-Public/cimg-base/blob/2b2cc9584c5ce2256d0781106218ff4158c790a0/22.04/Dockerfile SHELL ["/bin/bash", "-exo", "pipefail", "-c"] ENV DEBIAN_FRONTEND=noninteractive \ TERM=dumb \ PAGER=cat ARG MEMFAULT_SDK_APT_DEPS="\ build-essential \ cpputest \ curl \ gcc-12 \ g++-12 \ git \ gnupg \ python3.10 \ python3.10-venv\ " # Run commands and tests as circleci user RUN echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/90circleci && \ echo 'DPkg::Options "--force-confnew";' >> /etc/apt/apt.conf.d/90circleci && \ apt-get update && apt-get install -y --no-install-recommends \ locales \ sudo \ wget \ ${MEMFAULT_SDK_APT_DEPS} \ && \ locale-gen en_US.UTF-8 && \ rm -rf /var/lib/apt/lists/* && \ # create the circlci user useradd --uid=3434 --user-group --create-home circleci && \ echo 'circleci ALL=NOPASSWD: ALL' >> /etc/sudoers.d/50-circleci && \ echo 'Defaults env_keep += "DEBIAN_FRONTEND"' >> /etc/sudoers.d/env_keep && \ sudo -u circleci mkdir /home/circleci/project && \ sudo -u circleci mkdir /home/circleci/bin # Select gcc-12 as the default gcc/g++ version RUN sudo update-alternatives --install \ /usr/bin/gcc gcc /usr/bin/gcc-12 60 \ --slave /usr/bin/g++ g++ /usr/bin/g++-12 \ --slave /usr/bin/gcov gcov /usr/bin/gcov-12 ENV PATH=/home/circleci/bin:/home/circleci/.local/bin:$PATH \ LANG=en_US.UTF-8 \ LANGUAGE=en_US:en \ LC_ALL=en_US.UTF-8 # Install lcov and add to PATH ARG LCOV_VERSION=1.16 ARG LCOV_SHA256SUM=987031ad5528c8a746d4b52b380bc1bffe412de1f2b9c2ba5224995668e3240b RUN \ cd /tmp && \ wget https://github.com/linux-test-project/lcov/releases/download/v${LCOV_VERSION}/lcov-${LCOV_VERSION}.tar.gz -O lcov.tar.gz && \ echo "${LCOV_SHA256SUM} lcov.tar.gz" | shasum --algorithm=256 --check && \ tar zvxf lcov.tar.gz && \ cd lcov-${LCOV_VERSION} && sudo make install && \ cd .. && \ rm -rf ./lcov* && \ lcov --version && \ genhtml --version # Install clang-17 RUN \ echo deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main >> /etc/apt/sources.list.d/llvm.list && \ echo deb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main >> /etc/apt/sources.list.d/llvm.list && \ wget -q -O - http://apt.llvm.org/llvm-snapshot.gpg.key|apt-key add - && \ apt-get update && apt-get install -y clang-17 llvm-17 ENV PATH=/home/circleci/lcov-${LCOV_VERSION}/bin:$PATH USER circleci # Match the default CircleCI working directory WORKDIR /home/circleci/project # Create the virtualenv RUN python3 -m venv ~/venv # Auto-activate the virtualenv in the container RUN sudo mkdir -p /circleci/ && \ sudo bash -c "echo 'source ~/venv/bin/activate' >> /circleci/.bashrc_circleci" && \ echo 'source /circleci/.bashrc_circleci' >> ~/.bashrc ENTRYPOINT ["bash"] ================================================ FILE: .circleci/config.yml ================================================ # Python CircleCI 2.1 configuration file # # Check for more details: # # - https://circleci.com/docs/2.0/configuration-reference/#section=reference # - https://discuss.circleci.com/t/circleci-2-1-config-overview/26057 # # Validate this via: # # - brew install circleci # - circleci config validate # version: 2.1 executors: memfault-ci: docker: - image: memfault/memfault-firmware-sdk-ci:2024-01-19 working_directory: ~/repo commands: virtualenv_activate: steps: - run: name: Set environment variables and source virtualenv command: | if [ -f /circleci/.bashrc_circleci ]; then cat /circleci/.bashrc_circleci >> $BASH_ENV fi pip_install: steps: - restore_cache: name: Restore Python Package Cache keys: - v3-pip-dependencies-{{ checksum "requirements.txt" }} - v3-pip-dependencies- - run: pip install -r requirements.txt - save_cache: paths: - ~/venv key: v3-pip-dependencies-{{ checksum "requirements.txt" }} prepare: steps: - checkout - virtualenv_activate - pip_install jobs: # TODO: also build demo apps in public CI (not just in our private CI) fw-sdk-test: executor: memfault-ci steps: - prepare # run the tests with the sanitizer enabled first - run: inv -e test --coverage # now clean, and re-run with the sanitizer disabled- it adds extra # branches that cannot be hit in a normal run, impacting coverage statistics - run: git clean -dxff - run: MEMFAULT_DISABLE_ASAN=1 inv -e test --coverage - run: curl -s https://codecov.io/bash | bash -s -- -t ${CODECOV_TOKEN} -n ${CIRCLE_BUILD_NUM} -Z || echo 'Codecov upload failed' workflows: version: 2 build: jobs: - fw-sdk-test ================================================ FILE: .circleci/runas.sh ================================================ #!/usr/bin/env bash # This script can be used (on a Linux host, at least) to set up a docker # container to be able to write on a hosted volume, by using an overlay mount # inside the container. # # It requires running docker with the --priviledged flag. An example: # # ❯docker run --privileged --rm -i -t -v"$PWD":/hostdir memfault/memfault-firmware-sdk-ci /hostdir/.circleci/runas.sh set -euo pipefail BASE_DIR=/hostdir FINAL_DIR=/memfault-firmware-sdk USER=${USER-$(whoami)} mkdir -p /tmp/overlay/ sudo mount -t tmpfs tmpfs /tmp/overlay mkdir /tmp/overlay/{up,work} sudo mkdir "$FINAL_DIR" sudo mount -t overlay overlay \ -o lowerdir="$BASE_DIR,upperdir=/tmp/overlay/up/,workdir=/tmp/overlay/work/" \ "$FINAL_DIR" sudo chown -R "$USER:$USER $FINAL_DIR" exec bash ================================================ FILE: .clang-format ================================================ --- BasedOnStyle: Google AlignArrayOfStructures: None AllowShortBlocksOnASingleLine: Empty AllowShortFunctionsOnASingleLine: Empty AlwaysBreakBeforeMultilineStrings: false AttributeMacros: - MEMFAULT_ALIGNED - MEMFAULT_NAKED_FUNC - MEMFAULT_NO_ALLOC - MEMFAULT_NO_OPT - MEMFAULT_NORETURN - MEMFAULT_PACKED - MEMFAULT_PACKED_STRUCT - MEMFAULT_PRINTF_LIKE_FUNC - MEMFAULT_PUT_IN_SECTION - MEMFAULT_UNREACHABLE - MEMFAULT_UNUSED - MEMFAULT_USED - MEMFAULT_WEAK BitFieldColonSpacing: None BreakBeforeBinaryOperators: None BreakBeforeTernaryOperators: false ColumnLimit: 100 ContinuationIndentWidth: 2 Cpp11BracedListStyle: false DerivePointerAlignment: false FixNamespaceComments: false ForEachMacros: - MEMFAULT_SHELL_FOR_EACH_COMMAND IndentPPDirectives: BeforeHash IndentWidth: 2 NamespaceIndentation: All NamespaceMacros: - TEST - TEST_GROUP PointerAlignment: Right SpaceInEmptyBlock: true WhitespaceSensitiveMacros: - MEMFAULT_QUOTE - MEMFAULT_ZEPHYR_INCLUDE - Pragma ================================================ FILE: .codecov.yml ================================================ coverage: precision: 2 round: down range: "70...100" status: patch: off project: default: target: 70% informational: true ignore: - "tests" # Don't include the tests themselves in the code coverage report ================================================ FILE: .cyignore ================================================ cmake components/_feature_stubs components/_sortme examples internal makefile ports/atmel ports/dialog ports/emlib ports/esp_idf ports/freertos/src/memfault_sdk_metrics_freertos.c ports/lwip ports/mbedtls ports/mynewt ports/nrf5_sdk ports/nxp ports/particle ports/qp ports/s32sdk ports/silabs ports/stm32cube ports/templates ports/threadx ports/zephyr scripts tests $(SEARCH_memfault-firmware-sdk)/components/include/memfault/core $(SEARCH_memfault-firmware-sdk)/components/include/memfault/demo $(SEARCH_memfault-firmware-sdk)/components/include/memfault/http $(SEARCH_memfault-firmware-sdk)/components/include/memfault/metrics $(SEARCH_memfault-firmware-sdk)/components/include/memfault/panics $(SEARCH_memfault-firmware-sdk)/components/include/memfault/util $(SEARCH_memfault-firmware-sdk)/ports/include/memfault ================================================ FILE: .git-blame-ignore-revs ================================================ # https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view # # This file contains a list of commits that are not likely what you # are looking for in a blame, such as mass reformatting or renaming. # You can set this file as a default ignore file for blame by running # the following command. # # git config blame.ignoreRevsFile .git-blame-ignore-revs 0ca1a2a16f7cbf0420d795ca3a4f508fe09f95bd # SDK v1.6.2 clang-format run ================================================ FILE: .gitignore ================================================ build /examples/mbedtls /examples/modus __pycache__/ ================================================ FILE: CHANGELOG.md ================================================ # Memfault Firmware SDK Changelog All notable changes to this project will be documented in this file. 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.39.0] - 2026-05-06 This is a minor release, including new features, improvements, and bug fixes across several platforms. ### 📈 Added - Zephyr: - Enable Zephyr RTOS thread awareness for ESP32-S3. Non-active Xtensa threads now decode correctly. - Added a new Kconfig option, `CONFIG_MEMFAULT_HTTP_DISABLE_TLS`, which disables TLS for the Memfault HTTP transport and uses plain-text HTTP instead. **This option is intended for local debugging only (e.g. with a local proxy server) and must never be enabled in production firmware.** Two companion options are available when `CONFIG_MEMFAULT_HTTP_DISABLE_TLS` is set: - `CONFIG_MEMFAULT_HTTP_CHUNKS_API_HOST` - override the chunks API host (default: `chunks.memfault.com`) - `CONFIG_MEMFAULT_HTTP_DEVICE_API_HOST` - override the device API host (default: `device.memfault.com`) - Add support for `CONFIG_USE_SWITCH` alternate context switching option for ARM Cortex-M, added in Zephyr v4.4.0. - General: - Added build support for legacy ARM cores (ARMv4T, ARMv5TE/TEJ, ARMv6). Only compilation is supported; coredump and exception tracking are not supported on these platforms. - ThreadX: - Added `memfault_threadx_get_thread_regions()` and the accompanying `memfault/ports/threadx_coredump.h` header, which capture per-thread stack usage watermarks into the coredump at fault time. The helper walks the ThreadX created-thread list, records each thread's TCB and full stack, and scans the stack fill pattern to compute how many bytes remain unused at the bottom of each thread's stack. Results are stored in a compact `sMfltThreadXStackInfo[]` sidecar array that is captured as a coredump region alongside the stacks. Call it from `memfault_platform_coredump_get_regions()`: ```c #include "memfault/ports/threadx_coredump.h" // Add MEMFAULT_THREADX_MAX_TASK_REGIONS to your region array size: // #define COREDUMP_MAX_REGIONS (4 + MEMFAULT_THREADX_MAX_TASK_REGIONS) region_idx += memfault_threadx_get_thread_regions( &s_coredump_regions[region_idx], MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx); ``` The maximum number of tracked threads defaults to 16 and can be overridden by defining `MEMFAULT_THREADX_MAX_THREADS` in `memfault_platform_config.h`. The per-thread stack usage data is displayed in the Memfault web app alongside each thread's backtrace. A reference sample is provided at . ### 🔥 Removed - Mbed OS: removed the Mbed OS port, example app, and invoke tasks. The port was deprecated in 1.38.0. Cloud-side support is not affected. ### 🐛 Fixed - FreeRTOS: - Add some compatibility workarounds to support building with FreeRTOS V9.0.0. Relevant defines are `MEMFAULT_FREERTOS_DISABLE_STACK_OVERFLOW_HOOK` and `MEMFAULT_FREERTOS_PXENDOFSTACK_AVAILABLE`. - Zephyr: - Fix Xtensa thread stack capture in `memfault_zephyr_ram_regions.c`. - Fix a bug in `memfault_zephyr_port_http_upload_sdk_data()` / `memfault_zephyr_port_http_post_chunk()` where only HTTP 200 was treated as a successful response from the Memfault server. Fixed so any 2xx response is treated as a success. ## [1.38.0] - 2026-04-24 This is a minor release, including new features, improvements, and bug fixes across several platforms. ### 🚩 Deprecated - Mbed OS: Arm has announced that [Mbed OS will reach end of life in July 2026](https://os.mbed.com/blog/entry/Important-Update-on-Mbed/). As a result, the Memfault Firmware SDK port for Mbed OS is being deprecated, and will be removed in a following release. **Cloud-side support is not affected** - existing devices using the Mbed OS port will continue to report data to Memfault and the Memfault cloud platform will continue to process and display that data as before. If your project relies on the Mbed OS port and you need continued SDK support, please [contact us](https://mflt.io/contact-support)! ### 📈 Added - ESP-IDF: - Added a new Kconfig option, `CONFIG_MEMFAULT_RECORD_REBOOT_ON_BOOT` (default `y`), to allow deferring reboot reason event serialization to a later point in time. Set this option to `n` when reboot reason collection must be deferred (i.e. if device serial is not available immediately at boot). When deferring, the user application must call `memfault_esp_port_collect_reset_info()` (declared in `memfault/esp_port/core.h`) once device info initialization is complete, to record the reboot event. - Zephyr: - Add a new Kconfig option, `CONFIG_MEMFAULT_PERIODIC_UPLOAD_ENABLED_DEFAULT`, which controls the initial state of periodic Memfault data uploads at boot. Defaults to `y` (enabled), preserving existing behavior. Set to `n` to start with periodic uploads disabled at boot; the state can still be changed at runtime with `memfault_zephyr_port_periodic_upload_enable()`. - Add support to the [nRF91 sample app](examples/nrf-connect-sdk/nrf9160/) for FOTA over CoAP in the CoAP overlay. - Add a new Kconfig option, `CONFIG_MEMFAULT_PERIODIC_FOTA_CHECK`, to enable a periodic FOTA check. This runs along the `CONFIG_MEMFAULT_PERIODIC_UPLOAD` thread. - Add a new Kconfig option, `CONFIG_MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_PRIORITY`, which controls the workqueue priority for uploading data when `CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE=y`. The default is the lowest priority (`K_LOWEST_APPLICATION_THREAD_PRIO`). - Add a new system time implementation, used to timestamp events (Heartbeats, Logs, Trace Events) on-device, using the Zephyr System Clock API. This adds to the existing RTC and DATE_TIME implementations. It can be selected with `CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_SYS_CLOCK=y`, and is auto-enabled when other sources are not present but Zephyr provides `SYS_CLOCK_EXISTS` (system clock support). This implementation is useful on platforms lacking an RTC but do synchronize System Clock from an external time source (e.g. via NTP). - Add a coredump storage implementation for the STM32U5 series SOC. Enable it with `CONFIG_MEMFAULT_COREDUMP_STORAGE_STM32U5_FLASH=y`. Requires a device tree partition labeled `memfault_coredump_partition` to be defined. See [`ports/zephyr/common/coredump_storage/memfault_stm32u5_flash_backed_coredump.c`](ports/zephyr/common/coredump_storage/memfault_stm32u5_flash_backed_coredump.c) for details. ### 🐛 Fixed - Zephyr: - Change `memfault_zephyr_port_http_upload_sdk_data()` and `memfault_zephyr_port_http_post_chunk()` to return non-zero if an HTTP error status code is returned by the Memfault server. Previously, non-200 HTTP responses were silently ignored. - Zephyr v4.4 removes support for the Mbed TLS legacy crypto backend, only supporting PSA now. Update a compile time check in `memfault_platform_http.c` to support the new option, `CONFIG_PSA_WANT_ALG_SHA_1`, which replaces `CONFIG_MBEDTLS_SHA1`. - Fix some errors in the RRAM and MRAM-backed coredump storage implementations that were added in SDK v1.29.0. Incorrect offset computation was used, which could result in incorrect positioning of the coredump when certain flash region configurations are used (i.e. instead of partitions, flash regions are used). - Fix a few minor type inconsistencies in `memfault_platform_http.c`. - General: - Fix a few minor type inconsistencies in the following files: - `ports/cypress/psoc6/memfault_platform_http.c` - `ports/mbedtls/memfault_platform_http_client.c` - `ports/stm32cube/l4/flash_coredump_storage.c` - `ports/stm32cube/wb/flash_coredump_storage.c` ### 🛠️ Changed - Zephyr: - Changed the Kconfig symbol `MEMFAULT_COREDUMP_STORAGE_RRAM` to `MEMFAULT_COREDUMP_STORAGE_NRF_RRAM` to better reflect that it supports nRF devices only. - Changed the default thread priority when `CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE=y` from the highest thread priority (`K_HIGHEST_APPLICATION_THREAD_PRIO`) to the lowest (`K_LOWEST_APPLICATION_THREAD_PRIO`). This change was made to prevent Memfault data upload from pre-empting application threads. To change the priority level, use the new Kconfig `CONFIG_MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_PRIORITY`. - Handle rename of Kconfig symbol `MBEDTLS_CFG_FILE` -> `MBEDTLS_CONFIG_FILE`. Thanks to [@tomi-font](https://github.com/tomi-font) for the fix in [#113](https://github.com/memfault/memfault-firmware-sdk/pull/113) 🎉! - nRF Connect SDK: - Improved CoAP upload behavior when using the nRF Cloud CoAP library. `CONFIG_MEMFAULT_COAP_MAX_POST_SIZE` has been removed; the chunk size is now derived automatically from `CONFIG_COAP_CLIENT_BLOCK_SIZE` and `CONFIG_COAP_CLIENT_MESSAGE_HEADER_SIZE`, ensuring each Memfault chunk maps 1:1 to one CoAP block. The message limit is now workqueue-aware: when using a dedicated workqueue (`CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE`), uploads are unbounded so all pending data is drained in one pass; when running on the system workqueue, the limit defaults to 100 (`CONFIG_MEMFAULT_COAP_MAX_MESSAGES_TO_SEND`) to avoid blocking other work, but can still be configured to a convenient value. - The NCS FOTA implementation is now a backend option of the Zephyr FOTA framework, selectable via `CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS`. This backend is chosen automatically on NCS targets with a direct connection to the internet. The previous `CONFIG_MEMFAULT_FOTA` symbol no longer has any effect, and is removed in this release. The entry point `memfault_fota_start()` is replaced by `memfault_zephyr_fota_start()`, consistent with the other Zephyr FOTA backends. The `CONFIG_MEMFAULT_PERIODIC_FOTA_CHECK` option now works on NCS targets as well. The deprecated headers `memfault/nrfconnect_port/fota.h` and `memfault/nrfconnect_port/http.h` have been removed; use `memfault/ports/zephyr/fota.h` and `memfault/ports/zephyr/http.h` directly. - `CONFIG_MEMFAULT_NRF_SHELL` is removed in this release, the commands are now available from the general Memfault Zephyr shell commands. - Internal: bumped two python deps: `pytest` (7.0.1 → 9.0.3, includes CVE-2025-71176 fix) and `invoke` (2.1.1 → 3.0.3) in `sdk/embedded/requirements.txt`. ## [1.37.1] - 2026-03-24 This is a patch release, fixing a single item. ### 🐛 Fixed - Zephyr/nRF Connect SDK: - Fix an error in the previous release, where Kconfig symbol stubs were added to satisfy the Kconfig linter in certain project configurations. The stubs are removed in this release. To successfully run compliance checking on nRF Connect SDK versions other than current main, or on vanilla Zephyr projects, it's necessary to add additional ignores to the compliance check list. This addresses issue [#112](https://github.com/memfault/memfault-firmware-sdk/issues/112). ## [1.37.0] - 2026-03-23 This is a minor release, including several improvements and bug fixes across several platforms. ### 🛠️ Changed - nRF Connect SDK: - Use the native `/chunks` endpoint instead of the HTTP proxy endpoint for sending Memfault data via nRF Cloud CoAP. This also enables sending data with a Memfault Project Key built into the application. The `/chunks` endpoint will automatically route data to the associated Memfault Project 🪄! - Add `CONFIG_MEMFAULT_ROOT_CERT_INSTALL_ON_MODEM_LIB_INIT` to optionally install Memfault root certificates from an `NRF_MODEM_LIB_ON_INIT` callback after modem library initialization. This option is enabled by default for nRF modem users. Note that the Memfault root cert installation is idempotent. - Update the nRF9160 sample app to call `memfault_zephyr_port_install_root_certs()` only when `CONFIG_MEMFAULT_ROOT_CERT_INSTALL_ON_MODEM_LIB_INIT` is disabled. - Zephyr: - Update a check in the Memfault HTTP implementation that was not correctly using heap when `CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=-1` (default, using all unallocated memory for heap). Thanks to [@SeppoTakalo](https://github.com/SeppoTakalo) for providing this fix in [#104](https://github.com/memfault/memfault-firmware-sdk/pull/104) 🎉! - Add missing Kconfig dependencies for choice config `CONFIG_MEMFAULT_ROOT_CERT_STORAGE_CONTEXT`. - Generalize the nRF modem root certificate storage Kconfig naming by adding `CONFIG_MEMFAULT_ROOT_CERT_STORAGE_NRF_MODEM` and deprecating `CONFIG_MEMFAULT_ROOT_CERT_STORAGE_NRF9160_MODEM`. - General: - Move the SDK unit tests from `tests/` to `tests/unit/`, to allow for additional types of tests to be added in the future under `tests/` (i.e. integration/port tests). ### 🐛 Fixed - Zephyr: - Fixed a bug in the Ambiq MRAM driver, where `FIXED_PARTITION_OFFSET` was incorrectly used instead of `FIXED_PARTITION_ADDRESS` when accessing the coredump partition. - The Kconfig option `MBEDTLS_PEM_CERTIFICATE_FORMAT` was renamed into `MBEDTLS_PEM_PARSE_C && MBEDTLS_PEM_WRITE_C`. Update `CONFIG_MEMFAULT_TLS_CERTS_USE_PEM` to depend on the new symbol. Thanks to [@tomi-font](https://github.com/tomi-font) for providing this fix in [#105](https://github.com/memfault/memfault-firmware-sdk/pull/105) 🎉! - Fix a [number](https://github.com/memfault/memfault-firmware-sdk/issues/107) [of](https://github.com/memfault/memfault-firmware-sdk/issues/108) [Kconfig](https://github.com/memfault/memfault-firmware-sdk/issues/109) lints on older Zephyr versions. Thank you to [@JordanYates](https://github.com/JordanYates) for reporting these issues. - Fix a compilation issue due to missing `CONFIG_MEMFAULT_PROJECT_KEY` definition when using the Memfault MCUmgr command group. Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this issue in [#106](https://github.com/memfault/memfault-firmware-sdk/pull/106). ## [1.36.0] - 2026-03-11 This is a minor release, including several improvements and bug fixes across all platforms. Highlights: - Improved support for accessing Memfault over CoAP (vs. HTTPS) for Zephyr-based devices - Added support for optionally returning from a Watchdog interrupt handler, instead of rebooting immediately - For Zephyr-based devices, added a custom MCUmgr command group for Memfault metadata. This enables Memfault FOTA via MCUmgr without sending device information on a secondary interface (e.g., Bluetooth DIS) ### 📈 Added - ESP-IDF: - Add support for the upcoming ESP-IDF v6 release. - General: - Add support for optionally returning from a Watchdog interrupt handler, instead of rebooting. Use the define `MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN` to enable this behavior. Note that the user is required to handle any necessary clean up or reboot in this configuration. - Zephyr: - A custom [MCUmgr](https://docs.zephyrproject.org/latest/services/device_mgmt/mcumgr_handlers.html) command group for Memfault metadata, which allows a MCUmgr client to read the following device information over the MCUmgr protocol: - Device Serial - Software Version - Software Type - Hardware Version - Memfault Project Key This enables using Memfault FOTA with only MCUmgr enabled on the target device (i.e. SMP over BLE). Memfault recommends enabling the [Memfault Diagnostic Service (MDS)](https://docs.memfault.com/docs/mcu/mds) when using the Nordic nRF Connect SDK on a Bluetooth-enabled application: MDS provides a built-in way to export Memfault telemetry data over BLE, using the Nordic-provided companion mobile apps. See the [documentation here](https://docs.memfault.com/docs/mcu/mcumgr) for more details on the Memfault MCUmgr command group. ### 🛠️ Changed - nRF Connect SDK: - Decouple CoAP upload from HTTP. Data upload over nRF Cloud CoAP via `CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP` no longer requires `CONFIG_MEMFAULT_HTTP_ENABLE`. - Use new options `CONFIG_MEMFAULT_COAP_MAX_MESSAGES_TO_SEND`, `CONFIG_MEMFAULT_COAP_MAX_POST_SIZE`, and `CONFIG_MEMFAULT_COAP_FOTA_URL_BUFFER_SIZE` instead of HTTP or download-client configs to control CoAP upload behavior. - Remove redundant default-enabled configs from the [nRF91 sample app](examples/nrf-connect-sdk/nrf9160/) `prj.conf`. - Rename `mflt_http` thread to `mflt_upload`. The associated thread metric name is now `memory_mflt_upload_pct_max`. - Add `CONFIG_NETWORKING` as a dependency for `CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP`. - Add `CONFIG_COAP_CLIENT` and `CONFIG_NRF_CLOUD_COAP_MAX_USER_OPTIONS >= 2` as dependencies for `CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP`. - Use the NCS nRF Cloud CoAP APIs in the Memfault CoAP port to share connection ID with other nRF Cloud connections. Requires the latest NCS development version when using `CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP`. - Zephyr: - Deprecate `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD` and related Kconfig in favor of `CONFIG_MEMFAULT_PERIODIC_UPLOAD`. The legacy `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_*` configs remain as backwards-compatibility aliases. Choice config `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_CONTEXT` is kept to allow for choice override in NCS. - Refactor HTTP client implementation to use `zsock_` prefixed socket functions instead of depending on `CONFIG_POSIX_API`. This aligns with Zephyr's native socket API and removes the requirement for POSIX API compatibility layer to be enabled. As part of this change, add a missing `NET_SOCKETS` Kconfig dependency to `CONFIG_MEMFAULT_HTTP_ENABLE`; this dependency was always required, but not previously included. ### 🐛 Fixed - General: - Fix a few `-Wunused-variable` warnings in the battery metrics module when debug logs are disabled. - nRF-Connect SDK - Fix a missed reference of Kconfig symbol `MEMFAULT_USE_NRF_CLOUD_TRANSPORT` when it was updated to `MEMFAULT_USE_NRF_CLOUD_COAP` in SDK v1.34.0. - Fix a reference of `CONFIG_MEMFAULT_METRICS_SYNC_SUCCESS` to `CONFIG_MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS` when marking sync successes for the Memfault cloud [Sync Success Vital](https://docs.memfault.com/docs/platform/memfault-core-metrics#periodic-connectivity). - Zephyr: - Fix an error in the Kconfig dependencies for the `MEMFAULT_COREDUMP_STORAGE_AMBIQ_MRAM` symbol. This affects users on Ambiq Apollo4 and Apollo510 SOCs. ## [1.35.0] - 2026-01-29 ### 📈 Added - Zephyr: - Add a new API, `memfault_zephyr_port_http_periodic_upload_enabled()`, which returns the current state of periodic Memfault data uploads. ## [1.34.0] - 2026-01-26 ### 🔥 Removed - ESP8266: - This release removes support for the ESP8266 platform in the Memfault Firmware SDK. ESP8266 support was deprecated in SDK 1.33.0, and is disabled in the Memfault cloud. Please reach out to if you need assistance! ### 📈 Added - General: - Add the printing of the stored reboot reason on boot to the `memfault_platform_port.c` template when `MEMFAULT_ENABLE_REBOOT_DIAG_DUMP` is enabled. - ESP-IDF: - Add support for downloading OTA images in multiple HTTP requests. This feature is automatically enabled when `CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD=y` is set, but can also be manually controlled with `CONFIG_MEMFAULT_HTTP_PARTIAL_DOWNLOAD_ENABLE`. The chunk size for each request can be configured using `CONFIG_MEMFAULT_HTTP_MAX_REQUEST_SIZE`, which defaults to 15kB. This support can reduce RAM usage by allowing a smaller mbedTLS receive buffer. - Add the raw ESP reboot value and the Memfault-stored reboot reason to the reboot reason information printed on boot when `CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP=y`. Only the raw enumerated values are printed to save space. See [reboot_reason_types.h](components/include/memfault/core/reboot_reason_types.h) for all Memfault reboot reasons. Note: custom reason values equal the base unexpected/expected reboot reason address plus their declaration order in `memfault_reboot_reason_user_config.h`. - Zephyr: - Add the raw Zephyr hwinfo value and the Memfault-stored reboot reason to the reboot reason information printed on boot when `CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP=y`. Only the raw enumerated values are printed to save space. See [reboot_reason_types.h](components/include/memfault/core/reboot_reason_types.h) for all Memfault reboot reasons. Note: custom reason values equal the base unexpected/expected reboot reason address plus their declaration order in `memfault_reboot_reason_user_config.h`. - Add a new API, `memfault_zephyr_port_http_periodic_upload_enable()`, which can be used to enable or disable periodic Memfault data uploads at runtime. This complements the existing Kconfig option `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD`, which enables periodic uploads at boot time. See [`ports/zephyr/include/memfault/ports/zephyr/http.h`](ports/zephyr/include/memfault/ports/zephyr/http.h) for details. ### 🛠️ Changed - General - Align reboot tracking implementations to print the reboot reason register value when `MEMFAULT_ENABLE_REBOOT_DIAG_DUMP` is enabled. - nRF Connect SDK: - Remove references to `SOC_SERIES_NRF52X` and similar, which have been renamed in Zephyr v4.4. Thanks to [@nordicjm](https://github.com/nordicjm) for providing this fix in [#102](https://github.com/memfault/memfault-firmware-sdk/pull/102) 🎉! - Changed name of Kconfig option `MEMFAULT_USE_NRF_CLOUD_TRANSPORT` to `MEMFAULT_USE_NRF_CLOUD_COAP`. If you were using this Kconfig option, you will need to rename it in your `prj.conf`. - Zephyr: - Remove the dependencies on `NET_SOCKETS_OFFLOAD` and `NET_SOCKETS_OFFLOAD_DISPATCHER` for the Kconfig option `MEMFAULT_HTTP_SOCKET_DISPATCH`. It's possible to use `SO_BINDTODEVICE` sockopt without offloaded sockets, and it's useful to allow setting the `netif` used for Memfault HTTP operations via `memfault_zephyr_port_http_set_interface_name()` in cases where offloaded sockets are not used. Thanks to [@chirambaht](https://github.com/chirambaht) for reporting this issue in [#101](https://github.com/memfault/memfault-firmware-sdk/issues/101) 🎉! ### 🐛 Fixed - nRF-Connect SDK: - Support user-configurable values for `MEMFAULT_HTTP_CHUNKS_API_HOST` and `MEMFAULT_HTTP_DEVICE_API_HOST`. This was supported on other platforms, including Zephyr, but was not supported on the nRF Connect SDK port. ## [1.33.0] - 2026-01-07 This is a minor release. ### 🚩 Deprecated - ESP8266: - This release deprecates support for the ESP8266 platform in the Memfault Firmware SDK and backend. After this release, Memfault's cloud will no longer process coredumps from ESP8266 devices, and a future version of the SDK will remove ESP8266 support. ### 📈 Added - ESP-IDF: - Add a new Kconfig option, `MEMFAULT_COREDUMP_REGIONS_THREAD_ONLY`, which can be used to limit coredump collection to only thread-related data (stacks, TCBs, and FreeRTOS metadata), excluding .bss/.data and heap sections. This can be useful for reducing the size of coredumps. When enabled, the `MEMFAULT_PLATFORM_TASK_STACK_SIZE_TO_COLLECT` option can be used to adjust the amount of stack data collected for each task. - Zephyr - Add support for dispatching the socket used for Memfault HTTP upload to a specific network interface when multiple network interfaces are being used (i.e. `CONFIG_NET_SOCKETS_OFFLOAD_DISPATCHER=y`). Enable this support with `CONFIG_MEMFAULT_HTTP_SOCKET_DISPATCH` and specify the interface to dispatch the socket to at runtime with the new API `memfault_zephyr_port_http_set_interface_name()`. - Add a coredump storage implementation for the Ambiq Apollo4 and Apollo510 series. This storage implementation can be enabled with `CONFIG_MEMFAULT_COREDUMP_STORAGE_AMBIQ_MRAM=y`. Requires adding a fixed partition named `memfault_coredump_partition` to the device tree. See [`ports/zephyr/common/coredump_storage/memfault_ambiq_mram_backed_coredump.c`](ports/zephyr/common/coredump_storage/memfault_ambiq_mram_backed_coredump.c) for details. - nRF-Connect SDK - Add the nRF54LM20-DK to the list of tested targets for the [nRF52/53/54 sample app](examples/nrf-connect-sdk/nrf5/). ### 🛠️ Changed - ESP-IDF: - Eliminate some noisy warning/error logs that were being emitted on boot, such as: ```plaintext E (345) task_wdt: esp_task_wdt_status(765): TWDT was never initialized W (362) mflt_sleep: No log backup data W (372) mflt_sleep: No event storage backup data W (372) mflt_sleep: No metrics backup data ``` - Change the default implementation of `memfault_platform_reboot()` to call `panic_restart()` instead of `esp_restart()`. The latter is meant to be called during a graceful reboot, while `panic_restart()` is what should be used at the end of fault handling, to restart the system. Use `CONFIG_MEMFAULT_PLATFORM_REBOOT_CUSTOM=y` to provide a custom implementation of `memfault_platform_reboot()`. - Zephyr: - Changed the maximum compiled-in log level for `MEMFAULT_LOG_x()` statements to match `CONFIG_MEMFAULT_LOG_LEVEL`. Previously, all log levels were compiled in by default, and runtime filtering was applied based on the configured log level. Now, only log statements at or below the configured log level are compiled in, reducing code size. To restore the previous behavior, set `CONFIG_MEMFAULT_PLATFORM_HAS_LOG_CONFIG=n` to disable the custom log config header. ### 🐛 Fixed - Zephyr: - Fix a compilation error when targeting the VPR RISC-V coprocessors on Nordic chips such as the nRF54L15 and nRF54LM20. - Fix a few `-Wunused-variable` warnings when building with logs disabled (`CONFIG_LOG=n`). ## [1.32.0] - 2025-12-03 This is a minor release. Key updates: - Integrated fuel gauge battery metrics for nPM13xx PMICs on nRF Connect SDK. - Added CoAP client for uploading Memfault data via nRF Cloud. - Fixed build issues and compiler warnings on Zephyr and nRF Connect SDK. ### 📈 Added - nRF-Connect SDK: - Add a battery metrics port for the nPM1300 and nPM1304 PMICs, which includes collecting battery metrics for the [Battery Device Vital](https://docs.memfault.com/docs/platform/memfault-core-metrics?platform=MCU#battery) and a heartbeat metric for battery voltage. To leverage this port, set `CONFIG_MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX=y`. Note that users must provide the header `memfault_nrf_platform_battery_model.h`, which should define the battery model as specified by the nRF Fuel Gauge API. See the [Nordic docs](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/nrf_fuel_gauge/README.html#nrf-fuel-gauge) for more detail. Since this port calls `nrf_fuel_gauge_process()`, applications that want to read state of charge should call `memfault_platform_get_stateofcharge()` to avoid conflicting calls to the fuel gauge library. - Added a CoAP client implementation capable of uploading Memfault data through an [nRF Cloud](https://www.nrfcloud.com/) connection. This is primarily intended for use with the Nordic nRF91x series devices using LTE-M or NB-IoT connectivity. To enable, use `CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP=y`. This will change the protocol used by `memfault_zephyr_port_post_data()` (and `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD`), from HTTP to CoAP. - Zephyr: - Add the symbol `CONFIG_MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE` with a default value of 1000, which maps to 3 decimal places of precision for battery metrics. See the [Battery Device Vital docs](https://docs.memfault.com/docs/platform/memfault-core-metrics?platform=MCU#battery) for more information on configuring battery metric collection. ### 🐛 Fixed - Zephyr: - Fix a compiler warning on Zephyr v4.1, when using `CONFIG_MEMFAULT_METRICS_WIFI`. Thanks to [@chshzh](https://github.com/chshzh) for reporting this issue in [#98](https://github.com/memfault/memfault-firmware-sdk/issues/98) 🎉! - Fix an incorrect check for the Kconfig option `CONFIG_MEMFAULT_FAULT_HANDLER_LOG_PANIC` (previously was checking for `defined(MEMFAULT_FAULT_HANDLER_LOG_PANIC)`, which is incorrect). Thanks to [@konstk1](https://github.com/konstk1) for providing this fix in [#100](https://github.com/memfault/memfault-firmware-sdk/pull/100) 🎉! - General: - Fix a few files that were missing necessary `#include ` or `#include ` directives. - nRF Connect SDK: - Fix a compilation error when building for the nRF53 series on nRF Connect SDK v3.2.0-rc1 and later, caused by a change in the NRFX HAL. Thanks to [@Damian-Nordic](https://github.com/Damian-Nordic) for providing the fix in [#99](https://github.com/memfault/memfault-firmware-sdk/pull/99) 🎉! ## [1.31.0] - 2025-11-22 This is a minor feature and bugfix release. Key updates: - Fix for compact log serialization error when logs exceed max length - Handle an upcoming Zephyr API change that renames BLE connection interval field (used in built-in Bluetooth metrics) ### 📈 Added - Zephyr: - Added CPU temperature metrics support for additional platforms: - Devices with a die temp device tree node with `compatible = "nordic,nrf-temp"`; - Platforms using `die_temp` or `die_temp0` aliases, a `temp` nodelabel, or the `memfault_cpu_temp` alias. This support is enabled out of the box for the nRF54L15-DK board. - Add a new demo CLI command, `mflt get_reboot_reason`, which displays the device reboot reason and the prior stored reboot reason. Example output: ```bash uart:~$ mflt get_reboot_reason Current Reboot Reason Reg: 0x0008 Prior Stored Reboot Reason: 0x0002 ``` ### 🛠️ Changed - Zephyr: - Updated the `mflt export` command to print out chunk data using `shell_print()` instead of `printk()`. Using `printk()` can lead to dropped chunk data when `CONFIG_LOG_PRINTK=y` and `CONFIG_LOG_MODE_DEFERRED=y`. Using `shell_print()` avoids this issue. - Change the precedence of reboot reason bits when decoding the Zephyr hwinfo reset reason register. This improves the accuracy of reboot reasons on STM32 platforms, where the Pin Reset and Software Reset bits can be set simultaneously with other reset reasons. - Support an [upcoming change](https://github.com/zephyrproject-rtos/zephyr/commit/c14dcaf1995ea9c70b4ce334e4c9765da09eb35d) in Zephyr v4.4.0 / nRF Connect SDK v3.2.0, where the `bt_conn_le_info.interval` field changes to `bt_conn_le_info.interval_us`. Thanks to [@weeTike](https://github.com/weeTike) for providing this patch in [#97](https://github.com/memfault/memfault-firmware-sdk/pull/97) 🎉! This removes the existing metric `bt_connection_interval`, replacing with `bt_connection_interval_us`. Please reach out if you need support for the previous metric (which was added in SDK v1.29.0) - General: - Fix a few new warnings when building with clang 21.0.0+. ### 🐛 Fixed - General: - Fixed an error in Compact Log serialization that resulted in invalid log data, which caused decoding to fail when processing the log in the Memfault cloud. This issue only impacted compact logs that were within 4 bytes of the `MEMFAULT_LOG_MAX_LINE_SAVE_LEN` limit; longer compact logs were silently dropped but did not cause decoding failures. This fix will now insert a placeholder log line when a compact log exceeds the maximum length: `[MFLT] compact log too long: 1234 bytes (file:)` (where `1234` is the serialized log entry length that exceeded the limit). - FreeRTOS - Fixed an undefined macros build error on FreeRTOS versions < V11.0.0 in the FreeRTOS thread metrics port. Thread metric collection is enabled by default on FreeRTOS platforms but can be disabled with `#define MEMFAULT_FREERTOS_COLLECT_THREAD_METRICS 0`. ## [1.30.3] - 2025-10-23 This is a patch release, fixing build errors and one bug. ### 🛠️ Changed - Zephyr: - Update the RRAM coredump storage backend implementation to support the Nordic nRF54LM20 (and other Nordic nRF54L series chips) when not using partition manager to assign flash regions (i.e. using device tree fixed partitions). See [`ports/zephyr/common/memfault_rram_backed_coredump.c`](ports/zephyr/common/memfault_rram_backed_coredump.c) for details, and how to enable the coredump storage backend. - Fix a compilation error when `CONFIG_MEMFAULT_METRICS_THREADS_DEFAULTS=n`. - Remove an error log statement from [`memfault_platform_thread_metrics.c`](ports/zephyr/common/memfault_platform_thread_metrics.c). Previously this would log as ` mflt: No thread name registered for 0x2000eae8`, for example, but was not useful in many cases, and is not an error in any case. ### 🐛 Fixed - ESP-IDF: - Fix a compilation issue when building the [ESP32 sample app](examples/esp32) for an ESP32-C6 with `MEMFAULT_DISABLE=` set (i.e. `MEMFAULT_DISABLE=1 idf.py set-target esp32c6 build`). This only impacts the sample application. - Zephyr: - Disable invoking `LOG_PANIC()` during fault handling by default. When deferred logging is used (`CONFIG_LOG_MODE_DEFERRED=y`), triggering a panic flush from fault context may result in some log backends (`CONFIG_SHELL_LOG_BACKEND` for example) to double-fault, which results in failed coredump captures. This specifically can happen when running a non-secure application with TF-M, and passing back from a secure fault handler using `CONFIG_TFM_ALLOW_NON_SECURE_FAULT_HANDLING=y`. Users who are comfortable enabling this can re-enable `LOG_PANIC()` with `CONFIG_MEMFAULT_FAULT_HANDLER_LOG_PANIC=y`. - Remove an unnecessary `depends on PARTITION_MANAGER_ENABLED` in the `MEMFAULT_COREDUMP_STORAGE_RRAM` Kconfig setting. This feature only requires a partition labeled `memfault_coredump_partition`, but does not require partition manager specifically (which is a Nordic nRF-Connect SDK feature, not a Zephyr feature). Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this issue in [#96](https://github.com/memfault/memfault-firmware-sdk/issues/96)! ## [1.30.2] - 2025-10-10 This is a patch release, including only a change to boolean Kconfig prompts so they comply with Kconfig requirements in the nRF-Connect SDK. ## [1.30.1] - 2025-10-03 This is a patch release, fixing one bug and applying a minor code quality improvement to the Python tooling files. ### 🐛 Fixed - nRF-Connect SDK: - Fixed an issue where panic logs generated from ISRs caused a double fault for Nordic nRF91 users where `CONFIG_LOG_MODE_IMMEDIATE=n` and `MEMFAULT_LOG_TIMESTAMPS_ENABLE` is 1. - General: - Added Python type annotations to the files in the `tasks` directory. ## [1.30.0] - 2025-09-23 This is a minor release. Highlights: - Added active task stack collection control in Zephyr - Changed mount point selection for filesystem metric to look up from Zephyr device tree fstab entries - Fixed potential WiFi stack overflow on nRF70 series devices during HTTP uploads ### 📈 Added - Zephyr: - Add Kconfig option `CONFIG_MEMFAULT_COREDUMP_ACTIVE_TASK_STACK_SIZE_TO_COLLECT` to control how much of the active task stack is collected in coredumps. This can be used to prioritize capturing details about the running task when coredump storage space is limited. Defaults to `CONFIG_MEMFAULT_COREDUMP_STACK_SIZE_TO_COLLECT` for backwards compatibility. - Add the `mflt_http` workqueue thread to the default set of threads tracked with max stack usage metrics. The default thread metrics can be controlled with `CONFIG_MEMFAULT_METRICS_THREADS_DEFAULTS`. ### 🛠️ Changed - Zephyr: - Replace use of deprecated API `bt_hci_cmd_create()` with `bt_hci_cmd_alloc()` for Zephyr 4.2+. - Enable `FileSystem_BytesFree` metric by default only when fstab is present in the device tree. If present, the mount point is now automatically detected from checking fstab nodes. Manual configuration of the mount point via `CONFIG_MEMFAULT_FS_BYTES_FREE_VFS_PATH` still takes precedence when set. If not using fstab, set `CONFIG_MEMFAULT_FS_BYTES_FREE_METRIC=y` to enable collection. ### 🐛 Fixed - nRF-Connect SDK: - Increase the default value of `CONFIG_MEMFAULT_HTTP_DEDICATED_WORKQUEUE_STACK_SIZE` to 4kB when uploading via WiFi on the nRF70 series. This avoids potential stack overflows caused while performing periodic uploads via HTTP. Thanks to [@chshzh](https://github.com/chshzh) for reporting this and proposing a fix in [#95](https://github.com/memfault/memfault-firmware-sdk/issues/95)! ## [1.29.0] - 2025-09-11 This is a minor release. Highlights: - Added 9 new built-in metrics for BLE devices for Zephyr projects 🎉 - Added a session metrics API to deactivate a session - Fixed a bug where thread state was missing from small coredumps in ESP-IDF ### 📈 Added - General: - Add a new Session Metric API: `MEMFAULT_METRICS_SESSION_RESET()`, which can be used to deactivate an active session instead of ending it with `MEMFAULT_METRICS_SESSION_END()`. This will discard the metrics in the specified session. - nRF-Connect SDK: - Added an implementation for storing coredumps in RRAM, for SOCs that support it (nRF54L series for example). Enable with `CONFIG_MEMFAULT_COREDUMP_STORAGE_RRAM=y`. Requires a partition manager entry named `memfault_coredump_partition`. - Also added a coredump storage implementation for MRAM, specifically targeting the nRF54H series of devices. Enable with `CONFIG_MEMFAULT_COREDUMP_STORAGE_MRAM=y`. Requires adding a fixed partition entry named `memfault_coredump_partition`, for example via a devicetree overlay. For the `nrf54h20dk_nrf54h20_cpuapp`, the following sample overlay reduces the default size of the `storage_partition` and adds the necessary `memfault_coredump_partition` entry: ```c &mram1x { partitions { storage_partition: partition@1a4000 { reg = <0x1a4000 DT_SIZE_K(20)>; }; memfault_coredump_partition: partition@1b4000 { reg = <0x1a9000 DT_SIZE_K(20)>; }; }; }; ``` - Added example definition and usage of custom reboot reasons in the nRF9160 app. These reboot reasons can be triggered with a new shell command: `app reboot_custom ` ### 🛠️ Changed - nRF-Connect SDK: - Moved the default statement setting `CONFIG_MEMFAULT_HTTP_USES_MBEDTLS=n` when `CONFIG_NRF_MODEM_LIB=y` into the Memfault SDK Kconfig file. This default currently exists in NCS in a symbol re-definition but will be removed in the next version of NCS (`v3.2` expected). - Zephyr: - The default implementation of `memfault_zephyr_get_device_id()`, used to populate the Device Serial, will use the `hwinfo_get_device_id()` value by default if `CONFIG_HWINFO` is enabled. Previously this also required `CONFIG_MEMFAULT_REBOOT_REASON_GET_HWINFO=y`, and would default to `{BOARD}-testserial` when that was not enabled. - Add several new built-in BLE metrics: - `bt_gatt_mtu_size` - `bt_connection_remote_info` - `bt_connection_event_count` - `bt_connection_interval` - `bt_connection_latency` - `bt_connection_timeout` - `bt_connection_rssi` - `bt_connected_time_ms` - `bt_disconnect_count` These metrics are enabled by default when `CONFIG_BT=y` and `CONFIG_BT_CONN=y`. - ESP-IDF: - Support `CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD` optional HTTP client parameters in upcoming ESP-IDF v6 ([this change](https://github.com/espressif/esp-idf/commit/2db4bcf87db492c03c90cb086c0b3bde06e51201) made them optionally declared). - Update the post-link steps to be compatible with upcoming ESP-IDF build changes for ESP-IDF > 5.5. ### 🐛 Fixed - ESP-IDF - Fixed a bug where some FreeRTOS symbols needed for thread awareness were missing from coredumps if the coredump storage was too small. ## [1.28.0] - 2025-08-04 This is a minor update release. Highlights: - added a new test command for simulating hangs in ISRs - improved NMI exception capture - fixed a build issue in the nRF-Connect SDK port ### 📈 Added - Zephyr: - Add a new test command, `mflt test isr_hang`, which will cause a busy loop hang inside a `k_timer`, which normally is executing from an ISR context. The system will only exit this condition if there is a watchdog or other higher-priority interrupt capable of preempting the `k_timer` ISR. The [`qemu` sample app](examples/zephyr/qemu) is updated to enable a watchdog which generates an NMI exception, caught by Memfault. - Added a debug print when uploading data using `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD` that shows the bytes sent: ```bash [00:09:45.821,000] mflt: memfault_platform_log: Uploaded 118 bytes of Memfault data ``` Only enabled when debug level prints are enabled for Memfault (`CONFIG_MEMFAULT_LOG_LEVEL_DBG=y`). ### 🛠️ Changed - Zephyr: - NMI exceptions are now properly handled. Prior to this change, a coredump was captured on NMI exceptions but the MSP context was not fully unwound and the NVIC state was not included in the trace data. - Add a new Kconfig option, `CONFIG_MEMFAULT_COREDUMP_NVIC_INTERRUPTS_TO_COLLECT`, which controls the existing `memfault_platform_config.h` setting `MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT` for Cortex-M targets. The default now is to collect all `NUM_IRQS` as defined by Zephyr, which on many platforms will increase from the previous default of `32`. This improves the out-of-the-box information at the cost of 44 bytes in coredump storage consumed per additional 32 interrupts. Some example deltas shown below, including the byte delta in the stored coredump: | Platform | Previous Default | New Default | Byte Delta | | --------- | ---------------- | ----------------------------------------------- | ---------- | | nRF52840 | 32 | 64 (\*48, rounded up to nearest multiple of 32) | +44 | | nRF91 | 32 | 96 (\*65 rounded up) | +88 | | nRF53 | 32 | 96 (\*69 rounded up) | +88 | | STM32F407 | 32 | 96 (\*82 rounded up) | +88 | | STM32H7B0 | 32 | 160 (\*155 rounded up) | +176 | | STM32H563 | 32 | 160 (\*155 rounded up) | +176 | To restore the previous default, set `CONFIG_MEMFAULT_COREDUMP_NVIC_INTERRUPTS_TO_COLLECT=32`. - nRF-Connect SDK: - Fix a build issue impacting some nRF54 series chips related to reboot reason decoding. Thanks to [@grochu](https://github.com/grochu) for providing the fix in [#94](https://github.com/memfault/memfault-firmware-sdk/pull/94) 🎉! ## [1.27.0] - 2025-07-21 ### 📈 Added - General: - Add a reference software watchdog port for the STM32L4 series LPTIM peripheral. Users of the STM32 HAL can now compile in the reference port and the `MemfaultWatchdog_Handler`. The handler will save a coredump so the full system state can be recovered when a watchdog takes place. More details can be found in [`ports/include/memfault/ports/watchdog.h`](ports/include/memfault/ports/watchdog.h). - Add CLI commands `wdg_enable`, `wdg_disable`, and `wdog_update ` for testing a software watchdog port. These commands are disabled by default and can be enabled for platforms using the minimal shell/console with `MEMFAULT_DEMO_CLI_WATCHDOG`. - Zephyr: - Add a new Kconfig option, `CONFIG_MEMFAULT_HTTP_MAX_MESSAGES_TO_SEND`, which controls the max number of messages that will be sent in a single invocation of `memfault_zephyr_port_post_data()` or similar APIs. The default is `100`, which is suitable for most applications. Before this change, the limit was hard coded to 5 messages, which was too low for systems with infrequent upload intervals. - Support building for `native_sim` on arm64 hosts (specifically, the `native_sim/native/64` target), in addition to x86 hosts. ### 🛠️ Changed - Zephyr: - Improve the default implementation of `memfault_platform_sanitize_address_range()` to include all memory accessible by the kernel. This enables collection of heap-allocated task control blocks, which was previously unsupported. Users with discontiguous memory regions should provide their own implementation, as before. - Prioritize the thread bookkeeping array when collecting thread information in a coredump (when `CONFIG_MEMFAULT_COREDUMP_COLLECT_TASKS_REGIONS=y`, the default). This improves the quality of the processed coredump if the coredump region is too small to collect all stacks for all threads in the system (impacts systems with many threads and limited coredump storage space). - Add a new Kconfig option, `CONFIG_MEMFAULT_HTTP_PACKETIZER_BUFFER_SIZE`, which controls the size of the intermediate buffer used when reading data from the underlying data source (coredump storage, log buffer, CDR, etc) when uploading data to Memfault via HTTP. The default size is 128 bytes, and 1024 bytes on nRF91x series SOCs to better support modem trace CDR upload. Thanks to [@DematteisGiacomo](https://github.com/DematteisGiacomo) for submitting this in [#92](https://github.com/memfault/memfault-firmware-sdk/pull/92). - Make the `mflt post_chunks` CLI command available only when `CONFIG_NETWORKING` is enabled, to avoid confusion on platforms without networking support. - ESP-IDF: - Handle deprecated Deep Sleep API calls for upcoming ESP-IDF v5.5 and v6. - Supporting building with the Memfault CLI commands disabled, `CONFIG_MEMFAULT_CLI_ENABLED=n`. Thanks to [@finger563](https://github.com/finger563) for reporting this issue and providing a fix in [#93](https://github.com/memfault/memfault-firmware-sdk/issues/93) 🎉! ### 🐛 Fixed - Zephyr: - Fix an issue where the socket file descriptor can potentially be leaked when the connection terminated unexpectedly during an HTTP chunk upload. Thanks to [@DematteisGiacomo](https://github.com/DematteisGiacomo) for submitting this in [#92](https://github.com/memfault/memfault-firmware-sdk/pull/92). ## [1.26.1] - 2025-06-30 This is a minor fix release, addressing one future compatibility issue with the Zephyr port. ### 🛠️ Changed - Zephyr: - Apply a compatibility fix for upcoming Zephyr 4.2.0, fixing the size of the `net_mgmt` callback `mgmt_event` parameter. Thanks to [@rlubos](https://github.com/rlubos) for providing the fix in [#91](https://github.com/memfault/memfault-firmware-sdk/pull/91) 🎉! ## [1.26.0] - 2025-06-26 This is a feature release, primarily addressing future compatibility changes for the next nRF-Connect SDK release. ### 📈 Added - Zephyr: - Add support for the ESP32 chip on Zephyr, adding to the existing support for ESP32-S3 and ESP32 C series chips. - Add support for building the Memfault SDK on the `native_sim` board. Note that Memfault does not support reboot tracking or coredumps on this target, but other features are supported. ### 🛠️ Changed - General: - Add a `MEMFAULT_ENABLE_WARNING(warning)` macro complementing the existing `MEMFAULT_DISABLE_WARNING(warning)` macro. This macro is only implemented for GCC + Clang. - Zephyr: - Remove the External Module logic, used to support multiple Zephyr versions, and instead use the normal Zephyr module Kconfig path specifier. There should be no user-facing changes from this change. It addresses an issue with Kconfig symbol linting. - In the [Zephyr QEMU sample app](examples/zephyr/qemu/qemu-app/prj.conf), add the `--param=min-pagesize=0x1000` compiler option, which will catch dereferences to low memory addresses. This is only for static analysis purposes and does not affect any behavior. - ESP-IDF: - The heartbeat metrics timer is now enabled by default when `CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT=y`. Version `1.25.0`, which added deep sleep support, had disabled the normal heartbeat metrics timer by default. The default behavior can be overridden with the Kconfig option `CONFIG_MEMFAULT_METRICS_HEARTBEAT_TIMER_ENABLE`. - nRF-Connect SDK: - Replace use of `LTE_LC_ON_CFUN` with `NRF_MODEM_LIB_ON_CFUN` for nRF-Connect SDK v2.8.0+. This deprecated API is scheduled to be removed in the next nRF-Connect SDK release. ## [1.25.0] - 2025-06-09 This is a feature release of the Memfault Firmware SDK. The main new feature released in this version is support for tracking metrics through deep sleep on ESP32 devices. Full release notes are below. ### 📈 Added - Zephyr: - Add a new choice config `CONFIG_MEMFAULT_REBOOT_REASON_GET`. By default, `CONFIG_MEMFAULT_REBOOT_REASON_GET_HWINFO=y`, which is supported by `imply CONFIG_HWINFO` in the overarching `MEMFAULT` symbol. This default enables better reboot reasons out-of-the-box via Zephyr's `hwinfo` module. The fall-back option is `CONFIG_MEMFAULT_REBOOT_REASON_GET_BASIC`, which provides a simple implementation. As before, users can override the default implementations with `CONFIG_MEMFAULT_REBOOT_REASON_GET_CUSTOM=y`. - Add a new Kconfig setting, `CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP`, to print the reboot reason code on system boot, for debugging purposes. This feature is enabled by default. It can be disabled with `CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP=n`. - Add a `boot_time_ms` metric, which tracks how long the system takes to boot the application. Can be disabled with `CONFIG_MEMFAULT_METRICS_BOOT_TIME=n`. - Add new builtin Wi-Fi metrics, enabled by default when `CONFIG_WIFI=y`, and can be disabled with `CONFIG_MEMFAULT_METRICS_WIFI=n`: - `wifi_beacon_interval` - `wifi_dtim_interval` - `wifi_frequency_band` - `wifi_primary_channel` - `wifi_security_type` - `wifi_sta_rssi` - `wifi_standard_version` - `wifi_twt_capable` - `wifi_tx_rate_mbps` (Zephyr 4.1.0+ only) These add on top of the existing Zephyr Wi-Fi metrics: - `wifi_ap_oui` - `wifi_connected_time_ms` - `wifi_disconnect_count` - Add an option to upload logs by default when using `MEMFAULT_HTTP_PERIODIC_UPLOAD`, controlled with the Kconfig symbol `MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS`. This can also be controlled at runtime with the included API `memfault_zephyr_port_http_periodic_upload_logs(bool enable)` - Add a new Kconfig option, `CONFIG_MEMFAULT_PLATFORM_TIME_SINCE_BOOT_CUSTOM`, to provide a custom implementation of `memfault_platform_get_time_since_boot_ms()` in your application. The default is an implementation using `k_uptime_get()`. - ESP-IDF: - Add a `boot_time_ms` metric, which tracks how long the system takes to boot the application. Can be disabled with `CONFIG_MEMFAULT_METRICS_BOOT_TIME=n`. - Add support for tracking metrics across ESP32 deep sleep cycles, enabled with the Kconfig `CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT=y`. To utilize this feature, these functions must be called by the application: - `memfault_platform_deep_sleep_save_state()` Must be called just prior to entering deep sleep (`esp_deep_sleep_start()`) - `memfault_platform_deep_sleep_restore_state()` Must be called before `memfault_platform_boot()` in the application startup sequence. This feature includes built-in metrics for tracking deep sleep: - `deep_sleep_time_ms` - time spent in deep sleep - `active_time_ms` - time spent out of deep sleep - `deep_sleep_wakeup_count` - number of times the device woke up from deep sleep There are several Kconfig options for controlling the deep sleep feature, including controlling the heartbeat trigger and HTTP periodic upload. See `menuconfig` "Memfault deep sleep support" or [`ports/esp_idf/memfault/Kconfig`](ports/esp_idf/memfault/Kconfig) for details. - Add new metrics tracking flash usage: - `flash_spi_erase_bytes` - `flash_spi_write_bytes` - `flash_spi_total_size_bytes` - Add capture of the ESP-IDF Task Watchdog stuck task list in coredumps. This is enabled by default if ESP-IDF Task Watchdog is enabled, and can be disabled with the Kconfig `CONFIG_MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG=n`. ### 🛠️ Changed - nRF Connect SDK: - `CONFIG_MEMFAULT_REBOOT_REASON_GET_CUSTOM` is now a choice in the new choice config `CONFIG_MEMFAULT_REBOOT_REASON_GET`. As a result, it will be the default choice if `CONFIG_MEMFAULT_NRF_CONNECT_SDK=y` instead of being `imply`-ed by `CONFIG_MEMFAULT_NRF_CONNECT_SDK` to work around the restriction that choice configs cannot be selected. As before, users can override this behavior with `CONFIG_MEMFAULT_REBOOT_REASON_GET_CUSTOM=n`. - ESP-IDF: - Rename `CONFIG_MEMFAULT_TIME_SINCE_BOOT_CUSTOM` -> `CONFIG_MEMFAULT_PLATFORM_TIME_SINCE_BOOT_CUSTOM`. A new ESP-IDF port choice Kconfig `CONFIG_MEMFAULT_PLATFORM_TIME_SINCE_BOOT` now supports 3 settings for platform time since boot: 1. `MEMFAULT_PLATFORM_TIME_SINCE_BOOT_ESP_TIMER` default, suitable for most applications 2. `MEMFAULT_PLATFORM_TIME_SINCE_BOOT_DEEP_SLEEP` applicable for deep-sleep applications 3. `MEMFAULT_PLATFORM_TIME_SINCE_BOOT_CUSTOM` disable builtin implementations and implement a custom `memfault_platform_get_time_since_boot_ms()` - Renamed the `spi_flash_chip_id` metric (added in `1.23.0`) to `flash_spi_manufacturer_id`. - Renamed the `wifi_auth_mode` metric to `wifi_security_type` to more accurately indicate the property being measured. - General: - Add the `demo` component to the default set of components added to an Eclipse project when using the [`eclipse_patch.py`](scripts/eclipse_patch.py) utility. The default components can be overridden with the `--components` argument. - Coredumps no longer include the device serial by default. The uploading serial (passed to the chunks endpoint) is instead used to identify the device associated with the coredump. Enabling `#define MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL 1` in `memfault_platform_config.h` will include the device serial in coredumps. ### 🐛 Fixed - ESP-IDF: - Remove debug logging from `memfault_platform_time_get_current()`. When log timestamps are enabled, and debug level logs are enabled, this function can infinitely recurse. - Fix Memfault Build ID insertion when `CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y`. Previously, the build ID was inserted at the wrong build step, resulting in a missing build ID in the flashable image. - Zephyr: - Remove debug logging from the RTC-backed `memfault_platform_time_get_current()` (enabled when `CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_RTC=y`). When log timestamps are enabled, logging from this function can result in infinite recursion under certain conditions. - Update the west module allowlist to include `cmsis_6` in the [Zephyr QEMU Sample App](examples/zephyr/qemu/qemu-app). The `cmsis_6` module is now used for TF-M and Cortex-M as of [this PR](https://github.com/zephyrproject-rtos/zephyr/pull/89370/files). - Update the Memfault Zephyr logging backend to properly capture log lines when `CONFIG_LOG_MODE_IMMEDIATE=y` on Zephyr 3.7.0+. - Wiced: - Fix broken symlinks in the [Wiced example](examples/wiced). ## [1.24.0] - 2025-05-06 ### 📈 Added - General: - Add a new API, `memfault_log_get_unsent_count()`, which returns the log count and size in bytes of unsent logs in the log buffer. This can be used inside `memfault_log_handle_saved_callback()` for example to drain the packetized logs when a certain watermark is reached. - ESP-IDF: - Add a Kconfig option, `CONFIG_MEMFAULT_TIME_SINCE_BOOT_CUSTOM`, which when set `=n`, enables using a custom implementation of `memfault_platform_get_time_since_boot_ms()`. - Add 2 new metrics for tracking raw network bytes rx / tx. These metrics track network IO traffic on the default netif: - `network_rx_bytes` - `network_tx_bytes` These metrics are enabled by default, and can be disabled with `CONFIG_MEMFAULT_METRICS_NETWORK_IO=n` ### 🛠️ Changed - FreeRTOS (including ESP-IDF): - Rename the thread stack usage measurement variable included in coredumps when `MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE` is enabled from `high_watermark` to `stack_unused`. This change is to make the implementation more readable. The Memfault backend is updated to process both the old and new formats. - FreeRTOS-detected stack overflows (via `vApplicationStackOverflowHook`) will now be correctly tagged as `Stack Overflow` for the trace reason in Memfault, instead of `Assert`. ### 🐛 Fixed - Zephyr: - Fix a null dereference when calling `memfault_coredump_storage_compute_size_required()` (eg the shell command `mflt coredump_size`). - General: - For the emlib WDOG port implementation, [`ports/emlib/wdog_software_watchdog.c`](ports/emlib/wdog_software_watchdog.c), enable the WDOG when in EM1 mode for series2 chips. - Added support for `MEMFAULT_REBOOT_REASON_CLEAR` in the [`ports/nrf5_sdk/resetreas_reboot_tracking.c`](ports/nrf5_sdk/resetreas_reboot_tracking.c) implementation, by default enabled (like other ports). This permits opting out of the auto-clearing of the `NRF_POWER->RESETREAS` register, in case the user needs it after the function runs. - FreeRTOS (including ESP-IDF): - Fix incorrect computation of per-thread stack usage metrics (`MEMFAULT_FREERTOS_COLLECT_THREAD_METRICS`). Before this fix, the returned values had 2 errors: 1. the value is the _unused_ stack space, not the used stack space 2. on platforms where `sizeof(StackType_t)` is not 1 byte, the numerator when computing percentage is incorrectly scaled down by `sizeof(StackType_t)`, resulting in significant under-reporting of the stack usage percentage. Users can apply device and software version filtering on dashboards to filter out reports from devices that are running an old version of the SDK. - ESP-IDF: - Fix a potential issue that would cause the wrong implementation of `memfault_platform_time_get_current()` to be included in the final link, when `CONFIG_MEMFAULT_SYSTEM_TIME=y` is enabled (default). ### 🛠️ Changed - ESP-IDF: - `CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP` now defaults to `y` instead of `n`, to print out the reboot reason on boot. Disable it with `CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP=n`. ## [1.23.1] - 2025-04-17 ### 🐛 Fixed - Modify the test command used in our public CircleCI job, to work around an issue with [CircleCI's v2 container runtime](https://discuss.circleci.com/t/docker-executor-infrastructure-upgrade/52282), which uses `cgroupv2` instead of `cgroupv1`. ## [1.23.0] - 2025-04-16 ### 📈 Added - General: - Improved support for run time tracking on FreeRTOS kernel version v11 and later, by adding compile-time checks for possible configuration issues. - Add reboot reason decoding for the NXP RW61x chip series, using the `PMU.SYS_RST_STATUS` register to determine the reset reason. Add the file at [`ports/nxp/rw61x/pmu_reboot_tracking.c`](ports/nxp/rw61x/pmu_reboot_tracking.c) to your project to make use of it! - Add reboot reason decoding for the SiLabs SiWx91x chip series. This implementation supports `Power On Reset`, `Pin Reset`, and `Software Reset` causes only. Please [reach out](mflt.io/support) if you are interested in other reset reasons. Add the file at [`sdk/embedded/ports/silabs/wiseconnect/siwx91x/siwx91x_reboot_tracking.c`](ports/silabs/wiseconnect/siwx91x/siwx91x_reboot_tracking.c) to your project to make use of it. - Add an [implementation of `memfault_reboot_reason_get()`](ports/stm32cube/h5/rcc_reboot_tracking.c) for the STM32H5xx series of MCUs, using the `RCC-RSR` register to determine the reset reason. Add the file to your project to make use of it! - ESP-IDF: - Add 2 new out-of-box metrics: - `spi_flash_chip_id` : the 24-bit RDID value of the SPI flash chip, for example `"c84017"` = GigaDevice GD25Q64 8MiB - `esp_chip_revision` : the ESP32 chip and revision, for example `esp32c6-0.0` or `esp32s3-0.2` - For ESP-IDF v5.5 and later, enable `-ggdb3` by default for enhanced debugging. This is controlled with the Kconfig `CONFIG_MEMFAULT_GGDB3`. - Set the User-Agent to `MemfaultSDK/` when sending HTTP requests to Memfault. - Zephyr: - Improve support for flushing data cache prior to reboot, for SOCs with `CONFIG_DCACHE` enabled. ### 🛠️ Changed - General: - Updated the internally used `clang-format` version to 20.1.0, latest at time of writing. This resulted in a few minor format changes in the SDK. - Zephyr: - Update the [Zephyr QEMU example](examples/zephyr/qemu) to use the latest Zephyr version, v4.1.0. - nRF-Connect SDK: - Update the [nRF-Connect SDK examples](examples/nrf-connect-sdk) to use the latest nRF-Connect SDK version, v2.9.1. - Update the [nRF91 example](examples/nrf-connect-sdk/nrf9160) to enable the `DATETIME` subsystem, to tag SDK event and log data with timestamps once the network connection is activated. - ESP-IDF: - Update the `prv_panic_safe_putstr()` implementation to perform better on modern ESP-IDF versions (>=4.4.0). This function is used to output local prints during panic handling. - Add a Kconfig `CONFIG_MEMFAULT_DATA_CHUNK_HANDLERS_CUSTOM` to allow overriding `memfault_esp_port_data_available()` and `memfault_esp_port_get_chunk()`. If other MCUs are forwarding data to an ESP32 for transport, enable this option and provide definitions to handle additional sources. By default, these functions only check the local device for available chunks. ### 🐛 Fixed - ESP-IDF: - Correctly print the initial delay and period in seconds for the periodic upload task. Previously, this info log print was using the value in milliseconds. ## [1.22.0] - 2025-03-19 ### 📈 Added - General - Enable building the SDK on `aarch64`. Note that this only adds support for building the SDK on that target, full end-to-end support is not yet available. - Zephyr: - For SOC's with a data cache, flush the cache prior to performing a system reboot, to ensure data integrity of the RAM-backed reboot tracking data. This is added to the `memfault_platform_reboot()` default implementation, which can be overridden by the user if needed. SOC's without a cache will have no effect from this change. ### 🛠️ Changed - General: - `MEMFAULT_LOG_TIMESTAMPS_ENABLE` is now enabled by default. When enabled, and `memfault_platform_time_get_current()` returns a valid timestamp, logs will include a second-precision timestamp. This applies to both: - logs captured as part of a coredump - logs sent to Memfault after invoking `memfault_log_trigger_collection()` - Remove some 64-bit integer divides used when computing the built-in `uptime_s` metric, and in the FreeRTOS `memfault_platform_get_time_since_boot_ms()` implementation. - ESP-IDF: - Log lines that are only a single `'\n'` character are no longer recorded in the Memfault Log Buffer. - Zephyr: - The `hwinfo`-based Reset Reason implementation did not clear the reset reason register after reading the data. Updated to properly call `hwinfo_clear_reset_cause()` after reading the reset reason. ## [1.21.1] - 2025-03-07 ### 🐛 Fixed - General: - Disable `MEMFAULT_LOG_TIMESTAMPS_ENABLE` by default. The new timestamped log feature was enabled by default in the previous release, but only logs embedded in coredumps are fully supported. Logs captured with `memfault_log_trigger_collection()` do not yet support timestamps, so the feature should not be used in production until that support is added. ## [1.21.0] - 2025-03-06 ### 📈 Added - General: - Logs captured by Memfault now include a timestamp by default, when the platform implements `memfault_platform_time_get_current()`. This feature can be disabled by setting `#define MEMFAULT_LOG_TIMESTAMPS_ENABLE 0` in `memfault_platform_config.h`. - ESP-IDF: - Add new built-in Wi-Fi metrics: - `wifi_primary_channel` - the primary channel ID of the associated Wi-Fi access point - `wifi_auth_mode` - the authentication mode of the associated Wi-Fi access point, for example `WPA2_PSK` - `wifi_standard_version` - the Wi-Fi version of the associated Wi-Fi access point, for example `802.11n` These metrics are enabled by default and can be disabled (along with other built-in Wi-Fi metrics) with the Kconfig option `CONFIG_MEMFAULT_ESP_WIFI_METRICS` ### 🛠️ Changed - ESP-IDF: - Support cases where the `IDF_VER` build variable is set to `"HEAD-HASH-NOTFOUND"` (i.e. using an ESP-IDF SDK that is not a git repo), when setting the built-in metric `MemfaultSdkMetric_os_version`. In this case, the value is taken from the `ESP_IDF_VERSION_x` macros, which are less precise. - Use more specific Memfault reset reason codes for these watchdog reset types, previously all categorized as `HardwareWatchdog` - `ESP_RST_INT_WDT` -> `SoftwareWatchdog` - `ESP_RST_TASK_WDT` -> `TaskWatchdog` - `ESP_RST_WDT` (RTC watchdog, the real hardware watchdog), stays as `HardwareWatchdog` ### 🐛 Fixed - ESP-IDF: - Correctly set the Memfault Firmware SDK version inside the [espressif component](https://components.espressif.com/components/memfault/memfault-firmware-sdk) version of the SDK. Prior to this fix, the SDK version reports as `"MemfaultSdkMetric_sdk_version": "0.0.0"`. No change to the SDK, only a tooling/release change. ## [1.20.0] - 2025-02-06 ### 📈 Added - General: - Make `memfault_reboot_reason_get()` and `memfault_platform_metrics_timer_boot()` weakly defined in the [platform templates](ports/templates) to make removing them optional when first integrating - Added a configuration option, `MEMFAULT_CRC16_BUILTIN`, that permits disabling the built-in [crc16](components/util/src/memfault_crc16_ccitt.c) implementation. The user should provide a compatible implementation of `memfault_crc16_compute()`. For example, if the Zephyr CRC library is used, a compatible implementation would be: ```c #include uint16_t memfault_crc16_compute(uint16_t crc_initial_value, const void *data, size_t data_len_bytes) { return crc16_itu_t(crc_initial_value, data, data_len_bytes); } ``` A Zephyr Kconfig setting, `CONFIG_MEMFAULT_CRC16_BUILTIN`, is also provided to control this option. Thanks to [@JordanYates](https://github.com/JordanYates) for submitting this feature request in [#84](https://github.com/memfault/memfault-firmware-sdk/issues/84) ! - Added an example `daily_heartbeat` session to the [FreeRTOS QEMU example](examples/freertos/src/metrics.c), which demonstrates how to send a daily heartbeat session to Memfault. Daily Heartbeats are a special category of Session Metrics, and can be used to track device properties over a longer interval than heartbeat metrics. - Added an optional field to the built-in [FreeRTOS task stack usage metrics](ports/include/memfault/ports/freertos/thread_metrics.h), `.get_task_handle`, which allows the user to provide a custom function to get the task handle for a given thread name, instead of using the thread name to identify the thread. This is useful in systems where there are threads with ambiguous names. The [ESP32 example app](examples/esp32/apps/memfault_demo_app/main/metrics.c) is updated to use this feature for ESP-IDF <5.3, where on dual-core SOCs, the per-core idle threads are both named `IDLE`. - nRF Connect SDK: - Added a new Kconfig symbol `CONFIG_MEMFAULT_FOTA_HTTP_FRAG_SIZE` to enable controlling the HTTP fragment size when using NCS >=2.9.9. Previously, `CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_1024=y` was required, but this option was deprecated in NCS 2.9.9. - Add built-in support for the `thermal_cpu_c` (CPU temperature) metric for nRF5x chips (nRF52 and nRF54 app core supported). Use the Kconfig setting `MEMFAULT_METRICS_CPU_TEMP` to disable the metric. - Zephyr - Add a new Kconfig setting, `CONFIG_MEMFAULT_HTTP_CLIENT_TIMEOUT_MS`, which controls the timeout for the Memfault HTTP client, used both for chunk upload and OTA operations. The default timeout is 5 seconds. Connections with poor latency may require a longer timeout to avoid premature disconnection. Thanks to [@chirambaht](https://github.com/chirambaht) for submitting this in [#86](https://github.com/memfault/memfault-firmware-sdk/issues/86)! ### 🐛 Fixed - ESP-IDF: - Use the configuration `MEMFAULT_LOG_MAX_LINE_SAVE_LEN` to set the max length of a log line when `CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK=y`, which is the default setting in ESP-IDF. Previously, the log line was arbitrarily truncated in the Memfault vprintf hook before being saved to the Memfault log buffer. - General: - Remove the `MEMFAULT_PACKED` attribute for the `eMemfaultRebootReason` declaration; this compiler extension is not supported on IAR EWARM's compiler. Change the assembly shim to properly zero-extend the enum constant to avoid ABI issues when invoking the C fault handling code. ### 🛠️ Changed - General: - Remove an extra underscore in the folder name when using the [`eclipse_patch.py`](scripts/eclipse_patch.py) utility with a port name that is one folder deep (e.g. `freertos`) - Rename the `memfault_crc16_ccitt_compute()` function to `memfault_crc16_compute()`. The CRC-16 algorithm used is canonically named `CRC-16/XMODEM`, **not** `CRC-16/CCITT` (aka `CRC-16/KERMIT`). The file implementing that function is left as `memfault_crc16_ccitt.c` for backwards compatibility. Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this issue in [#83](https://github.com/memfault/memfault-firmware-sdk/issues/83)! - Zephyr: - Add a missing Kconfig dependency to `MEMFAULT_METRICS_THREADS`, `INIT_STACKS`. Also add missing dependencies to the Kconfig option `MEMFAULT_METRICS_DEFAULT_SET_ENABLE`: - `INIT_STACKS` - `THREAD_RUNTIME_STATS` - `THREAD_STACK_INFO` Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this problem in [#86](https://github.com/memfault/memfault-firmware-sdk/issues/86) ! - Update the [memfault_zephyr_port_post_data()/memfault_zephyr_port_post_data_return_size()](ports/zephyr/common/memfault_platform_http.c) functions to only open the TLS socket if there is data ready to send, which is otherwise wasteful, as the socket will be closed without sending any Memfault data. - Add an explicit module name, `memfault-firmware-sdk`, to the module manifest. This avoids issues when the SDK is registered in a Zephyr manifest under a directory name other than `memfault-firmware-sdk`. Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this issue in [#81](https://github.com/memfault/memfault-firmware-sdk/issues/81)! - Exclude unused stack space when capturing thread stacks, via the `thread.stack_info.delta` property. This reduces the amount of coredump storage used to capture thread stacks, especially when `CONFIG_STACK_POINTER_RANDOM` or `CONFIG_THREAD_LOCAL_STORAGE` is enabled. Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this issue in [#81](https://github.com/memfault/memfault-firmware-sdk/issues/85)! - Fix a configuration issue when building for any of the following parts, but NOT using nRF-Connect SDK (i.e. using Zephyr instead): - SOC_SERIES_NRF52X - SOC_SERIES_NRF53X - SOC_SERIES_NRF54LX - SOC_SERIES_NRF91X The Memfault SDK now correctly enables nRF-Connect-SDK-specific functionality ONLY when that SDK is included as a Zephyr module. Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this issue in [#81](https://github.com/memfault/memfault-firmware-sdk/issues/89)! - nRF Connect SDK: - Remove use of child and parent image functionality in the nRF9160 sample app, and replace with sysbuild support. Child image support was deprecated in NCS 2.7.0 in favor of sysbuild. - Use the downloader library instead of the download client library when using NCS >= 2.9.9. The download_client was recently deprecated in favor of the downloader. Download client support is now in `memfault_fota_legacy.c`. ## [1.19.0] - 2025-01-10 ### 📈 Added - General: - Add an option to set the [Self Test component](https://docs.memfault.com/docs/mcu/self-test) output log level, `MEMFAULT_SELF_TEST_OUTPUT_LOG`, to control the verbosity of the Self Test output. Set it by selecting the Memfault Log macro to use, for example `#define MEMFAULT_SELF_TEST_OUTPUT_LOG MEMFAULT_LOG_DEBUG`. The default level is the same as before, `MEMFAULT_LOG_INFO`. - Add an [implementation of `memfault_reboot_reason_get()`](ports/stm32cube/u5/rcc_reboot_tracking.c) for the STM32U5xx series of MCUs, using the `RCC-CSR` register to determine the reset reason. Add the file to your project to make use of it! - Add an [implementation for flash-backed coredump storage](ports/stm32cube/u5/flash_coredump_storage.c) for the STM32U5xx series of MCUs, using the internal flash memory to store coredumps. Add the file to your project to make use of it! - Enable the MPU (Memory Protection Unit) in the [FreeRTOS QEMU example](examples/freertos/), to demonstrate Memfault's [MPU region analysis feature](https://docs.memfault.com/docs/platform/trace-details#mpu-analysis). This feature is enabled in a Memfault project by setting `#define MEMFAULT_COLLECT_MPU_STATE 1` in `memfault_platform_config.h`. The MPU registers are captured as part of a coredump, and Memfault will analyze the configuration and include the result in the Trace viewer. - Add a new reboot reason code, `kMfltRebootReason_TaskWatchdog`, for marking crashes due to a Task Watchdog. Memfault has a [built-in Task Watchdog system](https://github.com/memfault/memfault-firmware-sdk/blob/master/components/include/memfault/core/task_watchdog.h), and [Zephyr](https://docs.zephyrproject.org/latest/services/task_wdt/index.html) and [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/wdts.html#task-watchdog-timer-twdt) both implement Task Watchdog systems. - FreeRTOS: - Add support for tracking per-thread stack usage in the [Memfault FreeRTOS port](ports/freertos/src/memfault_sdk_metrics_thread.c). This feature is enabled by default and can be disabled by setting `#define MEMFAULT_FREERTOS_COLLECT_THREAD_METRICS 0` in `memfault_platform_config.h`. The default threads monitored are `IDLE` and `Tmr Svc`. Threads are registered for tracking by defining `MEMFAULT_METRICS_DEFINE_THREAD_METRICS()` in the application. For example: ```c //! Set the list of threads to monitor for stack usage. The metric keys must //! be defined in memfault_metrics_heartbeat_config.def, ex: //! //! MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE( //! memory_main_pct_max, kMemfaultMetricType_Unsigned, //! CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR //! ) #include "memfault/ports/freertos/thread_metrics.h" MEMFAULT_METRICS_DEFINE_THREAD_METRICS ( // monitor the main thread stack usage { .thread_name = "main", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_main_pct_max), }, // monitor the shell_uart thread stack usage { .thread_name = "shell_uart", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_shell_uart_pct_max), }); ``` - Add example usage of per-thread stack usage support to the [FreeRTOS QEMU example](examples/freertos/) for the idle, timer service, console input, metrics, and heap tasks. - Add tracking of libc heap usage via the `memory_pct_max` metric to the [FreeRTOS QEMU example](examples/freertos/) - Zephyr: - Add support for tracking per-thread stack usage in the [Memfault Zephyr port](ports/zephyr/common/memfault_platform_metrics.c). This feature is enabled by default and can be disabled by setting `CONFIG_MEMFAULT_METRICS_THREADS=n`. The default threads monitored are `main` and `sysworkq`. Threads are registered for tracking by defining `MEMFAULT_METRICS_DEFINE_THREAD_METRICS()` in the application. For example: ```c //! Set the list of threads to monitor for stack usage. The metric keys must //! be defined in memfault_metrics_heartbeat_config.def, ex: //! //! MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE( //! memory_main_pct_max, kMemfaultMetricType_Unsigned, //! CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR //! ) #include "memfault/ports/zephyr/thread_metrics.h" MEMFAULT_METRICS_DEFINE_THREAD_METRICS ( { .thread_name = "main", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_main_pct_max), }, { .thread_name = "shell_uart", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_shell_uart_pct_max), }); ``` - Update to support removal of the global `CSTD` compiler property (deprecated in Zephyr v3.7.0, and just removed in Zephyr `main`), when `CONFIG_MEMFAULT_COMPACT_LOG` is enabled. Thanks to [@fouge](https://github.com/fouge) for providing this fix in [#78](https://github.com/memfault/memfault-firmware-sdk/pull/78) ! - Add a new built-in metric, `cpu_usage_pct`, which reports the percentage of the CPU used. This metric is enabled by default as part of the default set of metrics, controlled with `CONFIG_MEMFAULT_METRICS_DEFAULT_SET_ENABLE`. - For ARM targets implementing and enabling the MPU, automatically capture the and [analyze the MPU configuration](https://docs.memfault.com/docs/platform/trace-details#mpu-analysis) as part of a coredump. This can be controlled with the `CONFIG_MEMFAULT_COREDUMP_COLLECT_MPU_STATE` Kconfig option. - Add a new Kconfig option, `CONFIG_MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS`, which should be used instead of `#define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS xxx` in `memfault_platform_config.h`. A build error will occur if the value is set in `memfault_platform_config.h` to enforce migrating the setting. Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this feature request in [#80](https://github.com/memfault/memfault-firmware-sdk/issues/80) - ESP-IDF: - Add support for correctly marking crashes triggered due to a Task Watchdog. A test command, `esp_task_watchdog `, has been added to the [esp32 sample app](examples/esp32) to trigger a Task Watchdog fault on the specified core. Be sure to enable the Kconfig option `CONFIG_ESP_TASK_WDT_PANIC=y` to have the system panic when a Task Watchdog fault occurs. Memfault will capture and tag the fault appropriately, as for other fault types. - Add a new Kconfig option, `CONFIG_MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS`, which should be used instead of `#define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS xxx` in `memfault_platform_config.h`. A build error will occur if the value is set in `memfault_platform_config.h` to enforce migrating the setting. - nRF-Connect SDK: - Add an implementation for reboot reason tracking on the nRF54Lx series of MCUs, using the `RESETREAS` register to determine the reset reason. This will be automatically enabled when building for an nRF54Lx series device (`CONFIG_SOC_SERIES_NRF54LX=y`). - Add example usage of per-thread stack usage support to the [nRF9160 example](examples/nrf-connect-sdk/nrf9160) for the idle, sysworkq, mflt http, WDT, and shell uart tasks. ### 🐛 Fixed - Zephyr: - Fix the `MEMFAULT_METRICS_CPU_TEMP` Kconfig dependencies, to correctly check for presence of the DT `die-temp0` alias, and remove the dependency on `ADC`, which doesn't apply to all boards implementing a temp sensor. Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this issue in [#79](https://github.com/memfault/memfault-firmware-sdk/issues/79) ! ### 🛠️ Changed - General: - The [`eclipse_patch.py`](scripts/eclipse_patch.py) utility `--memfault-sdk-dir` argument is now optional, and defaults to the parent directory of the script folder. - FreeRTOS: - Renamed the [FreeRTOS QEMU sample app](examples/freertos) heap metrics from `Example_HeapFreeBytes` and `Example_HeapMinFreeBytes` to `FreeRTOS_HeapFreeBytes` and `FreeRTOS_HeapMinFreeBytes`. - nRF-Connect SDK: - Update the [nRF91 sample app](examples/nrf-connect-sdk/nrf91) to only enable the UART log backend. Previously both the SHELL and UART log backends were enabled, resulting in duplicate log lines emitted to the console. - Update the [nRF91 sample app](examples/nrf-connect-sdk/nrf91) and the [nRF5x sample app](examples/nrf-connect-sdk/nrf5) to use the latest version of the nRF-Connect SDK, v2.9.0. - Zephyr: - Renamed the [QEMU sample app](examples/zephyr/qemu/) metric `main_thread_cpu_time_permille` -> `cpu_usage_main_pct`. ## [1.18.0] - 2024-11-25 ### 📈 Added - General: - Add a new built-in metric, `uptime_s`, which reports the total uptime of the device in seconds. This metric is enabled by default, and can be disabled with `#define MEMFAULT_METRICS_UPTIME_ENABLE 0` in `memfault_platform_config.h`. - Zephyr: - Update the [QEMU sample app](examples/zephyr/qemu) to use newly-released Zephyr v4.0.0 🥳. - ESP-IDF: - Added support for dual-core coredumps on ESP32 and ESP32-S3. This feature is enabled by default and can be disabled with the Kconfig option `CONFIG_MEMFAULT_COREDUMP_CPU_COUNT=1`. Note: not all fault conditions will cause both CPU cores to be captured in the coredump. The SDK will always capture the core that triggered the fault, and if the non-faulting core is available for capture, it will be included as well. ### 🛠️ Changed - ESP-IDF: - Updated the [ESP32 example](examples/esp32) to no longer read some non-volatile values into stack-allocated buffers. This reduces overall stack usage for the `main` thread by about 350 bytes. ## [1.17.0] - 2024-11-14 ### 📈 Added - General: - Add parsing of an optional log message argument for the `test_trace` command in the [core demo cli](components/demo). This argument will be inserted as a custom log message with the trace event, which can be useful for testing the insertion of custom log messages into trace events at runtime. - Add the following built-in metrics, enabled by default (and quota-exempt): - `MemfaultSDKMetric_log_recorded_lines` : the total number of log lines written into the Memfault log buffer during the heartbeat interval - `MemfaultSDKMetric_log_dropped_lines` : the total number of log lines dropped (overwritten or not recorded) due to buffer exhaustion during the heartbeat interval For example, if the buffer is sufficiently large to store 10 logs, and 15 logs are written: - `MemfaultSDKMetric_log_recorded_lines` will be 15 - `MemfaultSDKMetric_log_dropped_lines` will be 5 If 5 more logs are written: - `MemfaultSDKMetric_log_recorded_lines` will be 20 - `MemfaultSDKMetric_log_dropped_lines` will be 10 - Cosmetic updates to the `examples/freertos/` app log format, including applying ANSI color codes based on log level. ```bash # Before: mflt> test_log Raw log! MFLT:[INFO] Info log! MFLT:[WARN] Warning log! MFLT:[ERRO] Error log! # After: mflt> test_log Raw log! 2024-11-14T17:01:12Z|4284 I Info log! 2024-11-14T17:01:12Z|4284 W Warning log! 2024-11-14T17:01:12Z|4284 E Error log! ``` - ESP-IDF: - Enable NTP time synchronization by default, controlled with the Kconfig option `CONFIG_MEMFAULT_NTP_SYNC`. After NTP synchronization, events (heartbeats and trace events) will be timestamped with the current device time. - Add a `test_trace` command to the ESP-IDF demo cli to capture an example trace event. This behaves the same as the `test_trace` command in the [core demo cli](components/demo). - Mark coredumps with the `Software Watchdog` crash reason if the IWDT triggered the fault, instead of marking them as `Hard Fault`. - Add OTA update check-ins to the HTTP client's periodic upload, controlled with the Kconfig option `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA`. When enabled, the system will be restarted after downloading the update. To customize this behavior, enable `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA_CUSTOM_CBS`, and implement the global callback struct `g_memfault_ota_update_handler`. This feature is disabled by default. ### 🐛 Fixed - General: - Add missing type promotion rules for variants of `char` pointers when encoding compact logs. Previously, these types that should promote to a string in the compact logging encoding fell into the default case of `int`, causing compact logging decode to fail when processed by the Memfault backend. - ESP-IDF: - Fix an issue when using compact logs with `CONFIG_MEMFAULT_USE_MEMFAULT_BUILD_ID=y` (default). The command would always run (was always out-of-date) when any `idf.py [build|flash]` command is run, even if the original .elf file did not change. This caused the `log_fmt` section (used for decoding compact logs) to be removed from the `/memfault-esp32-demo-app.elf.memfault_log_fmt` file, which causes Memfault Compact Logs to fail to decode. The command is fixed to only run when the .elf file changes. ## [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](https://github.com/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. - Added a Kconfig flag, `CONFIG_MEMFAULT_METRICS_LOGS_ENABLE`, to control the new log lines metrics. - 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. - Added a Kconfig flag, `CONFIG_MEMFAULT_METRICS_LOGS_ENABLE`, to control the new log lines metrics. ### 🛠️ 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 - General: - **EXPERIMENTAL**: Metrics Sessions now include a built-in metric for the [Stable Sessions Device Vital](https://docs.memfault.com/docs/platform/memfault-core-metrics#stable-sessions) (`session.operational_crashes`) which tracks crashes that occurred when a session is active. ### 🛠️ Changed - General: - Minor changes to support compiling with GCC ARM v4.9.3. ### 🐛 Fixed - Corrected a spelling error, renamed `MEMFAULT_METRIS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE()` to `MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE()` ## [1.14.0] - 2024-10-09 ### 📈 Added - ESP-IDF: - The Memfault port will now disable the `IWDT` (Interrupt Watchdog Timer) before starting coredump saving, to prevent interrupting the coredump process. The ESP-IDF fault handler enables the `WDT_RWDT` hardware watchdog when a fault occurs, so there is still protection if the fault handling hangs. ## [1.13.0] - 2024-10-07 ### 📈 Added - FreeRTOS: - The SDK now has a config to control whether to split CPU usage per core when building for a multi-core device. Enable this setting by adding `#define MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT 1` to `memfault_platform_config.h`. This setting is disabled by default. - ESP-IDF: - Added a Kconfig, `CONFIG_MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT`, to control `MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT`. This Kconfig is enabled by default for multi-core devices. ### 🛠️ Changed - FreeRTOS: - Changed previous idle task run time percent metrics to measure CPU usage (i.e. the complement of the idle task run time) - Renamed the following metrics: - Single-Core + Multi-Core Default: - `idle_task_run_time_percent` -> `cpu_usage_pct` - Multi-Core Split: - `idle0_task_run_time_percent` -> `cpu_usage_pct` - `idle1_task_run_time_percent` -> `cpu1_usage_pct` - ESP-IDF: - Unknown or unclassified reboot reason codes returned by `get_reset_reason()` are now correctly recorded as `kMfltRebootReason_Unknown` instead of `kMfltRebootReason_UnknownError` (`UnknownError` is reserved for an "unexpected" reboot path, where `Unknown` is used when the reboot reason cannot be determined). ### 🚩 Deprecated Support for the following vendor platform versions is deprecated in this release, and will be removed in a future release: - ESP-IDF < `v4.4` (Jan 26, 2022) - Zephyr < `v2.7.0` (Oct 16, 2021) - nRF-Connect SDK < `v1.9.2` (Jul 14, 2022) Please [contact us](https://mflt.io/contact-support) if you need support for earlier versions! ## [1.12.0] - 2024-09-25 ### 📈 Added - ESP-IDF: - The SDK now supports being installed as an [ESP Component](https://components.espressif.com/) from the Espressif registry, by adding the following lines to an esp-idf project's `idf_component.yml` manifest: ```yaml dependencies: memfault/memfault-firmware-sdk: version: "1.12.0" ``` - [Heap Allocation Tracking](https://docs.memfault.com/docs/mcu/heap-stats) is now enabled by default for ESP-IDF projects, controlled with the Kconfig symbol `CONFIG_MEMFAULT_HEAP_STATS`. The Memfault Trace UI will show information about the most recent heap allocations for `malloc()` calls. ### 🛠️ Changed - ESP-IDF: - The [Memfault Build ID](https://mflt.io/symbol-file-build-ids) will be applied by default, controlled by the Kconfig setting `CONFIG_MEMFAULT_USE_MEMFAULT_BUILD_ID`. This is _only_ valid for ESP-IDF versions >= **4.2.5** , and will cause a build error on older versions, requiring it to be set to `n`. Updating to this version of the SDK will **require** removing the similar logic in the project's `CMakeLists.txt` file (a build error will occur if both are present). - The Memfault Core Vital for [Periodic Connectivity](https://docs.memfault.com/docs/platform/memfault-core-metrics#periodic-connectivity) will now count failures to sync Memfault data if the HTTP connection cannot be established, but WiFi connectivity is available. This can occur when the WAN connection is down but the access point is still up, or if there is an external DNS failure. Previously this was not counted as a failure. - Zephyr - The Memfault HTTP client, enabled with Kconfig `CONFIG_MEMFAULT_HTTP_ENABLE`, requires `POSIX_API` to be enabled on newer versions of Zephyr. Previously, not explicitly enabling `POSIX_API` would result in a build error. Update it to be enabled by default in the Zephyr SDK, via Kconfig `imply POSIX_API`. - Zephyr 3.7.0+ requires enabling `CONFIG_MBEDTLS_SHA1` when using Zephyr's mbedtls implementation. Memfault added a build-time check for this setting in Memfault SDK 1.11.2, but that check would also trip when not using Zephyr's mbedtls implementation. Update the build check to be more precise. - nRF-Connect SDK: - Minor changes to provide compatibility with NCS versions > 2.7.0, which target a Zephyr fork that is compatible with 3.7.0 but provides a "development" version number, 3.6.99. ### 🐛 Fixed - ESP-IDF: - Corrected a theoretical integer overflow issue in the demo CLI `crash` command, detected by static analysis tools. The impacted function was and is exclusively called with an argument of `10`, so this issue was not exploitable in practice. ## [1.11.5] - 2024-09-18 ### 📈 Added - General: - Add two new core metrics, `MemfaultSdkMetric_os_name` and `MemfaultSdkMetric_os_version`. These are string metrics that map to the OS / platform name and version string respectively, and are included in the the ELF at build time, and processed out-of-band by Memfault for NCS, Zephyr, and ESP-IDF. They **are not** ever transmitted over the air (no bandwidth impact). For example, for Zephyr these metric string values would be `zephyr` and `3.7.0` for a project on Zephyr v3.7.0. These metrics are attributes by default and will not be counted towards attribute quotas. - Zephyr: - Add the following built-in WiFi metrics for Zephyr devices, enabled by default on systems with WiFi: - `wifi_connected_time_ms` : time in milliseconds the device has been connected to a WiFi network - `wifi_disconnect_count` : number of times the device has disconnected from a WiFi network - `wifi_ap_oui` : the OUI of the WiFi AP the device is connected to The metrics can be disabled by setting `CONFIG_MEMFAULT_METRICS_WIFI=n`. - The Memfault Zephyr fault handler now labels faults as Stack Overflow, Bus Fault, MemManage Fault, and Usage Fault, among others, in addition to the existing Hard Fault label. Note that this does not change the types of faults collected (all these faults are already supported), but it does correct the label presented in the Memfault UI. - Add a new test command, `mflt test stack_overflow`, that will trigger a stack overflow fault when `CONFIG_STACK_SENTINEL` or `CONFIG_MPU_STACK_GUARD` is enabled. - The `cpu_temp` metric has been renamed to `thermal_cpu_c` to better reflect the metric's purpose. The metric is still collected by default on platforms with an enabled `die-temp0` sensor, and can be disabled by setting `CONFIG_MEMFAULT_METRICS_CPU_TEMP=n`. - Add a new metric, `memory_pct_max`, which captures the max percentage of the heap used. It is enabled by default. This metric and the existing `Heap_BytesFree` metric are controlled with `CONFIG_MEMFAULT_METRICS_MEMORY_USAGE`. - ESP-IDF: - Add an option to upload logs by default when using `MEMFAULT_HTTP_PERIODIC_UPLOAD`, controlled with the Kconfig symbol `MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS`. This can also be controlled at runtime with the included API `memfault_esp_port_http_periodic_upload_logs(bool enable)` - Add a new metric, `memory_pct_max`, which captures the max percentage of the heap used. It is enabled by default. This metric and the existing `heap_*` metrics are now controlled with `CONFIG_MEMFAULT_METRICS_MEMORY_USAGE`. - Print the Memfault OTA URL from `memfault_esp_port_ota_update()` when a new update is available, for example: ```bash esp32> memfault_ota_check I (98125) mflt: Checking for OTA Update Download URL: https://ota-cdn.memfault.com/2950/9757/19036619757?token=0123456789abcdef&expires=1726192800&v=2 I (98775) mflt: Update available! esp32> memfault_ota_perform I (15515) mflt: Checking for OTA Update Download URL: https://ota-cdn.memfault.com/2950/9757/19036619757?token=0123456789abcdef&expires=1726192800&v=2 I (16205) mflt: Starting OTA download ... ``` ### 🛠️ Changed - Zephyr: - The `cpu_temp` metric has been renamed to `thermal_cpu_c` to better reflect the metric's purpose. The metric is still collected by default on platforms with an enabled `die-temp0` sensor, and can be disabled by setting `CONFIG_MEMFAULT_METRICS_CPU_TEMP=n`. - ESP-IDF: - The `cpu_temp` metric has been renamed to `thermal_cpu_c` to better reflect the metric's purpose. The metric is still collected by default on ESP32 variants that support it (all but ESP32), and can be disabled by setting `CONFIG_MEMFAULT_METRICS_CPU_TEMP=n`. - The Kconfig `CONFIG_MEMFAULT_ESP_HEAP_METRICS` has been replaced with `CONFIG_MEMFAULT_METRICS_MEMORY_USAGE`. ## [1.11.4] - 2024-09-10 ### 📈 Improvements - ESP-IDF: - Fix a minor issue where MEMFAULT_LOG_x() macros (MEMFAULT_LOG_ERROR() etc ) would be recorded twice in the log buffer. - General: - Fix an error in the unit test CI build due to the addition of the `-Wc11-c2x-compat` compiler warning flag. This flag is not valid for the C++ compiler, and has been changed to be enabled only for the C compiler. - nRF-Connect SDK: - Fix a possible stack overflow when tracking the Memfault Connectivity Connected Time Vital for the nRF9160 LTE connection, due to a change in the nRF-Connect SDK v2.7.0 from prior versions. ## [1.11.3] - 2024-09-05 ### 📈 Improvements - General: - Improve assert stack unwinding for Clang builds when `-flto` is enabled. - Zephyr: - Add a new Kconfig option, `CONFIG_MEMFAULT_SOC_FAMILY_ESP32`, to resolve an error when referencing the deprecated `CONFIG_SOC_FAMILY_ESP32` Kconfig (removed in [Zephyr 3.7.0](https://github.com/zephyrproject-rtos/zephyr/commit/8dc3f856229ce083c956aa301c31a23e65bd8cd8) as part of Hardware Model V2). This new option is used in the Memfault Zephyr port to check if an ESP32 SOC is being used. - Remove references to the deprecated `BOOTLOADER_ESP_IDF` Kconfig symbol in the Memfault Zephyr port (removed in Zephyr 3.7.0). ## [1.11.2] - 2024-08-29 ### 📈 Improvements - nRF-Connect SDK: - Add support for the following reboot reasons on nRF series SOCs. These reboot reasons are tracked as `kMfltRebootReason_DeepSleep`. - `NRF_POWER_RESETREAS_OFF_MASK` - `NRF_POWER_RESETREAS_NFC_MASK` - `NRF_POWER_RESETREAS_CTRLAP_MASK` - Zephyr: - Use `MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED` to control whether coredumps trigger a halt when a debugger is attached. - Add a new Kconfig option, `CONFIG_MEMFAULT_RAM_BACKED_COREDUMP_REGION`, to set the RAM region used for storing RAM-backed coredumps. - Fix a 🐛 when trying to communicate via HTTPS with Memfault on the nRF91x. On the nRF91x, socket operations are offloaded to the nRF modem lib, which does not currently support the socket option `TLS_CERT_NO_COPY`. In v1.9.4, this socket option was applied when TLS was enabled (`!g_mflt_http_client_config.disable_tls`) and Zephyr version was >=3.0.0 (when support for this socket option was added). Therefore, devices on SDK versions >=1.9.4 and <1.11.2 trying to communicate with Memfault will run into a no protocol support error (`ENOPROTOOPT`) in the nRF modem lib. Since this option is only required to use the DER format, `CONFIG_MEMFAULT_TLS_CERTS_USE_DER` now depends on the Zephyr-implemented socket operations being used (`!NET_SOCKET_OFFLOAD`). - Fix the build when using Zephyr 3.7.0 and leveraging the HTTP client and/or ESP32 port. The Memfault HTTP client would fail to build due to the wrong `crypto.h` header getting picked up in the build. Additionally, due to the [removal of default support for mbedTLS hash algorithms in Zephyr](https://docs.zephyrproject.org/latest/releases/migration-guide-3.7.html#mbed-tls), `CONFIG_MBEDTLS_SHA1` now must be enabled explicitly when using any of Memfault's CA certificates. When using PEM and leveraging Zephyr's in-tree mbedTLS and config file (`CONFIG_MBEDTLS_BUILTIN=y && CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h"`), `CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y` is required for PEM certificate support. To simplify certificate format selection, a new choice Kconfig called `CONFIG_MEMFAULT_TLS_CERTS_FORMAT` has been added. Use the configs `CONFIG_MEMFAULT_TLS_CERTS_USE_PEM` and `CONFIG_MEMFAULT_TLS_CERTS_USE_DER` to choose the certificate format.`CONFIG_MEMFAULT_TLS_CERTS_USE_DER` is the default choice config. Finally, the Kconfig `CONFIG_SOC_FAMILY_ESP32` is now deprecated. References of this Kconfig now also check the new Kconfig `CONFIG_SOC_FAMILY_ESPRESSIF_ESP32`. See [Zephyr's 3.7 Migration guide](https://docs.zephyrproject.org/latest/releases/migration-guide-3.7.html) for more details. - Dialog - Add support to the existing DA145xx port for the DA14535 by adding the correct memory region for `memfault_platform_sanitize_address_range()`. - Fix several 🐛s in `memfault_reboot_reason_get()` and `memfault_platform_reboot()` implementations in the DA146x port. In `memfault_reboot_reason_get()`, the bit masks were incorrectly applied and a block was missing to reset the reboot reason register if `MEMFAULT_REBOOT_REASON_CLEAR` is set. In `memfault_platform_reboot()`, the mechanism to reboot the system has been updated from `hw_cpm_reboot_system()` to `SWRESET`. ## [1.11.1] - 2024-08-12 ### 📈 Improvements - General: - Add additional stubs to `ports/templates/memfault_platform_port.c` to help when integrating on a new platform - ESP-IDF: - Trigger a null dereference crash when the `BOOT` button is pressed on the ESP32 example app. This is useful for testing the Memfault crash handling functionality without using the serial console. ## [1.11.0] - 2024-08-07 ### 💥 Breaking Changes - Add support for namespacing [Session Metrics](https://docs.memfault.com/docs/mcu/metrics-api#session-metrics) to support duplicate key names across heartbeats and sessions (eg `heartbeat.cpu_temp` and `session.cpu_temp`). This requires a change to how Session Metrics keys are set: - Before: `MEMFAULT_METRIC_SET_SIGNED(cpu_temp, 25)` (only 1 metric key `cpu_temp` supported across heartbeats/sessions) - After: - `MEMFAULT_METRIC_SET_SIGNED(cpu_temp, 25)` for setting `heartbeat.cpu_temp` - `MEMFAULT_METRIC_SESSION_SET_SIGNED(cpu_temp, session, 25)` for setting `session.cpu_temp` ### 📈 Improvements - General: - Fix a 🐛 in reboot tracking classification when calling `memfault_reboot_tracking_boot()` with a `bootup_info` parameter containing an unexpected type of reboot reason (or no reboot reason) after a previously recorded expected reason. This results in ["Expected Reboots"](components/include/memfault/core/reboot_reason_types.h#L33) recorded using `MEMFAULT_REBOOT_MARK_RESET_IMMINENT()` to be incorrectly flagged as an "Unexpected Reboot" and causes an incorrect `operational_crashfree_hours` metric getting reported and a lower Stability score. - Cleanup comment to old packetizer API - Add `-fdebug-prefix-map` to FreeRTOS QEMU example - ESP-IDF: - Improved the CLI results when running the `post_chunks` test command (used to trigger an upload to Memfault) to be more informative, for example: ```bash esp32> post_chunks I (12593) mflt: Data posted successfully, 262285 bytes sent esp32> post_chunks I (204093) mflt: No new data found ``` - Zephyr - Update samples to use the newly-released Zephyr v3.7.0 🥳 - Add explanatory output to Memfault `test busfault|hardfault` shell commands when Trusted Firmware-M (TF-M) is in use - The RTC-based implementation of [`memfault_platform_time_get_current()`](ports/zephyr/common/memfault_platform_system_time.c) used a define named `RTC`, which conflicts with the STM32 HAL definition [here](https://github.com/zephyrproject-rtos/hal_stm32/blob/f1317150eac951fdd8259337a47cbbc4c2e6d335/stm32cube/stm32h7xx/soc/stm32h7b3xxq.h#L2441-L2442). Rename the define to `MFLT_RTC_NODE` to avoid the conflict. - nRF-Connect SDK: - Fix a compilation error when building NCS v2.7.0 with the latest Memfault firmware SDK (v1.10.1, the previous SDK release). ## [1.10.1] - 2024-07-24 ### 📈 Improvements - General: - Removed a warning when compiling the SDK for Cortex-A targets (ARM v7a). Support for this target is no longer experimental. - Improve coredump capture on ARMv7-A/R (Cortex-A/R), capturing additional CPU registers `r8` + `r9` in the coredump. - Fix a compilation issue with older versions of `gcc-arm-none-eabi` that do not support the `PRIu64` format specifier (or are missing C99 format specifiers). Thanks to [@iotengtr](https://github.com/iotengtr) for reporting this issue in [#72](https://github.com/memfault/memfault-firmware-sdk/issues/72) ! - Zephyr: - Include the Zephyr Device Tree output in the ELF file in a debug section, which can be used for reference. Memfault is exploring other use cases for this data in our backend. This feature is controlled with `CONFIG_MEMFAULT_DTS_IN_ELF`, enabled by default. ## [1.10.0] - 2024-07-12 ### 📈 Improvements - Zephyr: - Add support for Memfault Logging when `CONFIG_LOG_MODE_MINIMAL=y`, in addition to the previously supported `LOG_MODE_DEFERRED` (Zephyr default) and `LOG_MODE_IMMEDIATE`. [Zephyr minimal log mode](https://docs.zephyrproject.org/3.6.0/kconfig.html#CONFIG_LOG_MODE_MINIMAL) disables all backends- logs are simply sent to `printk`. - For ESP32 chips, place the reboot reason tracking into a dedicated section, `.rtc_noinit`, to ensure the reboot reason is preserved across reboots. This was previously only supported on Zephyr ESP32-S3/S2 devices (all ESP32 devices on ESP-IDF already support this). - Fix a bug where the default `memfault_platform_get_device_info()`, which uses the hwinfo subsystem, when available, was incorrectly disabled when `CONFIG_MEMFAULT_REBOOT_REASON_GET_CUSTOM=y`. - General: - Improve the `memfault_demo_cli_cmd_busfault()` function (accessed via `test_busfault`/`mflt test busfault` demo CLI commands) to produce BusFaults on more devices. Previously, certain chips would either not produce a fault, or would produce a MemManage exception instead. ## [1.9.4] - 2024-07-01 ### 📈 Improvements - Zephyr: - Updated the Memfault fault handler to support deprecated exception info structure coming in Zephyr v3.7.0. This change is backward compatible with older Zephyr versions. - By default, include the `zephyr.meta` build metadata in the output ELF file as an unallocated section. This provides a list of module SHAs, useful when reconstructing the dependencies used to build a particular ELF file. This feature is controlled with `CONFIG_MEMFAULT_BUILD_META_IN_ELF`. - Add a new Kconfig option, `CONFIG_MEMFAULT_USE_MEMFAULT_BUILD_ID`, which will apply a [Memfault Build ID](https://mflt.io/symbol-file-build-ids) instead of a GNU Build ID when enabled. It defaults to enabled when `CONFIG_BOOTLOADER_ESP_IDF` is enabled- this applies to ESP32 boards that are using the ESP-IDF bootloader, instead of an MCUBoot bootloader. MCUBoot bootable images are compatible with the standard GNU Build IDs. - Add a new API, `memfault_zephyr_fota_download_callback()`, to the Zephyr FOTA implementation to allow users to write their own callback that is invoked when the download of a OTA payload is complete. The default download callback will mark the new image as pending, set a firmware update reboot reason, and reboot the system. Custom callbacks may, for example, be used to perform application-specific system shutdown procedures, or allow the FOTA update call to return to the calling context so a testing framework could mark the operation successful. Users can override the default callback with their own implementation using a new Kconfig option `CONFIG_MEMFAULT_ZEPHYR_FOTA_DOWNLOAD_CALLBACK_CUSTOM`. - ESP-IDF: - Add a periodic HTTP upload task. This feature starts a task dedicated to posting any available Memfault data via HTTPS. Enable with `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=y`. - General: - Add 2 new QEMU targets for the FreeRTOS QEMU example, for Cortex-M4F and Cortex-M33. The targets can be found [here](examples/freertos/boards/) ## [1.9.3] - 2024-06-10 ### 📈 Improvements - ESP-IDF: - The default Device Info Software Version (Kconfig `CONFIG_MEMFAULT_DEVICE_INFO_SOFTWARE_VERSION`) has been updated to `0.0.0` from `1.0.0-dev` for the built-in Device Info implementation. See `menuconfig` or [`ports/esp_idf/memfault/Kconfig`](ports/esp_idf/memfault/Kconfig) for details. This should only affect users who are using the default value for that Kconfig. - Fix a bug when `CONFIG_MEMFAULT_LWIP_METRICS=y` that may cause invalid metric values. LwIP stats by default are only 16-bits wide and this can cause underflow when calculating heartbeat values. This fix forces `LWIP_STATS_LARGE=1` to make LwIP use 32-bit counters. - Add a feature to the [esp32 example app](examples/esp32) to enable overriding the Memfault server URLs (ex: for a proxy). - Improve docs and comments in example app - General: - Updated the default exception handler name for Memory Management exceptions from `MemoryManagement_Handler` to `MemManage_Handler` (set by `MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT` in [`default_config.h`](components/include/memfault/default_config.h) when not overridden by the user). This aligns with the more recent CMSIS naming convention for exception handlers. A backwards-compatible implementation is included to avoid breaking users relying on the previous default. - Add [`libcurl`-based samples](examples/libcurl) for posting chunks to Memfault and checking for latest OTA release. ## [1.9.2] - 2024-05-29 ### 📈 Improvements - ESP-IDF: - Fix CLI command, `memfault_ota_check`, to return 0 to the console component when an update is available. - Add the temperature metric `cpu_temp` which is measured using an internal temperature sensor that many ESP32 boards have built-in. This metric is collected by default with the Kconfig `CONFIG_MEMFAULT_METRICS_CPU_TEMP=y`. - Enable recording vprintf data into the Memfault log buffer through a vprintf hook. Users can call `memfault_esp_port_vprintf_log_hook()` from their vprintf handler so they can use both their vprintf handler and record logs into Memfault's log buffer. To use this feature, set `CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK=n`. - Zephyr: - Fix a bug in `memfault_zephyr_port_post_data_return_size()` where a positive value could be returned in the event of a failure instead of a negative value. This would result in `mflt post_chunks` returning a successful post message even though there was a failure such as a DNS lookup failure. - Add the temperature metric `cpu_temp` which is measured using an internal temperature sensor that some Zephyr boards have. Similar to ESP-IDF, this metric is collected by default with the Kconfig `CONFIG_MEMFAULT_METRICS_CPU_TEMP=y`, but the board must have the device tree node `die-temp0` for this option to be used. - Add an example for collecting thread stack usage metrics when the thread handles are not accessible in the desired scope. Users can leverage the Zephyr routine `k_thread_foreach()` to register a callback that will be called with each thread's `k_thread` handle. In the callback, users can read the stack usage via the handle and set their metrics. ### 💥 Breaking Changes - Zephyr: - Change the error return value for `memfault_zephyr_port_http_upload_sdk_data()` to a negative value instead of 1. This change aligns with the error return value for the other Zephyr HTTP client APIs, and simplifies logic in the HTTP client. ### 🐛 Fixes - ESP-IDF: - Fix a case where `esp_http_client_cleanup()` was not called in certain scenarios (for example, if the access point is connected, but there is no outside internet access), which resulted in a memory leak. Thanks to [@mykmelez](https://github.com/mykmelez) for providing the fix in [#71](https://github.com/memfault/memfault-firmware-sdk/pull/71) 🎉! ## [1.9.1] - 2024-05-21 ### 📈 Improvements - ESP-IDF: - Fix an issue with the Memfault ESP-IDF integration to support the newly released ESP-IDF v5.1.4. - Zephyr: - Add support for tracking ESP32 reboots in `.rtc_noinit` section. This improves tracking of the OTA [Reboot Reason](https://mflt.io/reboot-reasons) when the device reboots after an OTA update. - Fix compilation warnings when `CONFIG_MEMFAULT_COREDUMP_FULL_THREAD_STACKS=y` is enabled. Thanks to @fouge for providing this fix in [#70](https://github.com/memfault/memfault-firmware-sdk/pull/70) 🎉! - General: - Extend [`eclipse_patch.py`](scripts/eclipse_patch.py) to support adding the Memfault SDK files to an STM32Cube IDE project. ## [1.9.0] - 2024-05-10 ### 📈 Improvements - General: - Correctly compile out the [non-volatile event storage feature](https://docs.memfault.com/docs/mcu/metrics-api/#non-volatile-event-storage) when `MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED` is set to `0`. This feature was previously not fully disabled, leaving some unused code in the final executable. This is a minor code size improvement (approximately 300 bytes) when the system is disabled (default). - Zephyr: - Improve support for [event timestamps](https://docs.memfault.com/docs/mcu/event-timestamps) using Zephyr RTC devices, by adding support for RTC nodes identified using `DT_ALIAS()` in addition to `DT_NODELABEL()`. Thanks to [@corytodd](https://github.com/corytodd) for providing the fix in [#68](https://github.com/memfault/memfault-firmware-sdk/issues/68)! - Add a Memfault HTTP OTA client backend for Zephyr MCUBoot platforms. This is enabled with the `CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_MCUBOOT` Kconfig flag. The OTA process is triggered by calling the `memfault_zephyr_fota_start()` function, which is also exposed in the shell via the `mflt get_latest_release` command. - Add basic support for Xtensa targets in the Memfault Zephyr port. This specifically targets the ESP32-S3 SOC. - Add a success message when the Zephyr HTTP client posts data to Memfault. - ESP-IDF: - Wrap calls to `esp_event_loop_create_default()` to prevent crashes if called multiple times. This feature is enabled by default and is disabled with `CONFIG_MEMFAULT_WRAP_EVENT_LOOP_CREATE_DEFAULT=n` - Add an example [Session Metric](https://docs.memfault.com/docs/mcu/metrics-api/#session-metrics) to the [ESP32 example app](examples/esp32), which tracks statistics during the ESP32 OTA session (bytes sent + received, etc). - nRF Connect SDK: - Enable periodic upload, `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=y`, by default for nRF91x builds - Default to `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE` when periodic upload is enabled - Improve the stability of the [nRF9160 sample app](examples/nrf-connect-sdk/nrf9160), by increasing the `CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE` to `2048`. ### 💥 Breaking Changes - Zephyr: - Add a built-in implementation of `g_mflt_http_client_config` for Zephyr devices using the Memfault HTTP client, which holds the [Memfault Project Key](https://mflt.io/project-key) set via the Kconfig symbol `CONFIG_MEMFAULT_PROJECT_KEY`. Users updating from a previous version will have to set this Kconfig symbol when building, and any existing implementation of `g_mflt_http_client_config` should be removed. ## [1.8.1] - 2024-04-24 ### 📈 Improvements - General: - A new platform function, `memfault_platform_metrics_connectivity_boot()`, can be enabled to be called from the Memfault SDK `metrics_boot()` function after the metrics subsystem is initialized. This platform function is used for setting any initial state information for Connectivity metrics, and has default implementations for ESP-IDF (WiFi) and nRF9160 (LTE) devices. - ESP-IDF: - Add a new Kconfig setting, `CONFIG_MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS`, which can be used to set an offset into the coredump storage area where coredumps are written. The full partition will still be erased, but coredumps will be written starting at the sector offset selected with this setting. The skipped sector will remain at the erased value, `0xff` for all bytes. - Zephyr: - Added a built-in weakly defined implementation of `memfault_platform_get_device_info()`, which provides: - `.device_serial` : A default device serial based on the SOC's unique ID registers, via the [`hwinfo` subsystem](https://docs.zephyrproject.org/3.6.0/hardware/peripherals/hwinfo.html). This is the default device serial when `CONFIG_HWINFO=y`. If `CONFIG_HWINFO=n`, the fallback device serial is `CONFIG_SOC "-testserial"`. - `.software_type` : Configurable with `CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_TYPE`, defaults to `"app"` - `.software_version` : Configurable with `CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_VERSION`. Defaults to an identifier based on the `VERSION` file in the application, using the Zephyr [Application Version](https://docs.zephyrproject.org/3.6.0/build/version/index.html) feature, or `"0.0.0"` if unavailable. - `.hardware_version` : Configurable with `CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_HARDWARE_VERSION`. Defaults to an identifier based on `CONFIG_BOARD` and `CONFIG_BOARD_REVISION` (if set). - Add a new Zephyr example app for the [ST NUCLEO-WBA55CG board](https://docs.zephyrproject.org/3.6.0/boards/st/nucleo_wba55cg/doc/nucleo_wba55cg.html), under [`examples/zephyr/nucleo_wba55cg`](examples/zephyr/nucleo_wba55cg). This example demonstrates the Memfault SDK integration on the NUCLEO-WBA55CG board. - nRF-Connect SDK: - Add [Connected Time Vital](https://mflt.io/connectivity-metrics) out-of-the-box for nRF9160 projects, tracking modem connected time. This is controlled with the Kconfig `CONFIG_MEMFAULT_NRF_CONNECTIVITY_CONNECTED_TIME_NRF91X`, enabled by default. ## [1.8.0] - 2024-04-17 ### 📈 Improvements - General: - Scale values now fully supported. This metric metadata will scale down metric values by the specified factor. Metrics with no specified scale value will not be scaled - Fix a 🐛 that would reset metric values after running a session start callback. This prevented setting metrics at the start of a session. - Zephyr: - Add a metric using a scale value to our Zephyr QEMU example. This metric measures CPU usage (in permille, 0.0-100.0%) of the main thread. ## [1.7.7] - 2024-04-15 - FreeRTOS: - Add a compile time error when the [`ports/freertos/config/memfault_metrics_heartbeat_freertos_config.def`](memfault_metrics_heartbeat_freertos_config.def) file is not included from the user `memfault_metrics_heartbeat_config.def` file. Compilation would fail previously with hard-to-follow errors due to missing metrics definitions. This change makes the compilation error explicit. - ESP-IDF: - Permit disabling the Memfault logging hook into `esp_log_set_vprintf()` (by default this is enabled on system boot). Use `CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK=n` to disable the Memfault hook. Note that this will disable capturing ESP-IDF logs in Memfault. - Allow disabling the built-in Connectivity [Core Metric](https://mflt.io/core-metrics?platform=MCU) collection, which is enabled by default to track WiFi uptime. Use `CONFIG_MEMFAULT_ESP_WIFI_CONNECTIVITY_TIME_METRICS=n` to disable the built-in implementation. - General: - EXPERIMENTAL: Add the option to set a scale factor when defining a Metric. The scale factor will in the future be used to divide the uploaded metric values. For example, if a Metric is defined with a scale factor of `10`, all values reported for that Metric will be divided by `10` when received by Memfault. The resulting value is stored as a floating point number in Memfault. - Minor tweak to the [Eclipse patcher script](scripts/eclipse_patch.py) to better handler folders with partially common path prefixes. ## [1.7.6] - 2024-04-03 ### 📈 Improvements - ESP-IDF: - Fix missing call to `memfault_boot()` in the [ESP32 example app](examples/esp32) that was causing the device to not boot correctly. This was a regression in v1.7.4. - FreeRTOS: - Add a built-in metric for tracking the FreeRTOS Timer Task stack bytes free, `timer_task_stack_free_bytes`. This metric is enabled by default and can be disabled by setting `#define MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES 0` in `memfault_platform_config.h`. ## [1.7.5] - 2024-03-29 ### 📈 Improvements - ESP-IDF: - Modify a message when coredump storage is detected to be too small from an error to a warning - FreeRTOS: - Fix an integer overflow issue affecting long heartbeat intervals (>> 1 hour), due to a limitation in the implementation of `pdMS_TO_TICKS()` in older FreeRTOS versions. Newer version of FreeRTOS (as of `V11.0.0` published December 2023) have fixed this [issue](https://github.com/FreeRTOS/FreeRTOS-Kernel/commit/9c649ea7d1dd0206092697d73c894fd2c4fe29b3). - Add a stack usage metric to the FreeRTOS example app ## [1.7.4] - 2024-03-26 ### 📈 Improvements - nRF-Connect SDK: - Update the Memfault SDK's nRF-Connect SDK [example apps](examples/nrf-connect-sdk) to nRF-Connect SDK v2.6.0 - In the Memfault SDK [nRF9160 example app](examples/nrf-connect-sdk/nrf9160), set the Kconfig flag `CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_1024=y` in `prj.conf` instead of via a conditional `CMakeLists.txt` expression. This makes the setting more explicit for users that are referencing the sample app. - ESP-IDF: - Enable disabling the Memfault component by setting the `MEMFAULT_DISABLE=1` environment variable in the build environment (ex: `MEMFAULT_DISABLE=1 idf.py build`). This is useful in cases where the Memfault component can not be n-selected in the application's root `CMakeLists.txt` but needs to be conditionally excluded, for example when building a reduced factory firmware image. ## [1.7.3] - 2024-03-19 ### 📈 Improvements - Zephyr: - Add new Kconfig flags for the following Memfault Core Metrics: - `MEMFAULT_METRICS_SYNC_SUCCESS` (default=y) - `MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS` (default=y) - `MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME` (default=y) - `MEMFAULT_METRICS_BATTERY_ENABLE` (default=n) Additionally, a Kconfig flag `MEMFAULT_BATTERY_METRICS_BOOT_ON_SYSTEM_INIT` will enable auto-initialization of battery metrics on system init (requires `memfault_platform_get_stateofcharge()` to be implemented). See for more information on Core Metrics. - ESP-IDF: - Add preliminary support for the upcoming ESP-IDF v5.3.0 release. ## [1.7.2] - 2024-03-09 ### 📈 Improvements - General: - The Memfault Self Test component boot check now prints all components that are booted (previously only un-booted components would print): ```plaintext MFLT:[INFO] ============================= MFLT:[INFO] Component Boot Test MFLT:[INFO] ============================= MFLT:[INFO] Component | Booted?| MFLT:[INFO] ----------------------------- MFLT:[INFO] Event Storage | yes| MFLT:[INFO] Logging | yes| MFLT:[INFO] Reboot Tracking | yes| MFLT:[INFO] Trace Event | yes| MFLT:[INFO] All components booted MFLT:[INFO] ============================= ``` - Restore the Memfault Demo CLI `heartbeat` command, which was unintentionally removed in v1.7.1. This command triggers a heartbeat on-demand, for testing heartbeat functionality. - Zephyr: - A few minor changes to support the upcoming Zephyr 3.6 release, specifically for devices that use mbedTLS for TLS connections. - ESP-IDF: - v1.7.0 of the SDK added a Kconfig flag to enabled Compact Logs, `CONFIG_MEMFAULT_COMPACT_LOG_ENABLE`. Updating from older versions of the SDK would require opting in to that option, even if Compact Logging was enabled via the Memfault config flag, `MEMFAULT_COMPACT_LOG_ENABLE`. Instead, support enabling Compact Logs both via the Memfault config flag or the Kconfig flag, to require no changes when updating the SDK for existing users. ## [1.7.1] - 2024-02-28 ### 📈 Improvements - General: - Fix a reboot reason test on MacOS by adding a stub implementation of a function to avoid an empty archive not allowed by the system clang install - Add a new macro `MEMFAULT_REBOOT_MARK_RESET_IMMINENT_CUSTOM()` that is more concise for custom reboot reasons by taking the reboot reason name directly. Previously, `MEMFAULT_REBOOT_MARK_RESET_IMMINENT()` was the only option, which requires passing a key with `MEMFAULT_REBOOT_REASON_KEY()`. - Add a session start callback with `memfault_metrics_session_register_start_cb()`. This change enables, for example, tracking battery percent drop across sessions; battery state tracking variables can now be initialized at the start of a session. - Add a coredump storage test to the self test which will now test the coredump storage implementations and check available storage capacity. This is currently experimental and must be explicitly enabled with the Kconfig `CONFIG_MEMFAULT_SHELL_SELF_TEST_COREDUMP_STORAGE=y` on Zephyr, `CONFIG_MEMFAULT_CLI_SELF_TEST_COREDUMP_STORAGE=y` on ESP-IDF, or by adding `#define MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE 1` to your `memfault_platform_config.h` for other platforms. - Convert the previous `heartbeat_dump` command to `metrics_dump` that can either dump all current heartbeat metrics with `metrics_dump heartbeat` or all session metrics with `metrics_dump sessions` that haven't yet been exported. These commands help with testing metrics collection locally without needing to push chunks to Memfault. - Apply a handful of changes to fix items raised by a new static analyzer - Fix a minor formatting issue that was causing a compilation error for CC ARM - 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. - Update the `README.md`s in example apps to point to the [Demo CLI](https://docs.memfault.com/docs/mcu/demo-cli) page on the Memfault docs website where comprehensive information is available on commands and their output - nRF-Connect SDK: - Fix a problem caused by mismatched root certificates in modem storage. This change will ensure that all certificates are updated if they do not match expected contents. Prior to v1.7.1, the SDK would only update a certificate by checking each tag for certificate existence, not certificate content. In v1.3.0, the order of root certificates was changed and one was removed. On devices running pre-1.3.0 firmware, updating to later versions can hit a mismatch between the expected certificates and those in modem storage. - Remove a root certificate deprecated in v1.3.0 if present in modem storage. Devices using an SDK version before v1.3.0 may contain a now deprecated root certificate. ## [1.7.0] - 2024-02-15 ### 🚀 New Features - General: - 🎉Custom Reboot Reasons!🎉 The SDK now allows creating custom reboot reasons in addition to those defined in [`components/include/memfault/core/reboot_reason_types.h`](components/include/memfault/core/reboot_reason_types.h). Users can create custom reboot reasons for both expected and unexpected reboots. For more information see [`components/include/memfault/core/reboot_tracking.h`](components/include/memfault/core/reboot_tracking.h). Enable this feature with Kconfigs in Zephyr and ESP-IDF (`CONFIG_MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE=y`) or by adding `#define MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE 1` to your `memfault_platform_config.h`. - Added an option to include a Project Key in a chunk message header. This allows a chunk to specify which project the included data should be sent to. The default behavior is to use the project specified by the Project Key header in the POST upload. To enable, add `#define MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY 1` and `#define MEMFAULT_PROJECT_KEY ""` to your `memfault_platform_config.h`. Any chunks sent using this configuration will be sent to the project routed with `MEMFAULT_PROJECT_KEY` rather than the project routed with the POST header's Project Key. ### 📈 Improvements - General: - Improved suggested linker snippets defining coredump memory regions - Added a self test to validate platform time functions, `memfault_platform_get_time_since_boot_ms()` and `memfault_platform_time_get_current()`. This test will catch errors like non-monotonically increasing counters and current time reported as near epoch time - Fixed backspace handling in the FreeRTOS QEMU example console - Added `-fsanitize=leak` to the SDK unit tests (Linux only) - Fixed a missing `strnlen()` definition when enabling the self test with some libc implementations. This function is part of a POSIX standard that might be missing from some libc implementations - Added the previous release commit for 1.6.2 to `.git-blame-ignore-revs` to allow for better `git blame` output since this commit contains _only_ formatting changes. - Zephyr: - Added an example CDR implementation to the example QEMU app - Added a Kconfig, (`CONFIG_MEMFAULT_RECORD_REBOOT_ON_SYSTEM_INIT`) to allow deferring reboot reason collection to a later point in time. Setting this option to `n` is intended to help when device info is only available after boot completes. - Added built-in metrics for Zephyr's IP subsystem, (`CONFIG_MEMFAULT_METRICS_TCP_IP`) - Added Kconfig to select either PEM or DER formats for TLS certificates, (`CONFIG_MEMFAULT_TLS_CERTS_USE_DER`) - ESP-IDF: - Added a Kconfig to enable/disable compact logging (`CONFIG_MEMFAULT_COMPACT_LOG_ENABLE`) - Improved build compatibility when building with PlatformIO - Added support for networking over Ethernet interfaces - Added a built-in `connectivity_connected_time` core metric for WiFi devices, (`CONFIG_MEMFAULT_ESP_WIFI_CONNECTIVITY_TIME_METRICS`) ## [1.6.2] - 2024-01-29 ### 📈 Improvements - General: - Apply `clang-format` to the entire codebase, to ensure consistent code formatting. The formatting rules are defined in [`.clang-format`](.clang-format). No other changes are included in this release. A [`.git-blame-ignore-revs`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-blameignoreRevsFile) entry will be added in the next release, to enable ignoring this commit when running `git blame`. Use this ignore file in your local repo with `git config blame.ignoreRevsFile=.git-blame-ignore-revs`. ## [1.6.1] - 2024-01-29 ### 📈 Improvements - General: - Add a reboot reason self test, which validates the reboot reason data is correctly preserved through device reboot. This test first sets a custom reboot reason, then calls `memfault_platform_reboot()` to reboot the system. To verify the operation succeeded, the self test is called again to check the reboot reason was saved. The reboot reason self test can be performed by passing the appropriate flags when calling the `memfault_self_test_run()` file (see [`components/include/memfault/core/self_test.h`](components/include/memfault/core/self_test.h) for details), and from the demo CLI as `self_test reboot`/`self_test reboot_verify`. - Fix a `ti-armcl` compiler warning due to enumeration mismatch in initialization (`#190-D`). - Add a compile-time option to enable the self-test module. Enabling the self-test increases code space utilization, and is usually of use during set-up and SDK integration testing. The self-test can be enabled by setting `#define MEMFAULT_SELF_TEST_ENABLED 1` in `memfault_platform_config.h`, or via `CONFIG_MEMFAULT_CLI_SELF_TEST=y` for ESP-IDF and `CONFIG_MEMFAULT_SHELL_SELF_TEST=y` for Zephyr. - ESP-IDF: - Expose a new function `memfault_esp_port_ota_get_release_url()` for fetching the OTA release URL without performing a download. This is useful for cases where the URL is needed for other purposes, such as for fetching a downstream device OTA artifact. See [`ports/esp_idf/memfault/include/memfault/esp_port/http_client.h`](ports/esp_idf/memfault/include/memfault/esp_port/http_client.h) for details. - nRF-Connect SDK: - Add support for the new `fota_download_any()` (see [documentation here](https://github.com/nrfconnect/sdk-nrf/blob/0692684d0e0b924335882969bc7bf474c673ac81/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst#L847-L848)), which accepts a list of certificates to use when executing FOTA downloads. This API is expected to be included in the upcoming nRF-Connect SDK v2.6.0 release. ## [1.6.0] - 2024-01-09 ### 📈 Improvements - ESP-IDF: - In the [ESP32 example app](examples/esp32), disable the shell history cache for the FatFS shell command handler, which saves some ~20kB of heap memory. - Also in the [ESP32 example app](examples/esp32), add a new built-in metric `sync_successful` which is set to `1` when a successful Memfault OTA check-in is performed. Primarily added for demonstration purposes for the metric. See more information about Memfault Core Metrics [here](https://docs.memfault.com/docs/platform/memfault-core-metrics). - General: - Enhanced the Self-Test component with the following new features: - Check that other components are initialized correctly (boot test) - Check that exporting data over the console is supported (primiarly checking that the output buffer is large enough) - Print information about the coredump regions that would be collected during a crash, for diagnostic purposes - nRF-Connect SDK: - Fix a use-after-free problem in the Memfault FOTA helper code ([`ports/zephyr/ncs/src/memfault_fota.c`](ports/zephyr/ncs/src/memfault_fota.c)), where the allocated Memfault OTA download URL was freed too early. This issue was introduced in Memfault SDK v1.5.0, where support for FOTA on nRF-Connect SDK v2.4+ was improved. - Zephyr: - Fix a concurrent access bug in the Memfault Zephyr Logging Backend. Only affected configurations with `CONFIG_LOG_MODE_IMMEDIATE=y`. In certain cases the logging `ctx` could be corrupted, causing unpredictable behavior. Replace the synchronization approach with an atomic primitive and correct a potential concurrency issue in the log panic handler. - Fix a build warning for certain Zephyr configurations (when using `CONFIG_NEWLIB_LIBC=y`) due to a missing declaration for `strnlen`. This warning was introduced with the Memfault Self-Test additions in Memfault SDK v1.5.2. ### 💥 Breaking Changes - General: - The Battery Metrics platform API has been consolidated into a single function, `int memfault_platform_get_stateofcharge(sMfltPlatformBatterySoc *soc)`, where the platform data is loaded into the `soc` struct. This should simplify the platform implementation, and enables platforms to return a non-zero value to indicate state-of-charge is unknown. See the new API in the header file [`memfault/metrics/platform/battery.h`](components/include/memfault/metrics/platform/battery.h) ## [1.5.2] - 2023-12-12 ### 📈 Improvements - General: - Add a self-test component which checks the device's integration state. This self test is available via the demo cli command `self_test`, or can be called directly via `memfault_self_test_run()` (see [`self_test.h`](components/include/memfault/core/self_test.h) for details). Currently, it validates device info and the build ID. - Add a helper macro called `MEMFAULT_REBOOT_MARK_RESET_IMMINENT` in order to make marking an imminent reboot easier. It takes the reboot reason as an argument. - ESP-IDF: - Add an opt-in assert on malloc failure, controlled with `CONFIG_MEMFAULT_ASSERT_ON_ALLOC_FAILURE`. This is useful for tracking heap memory issues. Depending on the design, a system may in general not have any expected malloc failures, and may not be set up to handle them. This feature will generate issues tagged as `Out Of Memory` in the Memfault platform. - Zephyr: - Adjust the implementation of `memfault_zephyr_port_http_upload_sdk_data()` to check the socket before each incremental call to the `send()` socket operation and wait for the socket to become available. If the socket takes too long (a healthy 5 second timeout is given), the function will return with an error. Note this function was previously blocking, but may now abort mid-transfer. This check was primarily added to prevent a busy loop that hogs the CPU, preventing lower priority threads from running. This situation could occur with larger HTTP transfers during which the socket may be busy processing tx data when another send request occurs. - Fix a build error in the Zephyr RTC port. ## [1.5.1] - 2023-12-07 ### 📈 Improvements - nRF-Connect SDK: - Correct a build error that occurs when `CONFIG_DOWNLOAD_CLIENT=y` + `CONFIG_MEMFAULT_FOTA=n`. This fixes the second issue reported in [#66](https://github.com/memfault/memfault-firmware-sdk/issues/66#issuecomment-1845301737) ### 💥 Breaking Changes - General: - The SDK config flag `MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED` now defaults to `0`, disabled. This saves a minor amount of code space. Most implementations don't need the feature; for users that require it, the flag will now need to be set in `memfault_platform_config.h` as `#define MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED 1`. ## [1.5.0] - 2023-11-29 ### 🚀 New Features - General: - Added support for Session Metrics. These are similar to Heartbeat Metrics, except they are set on an arbitrary session interval (not required to be aligned to Heartbeat interval). Session metrics are useful for tracking device operation sessions- for example, measuring properties for a BLE connected stove top during a single cooking session. Session metrics automatically include a "session duration" timer metric. Session metrics must be defined using the new session-specific APIs, but are set using the same `MEMFAULT_METRIC_SET_*` APIs as Heartbeat metrics. See the [`metrics.h` header file](components/include/memfault/metrics/metrics.h) for usage details. - New built in metrics for measuring the following properties: - **crash-free hours**: enabled by default, generates `operational_hours` and `operational_crashfree_hours` metrics, which are automatically processed by Memfault - **battery drop**: enabled with `#define MEMFAULT_METRICS_BATTERY_ENABLE 1` in `memfault_platform_config.h`. See more information in the [header file](components/include/memfault/metrics/battery.h) for how to use the metric. - **connectivity**: enabled with `MEMFAULT_METRICS_SYNC_SUCCESS`/`MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS`/`MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME`. See more information in the [header file](components/include/memfault/metrics/connectivity.h) for how to use the metric. These metrics are considered first-class metrics by Memfault, are exempt from quota limits, and are automatically processed by Memfault. - Zephyr: - By default, set the `sync_memfault_successful`/`sync_memfault_failure` metrics for devices using Memfault's Zephyr HTTP chunk upload functionality. This feature is controlled with the `CONFIG_MEMFAULT_SYNC_MEMFAULT_METRICS` Kconfig flag. - Automatically set captured timestamps for events for devices that either implement the RTC subsystem, or use the Nordic `date_time` library. The appropriate option is enabled by default based on which features are available, and can be controlled with the Kconfig flags: `CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_DATETIME` or `CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_RTC`. - ESP-IDF: - By default, set the `sync_memfault_successful`/`sync_memfault_failure` metrics for devices using Memfault's ESP-IDF HTTP chunk upload functionality. This feature is controlled with the `CONFIG_MEMFAULT_SYNC_MEMFAULT_METRICS` Kconfig flag. ### 📈 Improvements - General: - Add the ability to extend the Memfault Demo Shell command table with custom commands. This is used in the [`examples/freertos`](examples/freertos) demo project to add 2 new commands: - `freertos_tasks` : print FreeRTOS task information, via `vTaskList()` - `freertos_vassert` : trigger a `vAssertCalled` FreeRTOS assert, via `configASSERT()` The Shell extension API is documented in [`components/include/memfault/demo/shell_commands.h`](components/include/memfault/demo/shell_commands.h), and must be enabled by setting `#define MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS 1` in `memfault_platform_config.h`. - Zephyr: - Remove a warning in Zephyr 3.5+ where the `zephyr/random/rand32.h` header was renamed to `zephyr/random/random.h`. This was reported in [#66](https://github.com/memfault/memfault-firmware-sdk/issues/66)- thanks to [@nordicjm](https://github.com/nordicjm) for reporting this! - Add test commands for exercising Secure Faults in ARM TrustZone-enabled chips: - `mflt test badptr` - `mflt test isr_badptr` Note that non-TrustZone chips may not trigger a fault when running those commands. - nRF-Connect SDK: - Add the `CONFIG_AT_SHELL` setting to the [`examples/nrf-connect-sdk/nrf9160`](nrf-connect-sdk/nrf9160) sample app. This permits sending raw AT commands, useful for testing. - Specific to nRF-Connect based apps using FOTA, add a warning if `CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE > 1024`, which can sporadically error out on nRF9160 devices (there is a limitation in the modem, see `CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_2048=y`) and DevZone note [here](https://devzone.nordicsemi.com/f/nordic-q-a/68601/tls-2303-bytes-packet-limit/281107). - 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 support](https://mflt.io/contact-support) immediately if you encounter any cert-related issues. ### 💥 Breaking Changes - The metrics convenience API added in v1.4.3 (`MEMFAULT_HEARTBEAT_SET_*` and others) have been renamed to `MEMFAULT_METRIC_SET_*`, to better support the new Session Metric feature. ## [1.4.4] - 2023-11-13 ### 📈 Improvements - General: - Rename this file from [`CHANGES.md`] to [`CHANGELOG.md`]. - For FreeRTOS, add a warning when `configRECORD_STACK_HIGH_ADDRESS` is not enabled. Memfault uses this to show stack sizes in the coredump view for coredumps on FreeRTOS systems. The warning can be disabled by enabling the `configRECORD_STACK_HIGH_ADDRESS` FreeRTOS config flag, or by setting `#define MEMFAULT_FREERTOS_WARN_STACK_HIGH_ADDRESS_UNAVAILABLE 0`. - Make `memfault_packetizer_get_chunk()` return `false` if the buffer was too small to load a full chunk. Previously the function would return `true` but with `0` bytes loaded into the output buffer and `*buf_len` set to `0`. - Update all example Metrics implementations to use the new API from v1.4.3 (eg `MEMFAULT_METRIC_SET_UNSIGNED` instead of `memfault_metrics_heartbeat_set_unsigned`). - Fix compilation for systems not integrating the [Metrics](https://mflt.io/embedded-metrics) component but using the Demo CLI (this regressed in v1.4.3). ## [1.4.3] - 2023-11-08 ### 🚀 New Features - General: - Add a new streamlined Metrics setter API: - `MEMFAULT_METRIC_SET_SIGNED(key_name, signed_value)` - `MEMFAULT_METRIC_SET_UNSIGNED(key_name, unsigned_value)` - `MEMFAULT_METRIC_SET_STRING(key_name, value)` - `MEMFAULT_METRIC_TIMER_START(key_name)` - `MEMFAULT_METRIC_TIMER_STOP(key_name)` - `MEMFAULT_METRIC_ADD(key_name, amount)` These APIs can be used in place of the original APIs: - `memfault_metrics_heartbeat_set_signed(MEMFAULT_METRICS_KEY(key_name), signed_value)` - `memfault_metrics_heartbeat_set_unsigned(MEMFAULT_METRICS_KEY(key_name), unsigned_value)` - `memfault_metrics_heartbeat_set_string(MEMFAULT_METRICS_KEY(key_name), value)` - `memfault_metrics_heartbeat_timer_start(MEMFAULT_METRICS_KEY(key_name))` - `memfault_metrics_heartbeat_timer_stop(MEMFAULT_METRICS_KEY(key_name))` - `memfault_metrics_heartbeat_add(MEMFAULT_METRICS_KEY(key_name), amount)` Saving some typing! - Add the ability to compute FreeRTOS task stack high watermarks when storing coredumps. This is useful only if the entire RAM (`.data` + `.bss`) cannot be saved in the coredump. The feature is opt-in with the config flag `#define MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE 1`. - Add a `heartbeat` command to the [core demo cli](components/demo). This behaves the same as the commands of the same name already present in the Zephyr + ESP-IDF port. - Add a `test_cassert` command to the core demo cli. This command executes a C stdlib ```assert(0)` call. For platforms that do not implement a `assert()` handler, a config flag `MEMFAULT_DEMO_DISABLE_CASSERT` can be defined to `0` to disable the command. - ESP-IDF: - Add a new out-of-box metric, `wifi_ap_oui`, which will record the associated AP's Organizationally Unique Identifier (OUI) in the Memfault heartbeat. - Zephyr: - Organize the Memfault Coredump Kconfig settings under a `Memfault Coredump Settings` submenu, for easier navigation when using graphical Kconfig frontends like menuconfig. ### 📈 Improvements - General: - Disable a warning emitted by the ARM C Compiler v5 (`#188-D: enumerated type mixed with another type`) when initializing a structure in [`components/core/src/memfault_log.c`:314](https://github.com/memfault/memfault-firmware-sdk/blob/1.4.3/components/core/src/memfault_log.c#L313). - Improve the quality of Assert backtraces when using the ARM C Compiler v5. Certain frames in the assert call stack were missing link register information, due to compiler optimizations based on the `noreturn` and unreachable compiler hints. These hints have been removed for `armcc`, which should permit full stack unwinding for Assert coredumps generated from builds on that toolchain. - Perform an update of the timer when calling the `memfault_metrics_heartbeat_timer_read()` debug function. Fixes [#65](https://github.com/memfault/memfault-firmware-sdk/pull/65). Thanks to [@LuskeyNoah](https://github.com/LuskeyNoah) for providing this fix! - ESP-IDF: - Fix a missing piece enabling the "zero-config" integration (originally added in `1.4.0`)- the `memfault_platform_port.h` file was still incorrectly required. This is now fixed. ## [1.4.2] - 2023-11-02 ### 📈 Improvements - General: - Improve the trace quality for asserts when using the IAR compiler and high optimization settings (`-Oh`) - Add demo CLI command to print the current values of heartbeat metrics. Try this out in your demo CLI implementation or with the built-in CLI with Zephyr! ## [1.4.1] - 2023-10-31 ### 🚀 New Features - ESP-IDF: - Add the following built-in heap allocation metrics by default. These can be disabled with the `CONFIG_MEMFAULT_ESP_HEAP_METRICS` Kconfig flag. - `heap_free_bytes` - `heap_largest_free_block_bytes` - `heap_allocated_blocks_count` - `heap_min_free_bytes` ### 📈 Improvements - Zephyr: - Enable capturing [Memfault-style compact logs](https://mflt.io/compact-logs) on Zephyr systems. Note that this does not enable decoding [Zephyr "dictionary logs"](https://docs.zephyrproject.org/3.5.0/services/logging/index.html#dictionary-based-logging), but requires using the Memfault logging APIs directly (i.e. `MEMFAULT_LOG_INFO("...")` instead of `LOG_INF("...")`). - General: - Add a `coredump_size` CLI command to the Zephyr, ESP-IDF, and demo CLI implementations. This command will print the computed size of the coredump and the available storage space. Can be used to tune coredump size. - Enable providing the Memfault HTTP Client with a custom `memfault_platform_get_device_info()` callback, for when the device is uploading data for a downstream device, with different device info. - When [compact logging](https://mflt.io/compact-logs) is enabled, route all `MEMFAULT_LOG_x()` statements through the compact serializer (`MEMFAULT_COMPACT_LOG_SAVE`). Previously, logs had to explicitly use the `MEMFAULT_COMPACT_LOG_SAVE` API to store in the compact form. - Capture C stdlib `assert.h` asserts, by implementing the correct assert hooks for Newlib/Picolibc and IAR libc's. This can be disabled with the Memfault platform config `MEMFAULT_ASSERT_CSTDLIB_HOOK_ENABLED`. This should improve the Trace quality for systems that are using the C stdlib `assert(x)` functions. ### 📈 Improvements ## [1.4.0] - 2023-10-23 ### 🚀 New Features - ESP-IDF: - Implement support for a "zero-config" integration on ESP-IDF. This change makes the template + platform config files optional. See the [ESP32 Integration Guide](https://mflt.io/esp-tutorial) section on configuration files for details. ### 📈 Improvements - ESP-IDF: - Fix a build issue when overriding the default device-info implementation, `CONFIG_MEMFAULT_DEFAULT_GET_DEVICE_INFO=n`. This was a regression in **1.3.3**. - Add a new Kconfig flag, `MEMFAULT_COREDUMP_STORAGE_MAX_SIZE`, which can be used to set the Memfault SDK's built-in [ESP-IDF coredump storage implementation](https://github.com/memfault/memfault-firmware-sdk/blob/master/ports/esp_idf/memfault/common/memfault_platform_coredump.c) to artificially limit the maximum coredump storage size. This is useful for situations where the default `memfault_platform_coredump_get_regions()` function is still desirable, but the coredump maximum size needs to be limited (eg for bandwidth reasons). - Switch to using `ESP_DRAM_LOGE` when logging an error from Memfault's FreeRTOS task tracking when the number of tasks created on the system exceeds `MEMFAULT_PLATFORM_MAX_TRACKED_TASKS` (default of 16). - General: - Remove the setting of the Memfault Firmware SDK version into a string metric on system boot. This value is now automatically extracted by the Memfault backend from the symbol file, and does not need to be set by devices. ## [1.3.5] - 2023-10-14 ### 📈 Improvements - ESP-IDF: - Fix build errors when building the [ESP32 example app](examples/esp32) project on ESP-IDF versions earlier than v5. This was a regression in **1.3.4**. - Conditionally remove a redundant call to `esp_timer_init()` during the `memfault_boot()` sequence, which was causing a runtime error message. This call was originally required when `memfault_boot()` was called as part of ESP-IDF system init, which was disabled by default in **0.31.4** and deprecated in **0.40.0**. The redundant call was harmless but generated a nuisance error message. - Fix another build error in the public unit tests caused by the `-fanalyzer` flag. The `-fanalyzer` static analyzer was generating a false positive on GCC 11. Updating the GCC version to 12 removes the false positive. ## [1.3.4] - 2023-10-12 ### 📈 Improvements - ESP-IDF: - Add a missing dependency to the `memfault` component, `esp_https_ota`. It's only linked into the target project if the `memfault_esp_port_ota_update()` API is used. This was previously an implicit dependency from the `common` component dependencies, when building `memfault` into an ESP-IDF component-style project. This change fixes building for non-component-style ESP-IDF projects, where the default component dependencies might not be included. - Multiple changes to the [`examples/esp32`](examples/esp32/apps/memfault_demo_app) sample project: - Add a new `coredump_size` shell command, which prints out the maximum coredump size and the available coredump storage capacity. - Add new `settings_[get|set]` shell commands, and enable setting the LED brightness and blink interval to NVS. This is intended as a minor quality-of-life change for internal Memfault users of the example app. - Fix a build error in the public unit tests, caused by the recent addition of the `-fanalyzer` flag to the unit test compilation options. ## 1.3.3 - Oct 10, 2023 ### 📈 Improvements - Zephyr: - Add a new Kconfig flag, `CONFIG_MEMFAULT_FAULT_HANDLER_RETURN`, which will call the normal `z_fatal_error` handler at the end of Memfault fault processing instead of rebooting the system. This is useful when user code needs to run within `k_sys_fatal_error_handler()` just prior to system shutdown. Thanks to [@JordanYates](https://github.com/JordanYates) for the patch! Fixes [#59](https://github.com/memfault/memfault-firmware-sdk/issues/59). - Add a timeout to the initial `send()` socket operation in `memfault_zephyr_port_http_upload_sdk_data()`, to abort the transfer if the socket is blocking for too long. That function will execute repeated `send()` calls to drain all the buffered Memfault data; this update only changes the initial call to check for a timeout, but otherwise will keep trying until the process completes, or a watchdog triggers. This is to balance the existing behavior, where a badly performing socket will still eventually push data through, but improves the case where the socket fails on the initial send (more common failure mode). - Remove a nuisance build warning generated when configured with `CONFIG_LOG_PRINTK=y && CONFIG_LOG_MODE_DEFERRED=y`. This impacts the usability of exporting base64-encoded chunks on the shell for testing (`mflt export` command), but is otherwise harmless. - ESP-IDF: - Multiple changes to the [`examples/esp32`](examples/esp32/apps/memfault_demo_app) sample project: - Disable WiFi SoftAP by setting `CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n`, since it's unused in the sample app. This saves about 40kB flash. - Permit setting the Memfault Project Key at runtime, with a new cli command `project_key`. The key is saved in Non-Volatile Storage on the ESP32 board. - General: - Enable using compact logs with the IAR build tools, by adding the needed `__no_alloc` attribute to the compact log symbols, to have the IAR linker set the `NO_LOAD` attribute correctly on the compact log output section. ### 💥 Breaking Changes - ESP-IDF: - The [ESP-IDF port](ports/esp_idf/) now implements a default `memfault_get_device_info()` function, which uses the device MAC address for the Memfault Device Serial. When updating the Memfault SDK in an existing project, this implementation will cause a **linker error** due to duplicate definition. To disable the built-in definition, set `CONFIG_MEMFAULT_DEFAULT_GET_DEVICE_INFO=n`. ## 1.3.2 - Sept 26, 2023 ### 📈 Improvements - Zephyr: - use `` instead of ``. Thanks [@kmeinhar](https://github.com/kmeinhar) for this change! (see [#64](https://github.com/memfault/memfault-firmware-sdk/pull/64)) - nRF Connect SDK: - Add missing Kconfig flags `CONFIG_FLASH_MAP=y` + `CONFIG_STREAM_FLASH=y` for the [`examples/nrf-connect-sdk/nrf5/`](examples/nrf-connect-sdk/nrf5/) example app, for compatibility with nRF Connect SDK v2.4.1+. This was required due to an [upstream Zephyr change](https://github.com/zephyrproject-rtos/zephyr/commit/1b4b979f8789af6087f877c0daad0a660c1b9b28). - General: - Add support for Memfault Compact Logs for C++ source files (previously only supported in C source files). Compact logging can be enabled by setting `MEMFAULT_LOG_COMPACT_ENABLE=1` in `memfault_platform_config.h`. See [the docs](https://docs.memfault.com/docs/mcu/compact-logs/) for more details. - Fix a missing include of `` required by the IAR compiler ## 1.3.1 - Sept 21, 2023 ### 📈 Improvements - Zephyr: - Add a new Kconfig, `CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE`, to control whether thread stack usage is computed and collected on device during a coredump. ## 1.3.0 - Sept 20, 2023 ### 📈 Improvements - General: - Remove an unused root certificate and adjust the order of certs to minimize additional TLS handshakes; DigiCert G2 is the most likely root cert so it is now the first one attempted in every place certificates are used. This update is trickled down from [DigiCert's pivot in March 2023](https://knowledge.digicert.com/generalinformation/digicert-root-and-intermediate-ca-certificate-updates-2023.html) to their G2 root cert as their default certificate. - Zephyr: - Add support for the new location of the `nmi.h` header which [moved recently](https://github.com/zephyrproject-rtos/zephyr/pull/60031). ## 1.2.5 - Sept 18, 2023 ### 📈 Improvements - Add MQTT transport to esp32 example app ## 1.2.4 - Sept 12, 2023 ### 📈 Improvements - Fix a unit test failure introduced in 1.2.3. No on-target code was impacted. ## 1.2.3 - Sept 12, 2023 ### 📈 Improvements - General: - Include the current MCU SDK version in the http client user agent header - Zephyr: - Update include paths for CMSIS headers for upcoming Zephyr support. Thanks [@gmarull](https://github.com/gmarull) for the patch! - Modus Toolbox: - Allow a user application to override `memfault_metrics_heartbeat_collect_data`. ## 1.2.2 - Sept 5, 2023 ### 📈 Improvements - ESP-IDF: - Correct the heartbeat metrics key definition include configuration to generate a consistent key index across the application. This regressed in version 1.0.1 of the SDK. ### Changes between Memfault SDK 1.2.0 and 1.2.1 - Sept 1, 2023 #### 📈 Improvements - ESP-IDF: - Add a `heartbeat` cli command to the ESP-IDF port that does the same thing as in Memfault's default demo console + Zephyr's console. - Zephyr: - Add a new Kconfig option to the Zephyr port, `CONFIG_MEMFAULT_PLATFORM_EXTRA_CONFIG_FILE=y`, which causes `memfault_platform_config_extra.h` to be included in platform configurations. This can be utilized by a third party consumer of Zephyr to more easily extend the platform configurations set by Zephyr and avoid potentially losing a user's platform configurations set in `memfault_platform_config.h`, which [happened in the nRF Connect SDK recently](https://devzone.nordicsemi.com/f/nordic-q-a/103188/memfault-sdk-integration-missing-user-memfault_platform_config). ### Changes between Memfault SDK 1.1.3 and 1.2.0 - Aug 30, 2023 #### 📈 Improvements - ESP-IDF: - Eliminate several build warnings for our example app - FreeRTOS: - Fix a build error in our QEMU example when building on macOS - Modus Toolbox: - Update port to handle user override of SDK configs, metrics, trace_reasons, etc - Update port to fix errors when disabling built-in WiFi metrics - nRF Connect SDK: - Enable mcuboot & NCS 1.4 with our example apps - Silicon Labs: - Add a demo CLI component for the emblib port. Check out our [Simplicity Studio example app](https://github.com/memfault/simplicity-studio-example) for usage - Fix a build warning in emblib port flash storage (MSC) module - Zephyr: - Fix a 🐛 when building with LOG_MODE_DEFERRED that prevent log messages from flushing during a coredump - Fix a 🐛 and warnings involving older Zephyr header paths. Resolves [#62](https://github.com/memfault/memfault-firmware-sdk/issues/62) and [#57](https://github.com/memfault/memfault-firmware-sdk/issues/57). Thanks [@JordanYates](https://github.com/JordanYates) and [@YHusain1](https://github.com/YHusain1) for reporting these issues. ### Changes between Memfault SDK 1.1.2 and 1.1.3 - Aug 8, 2023 #### 📈 Improvements - Set Memfault SDK version in a string metric on device boot, for easier tracking of SDK versions in the Memfault UI - Support using a different identifier for the GNU build id symbol (previously was fixed to `__start_gnu_build_id_start`). Use the `MEMFAULT_GNU_BUILD_ID_SYMBOL` define in `memfault_platform_config.h` to override the default. For Zephyr, the Kconfig option `CONFIG_MEMFAULT_GNU_BUILD_ID_SOURCE_BUILTIN` can be used to override the builtin section specifier + linker fragment for the GNU build ID. Thanks to [@JordanYates](https://github.com/JordanYates) for posting this change in [#60](https://github.com/memfault/memfault-firmware-sdk/pull/60) 🎉 - Improvements to the ARMv7-R exception handling when the supervisor processor mode is active - Zephyr: - Add an optional mode to create and open the HTTP socket in separate function calls, if the user needs to set additional socket options before connecting. See [ports/zephyr/include/memfault/ports/zephyr/http.h](ports/zephyr/include/memfault/ports/zephyr/http.h) for details. Fixes [#52](https://github.com/memfault/memfault-firmware-sdk/issues/52)- thanks to [@anicare-tero](https://github.com/anicare-tero) for posting this 🎉 ### Changes between Memfault SDK 1.1.1 and 1.1.2 - July 11, 2023 #### 📈 Improvements - Improve compatibility in [reboot reason tracking](ports/emlib/rmu_reboot_tracking.c) and [watchdog implementation](ports/emlib/wdog_software_watchdog.c) on Silicon Labs Series 2 MCUs - Zephyr: - Fix a build error when `CONFIG_MEMFAULT_LOGGING=n`, see [#56](https://github.com/memfault/memfault-firmware-sdk/issues/56). Thanks to [@JordanYates](https://github.com/JordanYates) for reporting this issue! - Fix a potential bug in the Memfault Log Backend when `CONFIG_LOG_MODE_IMMEDIATE=y` when flushing of fault logs during a crash ### Changes between Memfault SDK 1.1.0 and 1.1.1 - June 30, 2023 #### 📈 Improvements - Reduce the amount of error logs emitted by the MbedTLS port HTTP client while polling for session established. This regressed in SDK version 1.1.0. ### Changes between Memfault SDK 1.0.1 and 1.1.0 - June 29, 2023 #### 📈 Improvements - New built-in metrics 🎉 ! - FreeRTOS Idle Task runtime percentage metric: `idle_task_run_time_percent`. This is automatically enabled for FreeRTOS builds with the correct tracing options enabled, see [ports/include/memfault/ports/freertos/metrics.h](ports/include/memfault/ports/freertos/metrics.h) for details on how to enable or disable this metric. - MbedTLS metrics for maximum and current memory used: `mbedtls_mem_max_bytes` and `mbedtls_mem_used_bytes`. These are automatically enabled for ESP-IDF projects, see [ports/mbedtls/memfault_mbedtls_metrics.c](ports/mbedtls/memfault_mbedtls_metrics.c) for usage details - Renamed the built-in LwIP metrics added in SDK version 1.0.1 from `Tcp_Drop_Count`/`Tcp_Rx_Count`/`Tcp_Tx_Count`, `Udp_Drop_Count`/`Udp_Rx_Count`/`Udp_Tx_Count` to be all lowercase `tcp_drop_count`/`tcp_rx_count`/`tcp_tx_count`, `udp_drop_count`/`udp_rx_count`/`udp_tx_count` - Add the following automatically enabled WiFi performance metrics to the ESP-IDF port: - `wifi_connected_time_ms` - `wifi_disconnect_count` - `wifi_sta_min_rssi` - Fix a bug in the [mbedtls port](ports/mbedtls/) causing an infinite loop under certain error conditions on TLS handshake - Zephyr: - Improve log flushing in the Memfault log backend during fault when deferred logging is enabled. This ensures the latest log statements are included in the coredump log data, when the `CONFIG_MEMFAULT_LOGGING_ENABLE=y` - ESP-IDF: - `ESP_ERROR_CHECK()` assert coredumps will now correctly show as assert in the Memfault coredump analysis view, instead of "Hard Fault" ### Changes between Memfault SDK 1.0.1 and 1.0.0 - June 9, 2023 #### 📈 Improvements - Zephyr: - Improve the quality of Zephyr stack usage detection when capturing less than the full stack contents for each thread. This feature was originally released in SDK `0.43.0`. - ESP-IDF: - Add Memfault Metrics tracking LwIP runtime statistics (packet counts). A new Kconfig flag `CONFIG_MEMFAULT_LWIP_METRICS` controls this feature, which is enabled by default. The LwIP metrics helper is available for non-ESP-IDF projects using LwIP, see [`ports/lwip/memfault_lwip_metrics.c`](ports/lwip/memfault_lwip_metrics.c) for details ### Changes between Memfault SDK 1.0.0 and 0.43.3 - June 1, 2023 🎉🎉🎉 The Memfault Firmware SDK is now version `1.0.0` as of this release! Note that this is just a procedural change, there are no breaking backwards-incompatible changes in this release. We forgot to update our major version back in 2019, but better late than never 😅! Hopefully the remaining 281,474,976,710,656 versions are enough 🤞. 🎉🎉🎉 #### 📈 Improvements - Zephyr: - Add a new built-in metric `FileSystem_BytesFree` for tracking VFS bytes free. This is enabled automatically when `CONFIG_FILE_SYSTEM=y`. Use the Kconfig option `MEMFAULT_FS_BYTES_FREE_VFS_PATH` to set the VFS mount point to monitor utilization (default value is `/lfs1`). The Kconfig option `MEMFAULT_FS_BYTES_FREE_METRIC` can be used to disable the metric. - Update a few spots in the Zephyr demo CLI to use `shell_print` instead of `MEMFAULT_LOG` for command usage errors ### Changes between Memfault SDK 0.43.3 and 0.43.2 - May 22, 2023 #### 📈 Improvements - Zephyr: - Add more granular Kconfig settings to control what's collected in [`memfault_zephyr_coredump_get_regions()`](ports/zephyr/common/memfault_platform_coredump_regions.c). Default settings are identical to before this change. - `CONFIG_MEMFAULT_COREDUMP_COLLECT_STACK_REGIONS` - `CONFIG_MEMFAULT_COREDUMP_COLLECT_KERNEL_REGION` - `CONFIG_MEMFAULT_COREDUMP_COLLECT_TASKS_REGIONS` - Fix a build error when an application is configured without `CONFIG_HEAP_MEM_POOL_SIZE=y` (i.e. no `kmalloc` in use). - Fix a build error when building the [nRF9160 example](examples/nrf-connect-sdk/nrf9160) on nRF-Connect SDK v2.3.0. - ESP-IDF: - Add support for upcoming v5.1 release of the ESP-IDF SDK - Add support in the [ESP32 example app](examples/esp32) for the ESP32-C6 chip ### Changes between Memfault SDK 0.43.2 and 0.43.1 - May 3, 2023 #### 📈 Improvements - Fix a bug 🐛 where metrics accumulated with `memfault_metrics_heartbeat_add()` would no longer be included in the serialized heartbeat data. This regression occurred in `0.42.0`. ### Changes between Memfault SDK 0.43.1 and 0.43.0 - April 26, 2023 #### 📈 Improvements - Zephyr: - The `z_NmiHandlerSet` function is renamed for the upcoming Zephyr 3.4. Support the new name. Fixes [#49](https://github.com/memfault/memfault-firmware-sdk/issues/49). Thanks [@mbolivar-nordic](https://github.com/mbolivar-nordic) for filing this issue! ### Changes between Memfault SDK 0.43.0 and 0.42.1 - April 18, 2023 #### 🚀 New Features - Add coredump support for Cortex-R chips (ARMv7-R) #### 📈 Improvements - Add a QEMU-based FreeRTOS example project, find it under [examples/freertos](examples/freertos) - Switch `printf` function attribute to use `__printf__`, to avoid collision with user code that redefines the `printf` token - Zephyr: - Compute thread stack high watermark values on-target when capturing a coredump. This enables showing high watermark information in the Memfault coredump analysis view without capturing the full stack for a thread ### Changes between Memfault SDK 0.42.1 and 0.42.0 - April 4, 2023 #### 📈 Improvements - Zephyr: - Remove `LEGACY_CONFIG_PATH` Kconfig selection, now that the `zephyr.h` header is no longer used as of Memfault SDK `0.42.0`. This option no longer exists after Zephyr v3.3.0. Fixes [#48](https://github.com/memfault/memfault-firmware-sdk/issues/48) - Minor changes to the [ESP8266 port](ports/esp8266_sdk) to improve out-of-the-box compilation - Add new functionality to output buffered log data via `memfault_log_export_logs()`. See the [`log.h` header](components/include/memfault/core/log.h) for detailed usage. ### Changes between Memfault SDK 0.42.0 and 0.41.2 - Mar 22, 2023 #### 📈 Improvements - Zephyr: - Add option to capture full thread stacks for classifying stack overflows and determining stack high watermarks. This feature is enabled by setting `CONFIG_MEMFAULT_COREDUMP_FULL_THREAD_STACKS=y` - Remove usage of the `zephyr.h` header in preparation for Zephyr v3.4.0. Thanks to [@jfischer-no](https://github.com/jfischer-no) for the patch! - `memfault_gdb.py`: - Add support for exporting data from GCC 12 compiled symbol files - Add arguments to override device serial ID, software type, software version, and hardware revision #### 💥 Breaking Changes - Metrics: - Integer type metrics (signed/unsigned) will reset to NULL when not set during a heartbeat interval. This NULL value will be discarded by Memfault when received. The previous behavior was to reset to 0 which makes discarding values difficult as 0 is a valid value for these types. For more info please see the [Metrics](https://docs.memfault.com/docs/mcu/metrics-api#setting-metric-values) docs. ### Changes between Memfault SDK 0.41.2 and SDK 0.41.1 - Mar 10, 2023 #### 📈 Improvements - Zephyr / nRF-Connect SDK: - Improve compatibility with Zephyr pre-3.0 deferred logging, when using the Memfault backend - Add an option to the [examples/nrf-connect-sdk/nrf5](examples/nrf-connect-sdk/nrf5) to enable capturing all of RAM in a coredump. ### Changes between Memfault SDK 0.41.1 and SDK 0.41.0 - Mar 1, 2023 #### 📈 Improvements - Zephyr / nRF-Connect SDK: - Fix a bug 🐛 in the Memfault Logging backend, that causes coredump saving to fail when building with Zephyr versions before 3.0 (nRF-Connect SDK < 2.0). This regression was introduced in Memfault Firmware SDK **0.33.3**. - ESP-IDF: - Updated the [ESP32 example app](examples/esp32) for the ESP32-C3 on ESP-IDF v5.0.1 to use size optimizations, to more closely mirror real world environments and codesize. - Fix a compilation error in the [ESP32 example app](examples/esp32) when targeting ESP-IDF >=4.4,<4.4.3 . This regression was introduced in Memfault Firmware SDK **0.39.1**. ### Changes between Memfault SDK 0.41.0 and SDK 0.40.0 - Feb 22, 2023 #### 🚀 New Features - ESP-IDF: - Added coredump support for the ESP32-C3 (RISC-V) chip. Thank you to @jlubawy for your work on this in [#42](https://github.com/memfault/memfault-firmware-sdk/pull/42) 🎉! ### Changes between Memfault SDK 0.40.0 and SDK 0.39.1 - Feb 15, 2023 #### 💥 Breaking Changes - ESP-IDF: - The Kconfig `CONFIG_MEMFAULT_AUTOMATIC_INIT` has been deprecated and is no longer supported. Users of this Kconfig should refactor their application to call `memfault_boot` during initialization. Use of this Kconfig now results in a build error. For more information please see ### Changes between Memfault SDK 0.39.1 and SDK 0.39.0 - Feb 3, 2023 #### 🚀 New Features - **Experimental** - CMSIS-Pack support - Out Of Memory reboot reason added #### 📈 Improvements - ESP-IDF: - The default implementation of `memfault_platform_coredump_get_regions` is changed to collect the current active stack, .bss, .data, and .heap regions. Additionally if you are using ESP-IDF >= 4.4.0, the SDK will prioritize collecting FreeRTOS regions containing task TCB and stack data. - Assert coredumps are now labeled with the Assert reason ### Changes between Memfault SDK 0.39.0 and SDK 0.38.0 - Feb 3, 2023 #### 💥 Breaking Changes - Breaking changes to the [`memfault_freertos_get_task_regions()`](ports/freertos/src/memfault_freertos_ram_regions.c) function, which can be used to capture FreeRTOS tasks when coredumps are sized smaller than all available RAM. The function will now, by default, capture a truncated copy of each FreeRTOS TCB, instead of the complete structure. This makes better use of coredump storage space; the TCB structures can be very large (>1kB), but Memfault only needs the first few fields for coredump decoding. The configuration flag `MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE` (see [`default_config.h`](components/include/memfault/default_config.h)) can be set to `0` in `memfault_platform_config.h` to return to the previous behavior. ### Changes between Memfault SDK 0.38.0 and SDK 0.37.2 - Feb 1, 2023 #### 🚀 New Features - Enable coredumps on the ESP32-S2 and ESP32-S3 chips. ### Changes between Memfault SDK 0.37.2 and SDK 0.37.1 - Jan 31, 2023 #### 📈 Improvements - Zephyr: - Support building with `CONFIG_POSIX_API=y` in the Zephyr port HTTP client - ESP-IDF: - Reduce the spamminess of the esp32 example app logging - Update [`scripts/memfault_gdb.py`](scripts/memfault_gdb.py): - When explicitly listing a region to insert into the coredump via `memfault coredump --region x y`, now support parseable GDB expressions for the range arguments instead of requiring integer values. Thanks to @alvarop for this patch [#43](https://github.com/memfault/memfault-firmware-sdk/pull/43) ! - Use the `info all-registers` command when dumping registers, instead of the deprecated `info registers all` command, which works better on certain arch/monitor setups. Thanks to [@alvarop](https://github.com/alvarop) for this patch [#44](https://github.com/memfault/memfault-firmware-sdk/pull/44) ! ### Changes between Memfault SDK 0.37.1 and SDK 0.37.0 - Jan 17, 2023 #### 📈 Improvements - FreeRTOS - Add support for collecting truncated TCBs. This is particularly useful with limited coredump space. Enable by defining `MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE` in `memfault_platform_config.h". - ESP-IDF - Add support for esp32s2/s3 platforms to the esp32 example application #### 🏠 Internal - Fixup some documentation typos/errors - Add support for Python 3.10 ### Changes between Memfault SDK 0.37.0 and SDK 0.36.1 - Dec 16, 2022 #### 📈 Improvements - Built-in Metrics - Add `MemfaultSdkMetric_UnexpectedRebootDidOccur` metric. This metric uses the platform's reboot register and any reasons by the SDK function `memfault_reboot_tracking_mark_reset_imminent` to classify a reboot. When reboot tracking determines a reboot is unexpected, this metric is set to 1. Otherwise this metric is 0. - [ModusToolbox™️ Software](https://www.infineon.com/cms/en/design-support/tools/sdk/modustoolbox-software/) - Add log capture during coredump to port - Demo CLI - Add `mflt test loadaddr` command. This command is used to test specific faults due to protected regions #### 💥 Breaking Changes - Built-in Metrics - The built-in metric, `MemfaultSdkMetric_UnexpectedRebootDidOccur`, classifies all reboot reasons greater than or equal to `kMfltRebootReason_UnknownError` **or** equal to `kMfltRebootReason_Unknown` as "unexpected reboots". It is recommended to ensure your platform's implementation of `memfault_reboot_reason_get` classifies the reboot register values as accurately and precisely as possible to avoid incorrect metric values. ### Changes between Memfault SDK 0.36.1 and SDK 0.36.0 - Dec 9, 2022 #### 📈 Improvements - ESP-IDF: - Fix a bug 🐛 in the [ESP32 example app](examples/esp32), where wifi join fails when using versions of ESP-IDF prior to 5.0 ### Changes between Memfault SDK 0.36.0 and SDK 0.35.0 - Dec 6, 2022 #### 📈 Improvements - ESP-IDF: - Add support for [just-released ESP-IDF v5](https://github.com/espressif/esp-idf/releases/tag/v5.0) 🎉! Thanks to [@jlubawy](https://github.com/jlubawy) and the patch supplied in #39 for this, very much appreciated! - Add an auto-OTA (and auto-WiFi-join) feature to the [ESP32 example app](examples/esp32)- enabled by default but can be disabled with Kconfig - The [Heap Stats tracing component](https://mflt.io/mcu-heap-stats) has been revamped to make more efficient usage of the bookkeeping structure. Usage should be the same as before, but now should provide more data without significantly expanding the memory utilization. ### Changes between Memfault SDK 0.35.0 and SDK 0.34.2 - Nov 22, 2022 #### 🚀 New Features - **Experimental** Custom Data Recording API - Allows sending custom data collected over the course of a recording period #### 📈 Improvements - Zephyr: - Modify heap stats to only collect info during allocations/deallocations from threads - ESP-IDF: - ESP32 reboot tracking into RTC noinit - nRF5 SDK: - NRF5 coredump regions -Wunused-macros, fixes warning for unused macros #### 🏠 Internal - Experiment: pytest as fw test frontend - README: Add additional details on port integration ### Changes between Memfault SDK 0.34.2 and SDK 0.34.1 - Nov 8, 2022 #### 📈 Improvements - [ModusToolbox™️ Software](https://www.infineon.com/cms/en/design-support/tools/sdk/modustoolbox-software/) - Updates SDK for compatibility with MTB 3.0 ### Changes between Memfault SDK 0.34.1 and SDK 0.34.0 - Nov 7, 2022 #### 📈 Improvements - nRF-Connect: - Updates for Zephyr upmerge 2022.11.03 (see #35 + #36) - Fix watchdog test (`mflt test hang`) in [`examples/nrf-connect-sdk/nrf5/`](examples/nrf-connect-sdk/nrf5/) - Zephyr: - Set `CONFIG_QEMU_ICOUNT=n` in [`examples/zephyr/qemu/`](examples/zephyr/qemu/), which fixes the emulated target execution speed - Add heap free and stack usage Metrics to [`examples/zephyr/qemu/`](examples/zephyr/qemu/) - Update the `memfault_demo_cli_cmd_assert()` test command to take a single arg, which is used in `MEMFAULT_ASSERT_RECORD()`. This enables testing that assert variant from the CLI. ### Changes between Memfault SDK 0.34.0 and SDK 0.33.5 - Nov 1, 2022 #### 📈 Improvements - Misc ESP32 [port](ports/esp_idf) & [example app](examples/esp32/apps/memfault_demo_app) improvements - Added diagnostic print line containing Build Id at boot up - Improved messaging displayed when using `memfault_ota_check` test command - Example app now prints device info on bootup - Fix an issue where incremental build (`idf.py build && idf.py build`) would report a nuisance failure. - Flatten + simplify the directory structure of the QEMU based example project - A new [`ports/mbedtls`](ports/mbedtls) is available, which implements a basic Mbed TLS client for performing Memfault data upload. - Zephyr: Collect sysheap stats using the [Memfault Heap Tracking](https://mflt.io/mcu-heap-stats) component. This is configured with the `CONFIG_MEMFAULT_HEAP_STATS` Kconfig option (enabled by default), and will track allocations done with `k_malloc()`. - Fix an enum-mismatch warning in `memfault_metrics.c` when using the ARMCC v5 compiler. #### 💥 Breaking Changes - If you are using the ESP32 HTTP Client, the Memfault Project Key is now configured directly via the [ESP32 Project Configuration System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html). You need to do the following: 1. Remove the `g_mflt_http_client_config` in your platform port 2. Add `CONFIG_MEMFAULT_PROJECT_KEY="YOUR_PROJECT_KEY"` to your projects `sdkconfig.defaults` ### Changes between Memfault SDK 0.33.4 and SDK 0.33.5 - Oct 19, 2022 #### 📈 Improvements - nRF-Connect: Update the nRF9160 example application, `examples/nrf-connect-sdk/nrf9160`, to build and run correctly with nRF-Connect SDK v2.1.0 - Zephyr: Add an example Zephyr application targeting the QEMU board - ESP-IDF: - Add a configuration option for setting the ESP-IDF HTTP client timeout value - Fix compilation for the ESP32-S3. _Note: coredumps are currently only supported on the ESP32, not the ESP32-S2, -S3, or -C3. This change only fixes compiling for the -S3 platform_ - Add support for ESP-IDF v4.2.3 #### 🏠 Internal - Support building the unit tests with GCC 12 - Miscellaneous fixes to unit test infrastructure to better support building in Mac OSX ### Changes between Memfault SDK 0.33.3 and SDK 0.33.4 - Sept 15, 2022 #### 📈 Improvements - Zephyr port updates: - Handle thread abort in the task stack capture hook. Previous to this change, aborted tasks would remain on the captured task list, and restarting the task would create a duplicate entry. ### Changes between Memfault SDK 0.33.2 and SDK 0.33.3 - Sept 14, 2022 #### 📈 Improvements - Zephyr port updates: - Add a call to `LOG_PANIC()` before running the Memfault fault handler, to flush any deferred logs before the reboot ### Changes between Memfault SDK 0.33.1 and SDK 0.33.2 - Sept 7, 2022 #### 📈 Improvements - Zephyr port updates: - fix a few minor nuisance build warnings on niche Zephyr configurations - enable `LOG_OUTPUT` when `MEMFAULT_LOGGING_ENABLE` is enabled- this fixes a build error if all other log backends are disabled. thanks to @balaji-nordic for this fix! closes #33 - Add a debug cli test command to the nRF-Connect SDK port for printing the OTA url ### Changes between Memfault SDK 0.33.0 and SDK 0.33.1 - Aug 26, 2022 #### 📈 Improvements - Fix a 🐛 in the heap stats component (#32), thanks [@christophgreen](https://github.com/christophgreen) for reporting it! - Zephyr port updates: - add support for the newly namespaced Zephyr include path in upcoming Zephyr v3.2 (`#include ` → `#include `). The includes were moved [prior to v3.1](https://github.com/zephyrproject-rtos/zephyr/commit/53ef68d4598b2f9005c5da3fc0b860ca1999d350) of Zephyr, but v3.2 [changes the backwards compatibility support to opt-in](https://github.com/zephyrproject-rtos/zephyr/commit/1ec0c6f5308937dc8e77acc2567d6f53cdd7a74e). The Memfault SDK is now updated to support both. - fix Zephyr Memfault log capture to have the correct prefix in the decoded output when using LOG2 - previously all log lines regardless of level would have an `E` prefix (regression introduced in Memfault SDK version 0.32.0) - fix Zephyr Memfault log capture when in `CONFIG_LOG_MODE_IMMEDIATE` and using LOG2 to capture the full log line instead of each logged character as a separate line. #### 🏠 Internal - Zephyr port folder for `v2.x` migrated to `common`, now that Zephyr v1.14 support has been removed (done in v0.32.0 of the Memfault SDK) - Update README's for the example projects to match the new demo shell command structure (`crash 1` → `test_hardfault`, etc). - Tidy up nrf9160 example app Kconfig setup - Fix parallel unit test invocation ### Changes between Memfault SDK 0.33.0 and SDK 0.32.2 - Aug 18, 2022 #### 📈 Improvements - Extend [memfault demo shell](components/demo/src/memfault_demo_shell.c) to support terminals that only emit CR for line endings - nRF5 SDK Updates: - Added a [software watchdog](https://mflt.io/root-cause-watchdogs) reference port for nRF5 SDK which makes use of the RTC Peripheral. See [ports/nrf5_sdk/software_watchdog.c](ports/nrf5_sdk/software_watchdog.c) for more details. - Updated [nRF5 example app](examples/nrf5/apps/memfault_demo_app/) to make use of hardware and new software watchdog port. - Zephyr Port Updates: - Added Kconfig option to fallback to using `printk` by default when no logging is enabled. This can be disabled by setting `CONFIG_MEMFAULT_PLATFORM_LOG_FALLBACK_TO_PRINTK=n`. - nRF Connect SDK Updates: - Fixed a 🐛 which could result in download errors when using [Memfault nRF Connect SDK FOTA client](ports/nrf-connect-sdk/zephyr/include/memfault/nrfconnect_port/fota.h) and enabled client in example application by default. - Added new example application for trying Memfault with nRF53 & nRF52 based development kits. See [examples/nrf-connect-sdk/nrf5](examples/nrf-connect-sdk/nrf5) for more details. ### Changes between Memfault SDK 0.32.2 and SDK 0.32.1 - Aug 16, 2022 #### 📈 Improvements - Zephyr port: added a fix for upcoming Zephyr 3.2 compatibility, thanks [@nordicjm](https://github.com/nordicjm) for the fix! ### Changes between Memfault SDK 0.32.1 and SDK 0.32.0 - Aug 8, 2022 #### 🏠 Internal - Added default config header for PSoC 6 port [ports/cypress/psoc6/psoc6_default_config.h](ports/cypress/psoc6/psoc6_default_config.h) so user doesn't have to create it ### Changes between Memfault SDK 0.32.0 and SDK 0.31.5 - Aug 8, 2022 #### 📈 Improvements - [ModusToolbox™️ Software](https://www.infineon.com/cms/en/design-support/tools/sdk/modustoolbox-software/) port updates - Added heartbeat metrics for heap and Wi-Fi performance tracking when using the default port for [CAT1A (PSoC™️ 6)](https://github.com/Infineon/mtb-pdl-cat1). See [ports/cypress/psoc6/memfault_platform_core.c](ports/cypress/psoc6/memfault_platform_core.c) for more details - Fixed reboot reason reported when PSoC 6 is fully reset to report "Power On Reset" instead of "Unknown" - Zephyr port updates - Memfault logs (eg `MEMFAULT_LOG_DEBUG()` etc) are now routed to the Zephyr logging infrastructure. The typical set of Kconfig options for Memfault logs are available (`CONFIG_MEMFAULT_LOG_LEVEL_WRN` etc). See details in "Breaking Changes" below for enabling logs in your project. - Added a new Kconfig option, `MEMFAULT_ZEPHYR_FATAL_HANDLER`, which can be used to disable the Zephyr fault handler print facilities. - Streamline support for nRF-Connect SDK based applications that don't need the Memfault root certificates (eg nRF53 or nRF52 devices), via a new Kconfig option `MEMFAULT_ROOT_CERT_STORAGE`, to avoid a nuisance build error #### 💥 Breaking Changes - Users will no longer see internal Memfault log output by default, but will have to enable it explicitly to see the output: ```ini # enable LOG CONFIG_LOG=y # not required- enabling the Memfault logging component enables including the # log buffer in coredumps CONFIG_MEMFAULT_LOGGING_ENABLE=y # if on pre-v3.1.0 zephyr, you can choose either the default LOG v1 # implementation, or select a LOG2 mode to enable LOG2. on zephyr 3.1.0+, LOG # v1 is removed and LOG v2 is now the only log implementation # CONFIG_LOG2_MODE_DEFERRED=y # make sure to select a log backend to see the output CONFIG_LOG_BACKEND_UART=y ``` The log statements affected by this change are likely only the internal Memfault SDK logs (`MEMFAULT_LOG_DEBUG()` etc), unless those macros are used in the user application. - Removed support for Zephyr LTS release 1.14 as it was superseded by [LTS V2 almost a year ago now](https://www.zephyrproject.org/zephyr-lts-v2-release/). A project using this release of Zephyr must target a memfault-firmware-sdk release less than 0.32.0. #### 🏠 Internal - More logically grouped Kconfig settings in Zephyr example app's [prj.conf](examples/zephyr/apps/memfault_demo_app/prj.conf) - Fixed a few typos in particle port documentation - Simplified compilation steps for the [nRF91 sample test app](examples/nrf-connect-sdk/nrf9160/memfault_demo_app) when compiling with older releases of the nRF Connect SDK and refreshed the example to target the v2.0.2 release by default - Updated default demo CLI commands to better align with [our suggested integration test commands](https://mflt.io/mcu-test-commands). The default set now looks like this: ```bash mflt> help clear_core: Clear an existing coredump drain_chunks: Flushes queued Memfault data. To upload data see https://mflt.io/posting-chunks-with-gdb export: Export base64-encoded chunks. To upload data see https://mflt.io/chunk-data-export get_core: Get coredump info get_device_info: Get device info test_assert: Trigger memfault assert test_busfault: Trigger a busfault test_hardfault: Trigger a hardfault test_memmanage: Trigger a memory management fault test_usagefault: Trigger a usage fault test_log: Writes test logs to log buffer test_log_capture: Trigger capture of current log buffer contents test_reboot: Force system reset and track it with a trace event test_trace: Capture an example trace event help: Lists all commands ``` ### Changes between Memfault SDK 0.31.5 and SDK 0.31.4 - July 22, 2022 #### 📈 Improvements - Zephyr port: enable proper backtraces for Zephyr `__ASSERT()` macro on aarch32/cortex_m. Prior to this fix, crashes from `__ASSERT()` triggering would show an incorrect PC/LR for the active thread. - Support for pre-release nRF Connect SDK v2.0.99 and Zephyr > v3.1: - Upcoming nRF Connect SDK and Zephyr releases removed logging v1. Add build support for these changes, and keep backwards compatibility for previous nRF Connect SDK/Zephyr releases - Correct an issue in the Memfault logging v2 backend, when the system was invoked from an ISR context. This could happen due to a recent change, in Memfault SDK v0.31.1, where the Zephyr fatal informational logs were output from `memfault_platform_reboot()` by default. It did not impact the collected backtrace, but it would show a nuisance `__ASSERT()` in the console output, if `CONFIG_ASSERT=y`. #### 🏠 Internal - Fix a compilation issue in the Dialog example app from the removal of `memfault_demo_cli_cmd_print_chunk()` in Memfault SDK release v0.31.4. ### Changes between Memfault SDK 0.31.4 and SDK 0.31.3 - July 19, 2022 #### 📈 Improvements - ESP32 port: add new Kconfig option, `CONFIG_MEMFAULT_AUTOMATIC_INIT`, that can be explicitly set to `n` to skip automatically initializing the Memfault SDK on boot. This can be useful if Memfault SDK initialization needs to be deferred to application start. - Zephyr port: add Kconfig options, `CONFIG_MEMFAULT_INIT_PRIORITY`/`CONFIG_MEMFAULT_INIT_LEVEL_POST_KERNEL` for controlling the Memfault SDK initialization level and priority. This can be useful when needing Memfault to initialize earlier in the system startup sequence, for example for diagnosing crashes in an early driver initialization. - Partial support, still in progress, for NRF Connect SDK + Zephyr v3.1: - Remove reference to the now-removed Kconfig symbol, `NET_SOCKETS_OFFLOAD_TLS` to enable building without warnings. **NOTE:** if mbedtls is enabled (`CONFIG_MBEDTLS=y`), but is _not_ being used for HTTP transfers (eg, mbedtls is used for security functions, but the device does not use HTTP for transferring data), it may be necessary to explicitly set `CONFIG_MEMFAULT_HTTP_USES_MBEDTLS=n`. #### 🏠 Internal - Zephyr port: remove an unused header file, `ports/zephyr/common/memfault_zephyr_http.h` - Remove `memfault_demo_cli_cmd_print_chunk()` demo function. `memfault_data_export_dump_chunks()` can be used instead, which is intended to be used with the "Chunks Debug" UI in the Memfault web application- see [here](https://mflt.io/chunk-data-export) for more details ### Changes between Memfault SDK 0.31.3 and SDK 0.31.2 - July 8, 2022 #### 📈 Improvements - Support Zephyr v3.1+ by conditionally compiling out Logger v1 code, thanks to [@tejlmand](https://github.com/tejlmand) for the patch! ### Changes between Memfault SDK 0.31.2 and SDK 0.31.1 - June 24, 2022 #### 📈 Improvements - Fixed a 🐛 in the [Zephyr port HTTP implementation](ports/zephyr/common/memfault_platform_http.c), where a socket file descriptor was leaked. This caused every HTTP operation after the first to fail on Zephyr platforms. Thanks to [@rerickson1](https://github.com/rerickson1) for the fix! - Added an update to improve the quality of stack traces when using `MEMFAULT_ASSERT` with the TI ARM Clang Compiler ### Changes between Memfault SDK 0.31.1 and SDK 0.31.0 - June 16, 2022 #### 📈 Improvements - Enable the Zephyr fault handler (including console fault prints) after Memfault handler runs. Can be disabled by implementing `memfault_platform_reboot()`. See details in [ports/zephyr/include/memfault/ports/zephyr/coredump.h](ports/zephyr/include/memfault/ports/zephyr/coredump.h) #### 🏠 Internal - Fixed compiler error in [nRF91 sample test app](examples/nrf-connect-sdk/nrf9160/memfault_demo_app) when compiling with the nRF Connect SDK v2.0.0 release ### Changes between Memfault SDK 0.31.0 and SDK 0.30.5 - June 6, 2022 #### 📈 Improvements - Added reference port for [CAT1A (PSoC™️ 6)](https://github.com/Infineon/mtb-pdl-cat1) based MCUs using the [ModusToolbox™️ Software](https://www.infineon.com/cms/en/design-support/tools/sdk/modustoolbox-software/) stack. For more details see [ports/cypress/psoc6](ports/cypress/psoc6) directory. - Added a convenience utility function for posting chunks using the Memfault http client. See [`memfault_http_client_post_chunk`](components/include/memfault/http/http_client.h#L101) for more details! #### 🏠 Internal - Fixed compiler error in [nRF91 sample test app](examples/nrf-connect-sdk/nrf9160/memfault_demo_app) when compiling with the nRF Connect SDK 1.8 release ### Changes between Memfault SDK 0.30.5 and SDK 0.30.4 - May 24, 2022 #### 🚀 New Features - ESP-IDF: add Memfault Compact Log example integration to the [`examples/esp32`](examples/esp32) project #### 📈 Improvements - ESP-IDF: Fix backtraces when using ESP-IDF v4.4+ - nRF-Connect SDK: enable the Kconfig flag `MEMFAULT_NRF_CONNECT_SDK` by default when targeting the nrf52 + nrf53 series SOCs (previously only enabled by default for nrf91 series) #### 🏠 Internal Added clarifications around licensing in ports and examples folders. See [README](README.md) for more details. ### Changes between Memfault SDK 0.30.4 and SDK 0.30.3 - May 4, 2022 #### 📈 Improvements - minor updates to [`scripts/eclipse_patch.py`](scripts/eclipse_patch.py) to support NXP's MCUXpresso IDE ### Changes between Memfault SDK 0.30.3 and SDK 0.30.2 - April 25, 2022 #### 📈 Improvements - Particle's Device OS port improvements: - A user initiated reboot will now be recorded as a User Shutdown instead of a Low Power reset - A custom hardware_version can now be specified using the `hardware_version` argument when initializing the Memfault library - Default hardware version now uses the `PLATFORM_NAME` macro instead of `PRODUCT_SERIES` macro - Zephyr port improvements - Exposed lower level APIs to Memfault's HTTP post implementation to allow easier custom handling. See [`ports/zephyr/include/memfault/ports/zephyr/http.h`](ports/zephyr/include/memfault/ports/zephyr/http.h) for more details #### 🏠 Internal - Misc README documentation improvements ### Changes between Memfault SDK 0.30.2 and SDK 0.30.1 - April 12, 2022 - Fix a build regression on nRF Connect SDK v1.2 caused by the new Kconfig flag `CONFIG_MEMFAULT_HTTP_USES_MBEDTLS` ### Changes between Memfault SDK 0.30.1 and SDK 0.30.0 - April 6, 2022 #### 📈 Improvements - Fix stack selection when in ISR context in some Zephyr versions - Fix a build error when building Zephyr with `CONFIG_NORDIC_SECURITY_BACKEND` enabled. New Kconfig flag `CONFIG_MEMFAULT_HTTP_USES_MBEDTLS` can be used to manually control this configuration if necessary (default should be set correctly in most cases) #### 🏠 Internal - Fix CI unit test build error from older version of gcc ### Changes between Memfault SDK 0.30.0 and SDK 0.29.1 - Mar 31, 2022 #### 🚀 New Features - Added a Task Watchdog optional module. This can be used to monitor and trigger a fault in the case of a task or thread that becomes stuck. See information in [components/include/memfault/core/task_watchdog.h](components/include/memfault/core/task_watchdog.h) for how to configure and use the module #### 📈 Improvements - Fix compilation when building for a Zephyr target that does not have the `CONFIG_ARM_MPU` flag enabled - Fix compilation errors to enable compatibility with Zephyr v3.0.0 ### Changes between Memfault SDK 0.29.1 and SDK 0.29.0 - Mar 16, 2022 #### 🏠 Internal - Updated Memfault Diagnostic GATT Service (MDS) based on feedback. This service can be used to transparently forward data collected by the SDK to a Bluetooth Low Energy gateway and proxied to the cloud. See [ports/include/memfault/ports/ble/mds.h](ports/include/memfault/ports/ble/mds.h#L1) - Updated Mbed OS invoke commands to be more resilient against python package conflicts #### 💥 Breaking Changes - If your project is based on Zephyr < 2.6, you now need to explicitly set `CONFIG_OPENOCD_SUPPORT=y` in your `prj.conf` ### Changes between Memfault SDK 0.29.0 and SDK 0.28.2 - Feb 28, 2022 #### 🚀 New Features - Added a port to Particle's Device OS. More details can be found in [`ports/particle/README.md`](ports/particle/README.md). - Added several more [reboot reason options](components/include/memfault/core/reboot_reason_types.h#L16): - `kMfltRebootReason_KernelPanic` for explicitly tracking fatal resets from within a OS or RTOS - `kMfltRebootReason_FirmwareUpdateError` for explicitly tracking resets due to firmware update failures or rollbacks #### 📈 Improvements - Added a convenience utility function for base64 encoding data in place. See [`memfault_base64_encode_inplace`](components/include/memfault/util/base64.h#L35) for more details! - Fixed compiler error in ESP-IDF port when compiling for ESP32-S2 targets #### 🏠 Internal - Added configuration option, [`MEMFAULT_COREDUMP_INCLUDE_BUILD_ID`](components/include/memfault/default_config.h#L1), which can be used to disable storing the Build Id in a coredump. - Fixed stale link in Mbed example app [`README`](examples/mbed/README.md). - Added [utility script](scripts/create_arduino_library.py) that can be used to "arduino-ify" the code in this repo. - Fixed linter errors in python scripts after addition of flake8-bugbear linter in CI. - Fixed compiler error in [nRF91 sample test app](examples/nrf-connect-sdk/nrf9160/memfault_demo_app) when compiling with the nRF Connect SDK 1.2 release ### Changes between Memfault SDK 0.28.2 and SDK 0.28.1 - Feb 1, 2022 #### 🏠 Internal - Updated [nRF91 sample test app](examples/nrf-connect-sdk/nrf9160/memfault_demo_app) to be compatible with nRF Connect SDK 1.9 release - Updated python scripts to be compatible with new flake8 linter options - Updated where `lcov` is sourced from when running unit tests in CI ### Changes between Memfault SDK 0.28.1 and SDK 0.28.0 - Jan 20, 2022 #### 📈 Improvements - Add an optional override flag to control the name used for Zephyr data regions- see [`ports/zephyr/common/memfault_zephyr_ram_regions.c`](ports/zephyr/common/memfault_zephyr_ram_regions.c). Only needed for unusual Zephyr + Memfault configurations prior to Zephyr v2.7 (for example, nRF Connect SDK v1.7.1 with Memfault SDK v0.27.3+) - Fix the STM32F7xx reboot reason port to correctly account for the internally wired Pin Reset - Fix a function prototype mismatch in the STM32L4 flash port (thanks to [@schultetwin](https://github.com/schultetwin) for reporting this in #22!) ### Changes between Memfault SDK 0.28.0 and SDK 0.27.3 - Jan 4, 2022 #### 🚀 New Features - Add support for setting string metrics, see `memfault_metrics_heartbeat_set_string()` in [`components/include/memfault/metrics/metrics.h`](components/include/memfault/metrics/metrics.h) for the new API. See the [Memfault Docs](https://mflt.io/embedded-metrics) for general information on using the metrics API. - Updated `memfault_metrics_heartbeat_debug_print()` to also print current timer values, instead of showing `0`. See [`components/include/memfault/metrics/metrics.h`](components/include/memfault/metrics/metrics.h) for details #### 📈 Improvements - Update the STM32 QP/C example ([`examples/qp`](examples/qp)) to compile and run correctly now - Add instructions for exercising Memfault OTA in the ESP32 example, see the "Testing OTA" section in [`examples/esp32/README.md`](examples/esp32/README.md) - Update the Memfault HTTP client to URL-encode query params when checking for OTA updates (in the case of Device properties containing reserved characters, eg `+`). Update the ESP port to check for reserved characters in query params and emit an error - Fix an outdated comment in `cmake/Memfault.cmake`, as reported in [issue #21](https://github.com/memfault/memfault-firmware-sdk/issues/21) (thank you [@C47D](https://github.com/C47D) !) #### 🏠 Internal - Update Python tests to use a mocked-out gdb instance ### Changes between Memfault SDK 0.27.3 and SDK 0.27.2 - Nov 22, 2021 #### 📈 Improvements - Fix a build error for the Nordic Connect SDK v1.7.99 development version - Correct an error in header file include order in the Mynewt port - Update the esp32 and zephyr examples to use `1.0.0-dev` instead of `1.0.0+<6 digits of build id>` for the version specifier. Build id is no longer required for symbol file reconciliation and the `+` character is a reserved character for URI schemes; this impacted OTA release requests. See this document for [Memfault's recommended versioning strategy](https://docs.memfault.com/docs/platform/software-version-hardware-version/#software-version) - Add a reboot reason port for the STM32F7xx family. #### 🏠 Internal - Re-run python `black` and `isort` formatters on python code ### Changes between Memfault SDK 0.27.2 and SDK 0.27.1 - Nov 5, 2021 #### 📈 Improvements - The Mynewt integration now supports the Memfault demo shell via `mflt_shell_init()`, see the [Mynewt port README.md](ports/mynewt/README.md) for details. Huge thanks to [@t3zeng](https://github.com/t3zeng) for providing this implementation! - Add support for ESP-IDF v4.3.1. This update should fix the bootlooping issue seen when using the port with v4.3.1+ of ESP-IDF. - Add support for `LOG2` deferred mode on zephyr. This should fix bootloops when enabling `LOG2`. - Fix flipped args passed from `MEMFAULT_ASSERT_RECORD()` to `MEMFAULT_ASSERT_EXTRA_AND_REASON()` (regression in v0.23.0). This affected the `_extra` additional context value passed via this macro. - Fix a typo in [`ports/esp_idf/memfault/common/memfault_platform_http_client.c`](ports/esp_idf/memfault/common/memfault_platform_http_client.c) which caused the OTA example to always return "OTA Update Available" when the current version is already the latest. #### 🏠 Internal - Updated list of sample apps in [`examples/README.md`](examples/README.md) ### Changes between Memfault SDK 0.27.1 and SDK 0.27.0 - Oct 11, 2021 #### 📈 Improvements - Extended mynewt RTOS port to capture coredumps via a [`os_coredump_cb`](ports/mynewt/src/memfault_platform_port.c) implementation when `MEMFAULT_COREDUMP_CB=1` #### 🏠 Internal - Fixed a few compiler warnings emitted when compiling with clang - Improved documentation for [coredump config with nRF5 SDK](ports/nrf5_sdk/nrf5_coredump_regions.c) ### Changes between Memfault SDK 0.27.0 and SDK 0.26.1 - Oct 5, 2021 #### 📈 Improvements - Added support for using [compact logs](https://mflt.io/compact-logs) with the Memfault [log subsystem](https://mflt.io/logging). - Added port for mynewt RTOS to Memfault SDK. (Huge thanks to [@t3zeng](https://github.com/t3zeng) for the help here!) See [sdk/embedded/ports/mynewt](sdk/embedded/ports/mynewt/README.md) for more details. - Added support for [Zephyr 2.7](https://docs.zephyrproject.org/latest/releases/release-notes-2.7.html) release. #### 🏠 Internal - Fixed a missing symbol linker error for `memfault_fault_handler` that could arise when compiling with `-flto`. - Fixed a compiler error in `memfault_fault_handling_arm.c` that arose when using certain versions of the Clang compiler. - Cleaned up python scripts after enabling additional PEP8 naming convention linters. ### Changes between Memfault SDK 0.26.1 and SDK 0.26.0 - Sept 20, 2021 #### 🏠 Internal - Updated [`modem_key_mgmt_exists`](ports/zephyr/ncs/src/memfault_nrf91_root_cert_storage.c) API usage to be compatible with changes upcoming in nRF Connect SDK 1.8. ### Changes between Memfault SDK 0.26.0 and SDK 0.25.0 - Sept 15, 2021 #### 📈 Improvements - Added preview of the Memfault Diagnostic GATT Service (MDS). This service can be used to transparently forward data collected by the SDK to a Bluetooth Low Energy gateway and proxied to the cloud. See [ports/include/memfault/ports/ble/mds.h](ports/include/memfault/ports/ble/mds.h#L1) for more details! - Added reference port of MDS for the DA1469x SDK to [ports/dialog/da1469x/memfault_diagnostic_service.c](ports/dialog/da1469x/memfault_diagnostic_service.c#L1) - Implemented [utility python script](scripts/eclipse_patch.py#L1) which can be used for quickly adding a `memfault-firmware-sdk` port to an eclipse project. For more details, run ```bash python scripts/eclipse_patch.py --help ``` - Added example project demonstrating integration of Memfault on Cypress' [CY8CKIT-064S0S2-4343W](https://www.cypress.com/documentation/development-kitsboards/psoc-64-standard-secure-aws-wi-fi-bt-pioneer-kit-cy8ckit) running [Amazon-FreeRTOS](https://github.com/aws/amazon-freertos) publishing data using an [AWS IoT MQTT broker](https://docs.aws.amazon.com/freertos/latest/userguide/getting_started_cypress_psoc64.html). For more details about how to run the Memfault example, see [examples/cypress/CY8CKIT-064S0S2-4343W/README.md](examples/cypress/CY8CKIT-064S0S2-4343W/README.md). - Fixed a compiler warning emitted when using TI's GCC Compiler as reported by [@albertskog](https://github.com/albertskog) in [issue #18](https://github.com/memfault/memfault-firmware-sdk/issues/18) #### 🏠 Internal - Apply some suggestions emitted by `flake8-pytest-style` linter to [scripts/tests/test_memfault_gdb.py](scripts/tests/test_memfault_gdb.py). ### Changes between Memfault SDK 0.25.0 and SDK 0.24.2 - August 30, 2021 #### 📈 Improvements - Added a workaround to [`event_storage.h`](components/include/memfault/core/event_storage.h) to prevent compilation errors when using [the unity test framework](http://www.throwtheswitch.org/unity) to generate mocks. - Updated [`makefiles/MemfaultWorker.mk`](makefiles/MemfaultWorker.mk) to use `sort` to guarantee a deterministic file list order irrespestive of [make version](https://savannah.gnu.org/bugs/index.php?52076). A consistent order is useful for [reproducible builds](https://mflt.io/reproducible-builds). - Make use of `__has_include()` in Zephy port to remove the requirement of always needing to create`memfault_platform_config.h`, `memfault_metrics_heartbeat_config.def`, & `memfault_trace_reason_user_config.def` for a build to compile. To force a compile failure instead when any of these files do not exist, a user can set [`CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL=n`](ports/zephyr/Kconfig) #### 🏠 Internal - The current version of the Memfault Firmware SDK can now be accessed programmatically from the [`memfault/version.h`](components/include/memfault/version.h). - Improved HTTP util parser when dealing with malformed status codes - Updated [nRF91 sample test app](examples/nrf-connect-sdk/nrf9160/memfault_demo_app) to be compatible with nRF Connect SDK 1.6 ### Changes between Memfault SDK 0.24.2 and SDK 0.24.1 - August 17, 2021 #### 📈 Improvements - Added a new utility API, `memfault_packetizer_set_active_sources`, to the [data_packetizer](components/include/memfault/core/data_packetizer.h) module, which let's one control the exact data sources that will get packetized. See description in the header for more details - Port Improvements: - NXP i.MX RT102x - [rich reboot reason info derived from SRC_SRSR register](ports/nxp/rt1021/src_reboot_tracking.c) ### Changes between Memfault SDK 0.24.1 and SDK 0.24.0 - August 9, 2021 #### 📈 Improvements - Applied suggestions from [@elliot-wdtl](https://github.com/elliot-wdtl) for the Zephyr ports ([#15](https://github.com/memfault/memfault-firmware-sdk/pull/15)): - Updated software watchdog port to make use of `MEMFAULT_SOFTWARE_WATCHDOG` macro - Applied suggestions from [@ioannisg](https://github.com/ioannisg) & [@mbolivar-nordic](https://github.com/mbolivar-nordic) in ([#14](https://github.com/memfault/memfault-firmware-sdk/pull/14)) to change the KConfig options used to select `CONFIG_MEMFAULT_HTTP_ENABLE` & `CONFIG_MEMFAULT_ROOT_CERT_STORAGE_NRF9160_MODEM` & `CONFIG_MEMFAULT_NRF_CONNECT_SDK` when using nRF91 targets. #### 🏠 Internal - Added `export` command to the demo cli to better mirror [our suggested integration test commands](https://mflt.io/mcu-test-commands) #### 💥 Breaking Changes - If you are were using a custom nRF91 based board config (i.e neither `BOARD_NRF9160DK_NRF9160NS` or `BOARD_THINGY91_NRF9160NS`), the following KConfig options will now be enabled by default. The following can be added to your `prj.conf` to restore the original behavior: - `CONFIG_MEMFAULT_HTTP_ENABLE=n` - `CONFIG_MEMFAULT_NRF_CONNECT_SDK=n` - `CONFIG_MEMFAULT_ROOT_CERT_STORAGE_TLS_CREDENTIAL_STORAGE=y` ### Changes between Memfault SDK 0.24.0 and SDK 0.23.0 - July 27, 2021 #### 📈 Improvements - Added "compact log" support to trace events. When enabled, the format string will be removed at compile time from calls to `MEMFAULT_TRACE_EVENT_WITH_LOG` and an integer along with arguments will be serialized instead. The actual string will recovered and formatted when it arrives in the Memfault cloud. This leads to a massive reduction in space & bandwidth needed to send trace events. For more details about how to set up, [check out this guide!](https://mflt.io/compact-logs) - Fixed a `-Wshadow` compiler error that would arise in [`memfault_coredump_regions_armv7.c`](components/panics/src/memfault_coredump_regions_armv7.c) when `MEMFAULT_COLLECT_MPU_STATE` was enabled - Updated debug print utility in [`memfault_coredump_storage_debug.c`](components/panics/src/memfault_coredump_storage_debug.c) to guard against potentially printing an uninitialized string. - Removed unnecessary extra argument from `MEMFAULT_SOFTWARE_WATCHDOG` #### 💥 Breaking Changes - If you were already using `MEMFAULT_SOFTWARE_WATCHDOG`, you will need to update your call site invocations to remove the argument being passed. i.e ```diff - MEMFAULT_SOFTWARE_WATCHDOG(0); + MEMFAULT_SOFTWARE_WATCHDOG(); ``` ### Changes between Memfault SDK 0.23.0 and SDK 0.22.0 - July 8, 2021 #### 📈 Improvements - Support for Dialog DA1469x chip family (Huge thanks to [@iandmorris](https://github.com/iandmorris) for the help here!) - Example eclipse project and more details about how to add the port to any DA1469x based project [can be found here](examples/dialog/da1469x). - Added a simple utility to track heap allocations. This can be used to more easily debug what allocations led to out of memory situations with the addition of only several hundred bytes to a coredump capture. For more details, see [`memfault/core/heap_stats.h`](components/include/memfault/core/heap_stats.h) - For FreeRTOS users, automatic tracking of heap allocations can be enabled with the Memfault port. To enable, see the "Heap Tracking" section in the README at [ports/freertos/](ports/freertos). - Added new convenience utilities for asserting when a watchdog event is detected, [`MEMFAULT_SOFTWARE_WATCHDOG`](components/include/memfault/panics/assert.h#L65). This will result in the issue in the Memfault UI being classified for as a "Software Watchdog" instead of an "Assert" for easier classification. - Fixed a 🐛 in [Zephyr port](ports/zephyr/common/memfault_platform_metrics.c) where cpu runtime metrics were never getting reset after a heartbeat was collected leading to always increasing runtime values getting reported. #### 🏠 Internal - Improved support for running [tests](tests/) against different versions of clang and gcc and enabled more address sanitizers such as [`-fsanitize=undefined`](https://interrupt.memfault.com/blog/ubsan-trap) - Misc documentation edits ### Changes between Memfault SDK 0.22.0 and SDK 0.21.1 - June 17, 2021 #### 📈 Improvements - Reduced code space utilized by metric subsystem by transitioning from a string representation of metric names to an enum representation. - Updated [`memfault_gdb.py`](scripts/memfault_gdb.py) helper script to use latest Memfault API for uploading symbol files. - Removed "DST Root CA X3" from the required Memfault [root certificate list](components/include/memfault/http/root_certs.h) as there is no infrastructure that relies on it anymore. - Updated PEM representation of all root certificates to include newlines after 64-character intervals to improve portability with various TLS stacks. #### 🏠 Internal - Updated [`fw_build_id.py`](scripts/fw_build_id.py) script. The same script can now also be installed via [`pypi`](https://pypi.org/project/mflt-build-id/): `pip install mflt_build_id` - Various improvements to example app documentation ### Changes between Memfault SDK 0.21.1 and SDK 0.21.0 - June 9, 2021 #### 📈 Improvements - Zephyr / nRF Connect SDK port: - Made periodic upload a named choice, `MEMFAULT_HTTP_PERIODIC_UPLOAD_CONTEXT`, so the default can be overridden from other Kconfig files. - Added prompt text for `MEMFAULT_HTTP_DEDICATED_WORKQUEUE_STACK_SIZE` ### Changes between Memfault SDK 0.21.0 and SDK 0.20.2 - June 8, 2021 #### 📈 Improvements - Zephyr / nRF Connect SDK port: - `CONFIG_MEMFAULT_NRF_CONNECT_SDK` is now only enabled by default when `CONFIG_MEMFAULT=y` - `CONFIG_DEBUG_THREAD_INFO=y` is now selected by default (in prep for deprecation of `CONFIG_OPENOCD_SUPPORT`) - Added new option (`CONFIG_MEMFAULT_USER_CONFIG_ENABLE=y`) which can be used to remove requirement of providing any user specific configuration. - Added two choices for periodic posting of memfault data: - `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_SYSTEM_WORKQUEUE`. This is the default and matches previous release behavior of posting data to Memfault from the system work queue - `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE` This is a new option which can be used to post data from a dedicated worker queue. This can be useful if the network stack may block for extended periods of time which would stall other system work queue jobs from getting processed. #### 💥 Breaking Changes - The Zephyr & nRF Connect SDK ports will now only enable the `MEMFAULT_SHELL` by default when the Zephyr shell is enabled. `CONFIG_SHELL=y` must now be enabled explicitly in your `prj.conf` for the Memfault Shell to be enabled. ### Changes between Memfault SDK 0.20.2 and SDK 0.20.1 - June 4, 2021 #### 🏠 Internal - Updated `sMemfaultBuildIdStorage` structure to track the build id length used for event serialization and updated [`fw_build_id.py`](scripts/fw_build_id.py) script to extract information. ### Changes between Memfault SDK 0.20.1 and SDK 0.20.0 - May 28, 2021 #### 📈 Improvements - Zephyr / nRF Connect SDK port: - Replaced `MEMFAULT_DEFAULT_REBOOT_REASON_IMPL` Kconfig option with `MEMFAULT_REBOOT_REASON_GET_CUSTOM` and updated default configuration for the nRF Connect SDK. This fixes an issue resulting in the [generic memfault_reboot_reason_get](ports/zephyr/common/memfault_platform_core.c#L53) getting linked rather than the [nRF Connect SDK port](ports/zephyr/ncs/src/nrfx_pmu_reboot_tracking.c#L139). ### Changes between Memfault SDK 0.20.0 and SDK 0.19.0 - May 27, 2021 #### 📈 Improvements - Updated [memfault_fault_handling_arm.c](components/panics/src/memfault_fault_handling_arm.c) to work around a compiler bug when using 6.x of the GNU ARM Toolchain - Port Improvements: - SAML10/SAML11 - [rich reboot reason info derived from RCAUSE register](ports/atmel/saml1x/rcause_reboot_tracking.c#L1) - Updated [esp-idf port](ports/esp_idf) to streamline integrations making use of the [amazon-freertos](https://github.com/aws/amazon-freertos) - Zephyr - Added several Kconfig options for better control over information collected in a coredump: - `CONFIG_MEMFAULT_COREDUMP_COLLECT_DATA_REGIONS` to enable/disable collection of `.data` region - `CONFIG_MEMFAULT_COREDUMP_COLLECT_BSS_REGIONS` to enable/disable collection of `.bss` region - `CONFIG_MEMFAULT_COREDUMP_STORAGE_CUSTOM=y` can be used to opt out of the default RAM backed coredump implementation and provide a custom one in the port. - Reduced instruction cycles required to update a [heartbeat metric](metrics/src/memfault_metrics.c) - Events will now store an abbreviated build id when serialized ### Changes between Memfault SDK 0.19.0 and SDK 0.18.0 - May 19, 2021 #### 📈 Improvements - Added support for collecting additional register information when a Hardfault takes place when using the Zephyr port. This information will be decoded and displayed in the Memfault UI in the "Exceptions" tab. - Updated [`buffered_coredump_storage.h`](ports/include/memfault/ports/buffered_coredump_storage.h) to use `memmov` instead of `memcpy` since `dst` and `src` buffers may overlap when all of `.bss` is saved in a coredump capture. - Added a new Kconfig option to the Zephyr port, `CONFIG_MEMFAULT_METRICS_EXTRA_DEFS_FILE=y`, which causes `memfault_metrics_heartbeat_extra.def` to be included in the metric definitions. This can be utilized by a third party consumer of Zephyr to more easily extend the default heartbeat metrics collected when using memfault. #### 🏠 Internal - Updated [`memfault_gdb.py`](scripts/memfault_gdb.py) helper script to use latest Memfault API for uploading symbol files. #### 💥 Breaking Changes - If you are using [nRF Connect SDK / Zephyr port](ports/zephyr/ncs/), the SDK will now automatically be picked up as a Zephyr Module! You will need to make two changes: 1. Remove the `ZEPHYR_EXTRA_MODULES` addition from your projects CMakeLists.txt, i.e ```diff --- a/your_application/CMakeLists.txt +++ b/your_application/CMakeLists.txt @@ -3,7 +3,6 @@ - list(APPEND ZEPHYR_EXTRA_MODULES $ENV{ZEPHYR_BASE}/../modules/memfault-firmware-sdk/ports/nrf-connect-sdk) ``` 2. Add `CONFIG_MEMFAULT_NRF_CONNECT_SDK=y` to your projects `prj.conf` ### Changes between Memfault SDK 0.18.0 and SDK 0.17.1 - May 14, 2021 #### 📈 Improvements - Support for Dialog DA145xx chip family (Huge thanks to [@iandmorris](https://github.com/iandmorris) for the help here!) - GCC & Keil based demo application for the DA14531 & DA14585/DA14586 [can be found here](examples/dialog/da145xx). - Ports for applications using the DA145xx SDK [can be found here](ports/dialog/da145xx). - ESP32 port improvements - Added example of periodically posting data to memfault via a background task. - Added a new Kconfig option, `MEMFAULT_COREDUMP_USE_OTA_SLOT=y` which can be used to save a coredump in an unused OTA slot rather than the default coredump partition. This can be useful in situations where Memfault is being integrated after a product has shipped and updating the partition table is no longer possible. - Added `MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED=0` which can be used to disable dynamic configuration of non-volatile storage. Setting this flag when the non-volatile event storage API is not in use will save several hundred bytes of codespace. - Hardened [memfault_http_parse_response()](components/http/src/memfault_http_utils.c) utility to parse HTTP responses with headers that exceed a length of 128 bytes - Fixed a 🐛 in`memfault_log_save_preformatted()` leading to invalid logs being reported when attempting to save log lines > 128 bytes. (thanks [@alvarop](https://github.com/alvarop) for the report!) - Added a convenience API, [`memfault_create_unique_version_string()`](components/include/memfault/core/platform/device_info.h), which can be used for easily appending a build id on the software version reported. #### 🏠 Internal - Updates to demo cli: - `MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE` can be used to shrink the maximum amount of bytes that can be buffered on a single line. - Made `memfault_demo_shell_commands.h` public and moved it to [`memfault/demo/shell_commands.h`](components/include/memfault/demo/shell_commands.h) to facilitate easier overriding of the default set of commands used in a build. ### Changes between Memfault SDK 0.17.1 and SDK 0.17.0 - April 30, 2021 #### 📈 Improvements - ESP32 Example App Updates: - Added `export` command to demonstrate how data can be dumped via the console - Added [Memfault Build ID](examples/esp32/apps/memfault_demo_app/CMakeLists.txt) to example app as a reference - Fixed a 🐛 in [`memfault_platform_sanitize_address_range()`](ports/templates/memfault_platform_port.c) template example. - Refactored nRF5 example app to mirror integration steps listed in the [latest integration guide](https://mflt.io/cortex-m-getting-started) - Added a new configuration option, `MEMFAULT_PLATFORM_FAULT_HANDLER_CUSTOM`, which can be used to explicitly disable the stub [`memfault_platform_fault_handler()` implementation](components/panics/src/memfault_fault_handling_arm.c) - Improve the quality of backtrace recovery for asserts when using Arm Compiler 5 by removing use of noreturn function attribute for `memfault_fault_handling_assert()` declaration. ### Changes between Memfault SDK 0.17.0 and SDK 0.16.1 - April 26, 2021 #### 🚀 New Features - Added support for collecting ARMv7-M MPU regions as part of coredump collection. To enable, set [`MEMFAULT_COLLECT_MPU_STATE=1`](components/include/memfault/default_config.h#L1) in your `memfault_platform_config.h`. Once enabled, the MPU will be automatically analyzed for configuration errors and results will be presented in the "MPU" tab in the Memfault UI for a coredump. - Added a new API, [`memfault_log_trigger_collection`](components/include/memfault/core/log.h#L143), which can be used to "freeze" the current contents of the log buffer when unexpected behavior takes place on the device for upload to Memfault. The logs can then be [uploaded to Memfault](https://mflt.io/data-to-cloud) just like any other data and appear in the UI for a device. For more details about the Memfault log subsystem see #### 📈 Improvements - Fixed compilation error when compiling the panics component against Cortex-M0+ with ARM Compiler 5 - Added several [default heartbeat metrics to the Zephyr port](ports/zephyr/config/memfault_metrics_heartbeat_zephyr_port_config.def) around timer task stack usage and execution time. #### 🏠 Internal - Refreshed esp32 example app README and updated instructions for the v3.3.5 esp-idf - Added `test_log` & `trigger_logs` CLI commands to nRF5 & Zephyr example applications to exercise new log collection functionality. ### Changes between Memfault SDK 0.16.1 and SDK 0.16.0 - April 12, 2021 #### 📈 Improvements - Fixed a 🐛 in Zephyr port leading to a compilation error with nRF Connect SDK when `CONFIG_DOWNLOAD_CLIENT=y` & `CONFIG_MEMFAULT_NRF_SHELL=y` - Dialog DA1468x [QSPI coredump storage port](ports/dialog/da1468x/qspi_coredump_storage.c#L1) updates: - default storage partition (`NVMS_LOG_PART`) can be overridden `MEMFAULT_PLATFORM_COREDUMP_STORAGE_PARTITION` - Max space used within partition can be limited using `MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES` - Updated [zephyr example application](examples/zephyr/README.md) and docs to be compatible with v2.5 release. #### 🏠 Internal - Added `memfault_log_save` stub to unit tests to facilitate easier testing of logging dependencies - Improved correctness of strategy used to capture `msp` & `psp` register in Cortex-M fault handler. - Added new convenience utility, `memfault_circular_buffer_read_with_callback()`, to [circular buffer api](components/include/memfault/util/circular_buffer.h). ### Changes between Memfault SDK 0.16.0 and SDK 0.15.0 - April 8, 2021 #### 📈 Improvements - Added new convenience APIs, `memfault_event_storage_bytes_used()` & `memfault_event_storage_bytes_free()`, to [event storage module](components/include/memfault/core/event_storage.h#L1). - Added a new configuration option, [`MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED`](components/include/memfault/default_config.h#L1). By default, it is off, but when enabled will cause `memfault_platform_halt_if_debugging()` to be called prior to triggering the full coredump capture. - Fixed a type conversion compiler warning emitted by IAR and ARMCC in [`resetreas_reboot_tracking.c`](ports/nrf5_sdk/resetreas_reboot_tracking.c#L1). - Port Updates: - Dialog DA1468x - [QSPI coredump storage port](ports/dialog/da1468x/qspi_coredump_storage.c#L1) - [Added `memfault_platform_reboot_tracking_boot()` implementation](ports/dialog/da1468x/reset_stat_reboot_tracking.c) #### 🏠 Internal - Removed "Heartbeat triggered!" print when `memfault_metrics_heartbeat_debug_trigger()` is called #### 💥 Breaking Changes - If you were using [`ports/dialog/da1468x/reset_stat_reboot_tracking.c`](ports/dialog/da1468x/reset_stat_reboot_tracking.c), the `memfault_platform_reboot_tracking_boot()` implementation from your `memfault_platform_port.c` file can be removed and the one in the port can be picked up. ### Changes between Memfault SDK 0.15.0 and SDK 0.14.0 - March 24, 2021 #### 📈 Improvements - Added a new convenience API, [`memfault_device_info_dump()`](components/include/memfault/core/device_info.h#L1) which can be used to pretty print the device information populated in the `memfault_platform_get_device_info()` dependency function. - Added [`memfault_platform_sanitize_address_range()`](components/include/memfault/panics/platform/coredump.h#L95). This functrions is intended for use in your `memfault_platform_coredump_get_regions()` implementation when capturing regions that are not defined at compile time. - Fixed a 🐛 with `memfault_coredump_storage_debug_test_*` which would generate a false positive test failure when the coredump storage area was not divisible by 16. - C++ header guards are now included in all headers in the ports/ directory for easier integration in mixed C/C++ environments - Port Updates: - Dialog DA1468x: Added patch which can be used to add [GNU Build ID](ports/dialog/da1468x/gnu-build-id.patch#L1) - Added support for FreeRTOS 8 to the [FreeRTOS port](ports/freertos/). - STM32H7 family / STM32CubeH7: - [rich reboot reason info derived from RCC RSR register](ports/stm32cube/h7/rcc_reboot_tracking.c#L1) - STM32WBxx family / STM32CubeWB: - [internal flash coredump storage](ports/stm32cube/wb/flash_coredump_storage.c#L1) - Improved configurability of [RAM backed coredump storage port](ports/panics/src/memfault_platform_ram_backed_coredump.c#L1) with new configuration options for to control where RAM is allocated, what memory regions are collected, and stack size to collect. - Added additional comments to [`ports/templates`](ports/templates) directory to facilitate porting. #### 🏠 Internal - [Demo CLI Shell commands](components/demo/src/memfault_demo_shell_commands.c#L50) are now defined as weak symbols so they can be overridden with a custom set. #### 💥 Breaking Changes - If you were using the [`ports/emlib/msc_coredump_storage.c`](ports/emlib/msc_coredump_storage.c) port in your system, you must add `MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH=1` to your `memfault_platform_config.h` - If you were using `ports/panics/src/memfault_platform_ram_backed_coredump.c`: - [`memfault_platform_sanitize_address_range()`](components/include/memfault/panics/platform/coredump.h) must be implemented. - The default coredump storage RAM size was changed from 700 to 1024 bytes so more information can be captured. The original size can be restored by setting `MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE 700` in your `memfault_platform_config.h` ### Changes between Memfault SDK 0.14.0 and SDK 0.13.1 - March 18, 2021 #### 📈 Improvements - Renamed `platforms` folder to [`examples`](examples/) to better capture that the folder contains "example" integrations of the Memfault SDK into hello world style apps from various SDKs - Port Updates: - Zephyr: added a [software watchdog port](ports/zephyr/common/memfault_software_watchdog.c#L1) which can be used for capturing coredumps ahead of a hardware watchdog reset. - EFR32: [rich reboot reason info derived from EMU_RSTCAUSE register](ports/emlib/rmu_reboot_tracking.c#L1) - Updated [nRF9160 Demo Application](examples/nrf-connect-sdk/nrf9160) to be compatible with [nRF Connect SDK 1.5](https://github.com/nrfconnect/sdk-nrf/tree/v1.5-branch) - Added support for capturing coredumps with GDB from GNU GCC 4.9 to [`memfault_gdb.py`](scripts/memfault_gdb.py) #### 🏠 Internal - Added support for automatically capturing logs with Zephyr when logging in synchronous mode `CONFIG_LOG_IMMEDIATE=y` - Unified nomenclature for references to the "Project Key" used to push data to Memfault #### 💥 Breaking Changes If you were linking any files from the `platforms` folder into your project, the path needs to be updated to `examples`: ```diff - ${MEMFAULT_FIRMWARE_SDK}/platforms/ + ${MEMFAULT_FIRMWARE_SDK}/examples/ ``` ### Changes between Memfault SDK 0.13.1 and SDK 0.13.0 - March 10, 2021 #### 📈 Improvements - Reference platform API implementations for DA1468x: - [rich reboot reason info derived from RESET_STAT_REG register](ports/dialog/da1468x/reset_stat_reboot_tracking.c#L1) - Fixed a 🐛 that led to a unit test failure in `test_coredump_storage_debug` in some environments where `const` arrays were getting dynamically built on the stack at runtime. - Worked around a limitation in GNU GCC 4.9's extended ASM to fix a compiler bug that would arise when compiling `memfault_fault_handling_arm.c` for Cortex-M0 targets. - Added a new [`ports/templates`](ports/templates) folder that can be copy/pasted into a project and used as a starting point for a Memfault port! ### Changes between Memfault SDK 0.13.0 and SDK 0.12.0 - March 4, 2021 #### 📈 Improvements - Improved documentation in [README](README.md) and [components](components/) directories. - Added coredump capture support to panics component for the AARCH64 architecture. - Reference platform API implementations for the following MCUs/SDKs: - STM32WBxx family / STM32CubeWB: - [rich reboot reason info derived from RCC CSR register](ports/stm32cube/wb/rcc_reboot_tracking.c#L1) - nRF5 SDK - [app_timer based port for metric dependencies](ports/nrf5_sdk/memfault_platform_metrics.c#L1) - [`fw_build_id.py`](scripts/fw_build_id.py) script improvements - script is now compatible with Python2 environments. - Added new `--dump ` option to simplify extraction of build id in automation, i.e: ```bash python scripts/fw_build_id.py --dump 7 3a3e81f ``` #### 🏠 Internal - Started work to enable automatic capture of logs in a coredump for the Zephyr & nRF Connect SDK. #### 💥 Breaking Changes - If you were linking `memfault_nrf5_coredump.c` in your project: - the file has been split into `nrf5_coredump_regions.c` (which defines the regions to collect in a coredump) & `nrf5_coredump_storage.c` (which implements the port for saving coredumps to internal flash). Both of these files must be added to your build system. - Linker script names for region to save coredump to and regions to collect need to be updated: - Update`__CoreStart` & `__MemfaultCoreStorageEnd` to `__MemfaultCoreStorageStart` & `__MemfaultCoreStorageEnd` in linker script. - Update `__MfltCoredumpRamStart` & `__MfltCoredumpRamEnd` to `__MemfaultCoredumpRamStart` & `__MemfaultCoredumpRamEnd` ### Changes between Memfault SDK 0.12.0 and SDK 0.11.4 - Feb 14, 2021 #### 📈 Improvements - The SDK now includes a central configuration header at [`components/include/memfault/config.h`](components/include/memfault/config.h#L1). Platform overrides can be defined in `memfault_platform_config.h` - Reference platform API implementations for the [nRF5 SDK](https://infocenter.nordicsemi.com/topic/struct_sdk/struct/sdk_nrf5_latest.html) - [rich reboot reason info derived from RESETREAS register](ports/nrf5_sdk/resetreas_reboot_tracking.c#L1) - [internal flash coredump storage](ports/nrf5_sdk/memfault_nrf5_coredump.c#L1) - All headers for Memfault "components" can now be found under [`components/include/memfault/`](components/include/memfault/). This simplifies adding components to build systems as only a single path is now needed! #### 💥 Breaking Changes - You must create the file `memfault_platform_config.h` and add it to your include path. This file can be used in place of compiler defines to tune the SDK configurations settings. - If you are not using a Memfault [build system helper](README.md#add-sources-to-build-system) 1. Remove all include paths at `${MEMFAULT_FIRMWARE_SDK}/components/${COMPONENT}/include` 2. Add include path `${MEMFAULT_FIRMWARE_SDK}/components/include` to your build system - If you were linking any of the nRF5 example app files in your project, the directory has changed: ```diff - ${MEMFAULT_FIRMWARE_SDK}/nrf5/libraries/memfault/platform_reference_impl/memfault_platform_reboot_tracking.c + ${MEMFAULT_FIRMWARE_SDK}/ports/nrf5_sdk/resetreas_reboot_tracking.c - ${MEMFAULT_FIRMWARE_SDK}/nrf5/libraries/memfault/platform_reference_impl/memfault_platform_coredump.c + ${MEMFAULT_FIRMWARE_SDK}/ports/nrf5_sdk/memfault_nrf5_coredump.c ``` ### Changes between Memfault SDK 0.11.4 and SDK 0.11.3 - Feb 4, 2021 - ESP8266 Updates - Added new Kconfig option which can be set via `make menuconfig` and be used to disable the Memfault integration, `CONFIG_MEMFAULT=n`. - Fixed a 🐛 leading to a compilation error when both`CONFIG_USING_ESP_CONSOLE=n` and `CONFIG_MEMFAULT_CLI_ENABLED=n` - Added default implementations for `MEMFAULT_GET_LR()`, `MEMFAULT_GET_PC()`, and `MEMFAULT_BREAKPOINT()` to [`compiler_gcc.h`](components/include/memfault/core/compiler_gcc.h) to facilitate compilations of the SDK against other architectures. ### Changes between Memfault SDK 0.11.3 and SDK 0.11.2 - Jan 31, 2021 #### 📈 Improvements - Reference platform API implementations for the following MCUs/SDKs: - nRF Connect SDK - Added support for nRF Connect SDK v1.3.x - Added support for FOTA with Memfault. See [`memfault_fota_start()`](ports/nrf-connect-sdk/zephyr/include/memfault/nrfconnect_port/fota.h) for more details - Zephyr - Added implementations for `memfault_platform_metrics_timer_boot()` & `memfault_platform_get_time_since_boot_ms()` memfault dependencies to Zephyr port. A custom implementation can still be provided by setting`MEMFAULT_METRICS_TIMER_CUSTOM=y` - Metrics support is now enabled by default when `CONFIG_MEMFAULT=y` and can be disabled by setting `CONFIG_MEMFAULT_METRICS=n` - Added support for periodically uploading Memfault data in the background. This is off by default and can be enabled with the `CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=y` option - Fixed a 🐛 that could lead to an invalid coredump being sent to Memfault when `memfault_packetizer_abort()` was called after a coredump was partially sent. ### Changes between Memfault SDK 0.11.2 and SDK 0.11.1 - Jan 21, 2021 #### 📈 Improvements - Added support for nRF Connect SDK v1.2.x and updated the [integration guide](https://mflt.io/nrf-connect-sdk-integration-guide) accordingly. ### Changes between Memfault SDK 0.11.0 and SDK 0.10.1 - Jan 19, 2021 #### 🚀 New Features - Added full support for the ESP8266 (Tensilica Xtensa LX106) MCU architecture! A step-by-step integration guide can be found [here](https://mflt.io/esp8266-tutorial) and the port to the ESP8266 RTOS SDK can be found [here](ports/esp8266_sdk/). #### 📈 Improvements - Added a convenience header for picking up includes for all Memfault components. If you are using the [build system helpers](README.md#add-sources-to-build-system), this path will be picked up automatically. ```c #include "memfault/components.h" // call to any Memfault API in the components folder ``` - Fixed a 🐛 leading to Root CAs not get loaded correctly when using the nRF Connect SDK port and the `MEMFAULT_ROOT_CERT_STORAGE_TLS_CREDENTIAL_STORAGE=y` Kconfig option. - Applied suggestions from [@rerickson1](https://github.com/rerickson1) for the Zephyr and nRF Connect SDK ports: - [`CONFIG_MEMFAULT_METRICS=y`](https://github.com/memfault/memfault-firmware-sdk/pull/8) can now be used to compile the metrics component into the Zephyr and nRF Connect SDK ports. - [`CONFIG_MEMFAULT=y`](https://github.com/memfault/memfault-firmware-sdk/pull/7) must now be specified to enable the Memfault integration. #### 🏠 Internal - Refactored the [nRF Connect SDK port](ports/nrf-connect-sdk) to build directly on top of the [zephyr port](ports/zephyr) reducing code duplication and facilitate the rollout of additional features to both SDKs at the same time moving forward. #### 💥 Breaking Changes - If you are using a Memfault [build system helper](README.md#add-sources-to-build-system) _and_ not using the trace or metrics functionality, you will now need to create a `memfault_trace_reason_user_config.def` or `memfault_metrics_heartbeat_config.def` file, respectively, and add it to your include path. - When using the Zephyr port, the memfault integration must be enabled explicitly by adding `CONFIG_MEMFAULT=y` to your `prj.conf` ### Changes between Memfault SDK 0.10.1 and SDK 0.10.0 - Jan 10, 2021 #### 📈 Improvements - Reference platform API implementations for the following MCUs/SDKs: - STM32F4 family / STM32CubeF4: - [rich reboot reason info derived from RCC CSR register](ports/stm32cube/f4/rcc_reboot_tracking.c) - STM32L4 family / STM32CubeL4 - [rich reboot reason info derived from RCM SRS register](ports/stm32cube/l4/rcc_reboot_tracking.c) - nRF Connect SDK - [rich reboot reason info derived from PMU RESETREAS register](ports/nrf-connect-sdk/nrfx/pmu_reboot_tracking.c) - refactored HTTP port to support multiple backing storage strategies for root certificates. See [`MEMFAULT_ROOT_CERT_STORAGE_*`](ports/nrf-connect-sdk/zephyr/Kconfig) Kconfig options for more details - Added support for Memfault OTA downloads. See [memfault_nrfconnect_port_ota_update()](ports/nrf-connect-sdk/zephyr/include/memfault/nrfconnect_port/http.h) for more details ### Changes between Memfault SDK 0.10.0 and SDK 0.9.2 - Jan 5, 2021 #### 📈 Improvements - Updated [`memfault_freertos_ram_regions.c`](ports/freertos/src/memfault_freertos_ram_regions.c) port to collect all TCBs and then stacks. This way, the state of all tasks can be recovered even if the coredump storage regin is filled while writing all the task stacks. - Reference platform API implementations for the following MCUs/SDKs: - STM32F4 family / STM32CubeF4 - [internal flash coredump storage](ports/stm32cube/f4/flash_coredump_storage.c) - STM32L4 family / STM32CubeL4 - [internal flash coredump storage](ports/stm32cube/l4/flash_coredump_storage.c) - NXP's S32K1xx family / S32K1 SDL - [internal flash coredump storage using FTFC peripheral](ports/s32sdk/ftfc_flash_coredump_storage.c) - [software watchdog implementation using LPIT peripheral](ports/s32sdk/lpit_software_watchdog.c) - [rich reboot reason info derived from RCM SRS register](ports/s32sdk/rcm_reboot_tracking.c) - Silicon Lab's EFM/EFR family / v3.0 Gecko SDK - [internal flash coredump storage using MSC peripheral h](ports/emlib/msc_coredump_storage.c) - [software watchdog implementation using warning interrupt in WDOG peripheral](ports/emlib/wdog_software_watchdog.c) - [reboot reason info derived from RMU RSTCAUSE register](ports/emlib/rmu_reboot_tracking.c) #### 🚀 New Features - Added several more [reboot reason options](components/include/memfault/core/reboot_reason_types.h#L16): - `kMfltRebootReason_PinReset` for explicitly tracking external pin resets. - `kMfltRebootReason_SoftwareWatchdog` & `kMfltRebootReason_HardwareWatchdog` for easier disambiguation between watchdog resets where a coredump was captured versus ones where no software handler ran and hardware reset the device. - `kMfltRebootReason_ClockFailure` for explicit tracking of resets due to loss of a clock signal or PLL lock. - `kMfltRebootReason_Lockup` for explicit tracking of faults from within the Hardfault or NMI exceptions on ARM Cortex-M MCUs. - Added a utility which can be used to verify a platform coredump storage implementation is working as corrected. For more details about how to use, see [memfault_coredump_storage_debug.c](components/panics/src/memfault_coredump_storage_debug.c#L1). #### 🏠 Internal - Added infrastructure to coredump collection in `panics` component to support ESP8266 (Tensilica Xtensa LX106) MCU architecture. ### Changes between Memfault SDK 0.9.3 and SDK 0.9.2 - Dec 14, 2020 #### 📈 Improvements - nRF Connect Updates: - Updated port to support [nRF Connect SDK v1.4.0](https://github.com/nrfconnect/sdk-nrf/tree/v1.4-branch) - Added port of HTTP client to post data to Memfault - Added support for capturing state of all Zephyr tasks during a crash. This way the state of all threads can be seen in the Memfault UI when a crash is uploaded. - Updated [memfault_demo_app](examples/nrf-connect-sdk/nrf9160/memfault_demo_app) to use the nRF9160-DK - Added notes to the [step-by-step integration guide](https://mflt.io/nrf-connect-sdk-integration-guide) for the nRF9160. ### Changes between Memfault SDK 0.9.2 and SDK 0.9.1 - Dec 10, 2020 #### 📈 Improvements - Added Memfault OTA support to esp-idf port. Updates can now be performed by calling `memfault_esp_port_ota_update()`. More details can be found in [`ports/esp_idf/memfault/include/memfault/esp_port/http_client.h`](ports/esp_idf/memfault/include/memfault/esp_port/http_client.h) - The esp-idf port debug CLI can now easily be disabled by using the `MEMFAULT_CLI_ENABLED=n` Kconfig option. - Added FreeRTOS utility to facilitate collecting minimal set of RAM in a coredump necessary to recover backtraces for all tasks. More details can be found in [`ports/freertos/include/memfault/ports/freertos_coredump.h`](ports/freertos/include/memfault/ports/freertos_coredump.h) - Previously, if the Memfault event storage buffer was out of space, a "storage out of space" error would be printed every time. Now, an error message is printed when the issue first happened and an info message is printed when space is free again. - Added a reference software watchdog port for the STM32H7 series LPTIM peripheral. Users of the STM32 HAL can now compile in the reference port and the `MemfaultWatchdog_Handler`. The handler will save a coredump so the full system state can be recovered when a watchdog takes place. More details can be found in [`ports/include/memfault/ports/watchdog.h`](ports/include/memfault/ports/watchdog.h). ### Changes between Memfault SDK 0.9.1 and SDK 0.9.0 - Nov 24, 2020 #### 📈 Improvements - A log can now be captured alongside a trace event by using a new API: [`MEMFAULT_TRACE_EVENT_WITH_LOG(reason, ...)`](components/include/memfault/core/trace_event.h#L77). This can be useful to capture arbitrary diagnostic data with an error event or to capture critical error logs that you would like to be alerted on when they happen. For example: ```c // @file memfault_trace_reason_user_config.def MEMFAULT_TRACE_REASON_DEFINE(Critical_Log) ``` ```c // @file your_platform_log_implementation.h #include "memfault/core/trace_event.h" #define YOUR_PLATFORM_LOG_CRITICAL(fmt, ....) \ MEMFAULT_TRACE_EVENT_WITH_LOG(Critical_Log, fmt, __VA_ARGS__) ``` ```c // @file your_platform_temperature_driver.c void record_temperature(void) { // ... // erase flash to free up space int rv = spi_flash_erase(...); if (rv != 0) { YOUR_PLATFORM_LOG_CRITICAL("Flash Erase Failure: rv=%d, spi_err=0x%x", spi_bus_get_status()); } } ``` - The error tracing facilities are now initialized automatically for the esp-idf - Fixed a 🐛 where an erroneous size was reported from `memfault_coredump_storage_check_size()` if `MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS=1`and `memfault_log_boot()` had not yet been called ### Changes between Memfault SDK 0.9.0 and SDK 0.8.2 - Nov 16, 2020 #### 📈 Improvements - ESP32 port improvements: - The Memfault `metrics` component is now included by default in the ESP32 port - `MEMFAULT_LOG_DEBUG` messages now print by default - Added a `heartbeat_dump` CLI command for easy viewing of current heartbeat metrics - Custom handling of collecting Memfault data can now easily be implemented in the ESP32 port using the new [`memfault_esp_port_data_available()` & `memfault_esp_port_get_chunk()`](ports/esp_idf/memfault/include/memfault/esp_port/core.h) APIS. This can be useful in scenarios where there are external MCUs forwarding Memfault chunks to the ESP32. - The platform port for the memfault log dependency can now be implemented by macros (rather than the `memfault_platform_log` dependency). See [`components/include/memfault/core/debug_log.h`](components/include/memfault/core/debug_log.h) for more details. #### 💥 Breaking Changes - If you were using the ESP32 port: - Call to `memfault_metrics_boot()` can now be removed - Custom implementations for `memfault_platform_metrics_timer_boot()` & `memfault_platform_get_time_since_boot_ms()` can be removed as they are now provided as part of the port. ### Changes between Memfault SDK 0.8.2 and SDK 0.8.1 - Nov 13, 2020 #### 📈 Improvements - Coredumps will now be truncated (instead of failing to save completely) when the memory regions requested take up more space than the platform storage allocated for saving. A warning will also be displayed in the Memfault UI when this happens. Regions are always read in the order returned from [`memfault_platform_coredump_get_regions()`](components/include/memfault/panics/platform/coredump.h) so it is recommended to order this list from the most to least important regions to capture. - Updated FreeRTOS port to use static allocation APIs by default when the `configSUPPORT_STATIC_ALLOCATION=1` configuration is used. ### Changes between Memfault SDK 0.8.1 and SDK 0.8.0 - Nov 3, 2020 #### 📈 Improvements - Added several more [reboot reason options](components/include/memfault/core/reboot_reason_types.h#L16): `kMfltRebootReason_SoftwareReset` & `kMfltRebootReason_DeepSleep`. - Extended [ESP32 port](https://mflt.io/esp-tutorial) to include integrations for [reboot reason tracking](https://mflt.io/reboot-reasons) and [log collection](https://mflt.io/logging). - Apply missing check to unit test [reported on Github](https://github.com/memfault/memfault-firmware-sdk/pull/6) ### Changes between Memfault SDK 0.8.0 and SDK 0.7.2 - Oct 26, 2020 #### 📈 Improvements - Added a new convenience API, [`memfault_coredump_storage_check_size()`](components/include/memfault/panics/coredump.h), to check that coredump storage is appropriately sized. - Fixed a 🐛 with heartbeat timers that would lead to an incorrect duration being reported if the timer was started and stopped within the same millisecond. - Fixed an issue when using TI's compiler that could lead to the incorrect register state being captured during a fault. #### 💥 Breaking Changes - If you were **not** using the [error tracing functionality](https://mflt.io/error-tracing), you will need to create the configuration file "memfault_trace_reason_user_config.def" and add it to your include path. This removes the requirement to manually define `MEMFAULT_TRACE_REASON_USER_DEFS_FILE` as part of the compiler flags. ### Changes between Memfault SDK 0.7.3 and SDK 0.7.2 - Oct 5, 2020 #### 📈 Improvements - Add support for sending multiple events in a single chunk. This can be useful for optimizing throughput or packing more data into a single transmission unit. The behavior is disabled by default but can be enabled with the `MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED` compiler flag. More details can be found in [memfault_event_storage.c](components/core/src/memfault_event_storage.c#L30) - Added convenience API, `memfault_build_id_get_string`, for populating a buffer with a portion of the [Memfault Build ID](components/include/memfault/core/build_info.h#L8-L42) as a string. - Added default implementations of several Memfault SDK dependency functions when using FreeRTOS to [ports/freertos](ports/freertos). ### Changes between Memfault SDK 0.7.2 and SDK 0.7.1 - Sept 1, 2020 #### 📈 Improvements - A status or error code (i.e bluetooth disconnect reason, errno value, etc) can now be logged alongside a trace event by using a new API: [`MEMFAULT_TRACE_EVENT_WITH_STATUS(reason, status_code)`](components/include/memfault/core/trace_event.h#L55). ### Changes between Memfault SDK 0.7.1 and SDK 0.7.0 - Sept 1, 2020 #### 📈 Improvements - Added support for TI's ARM-CGT Compiler - Removed dependency on NMI Exception Handler for `MEMFAULT_ASSERT`s. Instead of pending an NMI exception, the assert path will now "trap" into the fault handler by executing a `udf` instruction. This unifies the fault handling paths within the SDK and leaves the NMI Handler free for other uses within the user's environment. - Added several more [reboot reason options](components/include/memfault/core/reboot_reason_types.h#L16): `kMfltRebootReason_PowerOnReset`, `kMfltRebootReason_BrownOutReset`, & `kMfltRebootReason_Nmi`. ### Changes between Memfault SDK 0.7.0 and SDK 0.6.1 - Aug 6, 2020 #### 📈 Improvements - Added utility to facilitate collection of the memory regions used by the [logging module](components/include/memfault/core/log.h) as part of a coredump. With this change, when the SDK is compiled with `MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS=1`, the logging region will automatically be collected as part of a coredump. Step-by-step details can also be found in the [logging integration guide](https://mflt.io/logging). - Added `MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE()` which can be used for defining the minimum and maximum expected range for a heartbeat metric. This information is used by the Memfault cloud to better normalize the data when it is presented in the UI. #### 💥 Breaking Changes - If you are _not_ using our CMake or Make [build system helpers](README.md#add-sources-to-build-system) and are using the `panics` component, you will need to manually add the following file to your build system: `$(MEMFAULT_SDK_ROOT)/components/panics/src/memfault_coredump_sdk_regions.c` ### Changes between Memfault SDK 0.6.1 and SDK 0.6.0 - July 27, 2020 #### 📈 Improvements - Added a port for projects using the [nRF Connect SDK](ports/nrf-connect-sdk) along with a [step-by-step integration guide](https://mflt.io/nrf-connect-sdk-integration-guide). - Disabled optimizations for `memfault_data_export_chunk()` to guarantee the [GDB chunk test utility](https://mflt.io/send-chunks-via-gdb) can always be used to post chunks using the data export API. ### Changes between Memfault SDK 0.6.0 and SDK 0.5.1 - July 21, 2020 #### 🚀 New Features - Added [memfault/core/data_export.h](components/include/memfault/core/data_export.h#L5) API to facilitate production and evaluation use cases where Memfault data is extracted over a log interface (i.e shell, uart console, log file, etc). See the header linked above or the [integration guide](https://mflt.io/chunk-data-export) for more details. #### 📈 Improvements - Fixed a 🐛 that would cause the demo shell to get stuck if backspace characters were entered while no other characters had been entered. - Updated the [GDB chunk test utility](https://mflt.io/send-chunks-via-gdb) to automatically detect when the data export API is integrated and post-chunks to the cloud directly from GDB when the function is invoked. #### 💥 Breaking Changes - If you are _not_ using our CMake or Make [build system helpers](README.md#add-sources-to-build-system) and want to make use of the new data export API, you will need to manually add the following files to your build system: - Add: `$(MEMFAULT_SDK_ROOT)/components/core/src/memfault_data_export.c` - Add: `$(MEMFAULT_SDK_ROOT)/components/util/src/memfault_base64.c` ### Changes between Memfault SDK 0.5.1 and SDK 0.5.0 - June 24, 2020 #### 📈 Improvements - Updated code to support compilations with `-Wunused-parameter`, GNU GCC's `-Wformat-signedness`, and Clang's `-Wno-missing-prototypes` & `-Wno-missing-variable-declarations`. - Updated unit test setup to compile with newly supported warnings treated as errors #### 🏠 Internal - Misc utility additions including support for encoding floats and int64_t's in the cbor utility ### Changes between Memfault SDK 0.5.0 and SDK 0.4.2 - June 11, 2020 #### 🚀 New Features - Add additional utilities to the http component to facilitate easier [release management](https://mflt.io/release-mgmt) integration in environments with no pre-existing http stack. - Add new cli command, `mflt get_latest_release`, to the Zephyr demo application (tested on the STM32L4) to demonstrate querying the Memfault cloud for new firmware updates. #### 📈 Improvements - Refactored `demo` component to make it easier to integrate an individual CLI commands into a project since some of the commands can be helpful for validating integrations. More details can be found in the README at [components/demo/README.md](components/demo/README.md). #### 💥 Breaking Changes - If you are using the "demo" component _and_ are _not_ making use our CMake or Make [build system helpers](README.md#add-sources-to-build-system), you will need to make the following changes: - Update: `$(MEMFAULT_SDK_ROOT)/components/demo/src/{memfault_demo_cli.c => panics/memfault_demo_panics.c}` - Update: `$(MEMFAULT_SDK_ROOT)/components/demo/src/{ => panics}/memfault_demo_cli_aux.c` - Add: `$(MEMFAULT_SDK_ROOT)/components/demo/src/memfault_demo_core.c` - Add: `$(MEMFAULT_SDK_ROOT)/components/demo/src/http/memfault_demo_http.c` - If you are using the `http` component, the following macro names changed: ```diff -#define MEMFAULT_HTTP_GET_API_PORT() -#define MEMFAULT_HTTP_GET_API_HOST() +#define MEMFAULT_HTTP_GET_CHUNKS_API_PORT() +#define MEMFAULT_HTTP_GET_CHUNKS_API_HOST() ``` ### Changes between Memfault SDK 0.4.2 and SDK 0.4.1 - June 8, 2020 #### 📈 Improvements - Moved `reboot_tracking.h` to `core` component since it has no dependencies on anything from the `panics` component. This allows the reboot tracking to be more easily integrated in a standalone fashion. - [Published a new guide](https://mflt.io/release-mgmt) detailing how to manage firmware updates using Memfault. - Disabled optimizations for `memfault_fault_handling_assert()`. This improves the recovery of local variables of frames in the backtrace when certain optimization levels are used. - Updated `memfault_sdk_assert.c` to address a GCC warning (`-Wpointer-to-int-cast`) emitted when compiling the file for 64 bit architectures. - Misc README improvements. #### 💥 Breaking Changes - If you are already using reboot tracking in your system, you will need to update the following includes in your code: ```diff -#include "memfault/panics/reboot_tracking.h" -#include "memfault/panics/reboot_reason_types.h" +#include "memfault/core/reboot_tracking.h" +#include "memfault/panics/reboot_reason_types.h" ``` - If you are _not_ using our CMake or Make [build system helpers](README.md#add-sources-to-build-system), you will need to update the path for the following files: - `$(MEMFAULT_SDK_ROOT)/components/{panics => core}/src/memfault_ram_reboot_info_tracking.c` - `$(MEMFAULT_SDK_ROOT)/components/{panics => core}/src/memfault_reboot_tracking_serializer.c` - `eMfltResetReason` was renamed to `eMemfaultRebootReason`. ### Changes between Memfault SDK 0.4.1 and SDK 0.4.0 - May 20, 2020 #### 🚀 New Features - Added native SDK support for tracking and generating a unique firmware build id with any compiler! Quick integration steps can be found in [memfault/core/build_info.h](components/include/memfault/core/build_info.h#L8-L42). It is very common, especially during development, to not change the firmware version between editing and compiling the code. This will lead to issues when recovering backtraces or symbol information because the debug information in the symbol file may be out of sync with the actual binary. Tracking a build id enables the Memfault cloud to identify and surface when this happens! You can also make use of two new APIs: - `memfault_build_info_dump()` can be called on boot to display the build that is running. This can be a useful way to sanity check that your debugger successfully flashed a new image. - `memfault_build_info_read()` can be used to read the build id for your own use cases. For example you could append a portion of it to a debug version to make it unique. #### 📈 Improvements - The CMake [build system helper](README.md#add-sources-to-build-system) is now compatible with any 3.x version of CMake (previously required 3.6 or newer). - The unique firmware build id is stored automatically as part of coredump collection. #### 💥 Breaking Changes - If you are _not_ using our CMake or Make [build system helpers](README.md#add-sources-to-build-system), you will need to add the following files to your build: - `$(MEMFAULT_SDK_ROOT)/components/core/src/memfault_build_id.c` - `$(MEMFAULT_SDK_ROOT)/components/core/src/memfault_core_utils.c` - We also encourage you to add a [unique build id](components/include/memfault/core/build_info.h#L8-L42) to your build (several line code change). ### Changes between Memfault SDK 0.4.0 and SDK 0.3.4 - May 6, 2020 #### 🚀 New Features - Added support for (optionally) storing events collected by the SDK to non-volatile storage mediums. This can be useful for devices which experience prolonged periods of no connectivity. To leverage the feature, an end user must implement the [nonvolatile_event_storage platform API](components/include/memfault/core/platform/nonvolatile_event_storage.h#L7). #### 📈 Improvements - Added an assert used internally by the SDK which makes it easier to debug API misuse during bringup. The assert is enabled by default but can easily be disabled or overridden. For more details see [`memfault/core/sdk_assert.h`](components/include/memfault/core/sdk_assert.h#L6). - Added a default implementation of [`memfault_platform_halt_if_debugging()`](components/core/src/arch_arm_cortex_m.c#L20-L34) for Cortex-M targets. The function is defined as _weak_ so a user can still define the function to override the default behavior. #### 🏠 Internal - Updated [`memfault install_chunk_handler`](https://mflt.io/posting-chunks-with-gdb) to work with older versions of the GNU Arm Toolchain. #### 💥 Breaking Changes - If you are _not_ using our CMake or Make [build system helpers](README.md#add-sources-to-build-system), you will need to add `$(MEMFAULT_SDK_ROOT)/components/core/src/memfault_sdk_assert.c` to your project. ### Changes between Memfault SDK 0.3.4 and SDK 0.3.3 - April 22, 2020 #### 📈 Improvements - Moved `trace_event.h` to `core` component since it has no dependencies on anything from the `panics` component. This allows the trace event feature to be more easily integrated in a standalone fashion. #### 💥 Breaking Changes - If you are already using `MEMFAULT_TRACE_EVENT()` in your project, you will need to update the include as follows: ```diff -#include "memfault/panics/trace_event.h" +#include "memfault/core/trace_event.h" ``` - If you are _not_ using our CMake or Make [build system helpers](README.md#add-sources-to-build-system), you will need to update the source path for `components/panics/src/memfault_trace_event.c` to `components/core/src/memfault_trace_event.c` ### Changes between Memfault SDK 0.3.3 and SDK 0.3.2 - April 21, 2020 #### 🚀 New Features - Added a new [GDB command](https://mflt.io/posting-chunks-with-gdb) which can be used to post packetized Memfault data directly from GDB to the Memfault cloud. This can be helpful as a way to quickly test data collection functionality while working on an integration of the SDK. ### Changes between Memfault SDK 0.3.2 and SDK 0.3.1 - April 16, 2020 #### 🚀 New Features - The `captured_date` for an event can now be set by implementing [`memfault_platform_time_get_current()`](components/include/memfault/core/platform/system_time.h#L33). If the API is not implemented, the `captured_date` will continue to be set based on the time the event was received by the memfault cloud. #### 📈 Improvements - Added a reference implementation of [reboot reason tracking](https://mflt.io/2QlOlgH) to the [NRF52 demo app](examples/nrf5/libraries/memfault/platform_reference_impl/memfault_platform_reboot_tracking.c#L1) and a new `reboot` CLI command to easily exercise it. - A `reset_reason` can now optionally be provided as part of [`sResetBootupInfo`](components/include/memfault/core/reboot_tracking.h#L41). This can be useful for scenarios where the reboot reason is known on bootup but could not be set prior to the device crashing. - A reboot reason event will now _always_ be generated when `memfault_reboot_tracking_boot()` is called even if no information about the reboot has been provided. In this scenario, the reset reason will be [`kMfltRebootReason_Unknown`](components/include/memfault/core/reboot_reason_types.h#L16) #### 🏠 Internal - Updated a few Kconfig options in the Zephyr demo app to improve the ability to compute stack high water marks (`CONFIG_INIT_STACKS=y`) and determine if stacks has overflowed (`CONFIG_MPU_STACK_GUARD=y`). #### 💥 Breaking Changes - `device_serial` is no longer encoded by default as part of events. Instead, the `device_serial` in an event is populated from the the unique device identifier used when posting the data to the [chunks REST endpoint](https://mflt.io/chunks-api). This leads to ~20% reduction in the size of a typical event. Encoding `device_serial` as part of the event itself can still be enabled by adding [`-DMEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL=1`](components/core/src/memfault_serializer_helper.c#L23) as a compilation flag but should not be necessary for a typical integration. ### Changes between Memfault SDK 0.3.1 and SDK 0.3.0 - April 9, 2020 #### 🚀 New Features - Added [`memfault_log_read()`](components/include/memfault/core/log.h#L95-L121) API to make it possible to use the module to cache logs in RAM and then flush them out to slower mediums, such as a UART console or Flash, from a background task. #### 📈 Improvements - A pointer to the stack frame upon exception entry is now included in `sCoredumpCrashInfo` when [memfault_platform_coredump_get_regions](components/include/memfault/panics/platform/coredump.h#L56) is invoked. This can (optionally) be used to configure regions collected based on the state or run platform specific cleanup based on the state. - Added Root Certificates needed for release downloads to [`MEMFAULT_ROOT_CERTS_PEM`](components/http/include/memfault/http/root_certs.h#L146). #### 🏠 Internal - All sources that generate events now use the same utility function, `memfault_serializer_helper_encode_metadata()` to encode common event data. ### Changes between Memfault SDK 0.3.0 and SDK 0.2.5 - April 3, 2020 #### 🚀 New Features - Introduced a lightweight logging module. When used, the logs leading up to a crash will now be decoded and displayed from the Memfault Issue Details Web UI. For instructions about how to use the module in your project, check out [log.h](components/include/memfault/core/log.h). - The metrics component will now automatically collect the elapsed time and the number of unexpected reboots during a heartbeat interval. The Memfault cloud now uses this information to automatically compute and display the overall uptime of your fleet. #### 📈 Improvements - Added a [new document](https://mflt.io/fw-event-serialization) walking through the design of Memfault event serialization. - Cleaned up `test_memfault_root_cert.cpp` and moved the `base64` implementation within the unit test to the `util` component so it is easier to share the code elsewhere in the future. - Added `const` to a few circular_buffer.h API signatures. - Misc code comment improvements. #### 💥 Breaking Changes - The function signature for `memfault_metrics_boot()` changed as part of this update. If you are already using the `metrics` component, you will need to update the call accordingly. See the notes in [metrics.h](components/include/memfault/metrics/metrics.h) for more details. - If you are _not_ using our CMake or Make [build system helpers](README.md#add-sources-to-build-system), you will need to add `$(MEMFAULT_SDK_ROOT)/components/core/src/memfault_log.c` to your project. (Note that until [`memfault_log_boot()`](https://github.com/memfault/memfault-firmware-sdk/blob/master/components/include/memfault/core/log.h#L33-L38) is called, all calls made to the logging module will be a no-op). ### Changes between Memfault SDK 0.2.5 and SDK 0.2.4 - March 20, 2020 - Improve the `memfault_platform_coredump_storage_clear()` NRF52 reference implementation for situations where the SoftDevice is enabled and there is a lot of Bluetooth activity. (In this scenario, NRF52 flash operations may need retries or take a while to complete). - Fixed compiler error that could arise with the metrics component when using Arm Compiler 5 due to multiply defined weak symbols. ### Changes between Memfault SDK 0.2.4 and SDK 0.2.3 - March 10, 2020 - Add support for ESP32 (Tensilica Xtensa LX6 MCU) to the **panics** component. - A step-by-step integration guide can be found [here](https://mflt.io/esp-tutorial). - A drop-in port for an existing v3.x or v4.x based ESP-IDF project can be found at [ports/esp_idf](ports/esp_idf). - An example application exercising the memfault-firmware-sdk can be found [here](examples/esp32/README.md). ### Changes between Memfault SDK 0.2.3 and SDK 0.2.2 - March 3, 2020 - If a [software watchdog](https://mflt.io/root-cause-watchdogs) has been implemented on a Cortex-M device, `MemfaultWatchdog_Handler` can now be registered as the Exception Handler to automatically collect a coredump. - For heartbeat metrics, instead of serializing the name of each metric, we now recover it from the debug information in the symbol file in the Memfault cloud. For a typical heartbeat this reduces the serialization size by more than 50% and results in a smaller footprint than other structured serialization alternatives such as Protobuf. - Remove usage of `__has_include` macro for IAR compiler since not all versions fully support it. ### Changes between Memfault SDK 0.2.1 and SDK 0.2.2 - Feb 20, 2020 - Add support for calling `MEMFAULT_TRACE_EVENT()` from interrupts. Note: If you are _not_ using our CMake or Make [build system helpers](README.md#add-sources-to-build-system), this change requires that you add `$(MEMFAULT_SDK_ROOT)/components/core/src/arch_arm_cortex_m.c` to your project. - Misc documentation improvements. ### Changes between Memfault SDK 0.2.0 and SDK 0.2.1 - Feb 14, 2020 - Add support for compressing coredumps as they are sent using Run Length Encoding (RLE). More details can be found in [memfault/core/data_source_rle.h](components/include/memfault/core/data_source_rle.h). - Update **metrics** component to support compilation with the IAR ARM C/C++ Compiler. - Update Mbed OS 5 port to use `memfault_demo_shell` instead `mbed-client-cli`, since `mbed-client-cli` is not part of the main Mbed OS 5 distribution. - Update nrf52 example application to only collect the active parts of the stack to reduce the overall size of the example coredump. ### Changes between Memfault SDK 0.2.0 and SDK 0.1.0 - Feb 5, 2020 - Add a new API ("Trace Event") for tracking errors. This allows for tracing issues that are unexpected but are non-fatal (don't result in a device reboot). A step-by-step guide detailing how to use the feature can be found at: - Update GDB coredump collection script to work with the ESP32 (Tensilica Xtensa LX6 Architecture) - Remove `__task` from IAR Cortex-M function handler declarations since it's not explicitly required and can lead to a compiler issue if the function prototype does not also use it. - Misc documentation and comment tweaks to make nomenclature more consistent ### Changes between Memfault SDK 0.1.0 and SDK 0.0.18 - Jan 22, 2020 - Update **panics** component to support compilation with IAR ARM C/C++ Compiler. More details about integrating IAR can be found at . As part of the change `MEMFAULT_GET_LR()` now takes an argument which is the location to store the LR to (`void *lr = MEMFAULT_GET_LR()` -> `void *lr;` `MEMFAULT_GET_LR(lr)`) ### Changes between Memfault SDK 0.0.18 and SDK 0.0.17 - Jan 14, 2020 - Update the chunk protocol to encode CRC in last chunk of message instead of first. This allows the CRC to be computed incrementally and the underlying message to be read once (instead of twice). It also makes it easier to use the data packetizer in environments where reads from data sources need to be performed asynchronously. More details can be found at - Fixed a couple documentation links ### Changes between Memfault SDK 0.0.17 and SDK 0.0.16 - Jan 7, 2020 - Guarantee that all accesses to the platform coredump storage region route through `memfault_coredump_read` while the system is running. - Scrub unused portion of out buffer provided to packetizer with a known pattern for easier debugging ### Changes between Memfault SDK 0.0.16 and SDK 0.0.15 - Jan 6, 2020 - Add convenience API, `memfault_packetizer_get_chunk()`, to [data_packetizer](components/include/memfault/core/data_packetizer.h) module. - Add a new eMfltCoredumpRegionType, `MemoryWordAccessOnly` which can be used to force the region to be read 32 bits at a time. This can be useful for accessing certain peripheral register ranges which are not byte addressable. - Automatically collect Exception / ISR state for Cortex-M based targets. NOTE that the default config requires an additional ~150 bytes of coredump storage. The feature can be disabled completely by adding `-DMEMFAULT_COLLECT_INTERRUPT_STATE=0` to your compiler flags. More configuration options can be found in [memfault_coredump_regions_armv7.c](components/panics/src/memfault_coredump_regions_armv7.c). - Improve documentation about integrating the SDK within a project in README - Update Release note summary to use markdown headings for easier referencing. - Update try script used to collect coredumps via GDB to also collect Exception/ISR register information. ### Changes between Memfault SDK 0.0.15 and SDK 0.0.14 - Dec 19, 2019 - Add ARMv6-M fault handling port to **panics** component for MCUs such as the ARM Cortex-M0 and Cortex-M0+. ### Changes between Memfault SDK 0.0.14 and SDK 0.0.13 - Dec 18, 2019 - Update **panics** component to support compilation with Arm Compiler 5. ### Changes between Memfault SDK 0.0.13 and SDK 0.0.12 - Dec 16, 2019 - Updated Cortex-M fault handler in **panics** component to also collect `psp` and `msp` registers when the system crashes. This allows for the running thread backtrace to be more reliably recovered when a crash occurs from an ISR. - Added optional `MEMFAULT_EXC_HANDLER_...` preprocessor defines to enable custom naming of exception handlers in the **panics** component. - Add port for Cortex-M based targets using Quantum Leaps' QP™/C & QP™/C++ real-time embedded framework. See [ports/qp/README.md](ports/qp/README.md) for more details. - Add demo application running Quantum Leaps' QP™/C running on the [STM32F407 discovery board](https://www.st.com/en/evaluation-tools/stm32f4discovery.html). See [examples/qp/README.md](examples/qp/README.md) for more details. - Added demo application and port for Arm Mbed OS 5 running on the [STM32F429I-DISC1 evaluation board](https://www.st.com/en/evaluation-tools/32f429idiscovery.html). See [examples/mbed/README.md](examples/mbed/README.md) for more details. - Changed `print_core` to `print_chunk` in demo applications to better align with the Memfault nomenclature for [data transfer](https://mflt.io/data-to-cloud). ### Changes between Memfault SDK 0.0.12 and SDK 0.0.11 - Dec 4, 2019 - Expose root certificates used by Memfault CI in DER format for easier integration with TLS libraries which do not parse PEM formatted certificates. - Add utilities to the http component for constructing Memfault cloud **chunk** endpoint POST requests to facilitate easier integration in environments with no pre-existing http stack. - Add port for Cortex-M based targets in the Zephyr RTOS. Ports are available for the 1.14 Long Term Support Release as well as the 2.0 Release. See [ports/zephyr/README.md](ports/zephyr/README.md) for more details - Add Zephyr demo application (tested on the STM32L4). See [zephyr demo app directory](examples/zephyr/README.md) for more details ### Changes between Memfault SDK 0.0.11 and SDK 0.0.10 - Nov 25, 2019 - Release of **metrics** component. This API can easily be used to monitor device health over time (i.e connectivity, battery life, MCU resource utilization, hardware degradation, etc) and configure Alerts with the Memfault backend when things go astray. To get started, see this [document](https://mflt.io/2D8TRLX) ### Changes between Memfault SDK 0.0.10 and SDK 0.0.9 - Nov 22, 2019 - Updated `memfault_platform_coredump_get_regions()` to take an additional argument, `crash_info` which conveys information about the crash taking place (trace reason & stack pointer at time of crash). This allows platform ports to dynamically change the regions collected based on the crash if so desired. This will require an update that looks like the following to your port: ```diff -const sMfltCoredumpRegion *memfault_platform_coredump_get_regions(size_t *num_regions) { +const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( + const sCoredumpCrashInfo *crash_info, size_t *num_regions) { ``` - Added a new API, `memfault_coredump_storage_compute_size_required()` which can be called on boot to sanity check that platform coredump storage is large enough to hold a coredump. For example: ```c sMfltCoredumpStorageInfo storage_info = { 0 }; memfault_platform_coredump_storage_get_info(&storage_info); const size_t size_needed = memfault_coredump_storage_compute_size_required(); if (size_needed > storage_info.size) { MEMFAULT_LOG_ERROR("Coredump storage too small. Got %d B, need %d B", storage_info.size, size_needed); } MEMFAULT_ASSERT(size_needed <= storage_info.size); ``` - Added a convenience RAM backed [reference port](https://github.com/memfault/memfault-firmware-sdk/blob/master/ports/panics/src/memfault_platform_ram_backed_coredump.c) for coredump platform APIs. This can be used for persisting a coredump in RAM across a reset. ### Changes between Memfault Firmware SDK 0.0.9 and 0.0.8 - Nov 15, 2019 - Enhanced Reboot Tracking module within the **panics** component. Reboots that don't result in coredumps can now be easily tracked (i.e brown outs, system watchdogs, user initiated resets, resets due to firmware updates, etc). See `memfault/core/reboot_tracking.h` for more details and for a step-by-step setup tutorial. - Added Event Storage module within the **core** component. This is a small RAM backed data store that queues up traces to be published to the Memfault cloud. To minimize the space needed and transport overhead, all events collected within the SDK are stored using the Concise Binary Object Representation (CBOR) ### Changes between Memfault Firmware SDK 0.0.8 and 0.0.7 - Nov 7, 2019 - Added helper makefile (`makefiles/MemfaultWorker.mk`). A user of the SDK can include this makefile when using a make as their build system to easily collect the sources and include paths needed to build Memfault SDK components - Make unit tests public & add CI to external SDK ### Changes between Memfault Firmware SDK 0.0.7 and 0.0.6 - Oct 31, 2019 - Added firmware side support for the Memfault cloud **chunk** endpoint. This is a sessionless endpoint that allows chunks of arbitrary size to be sent and reassembled in the Memfault cloud. This transport can be used to publish _any_ data collected by the Memfault SDK. The data is read out on the SDK side by calling the `memfault_packetizer_get_next()` API in `data_packetizer.h`. More details [here](https://mflt.io/data-to-cloud) - Updated demo apps to use the new **chunk** endpoint - Added a new _weak_ function, `memfault_coredump_read()` so platform ports can easily add locking on reads to coredump storage if necessary for transmission. - Updates to this version of the sdk will require the **util** component get compiled when using the **panics** API ### Changes between Memfault Firmware SDK 0.0.6 and 0.0.5 - Oct 14, 2019 - Added example port and demo for the STM32H743I-NUCLEO144 evaluation board running ChibiOS. See `examples/stm32/stm32h743i/README.md` for more details. - general README documentation improvements - Fixed compilation error that could arise in `memfault_fault_handling_arm.c` when using old versions of GCC ### Changes between Memfault Firmware SDK 0.0.5 and 0.0.4 - September 23, 2019 - Updated **panics** SDK to support complex hardware toplogies such as systems with multiple MCUs or systems running multiple binaries on a single MCU. More details [here](https://mflt.io/34PyNGQ). Users of the SDK will need to update the implementation for `memfault_platform_get_device_info()` - Updated all ports to be in sync with SDK updates ### Changes between Memfault Firmware SDK 0.0.4 and 0.0.3 - August 19, 2019 - Updated **panics** core code to _always_ collect fault registers for ARMv6-M & ARMv7-M architectures. The Memfault cloud will auto-analyze these and present an analysis. - Updated gdb script to collect Cortex-M MPU region information for auto-analysis - general README documentation improvements - improved error reporting strategy and documentation in `memfault/core/errors.h` ### Changes between Memfault Firmware SDK 0.0.3 and 0.0.2 - July 2, 2019 - added example port and demo of **panics** SDK for the Nordic nRF52840 (PCA10056) development kit. See `examples/nrf5/README.md` for more details. - Made SDK headers suitable for includion in C++ files First Public Release of Memfault Firmware SDK 0.0.2 - June 26, 2019 - published initial Memfault SDK, see `README.md` in root for summary - published **panics** API which is a C SDK that can be integrated into any Cortex-M device to save a "core" (crash state) on faults and system asserts. See`components/panics/README.md` for more details - Added example port and demo of **panics** SDK for the BCM943364WCD1 evaluation board running the WICED Wifi stack. More details in `examples/wiced/README.md` - add python invoke based CLI wrapper for demo ports ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.12.4) if(ESP_PLATFORM) include(${CMAKE_CURRENT_LIST_DIR}/ports/esp_idf/memfault/CMakeLists.txt) else() include(${CMAKE_CURRENT_LIST_DIR}/cmake/Memfault.cmake) endif() ================================================ FILE: Kconfig ================================================ # If the repo is being used as an ESP-IDF component, bring in the ESP-IDF specific # Kconfig file. Otherwise this should be unused. if IDF_TARGET != "" rsource "ports/esp_idf/memfault/Kconfig" endif ================================================ FILE: LICENSE ================================================ Copyright (c) 2019 - Present, Memfault All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code or in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 2. Neither the name of Memfault nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 3. This software, with or without modification, must only be used with the Memfault services and integrated with the Memfault server. 4. Any software provided in binary form under this license must not be reverse engineered, decompiled, modified and/or disassembled. THIS SOFTWARE IS PROVIDED BY MEMFAULT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MEMFAULT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ # Memfault Firmware SDK [![CircleCI](https://circleci.com/gh/memfault/memfault-firmware-sdk.svg?style=svg)](https://circleci.com/gh/memfault/memfault-firmware-sdk) [![Coverage](https://img.shields.io/codecov/c/gh/memfault/memfault-firmware-sdk/master)](https://codecov.io/gh/memfault/memfault-firmware-sdk/) Ship Firmware with Confidence. More details about the Memfault platform itself, how it works, and step-by-step integration guides [can be found here](https://mflt.io/embedded-getting-started). ## Getting Started To start integrating in your platform today, [create a Memfault cloud account](https://mflt.io/signup). ## Components The SDK is designed as a collection of components, so you can include only what is needed for your project. The SDK has been designed to have minimal impact on code-space, bandwidth, and power consumption. The [`components`](components/) directory folder contains the various components of the SDK. Each component contains a `README.md`, source code, header files and "platform" header files. The platform header files describe the interfaces which the component relies on that you must implement. For some of the platform dependencies we have provided ports that can be linked into your system directly, or used as a template for further customization. You can find them in the [`ports`](ports/) folder. For some of the popular MCUs & vendor SDKs, we have already provided a reference implementation for platform dependencies which can be found in the [`examples`](examples/) folder. These can also serve as a good example when initially setting up the SDK on your platform. ### Main components - `panics` - fault handling, coredump and reboot tracking and reboot loop detection API. - `metrics` - used to monitor device health over time (i.e. connectivity, battery life, MCU resource utilization, hardware degradation, etc.) Please refer to the `README.md` in each of these for more details. ### Support components - `core` - common code that is used by all other components. - `demo` - common code that is used by demo apps for the various platforms. - `http` - http client API, to post coredumps and events directly to the Memfault service from devices. - `util` - various utilities. ## Integrating the Memfault SDK ## Add Memfault SDK to Your Repository The Memfault SDK can be added directly into your repository. The structure typically looks like: ```bash ├── third_party/memfault │ ├── memfault-firmware-sdk (submodule) │ │ │ │ # Files where port to your platform will be implemented │ ├── memfault_platform_port.c │ ├── memfault_platform_coredump_regions.c │ │ │ │ # Configuration headers │ ├── memfault_platform_config.h │ ├── memfault_trace_reason_user_config.def │ ├── memfault_metrics_heartbeat_config.def │ └── memfault_platform_log_config.h ``` If you are using `git`, the Memfault SDK is typically added to a project as a submodule: ```bash git submodule add https://github.com/memfault/memfault-firmware-sdk.git $YOUR_PROJECT/third_party/memfault/memfault-firmware-sdk ``` This makes it easy to track the history of the Memfault SDK. You should not need to make modifications to the Memfault SDK. The typical update flow is: - `git pull` the latest upstream - check [CHANGELOG.md](CHANGELOG.md) to see if any modifications are needed - update to the new submodule commit in your repo. Alternatively, the Memfault SDK may be added to a project as a git subtree or by copying the source into a project. ## Add sources to Build System ### Make If you are using `make`, `makefiles/MemfaultWorker.mk` can be used to very easily collect the source files and include paths required by the SDK. ```c MEMFAULT_SDK_ROOT := MEMFAULT_COMPONENTS := include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk += $(MEMFAULT_COMPONENTS_SRCS) += $(MEMFAULT_COMPONENTS_INC_FOLDERS) ``` ### Cmake If you are using `cmake`, `cmake/Memfault.cmake` in a similar fashion to collection source files and include paths: ```c set(MEMFAULT_SDK_ROOT ) list(APPEND MEMFAULT_COMPONENTS ) include(${MEMFAULT_SDK_ROOT}/cmake/Memfault.cmake) memfault_library(${MEMFAULT_SDK_ROOT} MEMFAULT_COMPONENTS MEMFAULT_COMPONENTS_SRCS MEMFAULT_COMPONENTS_INC_FOLDERS) # ${MEMFAULT_COMPONENTS_SRCS} contains the sources # needed for the library and ${MEMFAULT_COMPONENTS_INC_FOLDERS} contains the include paths ``` ### Other Build Systems If you are not using one of the above build systems, to include the SDK you need to do is: - Add the `.c` files located at `components//src/*.c` to your build system - Add `components//include` to the include paths you pass to the compiler #### Running the unit tests The SDK code is covered extensively by unit tests. They can be found in the `tests/` folder. If you'd like to run them yourself, check out the instructions in [tests/README.md](tests/README.md). To learn more about unit testing best practices for firmware development, check out [our blog post on this topic](https://interrupt.memfault.com/blog/unit-testing-basics)! The unit tests are run by CircleCI upon every commit to this repo. See badges at the top for build & test coverage status of the `master` branch. ## FAQ - Why does a coredump not show up under "Issues" after uploading it? - Make sure to upload the symbols to the same project to which you upload coredumps. Also make sure the software type and software version reported by the device (see "Device information" in `components/core/README.md`) match the software type and software version that was entered when creating the Software Version and symbol artifact online. [More information on Build Ids and uploading Symbol Files can be found here](https://mflt.io/symbol-file-build-ids). - I'm getting error XYZ, what to do now? - Don't hesitate to contact us for help! You can reach us through . ## License Unless specifically indicated otherwise in a file, all memfault-firmware-sdk files are all licensed under the [Memfault License](/LICENSE). (A few files in the [examples](/examples) and [ports](/ports) directory are licensed differently based on vendor requirements.) ================================================ FILE: VERSION ================================================ BUILD ID: 17586 GIT COMMIT: 09acdc44af VERSION: 1.39.0 ================================================ FILE: cmake/Memfault.cmake ================================================ # A convenience helper cmake function that can be used to collect the sources and include # paths needed for the Memfault SDK based on the components used # # USAGE # If you are using a Cmake build system, to pick up the Memfault include paths & source # files needed for a project, you can just add the following lines: # # set(MEMFAULT_SDK_ROOT ) # list(APPEND MEMFAULT_COMPONENTS ) # include(${MEMFAULT_SDK_ROOT}/cmake/Memfault.cmake) # memfault_library(${MEMFAULT_SDK_ROOT} MEMFAULT_COMPONENTS # MEMFAULT_COMPONENTS_SRCS MEMFAULT_COMPONENTS_INC_FOLDERS) # # NOTE: By default, the list of sources returned will be for ARM Cortex-M targets but ARCH_XTENSA can be # passed as an optional final argument to return sources expected for ARCH_XTENSA architectures # # After invoking the function ${MEMFAULT_COMPONENTS_SRCS} will contain the sources # needed for the library and ${MEMFAULT_COMPONENTS_INC_FOLDERS} will contain the include # paths # Explicitly enable IN_LIST use inside if() # https://cmake.org/cmake/help/v3.3/policy/CMP0057.html cmake_policy(SET CMP0057 NEW) function(memfault_library sdk_root components src_var_name inc_var_name) if(NOT IS_ABSOLUTE ${sdk_root}) set(sdk_root "${CMAKE_CURRENT_SOURCE_DIR}/${sdk_root}") endif() if ("demo" IN_LIST ${components}) # The demo component is enabled so let's pick up component specific cli commands # If the component is not enabled a weak implementation of the CLI command will get # picked up from "demo/src/memfault_demo_shell_commands.c" foreach(component IN LISTS ${components}) file(GLOB MEMFAULT_COMPONENT_DEMO_${component} ${sdk_root}/components/demo/src/${component}/*.c) list(APPEND SDK_SRC ${MEMFAULT_COMPONENT_DEMO_${component}}) endforeach() endif() list(APPEND SDK_INC ${sdk_root}/components/include ) foreach(component IN LISTS ${components}) file(GLOB MEMFAULT_COMPONENT_${component} ${sdk_root}/components/${component}/src/*.c) list(APPEND SDK_SRC ${MEMFAULT_COMPONENT_${component}}) endforeach() set(arch ${ARGN}) if(NOT arch) set(arch ARCH_ARM_CORTEX_M) endif() if(arch STREQUAL "ARCH_XTENSA") list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/core/src/arch_arm_cortex_m.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_coredump_regions_armv7.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_arm.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_riscv.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_posix.c) elseif(arch STREQUAL "ARCH_RISCV") list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/core/src/arch_arm_cortex_m.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_coredump_regions_armv7.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_arm.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_xtensa.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_posix.c) elseif(arch STREQUAL "ARCH_ARM_CORTEX_M") list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_riscv.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_xtensa.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_posix.c) elseif(arch STREQUAL "ARCH_POSIX") list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/core/src/arch_arm_cortex_m.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_coredump_regions_armv7.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_arm.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_xtensa.c) list(REMOVE_ITEM SDK_SRC ${sdk_root}/components/panics/src/memfault_fault_handling_riscv.c) else() message(FATAL_ERROR "Unsupported Arch: ${arch}") endif() set(${src_var_name} ${SDK_SRC} PARENT_SCOPE) set(${inc_var_name} ${SDK_INC} PARENT_SCOPE) endfunction() ================================================ FILE: components/README.md ================================================ # Memfault 💥 ## Components This directory contains the Memfault components that fall into two categories: - Main components - Support components A brief mention of each of the components is described here, however, see the `README.md` in the respective directories for a more complete description of each component. See also the online documentation at [Components](https://github.com/memfault/memfault-firmware-sdk#components). ### Main Components The main components are `panics` and `metrics`. These components provide the high level features of the Memfault SDK to your product. - panics - fault handling, coredump and reboot tracking and reboot loop detection API. - metrics - used to monitor device health over time (i.e. connectivity, battery life, MCU resource utilization, hardware degradation, etc.) ### Support Components The support components include `core`, `http`, `util` and `demo` which, except for `demo`, are needed by the main components. The `demo` component is there to help demonstrate how to use the Memfault SDK. - core - common code that is used by all other components. - demo - common code that is used by demo apps for the various platforms. - http - http client API, to post coredumps and events directly to the Memfault service from devices. - util - various utilities. ================================================ FILE: components/core/README.md ================================================ # core Common code that is used by all other components. ## Application/platform-specific customizations ### Device information The file `include/memfault/platform/device_info.h` contains the functions that the Memfault SDK will call to get the unique device identifier (device serial), software type, software version, hardware version, etc. The provided platform reference implementation usually uses something like the WiFi MAC or chip ID as base for the unique device identifier and uses preprocessor variables to get the firmware and hardware versions (with fallbacks to hard-coded values, i.e. `"xyz-main"` as software type `"1.0.0"` as software version and `"xyz-proto"` as hardware version). [To learn more about the concepts of software type, software version, hardware version, etc. take a look at this documentation](https://mflt.io/36NGGgi). Please change `memfault_platform_device_info.c` in ways that makes sense for your project. ### Core features - Build ID - Packetizing and serializing data used for sending dat to the Memfault cloud. - Event storage for metrics, heartbeat, reboot reasons and trace events. RAM based events can optionally be persisted to NVRAM via user-installed callbacks. - Logging support provides a lightweight logging infrastructure with level filtering and the ability to write log entries quickly with user-installable callback support to drain log entries to slower mediums like NVRAM or serial devices. Information stored in the log module will be uploaded to Memfault in the event of a crash. Stored logs will also be uploaded after `memfault_log_trigger_collection()` is called. - Reboot (reset) tracking captures a reset cause in a section of RAM that is not modified by any firmware during reset and rebooting. - Memfault assert macro that can halt if the debugger is attached and call a user function if defined. - Trace event capturing ### Storage allocations - `memfault_events_storage_boot()` to reserve storage for events (reboots, trace events, heartbeats) - `memfault_reboot_tracking_boot()` points to a memory region that is not touched by any firmware except for reboot tracking API functions. Reboot info can be pushed to the event storage for packetization to be sent to Memfault. - _`memfault_trace_event_boot()`_ not an allocation but connects a trace event context into the event storage allocation. - `memfault_log_boot()` to reserve storage for the logging module. The logging storage gets sent to Memfault as part of the core dump data if enabled. ================================================ FILE: components/core/src/.gitkeep ================================================ ================================================ FILE: components/core/src/arch_arm_cortex_m.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "memfault/core/arch.h" #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #if MEMFAULT_COMPILER_ARM_CORTEX_M bool memfault_arch_is_inside_isr(void) { // We query the "Interrupt Control State Register" to determine // if there is an active Exception Handler volatile uint32_t *ICSR = (uint32_t *)0xE000ED04; // Bottom byte makes up "VECTACTIVE" return ((*ICSR & 0xff) != 0x0); } void memfault_arch_disable_configurable_faults(void) { // Clear MEMFAULTENA, BUSFAULTENA, USGFAULTENA, SECUREFAULTENA // // This will force all faults to be routed through the HardFault Handler volatile uint32_t *SHCSR = (uint32_t *)0xE000ED24; *SHCSR &= ~((uint32_t)0x000F0000); } MEMFAULT_WEAK void memfault_platform_halt_if_debugging(void) { volatile uint32_t *dhcsr = (volatile uint32_t *)0xE000EDF0; const uint32_t c_debugen_mask = 0x1; if ((*dhcsr & c_debugen_mask) == 0) { // no debugger is attached so return since issuing a breakpoint instruction would trigger a // fault return; } // NB: A breakpoint with value 'M' (77) for easy disambiguation from other breakpoints that may // be used by the system. MEMFAULT_BREAKPOINT(77); } #endif /* MEMFAULT_COMPILER_ARM_CORTEX_M */ ================================================ FILE: components/core/src/memfault_batched_events.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include #include "memfault/core/batched_events.h" #include "memfault/core/sdk_assert.h" #include "memfault/util/cbor.h" void memfault_batched_events_build_header(size_t num_events, sMemfaultBatchedEventsHeader *header_out) { MEMFAULT_SDK_ASSERT(header_out != NULL); if (num_events <= 1) { header_out->length = 0; return; } // there's multiple events to read. We will add a header to indicate the total count sMemfaultCborEncoder encoder; memfault_cbor_encoder_init(&encoder, memfault_cbor_encoder_memcpy_write, header_out->data, sizeof(header_out->data)); memfault_cbor_encode_array_begin(&encoder, num_events); header_out->length = memfault_cbor_encoder_deinit(&encoder); } ================================================ FILE: components/core/src/memfault_build_id.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! See header for more details #include #include #include "memfault/config.h" #include "memfault/core/build_info.h" #include "memfault_build_id_private.h" // OS version information #if defined(MEMFAULT_OS_VERSION_NAME) ^ defined(MEMFAULT_OS_VERSION_STRING) #error "MEMFAULT_OS_VERSION_NAME and MEMFAULT_OS_VERSION_STRING must be both defined or undefined" #elif !(defined(MEMFAULT_OS_VERSION_NAME) && defined(MEMFAULT_OS_VERSION_STRING)) #if defined(__ZEPHYR__) // Either Zephyr or NCS. We can then rely on the GNU __has_include extension to // be available. #if __has_include("ncs_version.h") #include "ncs_version.h" #define MEMFAULT_OS_VERSION_NAME "ncs" #define MEMFAULT_OS_VERSION_STRING NCS_VERSION_STRING #else // This is Zephyr's version.h file, since Memfault's is namespaced as // "memfault/version.h". #include #define MEMFAULT_OS_VERSION_NAME "zephyr" #define MEMFAULT_OS_VERSION_STRING KERNEL_VERSION_STRING #endif // __has_include("ncs_version.h") #elif defined(ESP_PLATFORM) // ESP-IDF, prefer the command-line provided IDF_VER if set. In rare cases, // IDF_VER will be set to "HEAD-HASH-NOTFOUND", in which case we fall back // to the version provided by esp_idf_version.h. #define MEMFAULT_OS_VERSION_NAME "esp-idf" #define STRINGIFY(x) #x #define XSTRINGIFY(s) STRINGIFY(s) #include "esp_idf_version.h" #define MEMFAULT_OS_VERSION_STRING \ ((__builtin_strcmp(IDF_VER, "HEAD-HASH-NOTFOUND") != 0) ? \ (IDF_VER) : \ ("" XSTRINGIFY(ESP_IDF_VERSION_MAJOR) "." XSTRINGIFY( \ ESP_IDF_VERSION_MINOR) "." XSTRINGIFY(ESP_IDF_VERSION_PATCH) "")) #else // No OS version information available #define MEMFAULT_OS_VERSION_NAME "" #define MEMFAULT_OS_VERSION_STRING "" #endif // defined(__ZEPHYR__) #endif // defined(MEMFAULT_OS_VERSION_NAME) ^ defined(MEMFAULT_OS_VERSION_STRING) #if MEMFAULT_USE_GNU_BUILD_ID // Note: This variable is emitted by the linker script extern uint8_t MEMFAULT_GNU_BUILD_ID_SYMBOL[]; MEMFAULT_BUILD_ID_QUALIFIER sMemfaultBuildIdStorage g_memfault_build_id = { .type = kMemfaultBuildIdType_GnuBuildIdSha1, .len = sizeof(sMemfaultElfNoteSection), .short_len = MEMFAULT_EVENT_INCLUDED_BUILD_ID_SIZE_BYTES, .storage = MEMFAULT_GNU_BUILD_ID_SYMBOL, .sdk_version = MEMFAULT_SDK_VERSION, .os_version = { .name = MEMFAULT_OS_VERSION_NAME, .version = MEMFAULT_OS_VERSION_STRING, }, }; #else // NOTE: We start the array with a 0x1, so the compiler will never place the variable in bss MEMFAULT_BUILD_ID_QUALIFIER uint8_t g_memfault_sdk_derived_build_id[MEMFAULT_BUILD_ID_LEN] = { 0x1, }; MEMFAULT_BUILD_ID_QUALIFIER sMemfaultBuildIdStorage g_memfault_build_id = { .type = kMemfaultBuildIdType_None, .len = sizeof(g_memfault_sdk_derived_build_id), .short_len = MEMFAULT_EVENT_INCLUDED_BUILD_ID_SIZE_BYTES, .storage = g_memfault_sdk_derived_build_id, .sdk_version = MEMFAULT_SDK_VERSION, .os_version = { .name = MEMFAULT_OS_VERSION_NAME, .version = MEMFAULT_OS_VERSION_STRING, }, }; #endif ================================================ FILE: components/core/src/memfault_build_id_private.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Internal file that should never be included by a consumer of the SDK. See //! "memfault/core/build_info.h" for details on how to leverage the build id. #include #include #include "memfault/core/compiler.h" #include "memfault/version.h" #ifdef __cplusplus extern "C" { #endif // // Note: These structures and values are also used in $MEMFAULT_FIRMWARE_SDK/scripts/fw_build_id.py // Any change here will likely require an update to the script was well! // typedef enum { //! No Build ID present. kMemfaultBuildIdType_None = 1, //! Build Id which can be emitted by GCC/LLVM compilers (https://mflt.io/gnu-build-id) kMemfaultBuildIdType_GnuBuildIdSha1 = 2, //! Build Id Type patched in by $MEMFAULT_FIRMWARE_SDK/scripts/fw_build_id.py kMemfaultBuildIdType_MemfaultBuildIdSha1 = 3, } eMemfaultBuildIdType; typedef struct { const char *name; const char *version; } sMfltOsVersion; typedef struct { uint8_t type; // eMemfaultBuildIdType uint8_t len; // the length, in bytes, of the build id used when reporting data uint8_t short_len; uint8_t rsvd; const void *storage; const sMfltSdkVersion sdk_version; const sMfltOsVersion os_version; } sMemfaultBuildIdStorage; MEMFAULT_STATIC_ASSERT(((offsetof(sMemfaultBuildIdStorage, type) == 0) && (offsetof(sMemfaultBuildIdStorage, short_len) == 2)), "be sure to update fw_build_id.py!"); #if defined(MEMFAULT_UNITTEST) //! NB: For unit tests we want to be able to instrument the data in the test //! so we drop the `const` qualifier #define MEMFAULT_BUILD_ID_QUALIFIER #else #define MEMFAULT_BUILD_ID_QUALIFIER const #endif extern MEMFAULT_BUILD_ID_QUALIFIER sMemfaultBuildIdStorage g_memfault_build_id; extern MEMFAULT_BUILD_ID_QUALIFIER uint8_t g_memfault_sdk_derived_build_id[]; //! The layout of a Note section in an ELF. This is how Build Id information is laid out when //! using kMemfaultBuildIdType_GnuBuildIdSha1 typedef MEMFAULT_PACKED_STRUCT { uint32_t namesz; uint32_t descsz; uint32_t type; char namedata[]; } sMemfaultElfNoteSection; #ifdef __cplusplus } #endif ================================================ FILE: components/core/src/memfault_compact_log_serializer.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include #include "memfault/config.h" #if MEMFAULT_COMPACT_LOG_ENABLE #include "memfault/core/compact_log_helpers.h" #include "memfault/core/compact_log_serializer.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/util/cbor.h" //! Compact logs are placed in a linker section named "log_fmt". This is a symbol exposed by the //! linker which points to the start of the section extern uint32_t __start_log_fmt; //! Note: We don't read this in the firmware but it is used during the decode //! process to sanity check the section is being laid out as we would expect. MEMFAULT_PUT_IN_SECTION(".log_fmt_hdr") MEMFAULT_NO_ALLOC const sMemfaultLogFmtElfSectionHeader g_memfault_log_fmt_elf_section_hdr = { .magic = 0x66474f4c, /* LOGf */ .version = 1, }; bool memfault_vlog_compact_serialize(sMemfaultCborEncoder *encoder, uint32_t log_id, uint32_t compressed_fmt, va_list args) { const uint32_t bits_per_arg = 2; const uint32_t bits_per_arg_mask = (1 << bits_per_arg) - 1; const size_t num_args = (31UL - MEMFAULT_CLZ(compressed_fmt)) / bits_per_arg; if (!memfault_cbor_encode_array_begin(encoder, 1 /* log_id */ + num_args)) { return false; } // We use an offset within the section to reduce the space needed when serializing // the log. uint32_t log_fmt_offset = log_id - (uint32_t)(uintptr_t)&__start_log_fmt; if (!memfault_cbor_encode_unsigned_integer(encoder, log_fmt_offset)) { return false; } for (size_t i = 0; i < num_args; i++) { // See memfault/core/compact_log_helpers.h for more details. In essence, // the promotion type of a given va_arg is encoded within two bits of the // compressed_fmt field. const uint32_t type = (compressed_fmt >> ((num_args - i - 1) * bits_per_arg)) & bits_per_arg_mask; bool success = false; switch (type) { case MEMFAULT_LOG_ARG_PROMOTED_TO_INT32: { int32_t val = va_arg(args, int32_t); success = memfault_cbor_encode_signed_integer(encoder, val); break; } case MEMFAULT_LOG_ARG_PROMOTED_TO_INT64: { // We differentiate between an 64 bit and 32 bit integer arg // by packing the cbor encoded int in an array. uint64_t val = va_arg(args, uint64_t); success = memfault_cbor_encode_array_begin(encoder, 1) && memfault_cbor_encode_long_signed_integer(encoder, (int64_t)val); break; } case MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE: { // Note: Per the ARM ABI, doubles are just serialized out onto the stack // and occupy 8 bytes: // // Chapter 7: THE STANDARD VARIANTS // For a variadic function the base standard is always used both for argument passing and // result return. // // 6.5 Parameter Passing // In the base standard there are no arguments that are candidates for a co-processor // register class. // // So for ARM we could just do the following: // uint64_t val = va_arg(args, uint64_t) // // But parsing as a double is more portable and get's optimized away by compilers // like GCC anyway: https://godbolt.org/z/mVAS7D MEMFAULT_STATIC_ASSERT(sizeof(uint64_t) == sizeof(double), "double does not fit in uint64_t"); // Note: We use memcpy to properly type-pun / avoid strict-aliasing violation: // https://mflt.io/strict-aliasing-type-punning double val = va_arg(args, double); uint64_t double_as_uint64; memcpy(&double_as_uint64, &val, sizeof(val)); success = memfault_cbor_encode_uint64_as_double(encoder, double_as_uint64); break; } case MEMFAULT_LOG_ARG_PROMOTED_TO_STR: { const char *str = va_arg(args, const char *); success = memfault_cbor_encode_string(encoder, str != NULL ? str : "(null)"); break; } default: break; } if (!success) { return false; } } return true; } // serialize a fallback entry when the compact log is too large to fit the // provided log entry limit. the fallback looks like this: // [, {0:1, 1:serialized_len}] // where the map contains two entries: // key 0 -> indicates this is a fallback entry // key 1 -> the computed serialized length of the original compact log typedef enum { kMemfaultCompactLogExtendedReason_Fallback = 1, } eMemfaultCompactLogExtendedReason; bool memfault_vlog_compact_serialize_fallback_entry(sMemfaultCborEncoder *encoder, uint32_t log_id, uint32_t serialized_len) { if (!memfault_cbor_encode_array_begin(encoder, 2)) { return false; } // We use an offset within the section to reduce the space needed when serializing // the log. uint32_t log_fmt_offset = log_id - (uint32_t)(uintptr_t)&__start_log_fmt; if (!memfault_cbor_encode_unsigned_integer(encoder, log_fmt_offset)) { return false; } if (!memfault_cbor_encode_dictionary_begin(encoder, 2)) { return false; } if (!memfault_cbor_encode_unsigned_integer(encoder, 0 /* type is 0 */) || !memfault_cbor_encode_unsigned_integer(encoder, kMemfaultCompactLogExtendedReason_Fallback)) { return false; } if (!memfault_cbor_encode_unsigned_integer(encoder, 1 /* serialized_len */) || !memfault_cbor_encode_unsigned_integer(encoder, serialized_len)) { return false; } return true; } bool memfault_log_compact_serialize(sMemfaultCborEncoder *encoder, uint32_t log_id, uint32_t compressed_fmt, ...) { va_list args; va_start(args, compressed_fmt); const bool success = memfault_vlog_compact_serialize(encoder, log_id, compressed_fmt, args); va_end(args); return success; } #endif /* MEMFAULT_COMPACT_LOG_ENABLE */ ================================================ FILE: components/core/src/memfault_core_utils.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief #include #include #include "memfault/core/build_info.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/device_info.h" #include "memfault/core/math.h" #include "memfault/core/platform/device_info.h" #include "memfault_build_id_private.h" static const void *prv_get_build_id_start_pointer(void) { switch (g_memfault_build_id.type) { case kMemfaultBuildIdType_MemfaultBuildIdSha1: return g_memfault_build_id.storage; case kMemfaultBuildIdType_GnuBuildIdSha1: { const sMemfaultElfNoteSection *elf = (const sMemfaultElfNoteSection *)g_memfault_build_id.storage; return &elf->namedata[elf->namesz]; // Skip over { 'G', 'N', 'U', '\0' } } case kMemfaultBuildIdType_None: default: break; } return NULL; } bool memfault_build_info_read(sMemfaultBuildInfo *info) { const void *id = prv_get_build_id_start_pointer(); if (id == NULL) { return false; } memcpy(info->build_id, id, sizeof(info->build_id)); return true; } static char prv_nib_to_hex_ascii(uint8_t val) { return val < 10 ? (char)val + '0' : (char)(val - 10) + 'a'; } bool memfault_build_id_get_string(char *out_buf, size_t buf_len) { sMemfaultBuildInfo info; bool id_available = memfault_build_info_read(&info); if (!id_available || buf_len < 2) { return false; } // 2 hex characters per byte const size_t chars_per_byte = 2; const size_t max_entries = MEMFAULT_MIN(sizeof(info.build_id) * chars_per_byte, buf_len - 1); for (size_t i = 0; i < max_entries; i++) { const uint8_t c = info.build_id[i / chars_per_byte]; const uint8_t nibble = (i % chars_per_byte) == 0 ? (c >> 4) & 0xf : c & 0xf; out_buf[i] = prv_nib_to_hex_ascii(nibble); } out_buf[max_entries] = '\0'; return true; } const char *memfault_create_unique_version_string(const char *const version) { static char s_version[MEMFAULT_UNIQUE_VERSION_MAX_LEN] = { 0 }; // Immutable once created. if (s_version[0]) { return s_version; } if (version) { // Add one to account for the '+' we will insert downstream. const size_t version_len = strlen(version) + 1; // Use 6 characters of the build id to make our versions unique and // identifiable between releases. const size_t build_id_chars = 6 + 1 /* '\0' */; if (version_len + build_id_chars <= sizeof(s_version)) { memcpy(s_version, version, version_len - 1); s_version[version_len - 1] = '+'; const size_t build_id_num_chars = MEMFAULT_MIN(build_id_chars, sizeof(s_version) - version_len - 1); if (!memfault_build_id_get_string(&s_version[version_len], build_id_num_chars)) { // Tack on something obvious to aid with debug but don't fail. // We know we can safely fit 6 bytes here. memcpy(&s_version[version_len], "no-id", sizeof("no-id")); MEMFAULT_LOG_ERROR("No configured build id"); } return s_version; } } return NULL; } const char *memfault_get_unique_version_string(void) { return memfault_create_unique_version_string(NULL); } void memfault_build_info_dump(void) { sMemfaultBuildInfo info; bool id_available = memfault_build_info_read(&info); if (!id_available) { MEMFAULT_LOG_INFO("No Build ID available"); return; } char build_id_sha[41] = { 0 }; for (size_t i = 0; i < sizeof(info.build_id); i++) { uint8_t c = info.build_id[i]; size_t idx = i * 2; build_id_sha[idx] = prv_nib_to_hex_ascii((c >> 4) & 0xf); build_id_sha[idx + 1] = prv_nib_to_hex_ascii(c & 0xf); } (void)build_id_sha; MEMFAULT_LOG_INFO( "%s Build ID: %s", (g_memfault_build_id.type == kMemfaultBuildIdType_GnuBuildIdSha1) ? "GNU" : "Memfault", build_id_sha); } void memfault_device_info_dump(void) { struct MemfaultDeviceInfo info = { 0 }; memfault_platform_get_device_info(&info); MEMFAULT_LOG_INFO("S/N: %s", info.device_serial ? info.device_serial : ""); MEMFAULT_LOG_INFO("SW type: %s", info.software_type ? info.software_type : ""); MEMFAULT_LOG_INFO("SW version: %s", info.software_version ? info.software_version : ""); MEMFAULT_LOG_INFO("HW version: %s", info.hardware_version ? info.hardware_version : ""); } ================================================ FILE: components/core/src/memfault_custom_data_recording.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Implements sMemfaultDataSourceImpl API specified in data_packetizer_source.h to serialize a //! custom data recording such that it can be published to the Memfault cloud. #include #include #include #include "memfault/config.h" #include "memfault/core/custom_data_recording.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/sdk_assert.h" #include "memfault/core/serializer_helper.h" #include "memfault/core/serializer_key_ids.h" #include "memfault/util/cbor.h" #include "memfault_custom_data_recording_private.h" #if MEMFAULT_CDR_ENABLE // Holds CDR metadata -- we pre-serialize in prv_has_cdr to avoid an egregious amount of calls to // read out the metadata when a small chunk size is used by a platform typedef struct { size_t length; uint8_t data[MEMFAULT_CDR_MAX_ENCODED_METADATA_LEN]; } sMemfaultCdrEncodedMetadata; static const sMemfaultCdrSourceImpl *s_cdr_sources[MEMFAULT_CDR_MAX_DATA_SOURCES]; typedef struct { sMemfaultCborEncoder encoder; const sMemfaultCdrSourceImpl *active_source; size_t total_encode_len; sMemfaultCdrMetadata active_metadata; sMemfaultCdrEncodedMetadata encoded_metadata; } sMfltCdrSourceCtx; static sMfltCdrSourceCtx s_memfault_cdr_source_ctx; static bool prv_encode_cdr_metadata(sMemfaultCborEncoder *encoder, sMfltCdrSourceCtx *cdr_ctx) { const sMemfaultCdrMetadata *metadata = &cdr_ctx->active_metadata; if (!memfault_serializer_helper_encode_metadata_with_time(encoder, kMemfaultEventType_Cdr, &metadata->start_time)) { return false; } if (!memfault_cbor_encode_unsigned_integer(encoder, kMemfaultEventKey_EventInfo)) { return false; } const size_t cdr_num_pairs = 1 /* mime types array */ + 1 /* duration ms */ + 1 /* collection reason */ + 1 /* recording itself */; memfault_cbor_encode_dictionary_begin(encoder, cdr_num_pairs); if (!memfault_serializer_helper_encode_uint32_kv_pair(encoder, kMemfaultCdrInfoKey_DurationMs, metadata->duration_ms)) { return false; } if (!memfault_cbor_encode_unsigned_integer(encoder, kMemfaultCdrInfoKey_Mimetypes) || !memfault_cbor_encode_array_begin(encoder, metadata->num_mimetypes)) { return false; } for (size_t i = 0; i < metadata->num_mimetypes; i++) { if (!memfault_cbor_encode_string(encoder, metadata->mimetypes[i])) { return false; } } if (!memfault_cbor_encode_unsigned_integer(encoder, kMemfaultCdrInfoKey_Reason) || !memfault_cbor_encode_string(encoder, metadata->collection_reason)) { return false; } if (!memfault_cbor_encode_unsigned_integer(encoder, kMemfaultCdrInfoKey_Data)) { return false; } if (!memfault_cbor_encode_byte_string_begin(encoder, metadata->data_size_bytes)) { return false; } // Note: at this point all that's left to encode is the binary blob itself as a byte string return true; } static void prv_try_get_cdr_source_with_data(sMfltCdrSourceCtx *ctx) { if (ctx->active_source != NULL) { // a source is already active return; } for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_cdr_sources); i++) { const sMemfaultCdrSourceImpl *source = s_cdr_sources[i]; if (source == NULL) { continue; } if (source->has_cdr_cb(&ctx->active_metadata)) { ctx->active_source = source; return; } } } static bool prv_has_cdr(size_t *total_size) { sMfltCdrSourceCtx *cdr_ctx = &s_memfault_cdr_source_ctx; prv_try_get_cdr_source_with_data(cdr_ctx); if (cdr_ctx->active_source == NULL) { return false; } sMemfaultCborEncoder encoder; memfault_cbor_encoder_init(&encoder, memfault_cbor_encoder_memcpy_write, cdr_ctx->encoded_metadata.data, sizeof(cdr_ctx->encoded_metadata.data)); if (!prv_encode_cdr_metadata(&encoder, cdr_ctx)) { MEMFAULT_LOG_ERROR( "Not enough storage to serialized CDR, increase MEMFAULT_CDR_MAX_ENCODED_METADATA_LEN"); return false; } cdr_ctx->encoded_metadata.length = memfault_cbor_encoder_deinit(&encoder); cdr_ctx->total_encode_len = cdr_ctx->encoded_metadata.length + cdr_ctx->active_metadata.data_size_bytes; *total_size = cdr_ctx->total_encode_len; return true; } static bool prv_cdr_read(uint32_t offset, void *buf, size_t buf_len) { sMfltCdrSourceCtx *cdr_ctx = &s_memfault_cdr_source_ctx; if (cdr_ctx->active_source == NULL) { return false; } if ((offset + buf_len) > cdr_ctx->total_encode_len) { return false; } uint8_t *bufp = (uint8_t *)buf; if (offset < cdr_ctx->encoded_metadata.length) { const size_t metadata_bytes_to_copy = MEMFAULT_MIN(buf_len, cdr_ctx->encoded_metadata.length - offset); memcpy(bufp, &cdr_ctx->encoded_metadata.data[offset], metadata_bytes_to_copy); buf_len -= metadata_bytes_to_copy; if (buf_len == 0) { return true; } offset = 0; bufp += metadata_bytes_to_copy; } else { offset -= cdr_ctx->encoded_metadata.length; } if (!cdr_ctx->active_source->read_data_cb(offset, bufp, buf_len)) { return false; } return true; } static void prv_cdr_mark_sent(void) { sMfltCdrSourceCtx *cdr_ctx = &s_memfault_cdr_source_ctx; if (cdr_ctx->active_source == NULL) { return; } cdr_ctx->active_source->mark_cdr_read_cb(); *cdr_ctx = (sMfltCdrSourceCtx){ 0 }; } bool memfault_cdr_register_source(const sMemfaultCdrSourceImpl *impl) { // it is a configuration error if all the required dependencies are not implemented! MEMFAULT_SDK_ASSERT((impl != NULL) && (impl->has_cdr_cb != NULL) && (impl->read_data_cb != NULL) && (impl->mark_cdr_read_cb != NULL)); for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_cdr_sources); i++) { if (s_cdr_sources[i] == NULL) { s_cdr_sources[i] = impl; return true; } } MEMFAULT_LOG_ERROR("Memfault Cdr Register is full, %d entries", (int)MEMFAULT_ARRAY_SIZE(s_cdr_sources)); return false; } void memfault_cdr_source_reset(void) { memset(s_cdr_sources, 0x0, sizeof(s_cdr_sources)); s_memfault_cdr_source_ctx = (sMfltCdrSourceCtx){ 0x0 }; } //! Expose a data source for use by the Memfault Packetizer const sMemfaultDataSourceImpl g_memfault_cdr_source = { .has_more_msgs_cb = prv_has_cdr, .read_msg_cb = prv_cdr_read, .mark_msg_read_cb = prv_cdr_mark_sent, }; #endif /* MEMFAULT_CDR_ENABLE */ ================================================ FILE: components/core/src/memfault_custom_data_recording_private.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! APIs intended for internal use _only_. And end user should never invoke these #ifdef __cplusplus extern "C" { #endif //! Resets Cdr for unit testing purposes. void memfault_cdr_source_reset(void); #ifdef __cplusplus } #endif ================================================ FILE: components/core/src/memfault_data_export.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief #include #include "memfault/core/compiler.h" #include "memfault/core/data_export.h" #include "memfault/core/data_packetizer.h" #include "memfault/core/debug_log.h" #include "memfault/core/sdk_assert.h" #include "memfault/util/base64.h" MEMFAULT_WEAK void memfault_data_export_base64_encoded_chunk(const char *base64_chunk) { (void)base64_chunk; MEMFAULT_LOG_INFO("%s", base64_chunk); } static void prv_memfault_data_export_chunk(void *chunk_data, size_t chunk_data_len) { MEMFAULT_SDK_ASSERT(chunk_data_len <= MEMFAULT_DATA_EXPORT_CHUNK_MAX_LEN); char base64[MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN]; memcpy(base64, MEMFAULT_DATA_EXPORT_BASE64_CHUNK_PREFIX, MEMFAULT_DATA_EXPORT_BASE64_CHUNK_PREFIX_LEN); size_t write_offset = MEMFAULT_DATA_EXPORT_BASE64_CHUNK_PREFIX_LEN; memfault_base64_encode(chunk_data, chunk_data_len, &base64[write_offset]); write_offset += MEMFAULT_BASE64_ENCODE_LEN(chunk_data_len); memcpy(&base64[write_offset], MEMFAULT_DATA_EXPORT_BASE64_CHUNK_SUFFIX, MEMFAULT_DATA_EXPORT_BASE64_CHUNK_SUFFIX_LEN); write_offset += MEMFAULT_DATA_EXPORT_BASE64_CHUNK_SUFFIX_LEN; base64[write_offset] = '\0'; memfault_data_export_base64_encoded_chunk(base64); } //! Note: We disable optimizations for this function to guarantee the symbol is //! always exposed and our GDB test script (https://mflt.io/send-chunks-via-gdb) //! can be installed to watch and post chunks every time it is called. MEMFAULT_NO_OPT void memfault_data_export_chunk(void *chunk_data, size_t chunk_data_len) { prv_memfault_data_export_chunk(chunk_data, chunk_data_len); } static bool prv_try_send_memfault_data(void) { // buffer to copy chunk data into uint8_t buf[MEMFAULT_DATA_EXPORT_CHUNK_MAX_LEN]; size_t buf_len = sizeof(buf); bool data_available = memfault_packetizer_get_chunk(buf, &buf_len); if (!data_available) { return false; // no more data to send } // send payload collected to chunks/ endpoint memfault_data_export_chunk(buf, buf_len); return true; } void memfault_data_export_dump_chunks(void) { while (prv_try_send_memfault_data()) { } } ================================================ FILE: components/core/src/memfault_data_packetizer.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/data_source_rle.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/platform/debug_log.h" #include "memfault/util/chunk_transport.h" MEMFAULT_STATIC_ASSERT(MEMFAULT_PACKETIZER_MIN_BUF_LEN == MEMFAULT_MIN_CHUNK_BUF_LEN, "Minimum packetizer payload size must match underlying transport"); // // Weak definitions which get overridden when the component that implements that data source is // included and compiled in a project // static bool prv_data_source_has_event_stub(size_t *event_size) { *event_size = 0; return false; } static bool prv_data_source_read_stub(MEMFAULT_UNUSED uint32_t offset, MEMFAULT_UNUSED void *buf, MEMFAULT_UNUSED size_t buf_len) { return false; } static void prv_data_source_mark_event_read_stub(void) { } MEMFAULT_WEAK const sMemfaultDataSourceImpl g_memfault_data_rle_source = { .has_more_msgs_cb = prv_data_source_has_event_stub, .read_msg_cb = prv_data_source_read_stub, .mark_msg_read_cb = prv_data_source_mark_event_read_stub, }; MEMFAULT_WEAK const sMemfaultDataSourceImpl g_memfault_coredump_data_source = { .has_more_msgs_cb = prv_data_source_has_event_stub, .read_msg_cb = prv_data_source_read_stub, .mark_msg_read_cb = prv_data_source_mark_event_read_stub, }; MEMFAULT_WEAK const sMemfaultDataSourceImpl g_memfault_event_data_source = { .has_more_msgs_cb = prv_data_source_has_event_stub, .read_msg_cb = prv_data_source_read_stub, .mark_msg_read_cb = prv_data_source_mark_event_read_stub, }; MEMFAULT_WEAK const sMemfaultDataSourceImpl g_memfault_log_data_source = { .has_more_msgs_cb = prv_data_source_has_event_stub, .read_msg_cb = prv_data_source_read_stub, .mark_msg_read_cb = prv_data_source_mark_event_read_stub, }; MEMFAULT_WEAK const sMemfaultDataSourceImpl g_memfault_cdr_source = { .has_more_msgs_cb = prv_data_source_has_event_stub, .read_msg_cb = prv_data_source_read_stub, .mark_msg_read_cb = prv_data_source_mark_event_read_stub, }; MEMFAULT_WEAK bool memfault_data_source_rle_encoder_set_active( MEMFAULT_UNUSED const sMemfaultDataSourceImpl *active_source) { return false; } // NOTE: These values are used by the Memfault cloud chunks API typedef enum { kMfltMessageType_None = 0, kMfltMessageType_Coredump = 1, kMfltMessageType_Event = 2, kMfltMessageType_Log = 3, kMfltMessageType_Cdr = 4, kMfltMessageType_NumTypes } eMfltMessageType; //! Make sure our externally facing types match the internal ones MEMFAULT_STATIC_ASSERT((1 << kMfltMessageType_Coredump) == kMfltDataSourceMask_Coredump, "kMfltDataSourceMask_Coredump is incorrectly defined"); MEMFAULT_STATIC_ASSERT((1 << kMfltMessageType_Event) == kMfltDataSourceMask_Event, "kMfltDataSourceMask_Event, is incorrectly defined"); MEMFAULT_STATIC_ASSERT((1 << kMfltMessageType_Log) == kMfltDataSourceMask_Log, "kMfltDataSourceMask_Log is incorrectly defined"); MEMFAULT_STATIC_ASSERT((1 << kMfltMessageType_Cdr) == kMfltDataSourceMask_Cdr, "kMfltDataSourceMask_Cdr is incorrectly defined"); MEMFAULT_STATIC_ASSERT(kMfltMessageType_NumTypes == 5, "eMfltMessageType needs to be updated"); typedef struct MemfaultDataSource { eMfltMessageType type; bool use_rle; const sMemfaultDataSourceImpl *impl; } sMemfaultDataSource; static const sMemfaultDataSource s_memfault_data_source[] = { { .type = kMfltMessageType_Coredump, .use_rle = true, .impl = &g_memfault_coredump_data_source, }, { .type = kMfltMessageType_Event, .use_rle = false, .impl = &g_memfault_event_data_source, }, { .type = kMfltMessageType_Log, .use_rle = false, .impl = &g_memfault_log_data_source, }, // NB: We may want to enable RLE in the future here (probably a lot of repeat patterns). The one // thing to keep in mind is that when the encoder is enabled, it requires a lot more short reads // to take place on the data source which can be a slow operation for flash based filesystems. { .type = kMfltMessageType_Cdr, .use_rle = false, .impl = &g_memfault_cdr_source, } }; typedef struct { size_t total_size; sMemfaultDataSource source; } sMessageMetadata; typedef struct { bool active_message; sMessageMetadata msg_metadata; sMfltChunkTransportCtx curr_msg_ctx; } sMfltTransportState; #if MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY MEMFAULT_STATIC_ASSERT( sizeof(MEMFAULT_PROJECT_KEY) > 1, "MEMFAULT_PROJECT_KEY must be a string literal of at least 1 character + null terminator"); #endif typedef MEMFAULT_PACKED_STRUCT { // The first byte of the header is the message type. Bit layout: // ┌───────────┬─┬─┐ // │0|1|2|3|4|5│6│7│ // └─────┬─────┴┬┴┬┘ // │ │ │ // │ │ └►RLE in use: // │ │ 0b0=no RLE // │ │ 0b1=RLE // │ │ // │ └──►Project Key follows: // │ 0b0=no Project Key // │ 0b1=32 byte Project Key follows header byte // │ // └─────────►eMfltMessageType // 6 bits (63 values) #define MEMFAULT_MESSAGE_HEADER_RLE_ENABLE_MASK (1u << 7) #if MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY #define MEMFAULT_MESSAGE_HEADER_PROJECT_KEY_ENABLE_MASK (1u << 6) #endif uint8_t mflt_msg_type; #if MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY uint8_t project_key[sizeof(MEMFAULT_PROJECT_KEY)]; // including null term #endif } sMfltPacketizerHdr; static sMfltTransportState s_mflt_packetizer_state; static uint32_t s_active_data_sources = kMfltDataSourceMask_All; void memfault_packetizer_set_active_sources(uint32_t mask) { memfault_packetizer_abort(); s_active_data_sources = mask; } static void prv_reset_packetizer_state(void) { s_mflt_packetizer_state = (sMfltTransportState){ .active_message = false, }; memfault_data_source_rle_encoder_set_active(NULL); } static void prv_data_source_chunk_transport_msg_reader(uint32_t offset, void *buf, size_t buf_len) { uint8_t *bufp = (uint8_t *)buf; size_t read_offset = 0; const size_t hdr_size = sizeof(sMfltPacketizerHdr); const sMessageMetadata *msg_metadata = &s_mflt_packetizer_state.msg_metadata; if (offset < hdr_size) { const uint8_t msg_type = (uint8_t)msg_metadata->source.type; sMfltPacketizerHdr hdr = { .mflt_msg_type = msg_type, }; if (msg_metadata->source.use_rle) { const uint8_t rle_enable_mask = MEMFAULT_MESSAGE_HEADER_RLE_ENABLE_MASK; hdr.mflt_msg_type |= rle_enable_mask; } #if MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY const uint8_t prj_key_enable_mask = MEMFAULT_MESSAGE_HEADER_PROJECT_KEY_ENABLE_MASK; hdr.mflt_msg_type |= prj_key_enable_mask; memcpy(hdr.project_key, MEMFAULT_PROJECT_KEY, sizeof(hdr.project_key)); #endif uint8_t *hdr_bytes = (uint8_t *)&hdr; const size_t bytes_to_copy = MEMFAULT_MIN(hdr_size - offset, buf_len); memcpy(bufp, &hdr_bytes[offset], bytes_to_copy); bufp += bytes_to_copy; buf_len -= bytes_to_copy; } else { read_offset = offset - hdr_size; } if (buf_len == 0) { // no space left after writing the header return; } const bool success = msg_metadata->source.impl->read_msg_cb(read_offset, bufp, buf_len); if (!success) { // Read failures really should never happen. We have no way of knowing if the issue is // transient or not. If we aborted the transaction and the failure was persistent, we could get // stuck trying to flush out the same data. Instead, we just continue on. We scrub the // beginning of the chunk buffer with a known pattern to make the error easier to identify. MEMFAULT_LOG_ERROR("Read at offset 0x%" PRIx32 " (%d bytes) for source type %d failed", offset, (int)buf_len, (int)msg_metadata->source.type); memset(bufp, 0xEF, MEMFAULT_MIN(16, buf_len)); } } static bool prv_get_source_with_data(size_t *total_size, sMemfaultDataSource *active_source) { for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_memfault_data_source); i++) { const sMemfaultDataSource *data_source = &s_memfault_data_source[i]; const bool disabled_source = (((1 << data_source->type) & s_active_data_sources) == 0); if (disabled_source) { // sdk user has disabled extraction of data for specified source continue; } const bool rle_enabled = data_source->use_rle && memfault_data_source_rle_encoder_set_active(data_source->impl); *active_source = (sMemfaultDataSource){ .type = data_source->type, .use_rle = rle_enabled, .impl = rle_enabled ? &g_memfault_data_rle_source : data_source->impl, }; if (active_source->impl->has_more_msgs_cb(total_size)) { return true; } } return false; } static bool prv_more_messages_to_send(sMessageMetadata *msg_metadata) { size_t total_size; sMemfaultDataSource active_source; if (!prv_get_source_with_data(&total_size, &active_source)) { return false; } if (msg_metadata != NULL) { *msg_metadata = (sMessageMetadata){ .total_size = total_size, .source = active_source, }; } return true; } static bool prv_load_next_message_to_send(bool enable_multi_packet_chunks, sMfltTransportState *state) { sMessageMetadata msg_metadata; if (!prv_more_messages_to_send(&msg_metadata)) { return false; } const size_t hdr_size = sizeof(sMfltPacketizerHdr); *state = (sMfltTransportState){ .active_message = true, .msg_metadata = msg_metadata, .curr_msg_ctx = (sMfltChunkTransportCtx){ .total_size = msg_metadata.total_size + hdr_size, .read_msg = prv_data_source_chunk_transport_msg_reader, .enable_multi_call_chunk = enable_multi_packet_chunks, }, }; memfault_chunk_transport_get_chunk_info(&s_mflt_packetizer_state.curr_msg_ctx); return true; } static void prv_mark_message_send_complete_and_cleanup(void) { // we've finished sending the data so delete it s_mflt_packetizer_state.msg_metadata.source.impl->mark_msg_read_cb(); prv_reset_packetizer_state(); } void memfault_packetizer_abort(void) { prv_reset_packetizer_state(); } eMemfaultPacketizerStatus memfault_packetizer_get_next(void *buf, size_t *buf_len) { if (buf == NULL || buf_len == NULL) { // We may want to consider just asserting on these. For now, just log an error // and return NoMoreData MEMFAULT_LOG_ERROR("%s: NULL input arguments", __func__); return kMemfaultPacketizerStatus_NoMoreData; } if (!s_mflt_packetizer_state.active_message) { // To load a new message, memfault_packetizer_begin() must first be called return kMemfaultPacketizerStatus_NoMoreData; } size_t original_size = *buf_len; (void)original_size; // to silence compiler warning when logs are disabled bool md = memfault_chunk_transport_get_next_chunk(&s_mflt_packetizer_state.curr_msg_ctx, buf, buf_len); if (*buf_len == 0) { MEMFAULT_LOG_ERROR("Buffer of %d bytes too small to packetize data", (int)original_size); return kMemfaultPacketizerStatus_NoMoreData; } if (!md) { // the entire message has been chunked up, perform clean up prv_mark_message_send_complete_and_cleanup(); // we have reached the end of a message return kMemfaultPacketizerStatus_EndOfChunk; } return s_mflt_packetizer_state.curr_msg_ctx.enable_multi_call_chunk ? kMemfaultPacketizerStatus_MoreDataForChunk : kMemfaultPacketizerStatus_EndOfChunk; } bool memfault_packetizer_begin(const sPacketizerConfig *cfg, sPacketizerMetadata *metadata_out) { if ((cfg == NULL) || (metadata_out == NULL)) { MEMFAULT_LOG_ERROR("%s: NULL input arguments", __func__); return false; } if (!s_mflt_packetizer_state.active_message) { if (!prv_load_next_message_to_send(cfg->enable_multi_packet_chunk, &s_mflt_packetizer_state)) { // no new messages to send *metadata_out = (sPacketizerMetadata){ 0 }; return false; } } const bool send_in_progress = s_mflt_packetizer_state.curr_msg_ctx.read_offset != 0; *metadata_out = (sPacketizerMetadata){ .single_chunk_message_length = s_mflt_packetizer_state.curr_msg_ctx.single_chunk_message_length, .send_in_progress = send_in_progress, }; return true; } bool memfault_packetizer_data_available(void) { if (s_mflt_packetizer_state.active_message) { return true; } return prv_more_messages_to_send(NULL); } bool memfault_packetizer_get_chunk(void *buf, size_t *buf_len) { const sPacketizerConfig cfg = { // By setting this to false, every call to "memfault_packetizer_get_next()" will return one // "chunk" that you must send from the device .enable_multi_packet_chunk = false, }; sPacketizerMetadata metadata; bool data_available = memfault_packetizer_begin(&cfg, &metadata); if (!data_available) { // there are no more chunks to send return false; } eMemfaultPacketizerStatus packetizer_status = memfault_packetizer_get_next(buf, buf_len); // We know data is available from the memfault_packetizer_begin() call above // so anything but kMemfaultPacketizerStatus_EndOfChunk is unexpected if (packetizer_status != kMemfaultPacketizerStatus_EndOfChunk) { MEMFAULT_LOG_ERROR("Unexpected packetizer status: %d", (int)packetizer_status); return false; } return true; } ================================================ FILE: components/core/src/memfault_data_source_rle.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! See header for more details #include "memfault/config.h" #if MEMFAULT_DATA_SOURCE_RLE_ENABLED #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/data_source_rle.h" #include "memfault/core/math.h" #include "memfault/util/rle.h" //! Helper function that computes the RLE length of the message being processed //! //! @note Expects to be fed all the bytes from the backing message sequentially //! @return true when the check has completed and calling memfault_data_source_rle_has_more_msgs() //! will result in no more backing flash reads, false otherwise static bool prv_data_source_rle_has_more_msgs_prepare(const void *data, size_t data_len); //! @return the offset the next call to memfault_data_source_rle_read_msg() will start reading from static uint32_t prv_data_source_rle_get_backing_read_offset(void); //! Helper function which finds the next RLE sequence that will be written out //! //! @note Expects to be fed all the bytes from the backing message sequentially starting from //! prv_data_source_rle_get_backing_read_offset() //! @return true when enough data has been processed to figure out what needs to be written static bool prv_data_source_rle_read_msg_prepare(const void *data, size_t data_len); //! Helper function that builds buffer returned from memfault_data_source_rle_read_msg() //! //! @return 0 the number of bytes populated in buf static uint32_t prv_data_source_rle_build_msg_incremental(uint8_t *buf, size_t buf_len); static const sMemfaultDataSourceImpl *s_active_data_source = NULL; typedef enum { kMemfaultDataSourceRleState_Inactive = 0, // Searching for the next sequence to encode kMemfaultDataSourceRleState_FindingSeqLength, // A sequence to encode has been found and is being written out // via calls to memfault_data_source_rle_read_msg() kMemfaultDataSourceRleState_WritingSequence, } eMemfaultDataSourceRleState; typedef struct { eMemfaultDataSourceRleState state; uint8_t temp_buf[128]; // The number of bytes written within the current RLE sequence uint32_t write_offset; // The total number of bytes that have been processed from the backing data source uint32_t bytes_processed; // The current number of encoded bytes which have been written uint32_t curr_encoded_len; } sMemfaultDataSourceRleEncodeCtx; typedef struct { sMemfaultDataSourceImpl *data_source; size_t original_size; size_t total_rle_size; sMemfaultRleCtx rle_ctx; sMemfaultDataSourceRleEncodeCtx encode_ctx; } sMemfaultDataSourceRleState; static sMemfaultDataSourceRleState s_ds_rle_state; bool memfault_data_source_rle_encoder_set_active(const sMemfaultDataSourceImpl *source) { if (source == s_active_data_source) { return true; } s_ds_rle_state = (sMemfaultDataSourceRleState){ 0 }; s_active_data_source = source; return true; } static bool prv_data_source_rle_has_more_msgs_prepare(const void *data, size_t data_len) { const uint8_t *buf = (const uint8_t *)data; size_t bytes_encoded = 0; while (bytes_encoded != data_len) { bytes_encoded += memfault_rle_encode(&s_ds_rle_state.rle_ctx, &buf[bytes_encoded], data_len - bytes_encoded); } const bool all_bytes_processed = s_ds_rle_state.rle_ctx.curr_offset == s_ds_rle_state.original_size; if (!all_bytes_processed) { return false; } memfault_rle_encode_finalize(&s_ds_rle_state.rle_ctx); sMemfaultDataSourceRleEncodeCtx *encode_ctx = &s_ds_rle_state.encode_ctx; encode_ctx->state = kMemfaultDataSourceRleState_FindingSeqLength; return true; } static uint32_t prv_data_source_rle_get_backing_read_offset(void) { sMemfaultDataSourceRleEncodeCtx *encode_ctx = &s_ds_rle_state.encode_ctx; if (encode_ctx->state == kMemfaultDataSourceRleState_FindingSeqLength) { return encode_ctx->bytes_processed; } // else (encode_ctx->state == kMemfaultDataSourceRleState_WritingSequence) sMemfaultRleWriteInfo *write_info = &s_ds_rle_state.rle_ctx.write_info; if (encode_ctx->write_offset < write_info->header_len) { // Note: this should never happen since the read offset should only be looked up once the // header has been written but let's check just in case return write_info->write_start_offset; } const size_t data_bytes_written = encode_ctx->write_offset - write_info->header_len; return write_info->write_start_offset + data_bytes_written; } static bool prv_data_source_rle_read_msg_prepare(const void *data, size_t data_len) { sMemfaultRleCtx *rle_ctx = &s_ds_rle_state.rle_ctx; sMemfaultDataSourceRleEncodeCtx *encode_ctx = &s_ds_rle_state.encode_ctx; const size_t bytes_encoded = memfault_rle_encode(rle_ctx, data, data_len); if (data_len == 0) { memfault_rle_encode_finalize(rle_ctx); } encode_ctx->bytes_processed += bytes_encoded; sMemfaultRleWriteInfo *write_info = &s_ds_rle_state.rle_ctx.write_info; if (write_info->available) { encode_ctx->state = kMemfaultDataSourceRleState_WritingSequence; } return write_info->available; } static uint32_t prv_data_source_rle_build_msg_incremental(uint8_t *buf, size_t buf_len) { sMemfaultRleWriteInfo *write_info = &s_ds_rle_state.rle_ctx.write_info; if (!write_info->available) { return 0; } sMemfaultDataSourceRleEncodeCtx *encode_ctx = &s_ds_rle_state.encode_ctx; uint32_t write_offset = encode_ctx->write_offset; // write header size_t header_bytes_to_write = 0; if (write_offset < write_info->header_len) { header_bytes_to_write = MEMFAULT_MIN(buf_len, write_info->header_len - write_offset); memcpy(buf, &write_info->header[write_offset], header_bytes_to_write); encode_ctx->write_offset += header_bytes_to_write; buf_len -= header_bytes_to_write; if (buf_len == 0) { encode_ctx->curr_encoded_len += header_bytes_to_write; return header_bytes_to_write; } } const size_t total_write_len = write_info->header_len + write_info->write_len; size_t data_to_write = MEMFAULT_MIN(buf_len, total_write_len - encode_ctx->write_offset); const uint32_t start_offset = prv_data_source_rle_get_backing_read_offset(); s_active_data_source->read_msg_cb(start_offset, &buf[header_bytes_to_write], data_to_write); encode_ctx->write_offset += data_to_write; const size_t bytes_written = header_bytes_to_write + data_to_write; if (encode_ctx->write_offset == total_write_len) { encode_ctx->write_offset = 0; encode_ctx->state = kMemfaultDataSourceRleState_FindingSeqLength; *write_info = (sMemfaultRleWriteInfo){ 0 }; } encode_ctx->curr_encoded_len += bytes_written; return bytes_written; } static bool prv_data_source_rle_fill_msg(uint8_t **bufpp, size_t *buf_len) { const size_t bytes_written = prv_data_source_rle_build_msg_incremental(*bufpp, *buf_len); *buf_len -= bytes_written; *bufpp += bytes_written; return *buf_len == 0; } static bool prv_data_source_rle_read(uint32_t offset, void *buf, size_t buf_len) { sMemfaultDataSourceRleEncodeCtx *encode_ctx = &s_ds_rle_state.encode_ctx; if (offset != encode_ctx->curr_encoded_len) { return false; // Read happened from an unexpected offset } // if there is already a write pending, flush that data first uint8_t *bufp = (uint8_t *)buf; bool buf_full = prv_data_source_rle_fill_msg(&bufp, &buf_len); if (buf_full) { return true; } while (encode_ctx->bytes_processed != s_ds_rle_state.original_size) { uint8_t *working_buf = &encode_ctx->temp_buf[0]; const size_t working_buf_size = sizeof(encode_ctx->temp_buf); const size_t bytes_remaining = s_ds_rle_state.original_size - encode_ctx->bytes_processed; const size_t bytes_read = MEMFAULT_MIN(bytes_remaining, working_buf_size); const size_t read_offset = prv_data_source_rle_get_backing_read_offset(); s_active_data_source->read_msg_cb(read_offset, working_buf, bytes_read); prv_data_source_rle_read_msg_prepare(working_buf, bytes_read); // do we know what to write for the next block yet? buf_full = prv_data_source_rle_fill_msg(&bufp, &buf_len); if (buf_full) { return true; } } prv_data_source_rle_read_msg_prepare(NULL, 0); prv_data_source_rle_fill_msg(&bufp, &buf_len); return true; } //! Do one read pass over the data source currently saved in backing //! storage to compute what the total RLE size of the data we will be encoding static size_t prv_compute_rle_size(void) { sMemfaultRleCtx *rle_ctx = &s_ds_rle_state.rle_ctx; size_t bytes_processed = 0; while (bytes_processed != s_ds_rle_state.original_size) { sMemfaultDataSourceRleEncodeCtx *encode_ctx = &s_ds_rle_state.encode_ctx; uint8_t *working_buf = &encode_ctx->temp_buf[0]; const size_t working_buf_size = sizeof(encode_ctx->temp_buf); const size_t bytes_left = s_ds_rle_state.original_size - bytes_processed; const size_t bytes_to_read = MEMFAULT_MIN(bytes_left, working_buf_size); s_active_data_source->read_msg_cb(bytes_processed, working_buf, bytes_to_read); prv_data_source_rle_has_more_msgs_prepare(working_buf, bytes_to_read); bytes_processed += bytes_to_read; } s_ds_rle_state.total_rle_size = rle_ctx->total_rle_size; *rle_ctx = (sMemfaultRleCtx){ 0 }; return s_ds_rle_state.total_rle_size; } MEMFAULT_WEAK bool memfault_data_source_rle_read_msg(uint32_t offset, void *buf, size_t buf_len) { return prv_data_source_rle_read(offset, buf, buf_len); } bool memfault_data_source_rle_has_more_msgs(size_t *total_size_out) { // Check to see if the data source has any messages queued up const bool has_msgs = s_active_data_source->has_more_msgs_cb(&s_ds_rle_state.original_size); if (!has_msgs) { return has_msgs; } // we have already computed what the RLE size will be for the data // saved in storage, no need to do it again if (s_ds_rle_state.total_rle_size != 0) { *total_size_out = s_ds_rle_state.total_rle_size; return true; } *total_size_out = prv_compute_rle_size(); return true; } void memfault_data_source_rle_mark_msg_read(void) { s_ds_rle_state = (sMemfaultDataSourceRleState){ 0 }; s_active_data_source->mark_msg_read_cb(); } //! Expose a data source for use by the Memfault Packetizer const sMemfaultDataSourceImpl g_memfault_data_rle_source = { .has_more_msgs_cb = memfault_data_source_rle_has_more_msgs, .read_msg_cb = memfault_data_source_rle_read_msg, .mark_msg_read_cb = memfault_data_source_rle_mark_msg_read, }; #endif /* MEMFAULT_DATA_SOURCE_RLE_ENABLED */ ================================================ FILE: components/core/src/memfault_event_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A RAM-backed storage API for serialized events. This is where events (such as heartbeats and //! reset trace events) get stored as they wait to be chunked up and sent out over the transport. #include #include #include #include #include "memfault/config.h" #include "memfault/core/batched_events.h" #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/debug_log.h" #include "memfault/core/event_storage.h" #include "memfault/core/event_storage_implementation.h" #include "memfault/core/math.h" #include "memfault/core/platform/nonvolatile_event_storage.h" #include "memfault/core/platform/overrides.h" #include "memfault/core/platform/system_time.h" #include "memfault/core/sdk_assert.h" #include "memfault/util/circular_buffer.h" // // Routines which can optionally be implemented. // For more details see: // memfault/core/platform/system_time.h // memfault/core/platform/overrides.h // memfault/core/platform/event.h // #if MEMFAULT_EVENT_STORAGE_STUB_SYSTEM_TIME MEMFAULT_WEAK bool memfault_platform_time_get_current(MEMFAULT_UNUSED sMemfaultCurrentTime *time) { return false; } #endif MEMFAULT_WEAK void memfault_lock(void) { } MEMFAULT_WEAK void memfault_unlock(void) { } #if MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED MEMFAULT_WEAK void memfault_event_storage_request_persist_callback( MEMFAULT_UNUSED const sMemfaultEventStoragePersistCbStatus *status) { } static bool prv_nonvolatile_event_storage_enabled(void) { return false; } MEMFAULT_WEAK const sMemfaultNonVolatileEventStorageImpl g_memfault_platform_nv_event_storage_impl = { .enabled = prv_nonvolatile_event_storage_enabled, }; #endif typedef struct { bool write_in_progress; size_t bytes_written; } sMemfaultEventStorageWriteState; typedef struct { size_t active_event_read_size; size_t num_events; sMemfaultBatchedEventsHeader event_header; } sMemfaultEventStorageReadState; #define MEMFAULT_EVENT_STORAGE_WRITE_IN_PROGRESS 0xffff typedef MEMFAULT_PACKED_STRUCT { uint16_t total_size; } sMemfaultEventStorageHeader; typedef struct { sMfltCircularBuffer buffer; sMemfaultEventStorageWriteState write_state; sMemfaultEventStorageReadState read_state; } sMfltEventStorageContext; static sMfltEventStorageContext s_event_storage; #if MEMFAULT_EVENT_STORAGE_RESTORE_STATE MEMFAULT_STATIC_ASSERT(sizeof(s_event_storage) == MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES, "Update MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES to match s_event_storage."); sMfltEventStorageSaveState memfault_event_storage_get_state(void) { return (sMfltEventStorageSaveState){ .context = (void *)&s_event_storage, .context_len = sizeof(s_event_storage), .storage = s_event_storage.buffer.storage, .storage_len = s_event_storage.buffer.total_space, }; } #endif // MEMFAULT_EVENT_STORAGE_RESTORE_STATE #if MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED static void prv_invoke_request_persist_callback(void) { sMemfaultEventStoragePersistCbStatus status; memfault_lock(); { status = (sMemfaultEventStoragePersistCbStatus){ .volatile_storage = { .bytes_used = memfault_circular_buffer_get_read_size(&s_event_storage.buffer), .bytes_free = memfault_circular_buffer_get_write_size(&s_event_storage.buffer), }, }; } memfault_unlock(); memfault_event_storage_request_persist_callback(&status); } #endif static size_t prv_get_total_event_size(sMemfaultEventStorageReadState *state) { if (state->num_events == 0) { return 0; } const size_t hdr_overhead_bytes = state->num_events * sizeof(sMemfaultEventStorageHeader); return (state->active_event_read_size + state->event_header.length) - hdr_overhead_bytes; } //! Walk the ram-backed event storage and determine data to read //! //! @return true if computation was successful, false otherwise static void prv_compute_read_state(sMemfaultEventStorageReadState *state) { *state = (sMemfaultEventStorageReadState){ 0 }; while (1) { sMemfaultEventStorageHeader hdr = { 0 }; const bool success = memfault_circular_buffer_read( &s_event_storage.buffer, state->active_event_read_size, &hdr, sizeof(hdr)); if (!success || hdr.total_size == MEMFAULT_EVENT_STORAGE_WRITE_IN_PROGRESS) { break; } state->num_events++; state->active_event_read_size += hdr.total_size; #if (MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED == 0) // if batching is disabled, only one event will be read at a time break; #else if ((state->num_events > 1) && (prv_get_total_event_size(state) > MEMFAULT_EVENT_STORAGE_READ_BATCHING_MAX_BYTES)) { // more bytes than desired, so don't count this event state->num_events--; state->active_event_read_size -= hdr.total_size; break; } #endif /* MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED */ } #if (MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED != 0) memfault_batched_events_build_header(state->num_events, &state->event_header); #endif } static bool prv_has_data_ram(size_t *total_size) { // Check to see if a read is already in progress and return that size if true size_t curr_read_size; memfault_lock(); { curr_read_size = prv_get_total_event_size(&s_event_storage.read_state); } memfault_unlock(); if (curr_read_size != 0) { *total_size = curr_read_size; return ((*total_size) != 0); } // see if there are any events to read sMemfaultEventStorageReadState read_state; memfault_lock(); { prv_compute_read_state(&read_state); s_event_storage.read_state = read_state; } memfault_unlock(); *total_size = prv_get_total_event_size(&s_event_storage.read_state); return ((*total_size) != 0); } static bool prv_event_storage_read_ram(uint32_t offset, void *buf, size_t buf_len) { const size_t total_event_size = prv_get_total_event_size(&s_event_storage.read_state); if ((offset + buf_len) > total_event_size) { return false; } // header_length != 0 when we encode multiple events in a single read so // first check to see if we need to copy any of that uint8_t *bufp = (uint8_t *)buf; if (offset < s_event_storage.read_state.event_header.length) { const size_t bytes_to_copy = MEMFAULT_MIN(buf_len, s_event_storage.read_state.event_header.length - offset); memcpy(bufp, &s_event_storage.read_state.event_header.data[offset], bytes_to_copy); buf_len -= bytes_to_copy; offset = 0; bufp += bytes_to_copy; } else { offset -= s_event_storage.read_state.event_header.length; } uint32_t curr_offset = 0; uint32_t read_offset = 0; while (buf_len > 0) { sMemfaultEventStorageHeader hdr = { 0 }; const bool success = memfault_circular_buffer_read(&s_event_storage.buffer, read_offset, &hdr, sizeof(hdr)); if (!success) { // not possible to get here unless there is corruption return false; } read_offset += sizeof(hdr); const size_t event_size = hdr.total_size - sizeof(hdr); if ((curr_offset + event_size) < offset) { // we haven't reached the offset we were trying to read from curr_offset += event_size; read_offset += event_size; continue; } // offset within the event to start reading at const size_t evt_start_offset = offset - curr_offset; const size_t bytes_to_read = MEMFAULT_MIN(event_size - evt_start_offset, buf_len); if (!memfault_circular_buffer_read(&s_event_storage.buffer, read_offset + evt_start_offset, bufp, bytes_to_read)) { // not possible to get here unless there is corruption return false; } bufp += bytes_to_read; curr_offset += event_size; read_offset += event_size; buf_len -= bytes_to_read; offset += bytes_to_read; } return true; } static void prv_event_storage_mark_event_read_ram(void) { if (s_event_storage.read_state.active_event_read_size == 0) { // no active event to clear return; } memfault_lock(); { memfault_circular_buffer_consume(&s_event_storage.buffer, s_event_storage.read_state.active_event_read_size); s_event_storage.read_state = (sMemfaultEventStorageReadState){ 0 }; } memfault_unlock(); } // "begin" to write event data & return the space available static size_t prv_event_storage_storage_begin_write(void) { if (s_event_storage.write_state.write_in_progress) { return 0; } const sMemfaultEventStorageHeader hdr = { .total_size = MEMFAULT_EVENT_STORAGE_WRITE_IN_PROGRESS, }; bool success; memfault_lock(); { success = memfault_circular_buffer_write(&s_event_storage.buffer, &hdr, sizeof(hdr)); } memfault_unlock(); if (!success) { return 0; } s_event_storage.write_state = (sMemfaultEventStorageWriteState){ .write_in_progress = true, .bytes_written = sizeof(hdr), }; return memfault_circular_buffer_get_write_size(&s_event_storage.buffer); } static bool prv_event_storage_storage_append_data(const void *bytes, size_t num_bytes) { bool success; memfault_lock(); { success = memfault_circular_buffer_write(&s_event_storage.buffer, bytes, num_bytes); } memfault_unlock(); if (success) { s_event_storage.write_state.bytes_written += num_bytes; } return success; } static void prv_event_storage_storage_finish_write(bool rollback) { if (!s_event_storage.write_state.write_in_progress) { return; } memfault_lock(); { if (rollback) { memfault_circular_buffer_consume_from_end(&s_event_storage.buffer, s_event_storage.write_state.bytes_written); } else { const sMemfaultEventStorageHeader hdr = { .total_size = (uint16_t)s_event_storage.write_state.bytes_written, }; memfault_circular_buffer_write_at_offset( &s_event_storage.buffer, s_event_storage.write_state.bytes_written, &hdr, sizeof(hdr)); } } memfault_unlock(); // reset the write state s_event_storage.write_state = (sMemfaultEventStorageWriteState){ 0 }; #if MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED if (!rollback) { prv_invoke_request_persist_callback(); } #endif } static size_t prv_get_size_cb(void) { return memfault_circular_buffer_get_read_size(&s_event_storage.buffer) + memfault_circular_buffer_get_write_size(&s_event_storage.buffer); } const sMemfaultEventStorageImpl *memfault_events_storage_boot(void *buf, size_t buf_len) { #if MEMFAULT_EVENT_STORAGE_RESTORE_STATE sMfltEventStorageSaveState state = { 0 }; bool retval = memfault_event_storage_restore_state(&state); if (retval && (sizeof(s_event_storage) == state.context_len) && (buf_len == state.storage_len)) { // restore the state s_event_storage = *(sMfltEventStorageContext *)state.context; // restore the storage buffer s_event_storage.buffer.storage = (uint8_t *)buf; memmove(s_event_storage.buffer.storage, state.storage, state.storage_len); s_event_storage.buffer.total_space = state.storage_len; } // if the user didn't restore the state or there was a size mismatch, we // need to initialize it else { if (retval) { MEMFAULT_LOG_ERROR("Event storage restore size mismatch: %d != %d or %d != %d", (int)sizeof(s_event_storage), (int)state.context_len, (int)buf_len, (int)state.storage_len); } #else { #endif // MEMFAULT_EVENT_STORAGE_RESTORE_STATE memfault_circular_buffer_init(&s_event_storage.buffer, buf, buf_len); s_event_storage.write_state = (sMemfaultEventStorageWriteState){ 0 }; s_event_storage.read_state = (sMemfaultEventStorageReadState){ 0 }; } static const sMemfaultEventStorageImpl s_event_storage_impl = { .begin_write_cb = &prv_event_storage_storage_begin_write, .append_data_cb = &prv_event_storage_storage_append_data, .finish_write_cb = &prv_event_storage_storage_finish_write, .get_storage_size_cb = &prv_get_size_cb, }; return &s_event_storage_impl; } #if MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED static bool prv_save_event_to_persistent_storage(void) { size_t total_size; if (!prv_has_data_ram(&total_size)) { return false; } MEMFAULT_SDK_ASSERT(g_memfault_platform_nv_event_storage_impl.write != NULL); const bool success = g_memfault_platform_nv_event_storage_impl.write(prv_event_storage_read_ram, total_size); if (success) { prv_event_storage_mark_event_read_ram(); } return success; } static bool prv_nv_event_storage_enabled(void) { static bool s_nv_event_storage_enabled = false; MEMFAULT_SDK_ASSERT(g_memfault_platform_nv_event_storage_impl.enabled != NULL); const bool enabled = g_memfault_platform_nv_event_storage_impl.enabled(); if (s_nv_event_storage_enabled && !enabled) { // This shouldn't happen and is indicative of a failure in nv storage. Let's reset the read // state in case we were in the middle of a read() trying to copy data into nv storage. s_event_storage.read_state = (sMemfaultEventStorageReadState){ 0 }; } if (enabled) { // if nonvolatile storage is enabled, it is a configuration error if all the // required dependencies are not implemented! MEMFAULT_SDK_ASSERT((g_memfault_platform_nv_event_storage_impl.has_event != NULL) && (g_memfault_platform_nv_event_storage_impl.read != NULL) && (g_memfault_platform_nv_event_storage_impl.consume != NULL) && (g_memfault_platform_nv_event_storage_impl.write != NULL)); } s_nv_event_storage_enabled = enabled; return s_nv_event_storage_enabled; } int memfault_event_storage_persist(void) { if (!prv_nv_event_storage_enabled()) { return 0; } int events_saved = 0; while (prv_save_event_to_persistent_storage()) { events_saved++; } return events_saved; } static void prv_nv_event_storage_mark_read_cb(void) { g_memfault_platform_nv_event_storage_impl.consume(); size_t total_size; if (!prv_has_data_ram(&total_size)) { return; } prv_invoke_request_persist_callback(); } #endif /* MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED */ static const sMemfaultDataSourceImpl *prv_get_active_event_storage_source(void) { static const sMemfaultDataSourceImpl s_memfault_ram_event_storage = { .has_more_msgs_cb = prv_has_data_ram, .read_msg_cb = prv_event_storage_read_ram, .mark_msg_read_cb = prv_event_storage_mark_event_read_ram, }; #if MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED static sMemfaultDataSourceImpl s_memfault_nv_event_storage = { 0 }; s_memfault_nv_event_storage = (sMemfaultDataSourceImpl){ .has_more_msgs_cb = g_memfault_platform_nv_event_storage_impl.has_event, .read_msg_cb = g_memfault_platform_nv_event_storage_impl.read, .mark_msg_read_cb = prv_nv_event_storage_mark_read_cb, }; return prv_nv_event_storage_enabled() ? &s_memfault_nv_event_storage : &s_memfault_ram_event_storage; #else return &s_memfault_ram_event_storage; #endif /* MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED */ } static bool prv_has_event(size_t *event_size) { const sMemfaultDataSourceImpl *impl = prv_get_active_event_storage_source(); return impl->has_more_msgs_cb(event_size); } static bool prv_event_storage_read(uint32_t offset, void *buf, size_t buf_len) { const sMemfaultDataSourceImpl *impl = prv_get_active_event_storage_source(); return impl->read_msg_cb(offset, buf, buf_len); } static void prv_event_storage_mark_event_read(void) { const sMemfaultDataSourceImpl *impl = prv_get_active_event_storage_source(); impl->mark_msg_read_cb(); } //! Expose a data source for use by the Memfault Packetizer const sMemfaultDataSourceImpl g_memfault_event_data_source = { .has_more_msgs_cb = prv_has_event, .read_msg_cb = prv_event_storage_read, .mark_msg_read_cb = prv_event_storage_mark_event_read, }; // These getters provide the information that user doesn't have. The user knows the total size // of the event storage because they supply it but they need help to get the free/used stats. size_t memfault_event_storage_bytes_used(void) { size_t bytes_used; memfault_lock(); { bytes_used = memfault_circular_buffer_get_read_size(&s_event_storage.buffer); } memfault_unlock(); return bytes_used; } size_t memfault_event_storage_bytes_free(void) { size_t bytes_free; memfault_lock(); { bytes_free = memfault_circular_buffer_get_write_size(&s_event_storage.buffer); } memfault_unlock(); return bytes_free; } bool memfault_event_storage_booted(void) { // The event storage component does not have any internal state we can check to see if // memfault_events_storage_boot was called. As an indirect method, we can check the value of the // circular buffer used internally. If the buffer structure has been initialized (storage pointer // is not null), then we can assume that memfault_events_storage_boot was called. // This check breaks the circular buffer API contract but is intentional for simplicity/efficiency return (s_event_storage.buffer.storage != NULL); } // Resets the internal structures of the event storage component. This function is to only be used // in event storage unit tests. Declaration here to silence -Wmissing-prototypes void memfault_event_storage_reset(void); void memfault_event_storage_reset(void) { // Delete the circular buffer and read/write state // NB: storage implementation is const so cannot be reset memset(&s_event_storage.buffer, 0, sizeof(s_event_storage.buffer)); s_event_storage.write_state = (sMemfaultEventStorageWriteState){ 0 }; s_event_storage.read_state = (sMemfaultEventStorageReadState){ 0 }; } ================================================ FILE: components/core/src/memfault_heap_stats.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Simple heap allocation tracking utility. Intended to shim into a system's //! malloc/free implementation to track last allocations with callsite //! information. #include #include #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/heap_stats.h" #include "memfault/core/heap_stats_impl.h" #include "memfault/core/math.h" #include "memfault/core/platform/debug_log.h" #include "memfault/core/platform/overrides.h" #include "memfault/core/sdk_assert.h" #define MEMFAULT_HEAP_STATS_VERSION 2 MEMFAULT_STATIC_ASSERT(MEMFAULT_HEAP_STATS_MAX_COUNT < MEMFAULT_HEAP_STATS_LIST_END, "Number of entries in heap stats exceeds limits"); // By default, tally in use blocks and max in use blocks in the instrumentation // functions. Disable this to perform manual tallying. #if !defined(MEMFAULT_IN_USE_BLOCK_COUNT_AUTOMATIC) #define MEMFAULT_IN_USE_BLOCK_COUNT_AUTOMATIC 1 #endif sMfltHeapStats g_memfault_heap_stats = { .version = MEMFAULT_HEAP_STATS_VERSION, .stats_pool_head = MEMFAULT_HEAP_STATS_LIST_END, }; sMfltHeapStatEntry g_memfault_heap_stats_pool[MEMFAULT_HEAP_STATS_MAX_COUNT]; static void prv_heap_stats_lock(void) { #if MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE memfault_lock(); #endif } static void prv_heap_stats_unlock(void) { #if MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE memfault_unlock(); #endif } void memfault_heap_stats_reset(void) { prv_heap_stats_lock(); g_memfault_heap_stats = (sMfltHeapStats){ .version = MEMFAULT_HEAP_STATS_VERSION, .stats_pool_head = MEMFAULT_HEAP_STATS_LIST_END, }; memset(g_memfault_heap_stats_pool, 0, sizeof(g_memfault_heap_stats_pool)); prv_heap_stats_unlock(); } bool memfault_heap_stats_empty(void) { // if the first entry has a zero size, we know there was no entry ever // populated return g_memfault_heap_stats_pool[0].info.size == 0; } //! Get the previous entry index of the entry index to be found //! //! Iterates through the entry array checking if entry is in use and if the //! next entry index matches the search index. If the previous entry cannot be found, //! MEMFAULT_HEAP_STATS_LIST_END is returned static uint16_t prv_get_previous_entry(uint16_t search_entry_index) { uint16_t index = MEMFAULT_HEAP_STATS_LIST_END; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { sMfltHeapStatEntry *entry = &g_memfault_heap_stats_pool[i]; if (entry->info.in_use && entry->info.next_entry_index == search_entry_index) { index = (uint16_t)i; break; } } return index; } //! Return the next entry index to write new data to //! //! First searches for never-used entries, then unused (used + freed) entries. //! If none are found then traverses list to oldest (last) entry. static uint16_t prv_get_new_entry_index(void) { sMfltHeapStatEntry *entry; uint16_t unused_index = MEMFAULT_HEAP_STATS_LIST_END; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { entry = &g_memfault_heap_stats_pool[i]; if (!entry->info.in_use) { if (entry->info.size == 0) { return (uint16_t)i; // favor never used entries } // save the first inactive entry found if (unused_index == MEMFAULT_HEAP_STATS_LIST_END) { unused_index = (uint16_t)i; } } } if (unused_index != MEMFAULT_HEAP_STATS_LIST_END) { return unused_index; } // No unused entry found, return the oldest (entry with next index of // MEMFAULT_HEAP_STATS_LIST_END) return prv_get_previous_entry(MEMFAULT_HEAP_STATS_LIST_END); } void memfault_heap_stats_increment_in_use_block_count(void) { g_memfault_heap_stats.in_use_block_count++; if (g_memfault_heap_stats.in_use_block_count > g_memfault_heap_stats.max_in_use_block_count) { g_memfault_heap_stats.max_in_use_block_count = g_memfault_heap_stats.in_use_block_count; } } void memfault_heap_stats_decrement_in_use_block_count(void) { g_memfault_heap_stats.in_use_block_count--; } void memfault_heap_stats_malloc(const void *lr, const void *ptr, size_t size) { prv_heap_stats_lock(); if (ptr) { #if MEMFAULT_IN_USE_BLOCK_COUNT_AUTOMATIC memfault_heap_stats_increment_in_use_block_count(); #endif uint16_t new_entry_index = prv_get_new_entry_index(); // Ensure a valid entry index is returned. An invalid index can indicate a concurrency // misconfiguration. MEMFAULT_HEAP_STATS_MALLOC/FREE must prevent concurrent access via // memfault_lock/unlock or other means MEMFAULT_SDK_ASSERT(new_entry_index != MEMFAULT_HEAP_STATS_LIST_END); sMfltHeapStatEntry *new_entry = &g_memfault_heap_stats_pool[new_entry_index]; *new_entry = (sMfltHeapStatEntry){ .lr = lr, .ptr = ptr, .info = { .size = size & (~(1u << 31)), .in_use = 1u, }, }; // Append new entry to head of the list new_entry->info.next_entry_index = g_memfault_heap_stats.stats_pool_head; g_memfault_heap_stats.stats_pool_head = new_entry_index; // Find next oldest entry (points to new entry) // Update next entry index to MEMFAULT_HEAP_STATS_LIST_END uint16_t next_oldest_entry_index = prv_get_previous_entry(new_entry_index); if (next_oldest_entry_index != MEMFAULT_HEAP_STATS_LIST_END) { g_memfault_heap_stats_pool[next_oldest_entry_index].info.next_entry_index = MEMFAULT_HEAP_STATS_LIST_END; } } prv_heap_stats_unlock(); } void memfault_heap_stats_free(const void *ptr) { prv_heap_stats_lock(); if (ptr) { #if MEMFAULT_IN_USE_BLOCK_COUNT_AUTOMATIC memfault_heap_stats_decrement_in_use_block_count(); #endif // if the pointer exists in the tracked stats, mark it as freed for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { sMfltHeapStatEntry *entry = &g_memfault_heap_stats_pool[i]; if ((entry->ptr == ptr) && entry->info.in_use) { entry->info.in_use = 0; // Find the entry previous to the removed entry uint16_t previous_entry_index = prv_get_previous_entry((uint16_t)i); // If list head removed, set next entry as new list head if (g_memfault_heap_stats.stats_pool_head == i) { g_memfault_heap_stats.stats_pool_head = entry->info.next_entry_index; } // If there's a valid previous entry, set its next index to removed entry's if (previous_entry_index != MEMFAULT_HEAP_STATS_LIST_END) { g_memfault_heap_stats_pool[previous_entry_index].info.next_entry_index = entry->info.next_entry_index; } entry->info.next_entry_index = MEMFAULT_HEAP_STATS_LIST_END; break; } } } prv_heap_stats_unlock(); } ================================================ FILE: components/core/src/memfault_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A simple RAM backed logging storage implementation. When a device crashes and the memory region //! is collected using the panics component, the logs will be decoded and displayed in the Memfault //! cloud UI. #include #include #include #include #include "memfault/config.h" #include "memfault/core/compact_log_serializer.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/log.h" #include "memfault/core/log_impl.h" #include "memfault/core/math.h" #include "memfault/core/platform/debug_log.h" #include "memfault/core/platform/overrides.h" #include "memfault/core/platform/system_time.h" #include "memfault/core/sdk_assert.h" #include "memfault/util/base64.h" #include "memfault/util/circular_buffer.h" #include "memfault/util/crc16.h" #include "memfault_log_private.h" #if MEMFAULT_LOG_DATA_SOURCE_ENABLED #include "memfault_log_data_source_private.h" #endif #define MEMFAULT_RAM_LOGGER_VERSION 1 typedef struct MfltLogStorageInfo { void *storage; size_t len; uint16_t crc16; } sMfltLogStorageRegionInfo; typedef struct { uint8_t version; bool enabled; // the minimum log level level that will be saved // Can be changed with memfault_log_set_min_save_level() eMemfaultPlatformLogLevel min_log_level; uint32_t log_read_offset; // The total number of messages that were not recorded into the buffer, either // due to lack of space, or if the buffer was locked by the data source. uint32_t dropped_msg_count; // The total number of messages that were recorded into the buffer uint32_t recorded_msg_count; sMfltCircularBuffer circ_buffer; // When initialized we keep track of the user provided storage buffer and crc the location + // size. When the system crashes we can check to see if this info has been corrupted in any way // before trying to collect the region. sMfltLogStorageRegionInfo region_info; } sMfltRamLogger; static sMfltRamLogger s_memfault_ram_logger = { .enabled = false, }; #if MEMFAULT_LOG_RESTORE_STATE MEMFAULT_STATIC_ASSERT(sizeof(sMfltRamLogger) == MEMFAULT_LOG_STATE_SIZE_BYTES, "Update MEMFAULT_LOG_STATE_SIZE_BYTES to match s_memfault_ram_logger."); // clang-format off // Uncomment below to see the size of MEMFAULT_LOG_STATE_SIZE_BYTES at compile time // #define BOOM(size_) MEMFAULT_PACKED_STRUCT kaboom { char dummy[size_]; }; char (*__kaboom)[sizeof(struct kaboom)] = 1; // BOOM(MEMFAULT_LOG_STATE_SIZE_BYTES) // BOOM(sizeof(sMfltRamLogger)) // clang-format on sMfltLogSaveState memfault_log_get_state(void) { sMfltLogSaveState state = { 0 }; if (s_memfault_ram_logger.enabled) { state.context = &s_memfault_ram_logger; state.context_len = sizeof(s_memfault_ram_logger); state.storage = s_memfault_ram_logger.circ_buffer.storage; state.storage_len = s_memfault_ram_logger.circ_buffer.total_space; } return state; } #endif // MEMFAULT_LOG_RESTORE_STATE static uint16_t prv_compute_log_region_crc16(void) { return memfault_crc16_compute(MEMFAULT_CRC16_INITIAL_VALUE, &s_memfault_ram_logger.region_info, offsetof(sMfltLogStorageRegionInfo, crc16)); } uint32_t memfault_log_get_dropped_count(void) { return s_memfault_ram_logger.dropped_msg_count; } uint32_t memfault_log_get_recorded_count(void) { return s_memfault_ram_logger.recorded_msg_count; } bool memfault_log_get_regions(sMemfaultLogRegions *regions) { if (!s_memfault_ram_logger.enabled) { return false; } const sMfltLogStorageRegionInfo *region_info = &s_memfault_ram_logger.region_info; const uint16_t current_crc16 = prv_compute_log_region_crc16(); if (current_crc16 != region_info->crc16) { return false; } *regions = (sMemfaultLogRegions){ .region = { { .region_start = &s_memfault_ram_logger, .region_size = sizeof(s_memfault_ram_logger), }, { .region_start = region_info->storage, .region_size = region_info->len, } } }; return true; } static uint8_t prv_build_header(eMemfaultPlatformLogLevel level, eMemfaultLogRecordType type, bool timestamped) { MEMFAULT_STATIC_ASSERT(kMemfaultPlatformLogLevel_NumLevels <= 8, "Number of log levels exceed max number that log module can track"); MEMFAULT_STATIC_ASSERT(kMemfaultLogRecordType_NumTypes <= 2, "Number of log types exceed max number that log module can track"); const uint8_t level_field = (level << MEMFAULT_LOG_HDR_LEVEL_POS) & MEMFAULT_LOG_HDR_LEVEL_MASK; const uint8_t type_field = (type << MEMFAULT_LOG_HDR_TYPE_POS) & MEMFAULT_LOG_HDR_TYPE_MASK; const uint8_t timestamped_field = timestamped ? MEMFAULT_LOG_HDR_TIMESTAMPED_MASK : 0; return level_field | type_field | timestamped_field; } void memfault_log_set_min_save_level(eMemfaultPlatformLogLevel min_log_level) { s_memfault_ram_logger.min_log_level = min_log_level; } static bool prv_try_free_space(sMfltCircularBuffer *circ_bufp, int bytes_needed) { const size_t bytes_free = memfault_circular_buffer_get_write_size(circ_bufp); bytes_needed -= bytes_free; if (bytes_needed <= 0) { // no work to do, there is already enough space available return true; } size_t tot_read_space = memfault_circular_buffer_get_read_size(circ_bufp); if (bytes_needed > (int)tot_read_space) { // Even if we dropped all the data in the buffer there would not be enough space // This means the message is larger than the storage area we have allocated for the buffer return false; } #if MEMFAULT_LOG_DATA_SOURCE_ENABLED if (memfault_log_data_source_has_been_triggered()) { // Don't allow expiring old logs. memfault_log_trigger_collection() has been // called, so we're in the process of uploading the logs in the buffer. return false; } #endif // Expire oldest logs until there is enough room available while (tot_read_space != 0) { // Log lines are stored as 2 entries in the circular buffer: // 1. sMfltRamLogEntry header // 2. log message (compact or formatted) // When freeing space, clear both the header and the message sMfltRamLogEntry curr_entry = { 0 }; memfault_circular_buffer_read(circ_bufp, 0, &curr_entry, sizeof(curr_entry)); const size_t space_to_free = curr_entry.len + sizeof(curr_entry); if ((curr_entry.hdr & (MEMFAULT_LOG_HDR_READ_MASK | MEMFAULT_LOG_HDR_SENT_MASK)) != 0) { // note: log_read_offset can safely wrap around, circular buffer API // handles the modulo arithmetic s_memfault_ram_logger.log_read_offset -= space_to_free; } else { // We are removing a message that was not read via memfault_log_read(). s_memfault_ram_logger.dropped_msg_count++; } memfault_circular_buffer_consume(circ_bufp, space_to_free); bytes_needed -= space_to_free; if (bytes_needed <= 0) { return true; } tot_read_space = memfault_circular_buffer_get_read_size(circ_bufp); } return false; // should be unreachable } static void prv_iterate(MemfaultLogIteratorCallback callback, sMfltLogIterator *iter) { sMfltCircularBuffer *const circ_bufp = &s_memfault_ram_logger.circ_buffer; bool should_continue = true; while (should_continue) { if (!memfault_circular_buffer_read(circ_bufp, iter->read_offset, &iter->entry, sizeof(iter->entry))) { return; } // Note: At this point, the memfault_log_iter_update_entry(), // memfault_log_entry_get_msg_pointer() calls made from the callback should never fail. // A failure is indicative of memory corruption (e.g calls taking place from multiple tasks // without having implemented memfault_lock() / memfault_unlock()) should_continue = callback(iter); iter->read_offset += sizeof(iter->entry) + iter->entry.len; } } void memfault_log_iterate(MemfaultLogIteratorCallback callback, sMfltLogIterator *iter) { memfault_lock(); prv_iterate(callback, iter); memfault_unlock(); } bool memfault_log_iter_update_entry(sMfltLogIterator *iter) { sMfltCircularBuffer *const circ_bufp = &s_memfault_ram_logger.circ_buffer; const size_t offset_from_end = memfault_circular_buffer_get_read_size(circ_bufp) - iter->read_offset; return memfault_circular_buffer_write_at_offset(circ_bufp, offset_from_end, &iter->entry, sizeof(iter->entry)); } bool memfault_log_iter_copy_msg(sMfltLogIterator *iter, MemfaultLogMsgCopyCallback callback) { sMfltCircularBuffer *const circ_bufp = &s_memfault_ram_logger.circ_buffer; return memfault_circular_buffer_read_with_callback( circ_bufp, iter->read_offset + sizeof(iter->entry), iter->entry.len, iter, (MemfaultCircularBufferReadCallback)callback); } typedef struct { sMemfaultLog *log; bool has_log; } sMfltReadLogCtx; static bool prv_read_log_iter_callback(sMfltLogIterator *iter) { sMfltReadLogCtx *const ctx = (sMfltReadLogCtx *)iter->user_ctx; sMfltCircularBuffer *const circ_bufp = &s_memfault_ram_logger.circ_buffer; // mark the message as read iter->entry.hdr |= MEMFAULT_LOG_HDR_READ_MASK; if (!memfault_log_iter_update_entry(iter)) { return false; } if (!memfault_circular_buffer_read(circ_bufp, iter->read_offset + sizeof(iter->entry), ctx->log->msg, iter->entry.len)) { return false; } ctx->log->level = memfault_log_get_level_from_hdr(iter->entry.hdr); ctx->log->type = memfault_log_get_type_from_hdr(iter->entry.hdr); ctx->log->msg_len = iter->entry.len; #if MEMFAULT_LOG_TIMESTAMPS_ENABLE const size_t timestamp_len = sizeof(ctx->log->timestamp); if (memfault_log_hdr_is_timestamped(iter->entry.hdr) && (iter->entry.len >= timestamp_len)) { memcpy(&ctx->log->timestamp, ctx->log->msg, timestamp_len); // shift the message over to remove the timestamp memmove(ctx->log->msg, &ctx->log->msg[timestamp_len], ctx->log->msg_len - timestamp_len); ctx->log->msg_len -= timestamp_len; } else { ctx->log->timestamp = 0; } #endif ctx->log->msg[ctx->log->msg_len] = '\0'; ctx->has_log = true; return false; } static bool prv_read_log(sMemfaultLog *log) { // Note: if the log state is restored through a deep sleep loss of system // state, this static variable will be desynchronized. static uint32_t s_last_dropped_count = 0; if (s_last_dropped_count != s_memfault_ram_logger.dropped_msg_count) { s_last_dropped_count = s_memfault_ram_logger.dropped_msg_count; if (s_last_dropped_count > 0) { log->level = kMemfaultPlatformLogLevel_Warning; const int rv = snprintf(log->msg, sizeof(log->msg), "... %d messages dropped ...", (int)s_memfault_ram_logger.dropped_msg_count); log->msg_len = (rv <= 0) ? 0 : MEMFAULT_MIN((uint32_t)rv, sizeof(log->msg) - 1); log->type = kMemfaultLogRecordType_Preformatted; return true; } } sMfltReadLogCtx user_ctx = { .log = log }; sMfltLogIterator iter = { .read_offset = s_memfault_ram_logger.log_read_offset, .user_ctx = &user_ctx }; prv_iterate(prv_read_log_iter_callback, &iter); s_memfault_ram_logger.log_read_offset = iter.read_offset; return user_ctx.has_log; } bool memfault_log_read(sMemfaultLog *log) { if (!s_memfault_ram_logger.enabled || (log == NULL)) { return false; } memfault_lock(); const bool found_unread_log = prv_read_log(log); memfault_unlock(); return found_unread_log; } MEMFAULT_WEAK void memfault_log_export_msg(MEMFAULT_UNUSED eMemfaultPlatformLogLevel level, const char *msg, MEMFAULT_UNUSED size_t msg_len) { memfault_platform_log_raw("%s", msg); } void memfault_log_export_log(sMemfaultLog *log) { MEMFAULT_SDK_ASSERT(log != NULL); char base64[MEMFAULT_LOG_EXPORT_BASE64_CHUNK_MAX_LEN]; size_t log_read_offset = 0; switch (log->type) { case kMemfaultLogRecordType_Compact: while (log_read_offset < log->msg_len) { memcpy(base64, MEMFAULT_LOG_EXPORT_BASE64_CHUNK_PREFIX, MEMFAULT_LOG_EXPORT_BASE64_CHUNK_PREFIX_LEN); size_t log_read_len = MEMFAULT_MIN(log->msg_len - log_read_offset, MEMFAULT_LOG_EXPORT_CHUNK_MAX_LEN); size_t write_offset = MEMFAULT_LOG_EXPORT_BASE64_CHUNK_PREFIX_LEN; memfault_base64_encode(&log->msg[log_read_offset], log_read_len, &base64[write_offset]); log_read_offset += log_read_len; write_offset += MEMFAULT_BASE64_ENCODE_LEN(log_read_len); memcpy(&base64[write_offset], MEMFAULT_LOG_EXPORT_BASE64_CHUNK_SUFFIX, MEMFAULT_LOG_EXPORT_BASE64_CHUNK_SUFFIX_LEN); write_offset += MEMFAULT_LOG_EXPORT_BASE64_CHUNK_SUFFIX_LEN; base64[write_offset] = '\0'; memfault_log_export_msg(log->level, base64, write_offset); } break; case kMemfaultLogRecordType_Preformatted: memfault_log_export_msg(log->level, log->msg, log->msg_len); break; case kMemfaultLogRecordType_NumTypes: // silences -Wswitch-enum default: break; } } void memfault_log_export_logs(void) { while (1) { // the TI ARM compiler warns about enumerated type mismatch in this // zero-initializer, but we depend on the C99 spec (vs. the gcc extension, // empty brace {}), so suppress the diagnostic here. Clang throws an invalid // -Wmissing-field-initializers warning if we just cast, unfortunately. #if defined(__TI_ARM__) #pragma diag_push #pragma diag_remark 190 #endif #if defined(__CC_ARM) #pragma push // This armcc diagnostic is technically violating the C standard, which // _explicitly_ requires enums to be type-equivalent to ints. See ISO/IEC // 9899:TC3 6.2.5.16, and specifically 6.4.4.3, which states: "An // identifier declared as an enumeration constant has type int." #pragma diag_suppress 188 // enumerated type mixed with another type #endif sMemfaultLog log = { 0 }; #if defined(__TI_ARM__) #pragma diag_pop #endif #if defined(__CC_ARM) #pragma pop #endif const bool log_found = memfault_log_read(&log); if (!log_found) { break; } memfault_log_export_log(&log); } } static bool prv_should_log(eMemfaultPlatformLogLevel level) { if (!s_memfault_ram_logger.enabled) { return false; } if (level < s_memfault_ram_logger.min_log_level) { return false; } return true; } //! Stub implementation that a user of the SDK can override. See header for more details. MEMFAULT_WEAK void memfault_log_handle_saved_callback(void) { return; } void memfault_vlog_save(eMemfaultPlatformLogLevel level, const char *fmt, va_list args) { if (!prv_should_log(level)) { return; } char log_buf[MEMFAULT_LOG_MAX_LINE_SAVE_LEN + 1]; const size_t available_space = sizeof(log_buf); const int rv = vsnprintf(log_buf, available_space, fmt, args); if (rv <= 0) { return; } size_t bytes_written = (size_t)rv; if (bytes_written >= available_space) { bytes_written = available_space - 1; } memfault_log_save_preformatted(level, log_buf, bytes_written); } void memfault_log_save(eMemfaultPlatformLogLevel level, const char *fmt, ...) { va_list args; va_start(args, fmt); memfault_vlog_save(level, fmt, args); va_end(args); } static void prv_log_save(eMemfaultPlatformLogLevel level, const void *log, size_t log_len, eMemfaultLogRecordType log_type, bool should_lock) { if (!prv_should_log(level)) { return; } // Compact log msg should never exceed MEMFAULT_LOG_MAX_LINE_SAVE_LEN. Return // immediately if it does. if ((log_type == kMemfaultLogRecordType_Compact) && (log_len > MEMFAULT_LOG_MAX_LINE_SAVE_LEN)) { return; } bool log_written = false; #if MEMFAULT_LOG_TIMESTAMPS_ENABLE sMemfaultCurrentTime timestamp; const bool timestamped = memfault_platform_time_get_current(×tamp); uint32_t timestamp_val; // forward declaration for sizeof() const size_t timestamped_len = timestamped ? sizeof(timestamp_val) : 0; #else const bool timestamped = false; const size_t timestamped_len = 0; #endif // safe to truncate now- this can only happen for preformatted logs const size_t truncated_log_len = MEMFAULT_MIN(log_len, MEMFAULT_LOG_MAX_LINE_SAVE_LEN); // total log length for the log entry .len field includes the timestamp. const uint8_t total_log_len = (uint8_t)(truncated_log_len + timestamped_len); // circular buffer space needed includes the metadata (hdr + len) and msg const size_t bytes_needed = sizeof(sMfltRamLogEntry) + total_log_len; if (should_lock) { memfault_lock(); } { sMfltCircularBuffer *circ_bufp = &s_memfault_ram_logger.circ_buffer; const bool space_free = prv_try_free_space(circ_bufp, (int)bytes_needed); if (space_free) { s_memfault_ram_logger.recorded_msg_count++; sMfltRamLogEntry entry = { .hdr = prv_build_header(level, log_type, timestamped), .len = total_log_len, }; memfault_circular_buffer_write(circ_bufp, &entry, sizeof(entry)); #if MEMFAULT_LOG_TIMESTAMPS_ENABLE if (timestamped) { timestamp_val = timestamp.info.unix_timestamp_secs; memfault_circular_buffer_write(circ_bufp, ×tamp_val, sizeof(timestamp_val)); } #endif memfault_circular_buffer_write(circ_bufp, log, truncated_log_len); log_written = true; } else { s_memfault_ram_logger.dropped_msg_count++; } } if (should_lock) { memfault_unlock(); } if (log_written) { memfault_log_handle_saved_callback(); } } #if MEMFAULT_COMPACT_LOG_ENABLE void memfault_compact_log_save(eMemfaultPlatformLogLevel level, uint32_t log_id, uint32_t compressed_fmt, ...) { char log_buf[MEMFAULT_LOG_MAX_LINE_SAVE_LEN + 1]; sMemfaultCborEncoder encoder; memfault_cbor_encoder_init(&encoder, memfault_cbor_encoder_memcpy_write, log_buf, sizeof(log_buf)); va_list args; va_start(args, compressed_fmt); bool success = memfault_vlog_compact_serialize(&encoder, log_id, compressed_fmt, args); va_end(args); if (!success) { // if we failed serialization due to lack of space (entire CBOR structure too large), // insert a placeholder instead. Note: truncation only handles string content being too long if (memfault_cbor_encoder_get_status(&encoder) == MEMFAULT_CBOR_ENCODER_STATUS_ENOMEM) { // first compute serialized size memfault_cbor_encoder_size_only_init(&encoder); va_start(args, compressed_fmt); memfault_vlog_compact_serialize(&encoder, log_id, compressed_fmt, args); va_end(args); size_t computed_size = memfault_cbor_encoder_deinit(&encoder); // now serialize the fallback entry memfault_cbor_encoder_init(&encoder, memfault_cbor_encoder_memcpy_write, log_buf, sizeof(log_buf)); success = memfault_vlog_compact_serialize_fallback_entry(&encoder, log_id, computed_size); } } // if we succeeded in serializing either the original or fallback entry, save it if (success) { const size_t bytes_written = memfault_cbor_encoder_deinit(&encoder); prv_log_save(level, log_buf, bytes_written, kMemfaultLogRecordType_Compact, true); } } #endif /* MEMFAULT_COMPACT_LOG_ENABLE */ void memfault_log_save_preformatted(eMemfaultPlatformLogLevel level, const char *log, size_t log_len) { prv_log_save(level, log, log_len, kMemfaultLogRecordType_Preformatted, true); } void memfault_log_save_preformatted_nolock(eMemfaultPlatformLogLevel level, const char *log, size_t log_len) { prv_log_save(level, log, log_len, kMemfaultLogRecordType_Preformatted, false); } bool memfault_log_boot(void *storage_buffer, size_t buffer_len) { if (storage_buffer == NULL || buffer_len == 0 || s_memfault_ram_logger.enabled) { return false; } #if MEMFAULT_LOG_RESTORE_STATE sMfltLogSaveState state = { 0 }; bool retval = memfault_log_restore_state(&state); if (retval && (sizeof(s_memfault_ram_logger) == state.context_len) && (buffer_len == state.storage_len)) { // restore the state memmove(&s_memfault_ram_logger, state.context, sizeof(s_memfault_ram_logger)); // restore the storage buffer s_memfault_ram_logger.circ_buffer.storage = (uint8_t *)storage_buffer; memmove(s_memfault_ram_logger.circ_buffer.storage, state.storage, state.storage_len); s_memfault_ram_logger.circ_buffer.total_space = state.storage_len; } // if the user didn't restore the state or there was a size mismatch, we // need to initialize it else { if (retval) { MEMFAULT_LOG_ERROR("Log restore size mismatch: %d != %d or %d != %d", (int)sizeof(s_memfault_ram_logger), (int)state.context_len, (int)buffer_len, (int)state.storage_len); } #else { #endif // MEMFAULT_LOG_RESTORE_STATE s_memfault_ram_logger = (sMfltRamLogger){ .version = MEMFAULT_RAM_LOGGER_VERSION, .min_log_level = MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL, .region_info = { .storage = storage_buffer, .len = buffer_len, }, }; memfault_circular_buffer_init(&s_memfault_ram_logger.circ_buffer, storage_buffer, buffer_len); } s_memfault_ram_logger.region_info.crc16 = prv_compute_log_region_crc16(); // finally, enable logging s_memfault_ram_logger.enabled = true; return true; } void memfault_log_reset(void) { s_memfault_ram_logger = (sMfltRamLogger){ .enabled = false, }; } bool memfault_log_booted(void) { return s_memfault_ram_logger.enabled; } ================================================ FILE: components/core/src/memfault_log_data_source.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include "memfault/config.h" #if MEMFAULT_LOG_DATA_SOURCE_ENABLED #include #include #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/log.h" #include "memfault/core/math.h" #include "memfault/core/platform/overrides.h" #include "memfault/core/platform/system_time.h" #include "memfault/core/serializer_helper.h" #include "memfault/core/serializer_key_ids.h" #include "memfault/util/cbor.h" #include "memfault_log_data_source_private.h" #include "memfault_log_private.h" typedef struct { bool triggered; size_t num_logs; sMemfaultCurrentTime trigger_time; } sMfltLogDataSourceCtx; static sMfltLogDataSourceCtx s_memfault_log_data_source_ctx; static bool prv_log_is_sent(uint8_t hdr) { return hdr & MEMFAULT_LOG_HDR_SENT_MASK; } typedef struct { size_t num_logs; size_t bytes; } sMfltLogCountingCtx; static bool prv_log_iterate_counting_callback(sMfltLogIterator *iter) { sMfltLogCountingCtx *const ctx = (sMfltLogCountingCtx *)(iter->user_ctx); if (!prv_log_is_sent(iter->entry.hdr)) { ++ctx->num_logs; // tally the size of the log message ctx->bytes += iter->entry.len + sizeof(iter->entry.hdr); } return true; } void memfault_log_trigger_collection(void) { if (s_memfault_log_data_source_ctx.triggered) { return; } sMfltLogCountingCtx ctx = { 0 }; sMfltLogIterator iter = { .user_ctx = &ctx }; memfault_log_iterate(prv_log_iterate_counting_callback, &iter); if (ctx.num_logs == 0) { return; } memfault_lock(); { // Check again in the unlikely case this function was called concurrently: if (s_memfault_log_data_source_ctx.triggered) { memfault_unlock(); return; } s_memfault_log_data_source_ctx.triggered = true; if (!memfault_platform_time_get_current(&s_memfault_log_data_source_ctx.trigger_time)) { s_memfault_log_data_source_ctx.trigger_time.type = kMemfaultCurrentTimeType_Unknown; } s_memfault_log_data_source_ctx.num_logs = ctx.num_logs; } memfault_unlock(); } bool memfault_log_data_source_has_been_triggered(void) { // Note: memfault_lock() is held when this is called by memfault_log return s_memfault_log_data_source_ctx.triggered; } typedef struct { size_t num_logs; sMemfaultCurrentTime trigger_time; sMemfaultCborEncoder encoder; bool has_encoding_error; bool should_stop_encoding; union { size_t num_encoded_logs; size_t num_marked_sent_logs; }; } sMfltLogEncodingCtx; #if defined(MEMFAULT_UNITTEST) #if defined(__clang__) __attribute__((no_sanitize("undefined"))) #else __attribute__((no_sanitize_undefined)) #endif #endif static bool prv_serialize_msg_callback(sMfltLogIterator *iter, MEMFAULT_UNUSED size_t offset, const char *buf, size_t buf_len) { sMfltLogEncodingCtx *const ctx = (sMfltLogEncodingCtx *)iter->user_ctx; #if MEMFAULT_LOG_TIMESTAMPS_ENABLE // Encode the timestamp value, if it's present, otherwise insert a 0 uint32_t timestamp; if (memfault_log_hdr_is_timestamped(iter->entry.hdr)) { if (buf_len < sizeof(timestamp)) { return false; } memcpy(×tamp, buf, sizeof(timestamp)); buf += sizeof(timestamp); buf_len -= sizeof(timestamp); } else { timestamp = 0; } if (!memfault_cbor_encode_unsigned_integer(&ctx->encoder, timestamp)) { return false; } #endif // Encode the log level if (!memfault_cbor_encode_unsigned_integer(&ctx->encoder, memfault_log_get_level_from_hdr(iter->entry.hdr))) { return false; } eMemfaultLogRecordType type = memfault_log_get_type_from_hdr(iter->entry.hdr); // Note: We encode "preformatted" logs (i.e logs that have run through printf) as cbor text // string and "compact" logs as a cbor byte array so we can differentiate between the two while // decoding if (type == kMemfaultLogRecordType_Preformatted) { // The message is not null-terminated, so we can't use // memfault_cbor_encode_string() directly. bool success = memfault_cbor_encode_string_begin(&ctx->encoder, buf_len); return success && memfault_cbor_join(&ctx->encoder, buf, buf_len); } else { // kMemfaultLogRecordType_Compact return memfault_cbor_encode_byte_string(&ctx->encoder, buf, buf_len); } } static bool prv_log_iterate_encode_callback(sMfltLogIterator *iter) { sMfltLogEncodingCtx *const ctx = (sMfltLogEncodingCtx *)iter->user_ctx; if (ctx->should_stop_encoding) { return false; } if (!prv_log_is_sent(iter->entry.hdr)) { ctx->has_encoding_error |= !memfault_log_iter_copy_msg(iter, prv_serialize_msg_callback); // It's possible more logs have been added to the buffer // after the memfault_log_data_source_has_been_triggered() call. They cannot be included, // because the total message size has already been communicated to the packetizer. if (++ctx->num_encoded_logs == ctx->num_logs) { return false; } } return true; } static bool prv_encode(sMemfaultCborEncoder *encoder, void *iter) { sMfltLogEncodingCtx *ctx = (sMfltLogEncodingCtx *)((sMfltLogIterator *)iter)->user_ctx; #if MEMFAULT_LOG_TIMESTAMPS_ENABLE eMemfaultEventType event_type = kMemfaultEventType_LogsTimestamped; // To save space, all logs are encoded into a single array (as opposed to using a map or // array per log): const size_t elements_per_log = 3; // timestamp, lvl, msg #else eMemfaultEventType event_type = kMemfaultEventType_Logs; const size_t elements_per_log = 2; // lvl, msg #endif if (!memfault_serializer_helper_encode_metadata_with_time(encoder, event_type, &ctx->trigger_time)) { return false; } if (!memfault_cbor_encode_unsigned_integer(encoder, kMemfaultEventKey_EventInfo)) { return false; } if (!memfault_cbor_encode_array_begin(encoder, elements_per_log * ctx->num_logs)) { return false; } memfault_log_iterate(prv_log_iterate_encode_callback, (sMfltLogIterator *)iter); return ctx->has_encoding_error; } static void prv_init_encoding_ctx(sMfltLogEncodingCtx *ctx) { *ctx = (sMfltLogEncodingCtx){ .num_logs = s_memfault_log_data_source_ctx.num_logs, .trigger_time = s_memfault_log_data_source_ctx.trigger_time, }; } static bool prv_has_logs(size_t *total_size) { if (!s_memfault_log_data_source_ctx.triggered) { return false; } sMfltLogEncodingCtx ctx; prv_init_encoding_ctx(&ctx); sMfltLogIterator iter = { .read_offset = 0, .user_ctx = &ctx }; *total_size = memfault_serializer_helper_compute_size(&ctx.encoder, prv_encode, &iter); return true; } typedef struct { uint32_t offset; uint8_t *buf; size_t buf_len; size_t data_source_bytes_written; sMfltLogEncodingCtx encoding_ctx; } sMfltLogsDestCtx; static void prv_encoder_callback(void *encoder_ctx, uint32_t src_offset, const void *src_buf, size_t src_buf_len) { sMfltLogsDestCtx *dest = (sMfltLogsDestCtx *)encoder_ctx; const size_t dest_end_offset = dest->offset + dest->buf_len; // Optimization: stop encoding if the encoder writes are past the destination buffer: if (src_offset > dest_end_offset) { dest->encoding_ctx.should_stop_encoding = true; return; } const size_t src_end_offset = src_offset + src_buf_len; const size_t intersection_start_offset = MEMFAULT_MAX(src_offset, dest->offset); const size_t intersection_end_offset = MEMFAULT_MIN(src_end_offset, dest_end_offset); if (intersection_end_offset <= intersection_start_offset) { return; // no intersection } const size_t intersection_len = intersection_end_offset - intersection_start_offset; memcpy(dest->buf + (intersection_start_offset - dest->offset), ((const uint8_t *)src_buf) + (intersection_start_offset - src_offset), intersection_len); dest->data_source_bytes_written += intersection_len; } static bool prv_logs_read(uint32_t offset, void *buf, size_t buf_len) { sMfltLogsDestCtx dest_ctx = (sMfltLogsDestCtx){ .offset = offset, .buf = (uint8_t *)buf, .buf_len = buf_len, }; sMfltLogIterator iter = { .user_ctx = &dest_ctx.encoding_ctx, }; prv_init_encoding_ctx(&dest_ctx.encoding_ctx); // Note: UINT_MAX is passed as length, because it is possible and expected that the output is // written partially by the callback. The callback takes care of not overrunning the output buffer // itself. memfault_cbor_encoder_init(&dest_ctx.encoding_ctx.encoder, prv_encoder_callback, &dest_ctx, UINT32_MAX); prv_encode(&dest_ctx.encoding_ctx.encoder, &iter); return buf_len == dest_ctx.data_source_bytes_written; } static bool prv_log_iterate_mark_sent_callback(sMfltLogIterator *iter) { sMfltLogEncodingCtx *const ctx = (sMfltLogEncodingCtx *)iter->user_ctx; if (!prv_log_is_sent(iter->entry.hdr)) { iter->entry.hdr |= MEMFAULT_LOG_HDR_SENT_MASK; memfault_log_iter_update_entry(iter); if (++ctx->num_marked_sent_logs == ctx->num_logs) { return false; } } return true; } static void prv_logs_mark_sent(void) { sMfltLogEncodingCtx ctx; sMfltLogIterator iter = { .read_offset = 0, .user_ctx = &ctx }; prv_init_encoding_ctx(&ctx); memfault_log_iterate(prv_log_iterate_mark_sent_callback, &iter); memfault_lock(); s_memfault_log_data_source_ctx = (sMfltLogDataSourceCtx){ 0 }; memfault_unlock(); } //! Expose a data source for use by the Memfault Packetizer const sMemfaultDataSourceImpl g_memfault_log_data_source = { .has_more_msgs_cb = prv_has_logs, .read_msg_cb = prv_logs_read, .mark_msg_read_cb = prv_logs_mark_sent, }; void memfault_log_data_source_reset(void) { s_memfault_log_data_source_ctx = (sMfltLogDataSourceCtx){ 0 }; } size_t memfault_log_data_source_count_unsent_logs(void) { sMfltLogCountingCtx ctx = { 0 }; sMfltLogIterator iter = { .user_ctx = &ctx }; memfault_log_iterate(prv_log_iterate_counting_callback, &iter); return ctx.num_logs; } sMfltLogUnsentCount memfault_log_get_unsent_count(void) { sMfltLogCountingCtx ctx = { 0 }; sMfltLogIterator iter = { .user_ctx = &ctx }; memfault_log_iterate(prv_log_iterate_counting_callback, &iter); return (sMfltLogUnsentCount){ .num_logs = ctx.num_logs, .bytes = ctx.bytes, }; } #endif /* MEMFAULT_LOG_DATA_SOURCE_ENABLED */ ================================================ FILE: components/core/src/memfault_log_data_source_private.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Internal logging data source #include #include "memfault/util/cbor.h" #ifdef __cplusplus extern "C" { #endif //! @note Internal function bool memfault_log_data_source_has_been_triggered(void); //! Reset the state of the logging data source //! //! @note Internal function only intended for use with unit tests void memfault_log_data_source_reset(void); //! @note Internal function only intended for use with unit tests size_t memfault_log_data_source_count_unsent_logs(void); #ifdef __cplusplus } #endif ================================================ FILE: components/core/src/memfault_log_private.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Utilities to assist with querying the log buffer //! //! @note A user of the Memfault SDK should _never_ call any //! of these routines directly #include #include #include "memfault/core/compiler.h" #include "memfault/core/log.h" #include "memfault/core/platform/debug_log.h" #ifdef __cplusplus extern "C" { #endif // Note: We do not use bitfields here to avoid portability complications on the decode side since // alignment of bitfields as well as the order of bitfields is left up to the compiler per the C // standard. // // Header Layout: // 0brsxT.tlll // where // r = read (1 if the message has been read, 0 otherwise) // s = sent (1 if the message has been sent, 0 otherwise) // x = reserved // T = timestamped (1 if the first 4 bytes of the message is a timestamp, 0 otherwise) // t = type (0 = formatted log, 1 = compact log) // l = log level (eMemfaultPlatformLogLevel) // * note: real levels are only 0-3 (see debug_log.h). upper values can have alternate // meanings: // * 4 = log line indicating truncated message: // * - only if type=compact, the msg is a special payload: // * [log_id, size_of_msg (encoded CBOR compact log size that didn't fit)] // * 5-7 = reserved for future use #define MEMFAULT_LOG_HDR_LEVEL_POS 0 #define MEMFAULT_LOG_HDR_LEVEL_MASK 0x07u #define MEMFAULT_LOG_HDR_TYPE_POS 3u #define MEMFAULT_LOG_HDR_TYPE_MASK 0x08u #define MEMFAULT_LOG_HDR_READ_MASK 0x80u // Log has been read through memfault_log_read() #define MEMFAULT_LOG_HDR_SENT_MASK 0x40u // Log has been sent through g_memfault_log_data_source #define MEMFAULT_LOG_HDR_TIMESTAMPED_MASK 0x10u // Log payload includes a leading 4-byte timestamp static inline eMemfaultPlatformLogLevel memfault_log_get_level_from_hdr(uint8_t hdr) { return (eMemfaultPlatformLogLevel)((hdr & MEMFAULT_LOG_HDR_LEVEL_MASK) >> MEMFAULT_LOG_HDR_LEVEL_POS); } static inline eMemfaultLogRecordType memfault_log_get_type_from_hdr(uint8_t hdr) { return (eMemfaultLogRecordType)((hdr & MEMFAULT_LOG_HDR_TYPE_MASK) >> MEMFAULT_LOG_HDR_TYPE_POS); } static inline bool memfault_log_hdr_is_timestamped(uint8_t hdr) { return (hdr & MEMFAULT_LOG_HDR_TIMESTAMPED_MASK) != 0; } // A log entry has the following layout: // // [ 1 byte ][ 1 byte ][ len bytes ] // [ hdr ][ len ][ msg ] // // If the timestamped bit is set in the header, the first 4 bytes of the message // will be a little-endian UNIX timestamp: // // [ 1 byte ][ 1 byte ][ 4 bytes ][ len - 4 bytes] // [ hdr ][ len ][ timestamp ][ msg ] typedef MEMFAULT_PACKED_STRUCT { // data about the message stored (details below) uint8_t hdr; // the length of the msg uint8_t len; // underlying message. note that if the timestamped bit is set, the first 4 // bytes of the message will be the little-endian UNIX timestamp. uint8_t msg[]; } sMfltRamLogEntry; // In the current version of the log entry structure, the maximum length of a // log message is 255 bytes due to the width of the 'len' field. MEMFAULT_STATIC_ASSERT(MEMFAULT_LOG_MAX_LINE_SAVE_LEN <= 251, "MEMFAULT_LOG_MAX_LINE_SAVE_LEN must be <= 251"); typedef struct { uint32_t read_offset; void *user_ctx; sMfltRamLogEntry entry; } sMfltLogIterator; //! The callback invoked when "memfault_log_iterate" is called //! //! @param ctx The context provided to "memfault_log_iterate" //! @param iter The iterator originally passed to "memfault_log_iterate". The //! iter->entry field gets updated before entering this callback. The //! iter->read_offset field gets updated after exiting this callback. //! //! @return bool to continue iterating, else false typedef bool (*MemfaultLogIteratorCallback)(sMfltLogIterator *iter); //! Iterates over the logs in the buffer, calling the callback for each log. void memfault_log_iterate(MemfaultLogIteratorCallback callback, sMfltLogIterator *iter); //! Update/rewrite the entry header at the position of the iterator. //! @note This MUST ONLY be called from a memfault_log_iterate() callback (it //! assumes memfault_lock has been taken by the caller). bool memfault_log_iter_update_entry(sMfltLogIterator *iter); typedef bool (*MemfaultLogMsgCopyCallback)(sMfltLogIterator *iter, size_t offset, const char *buf, size_t buf_len); //! @note This MUST ONLY be called from a memfault_log_iterate() callback (it //! assumes memfault_lock has been taken by the caller). bool memfault_log_iter_copy_msg(sMfltLogIterator *iter, MemfaultLogMsgCopyCallback callback); #ifdef __cplusplus } #endif ================================================ FILE: components/core/src/memfault_ram_reboot_info_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A RAM-backed implementation used for tracking state across system reboots. More details about //! how to use the API can be found in reboot_tracking.h //! Assumptions: //! - RAM state survives across resets (this is generally true as long as power is stable) //! If power is lost, nothing will fail but the reboot will not be recorded //! - The memory which needs to persist in RAM must _not_ be initialized by any of the firmwares //! upon reboot & the memory must be placed in the same region for the firmwares running on the //! system (i.e bootloader & main image). #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/errors.h" #include "memfault/core/reboot_tracking.h" #include "memfault_reboot_tracking_private.h" #define MEMFAULT_REBOOT_INFO_MAGIC 0x21544252 #define MEMFAULT_REBOOT_INFO_VERSION 2 // clang-format off // Internal value used to initialize sMfltRebootInfo.last_reboot_reason. Care must be taken when // comparing this value to eMemfaultRebootReason as different platforms produce different behavior // comparing uint32_t to enum types. In general, cast this value to the type it is compared against. // Examples: // // uint32_t last_reboot_reason != MEMFAULT_REBOOT_REASON_NOT_SET (GOOD) // eMemfaultRebootReason reboot_reason != (eMemfaultRebootReason)MEMFAULT_REBOOT_REASON_NOT_SET (GOOD) // uint32_t last_reboot_reason != (eMemfaultRebootReason)MEMFAULT_REBOOT_REASON_NOT_SET (BAD) // clang-format on #define MEMFAULT_REBOOT_REASON_NOT_SET 0xffffffff MEMFAULT_STATIC_ASSERT(sizeof(sMfltRebootInfo) == MEMFAULT_REBOOT_TRACKING_REGION_SIZE, "struct doesn't match expected size"); MEMFAULT_STATIC_ASSERT(sizeof(sMfltRebootInfo) % 4 == 0, "struct size must be multiple of 4"); MEMFAULT_STATIC_ASSERT(sizeof(eMemfaultRebootReason) <= sizeof(((sMfltRebootInfo){ 0 }).last_reboot_reason), "enum does not fit within sMfltRebootInfo.last_reboot_reason"); static sMfltRebootInfo *s_mflt_reboot_info; //! Struct to retrieve reboot reason data from. Matches the fields of sMfltRebootReason //! as documented in reboot_tracking.h typedef struct { eMemfaultRebootReason reboot_reg_reason; eMemfaultRebootReason prior_stored_reason; bool is_valid; } sMfltRebootReasonData; // Private struct to store reboot reason after reboot tracking is initialized static sMfltRebootReasonData s_reboot_reason_data = { .is_valid = false, }; static bool prv_check_or_init_struct(void) { if (s_mflt_reboot_info == NULL) { return false; } if (s_mflt_reboot_info->magic == MEMFAULT_REBOOT_INFO_MAGIC) { return true; } // structure doesn't match what we expect, reset it *s_mflt_reboot_info = (sMfltRebootInfo){ .magic = MEMFAULT_REBOOT_INFO_MAGIC, .version = MEMFAULT_REBOOT_INFO_VERSION, .last_reboot_reason = MEMFAULT_REBOOT_REASON_NOT_SET, }; return true; } static bool prv_read_reset_info(sMfltResetReasonInfo *info) { // prior_stored_reason is a uint32_t, no need to cast MEMFAULT_REBOOT_REASON_NOT_SET if ((s_mflt_reboot_info->last_reboot_reason == MEMFAULT_REBOOT_REASON_NOT_SET) && (s_mflt_reboot_info->reset_reason_reg0 == 0)) { return false; // no reset crashes! } *info = (sMfltResetReasonInfo){ .reason = (eMemfaultRebootReason)s_mflt_reboot_info->last_reboot_reason, .pc = s_mflt_reboot_info->pc, .lr = s_mflt_reboot_info->lr, .reset_reason_reg0 = s_mflt_reboot_info->reset_reason_reg0, .coredump_saved = s_mflt_reboot_info->coredump_saved == 1, }; return true; } //! Records reboot reasons from reboot register and prior saved reboot //! //! Stores both the new reboot reason derived from a platform's reboot register and //! any previously saved reboot reason. If there is no previously stored reboot reason, //! the reboot register reason is used. //! //! @param reboot_reg_reason New reboot reason from this boot //! @param prior_stored_reason Prior reboot reason stored in s_mflt_reboot_info static void prv_record_reboot_reason(eMemfaultRebootReason reboot_reg_reason, uint32_t prior_stored_reason) { s_reboot_reason_data.reboot_reg_reason = reboot_reg_reason; // prior_stored_reason is a uint32_t, no need to cast MEMFAULT_REBOOT_REASON_NOT_SET if (prior_stored_reason != MEMFAULT_REBOOT_REASON_NOT_SET) { s_reboot_reason_data.prior_stored_reason = (eMemfaultRebootReason)prior_stored_reason; } else { s_reboot_reason_data.prior_stored_reason = reboot_reg_reason; } s_reboot_reason_data.is_valid = true; } static bool prv_get_unexpected_reboot_occurred(void) { // Use prior_stored_reason as source of reboot reason. Fallback to reboot_reg_reason if // prior_stored_reason is not set eMemfaultRebootReason reboot_reason; // s_reboot_reason_data.prior_stored_reason is an eMemfaultRebootReason type, cast // MEMFAULT_REBOOT_REASON_NOT_SET if (s_reboot_reason_data.prior_stored_reason != (eMemfaultRebootReason)MEMFAULT_REBOOT_REASON_NOT_SET) { reboot_reason = s_reboot_reason_data.prior_stored_reason; } else { reboot_reason = s_reboot_reason_data.reboot_reg_reason; } // Check if selected reboot_reason is unexpected if in error range or unknown return (reboot_reason == kMfltRebootReason_Unknown || reboot_reason >= kMfltRebootReason_UnknownError); } static void prv_record_reboot_event(eMemfaultRebootReason reboot_reason, const sMfltRebootTrackingRegInfo *reg) { // Store both the new reason reported by hardware and the current recorded reason // The combination of these will be used to determine if the bootup was expected // by the metrics subsystem // s_mflt_reboot_info can be cleared by any call to memfault_reboot_tracking_collect_reset_info prv_record_reboot_reason(reboot_reason, s_mflt_reboot_info->last_reboot_reason); // last_reboot_reason is a uint32_t, no need to cast MEMFAULT_REBOOT_REASON_NOT_SET if (s_mflt_reboot_info->last_reboot_reason != MEMFAULT_REBOOT_REASON_NOT_SET) { // we are already tracking a reboot. We don't overwrite this because generally the first reboot // in a loop reveals what started the crash loop return; } s_mflt_reboot_info->last_reboot_reason = reboot_reason; if (reg == NULL) { // we don't have any extra metadata return; } s_mflt_reboot_info->pc = reg->pc; 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 = (sMfltRebootInfo *)start_addr; if (start_addr == NULL) { return; } memfault_reboot_tracking_load((sMemfaultRebootTrackingStorage *)s_mflt_reboot_info); if (!prv_check_or_init_struct()) { return; } eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; if (bootup_info != NULL) { s_mflt_reboot_info->reset_reason_reg0 = bootup_info->reset_reason_reg; reset_reason = bootup_info->reset_reason; } prv_record_reboot_event(reset_reason, NULL); if (prv_get_unexpected_reboot_occurred()) { s_mflt_reboot_info->crash_count++; } } void memfault_reboot_tracking_mark_reset_imminent(eMemfaultRebootReason reboot_reason, const sMfltRebootTrackingRegInfo *reg) { if (!prv_check_or_init_struct()) { return; } 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) { if (info == NULL) { return false; } if (!prv_check_or_init_struct()) { return false; } return prv_read_reset_info(info); } void memfault_reboot_tracking_reset_crash_count(void) { if (!prv_check_or_init_struct()) { return; } s_mflt_reboot_info->crash_count = 0; } size_t memfault_reboot_tracking_get_crash_count(void) { if (!prv_check_or_init_struct()) { return 0; } return s_mflt_reboot_info->crash_count; } void memfault_reboot_tracking_clear_reset_info(void) { if (!prv_check_or_init_struct()) { return; } s_mflt_reboot_info->last_reboot_reason = MEMFAULT_REBOOT_REASON_NOT_SET; s_mflt_reboot_info->coredump_saved = 0; s_mflt_reboot_info->pc = 0; s_mflt_reboot_info->lr = 0; s_mflt_reboot_info->reset_reason_reg0 = 0; } void memfault_reboot_tracking_mark_coredump_saved(void) { if (!prv_check_or_init_struct()) { return; } s_mflt_reboot_info->coredump_saved = 1; } int memfault_reboot_tracking_get_reboot_reason(sMfltRebootReason *reboot_reason) { if (reboot_reason == NULL || !s_reboot_reason_data.is_valid) { return -1; } *reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = s_reboot_reason_data.reboot_reg_reason, .prior_stored_reason = s_reboot_reason_data.prior_stored_reason, }; return 0; } int memfault_reboot_tracking_get_unexpected_reboot_occurred(bool *unexpected_reboot_occurred) { if (unexpected_reboot_occurred == NULL || !s_reboot_reason_data.is_valid) { return -1; } *unexpected_reboot_occurred = prv_get_unexpected_reboot_occurred(); return 0; } void memfault_reboot_tracking_clear_reboot_reason(void) { s_reboot_reason_data = (sMfltRebootReasonData){ .is_valid = false, }; } bool memfault_reboot_tracking_booted(void) { return ((s_mflt_reboot_info != NULL) && (s_mflt_reboot_info->magic == MEMFAULT_REBOOT_INFO_MAGIC)); } void memfault_reboot_tracking_metrics_session(bool active, uint32_t index) { if (!prv_check_or_init_struct()) { return; } if (active) { s_mflt_reboot_info->active_sessions |= (1u << index); } else { s_mflt_reboot_info->active_sessions &= ~(1u << index); } } void memfault_reboot_tracking_clear_metrics_sessions(void) { if (!prv_check_or_init_struct()) { return; } s_mflt_reboot_info->active_sessions = 0; } bool memfault_reboot_tracking_metrics_session_was_active(uint32_t index) { if (!prv_check_or_init_struct()) { return false; } return (s_mflt_reboot_info->active_sessions & (1 << index)) != 0; } ================================================ FILE: components/core/src/memfault_reboot_tracking_private.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Internal utilities used for tracking reboot reasons #include #include #include "memfault/core/reboot_tracking.h" #ifdef __cplusplus extern "C" { #endif typedef MEMFAULT_PACKED_STRUCT MfltRebootInfo { //! A cheap way to check if the data within the struct is valid uint32_t magic; //! Version of the struct. If a new field is added it should be appended right before rsvd. This //! way we can remain backwards compatible but know what fields are valid. uint8_t version; //! The number of times the system has reset due to an error //! without any crash data being read out via the Memfault packetizer uint8_t crash_count; uint8_t rsvd1[1]; uint8_t coredump_saved; uint32_t last_reboot_reason; // eMemfaultRebootReason or MEMFAULT_REBOOT_REASON_NOT_SET uint32_t pc; uint32_t lr; //! Most MCUs have a register which reveals why a device rebooted. //! //! This can be particularly useful for debugging reasons for unexpected reboots //! (where no coredump was saved or no user initiated reset took place). Examples //! of this include brown out resets (BORs) & hardware watchdog resets. uint32_t reset_reason_reg0; //! Bitfield tracking sessions that were active prior to reboot uint32_t active_sessions; // Reserved for future additions uint32_t rsvd2[9]; } sMfltRebootInfo; typedef struct MfltResetReasonInfo { eMemfaultRebootReason reason; uint32_t pc; uint32_t lr; uint32_t reset_reason_reg0; bool coredump_saved; } sMfltResetReasonInfo; //! Clears any crash information which was stored void memfault_reboot_tracking_clear_reset_info(void); //! Clears stored reboot reason stored at bootup void memfault_reboot_tracking_clear_reboot_reason(void); bool memfault_reboot_tracking_read_reset_info(sMfltResetReasonInfo *info); #ifdef __cplusplus } #endif ================================================ FILE: components/core/src/memfault_reboot_tracking_serializer.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Reads the current reboot tracking information and converts it into an "trace" event which can //! be sent to the Memfault cloud #include #include "memfault/core/data_packetizer_source.h" #include "memfault/core/debug_log.h" #include "memfault/core/event_storage.h" #include "memfault/core/event_storage_implementation.h" #include "memfault/core/platform/device_info.h" #include "memfault/core/serializer_helper.h" #include "memfault/core/serializer_key_ids.h" #include "memfault/util/cbor.h" #include "memfault_reboot_tracking_private.h" #define MEMFAULT_REBOOT_TRACKING_BAD_PARAM (-1) #define MEMFAULT_REBOOT_TRACKING_STORAGE_TOO_SMALL (-2) static bool prv_serialize_reboot_info(sMemfaultCborEncoder *e, const sMfltResetReasonInfo *info) { const size_t extra_event_info_pairs = 1 /* coredump_saved */ + ((info->reset_reason_reg0 != 0) ? 1 : 0); const sMemfaultTraceEventHelperInfo helper_info = { .reason_key = kMemfaultTraceInfoEventKey_Reason, .reason_value = info->reason, .pc = info->pc, .lr = info->lr, .extra_event_info_pairs = extra_event_info_pairs, }; bool success = memfault_serializer_helper_encode_trace_event(e, &helper_info); if (success && info->reset_reason_reg0) { success = memfault_serializer_helper_encode_uint32_kv_pair( e, kMemfaultTraceInfoEventKey_McuReasonRegister, info->reset_reason_reg0); } if (success) { success = memfault_serializer_helper_encode_uint32_kv_pair( e, kMemfaultTraceInfoEventKey_CoredumpSaved, info->coredump_saved); } return success; } static bool prv_encode_cb(sMemfaultCborEncoder *encoder, void *ctx) { const sMfltResetReasonInfo *info = (const sMfltResetReasonInfo *)ctx; return prv_serialize_reboot_info(encoder, info); } size_t memfault_reboot_tracking_compute_worst_case_storage_size(void) { // a reset reason with maximal values so we can compute the worst case encoding size sMfltResetReasonInfo reset_reason = { .reason = kMfltRebootReason_HardFault, .pc = UINT32_MAX, .lr = UINT32_MAX, .reset_reason_reg0 = UINT32_MAX, .coredump_saved = 1, }; sMemfaultCborEncoder encoder = { 0 }; return memfault_serializer_helper_compute_size(&encoder, prv_encode_cb, &reset_reason); } int memfault_reboot_tracking_collect_reset_info(const sMemfaultEventStorageImpl *impl) { if (impl == NULL) { return MEMFAULT_REBOOT_TRACKING_BAD_PARAM; } memfault_serializer_helper_check_storage_size( impl, memfault_reboot_tracking_compute_worst_case_storage_size, "reboot"); // we'll fall through and try to encode anyway and later return a failure // code if the event could not be stored. This line is here to give the user // an idea of how they should size things sMfltResetReasonInfo info; if (!memfault_reboot_tracking_read_reset_info(&info)) { // Two ways we get here: // 1. memfault_reboot_tracking_boot() has not yet been called // 2. memfault_reboot_tracking_boot() was called but there's no info // about the last reboot reason. To fix this, pass bootup_info when // calling memfault_reboot_tracking_boot() // For more details about reboot tracking in general see https://mflt.io//2QlOlgH MEMFAULT_LOG_WARN("%s: No reset info collected", __func__); return 0; } sMemfaultCborEncoder encoder = { 0 }; const bool success = memfault_serializer_helper_encode_to_storage(&encoder, impl, prv_encode_cb, &info); if (!success) { const size_t storage_max_size = impl->get_storage_size_cb(); const size_t worst_case_size_needed = memfault_reboot_tracking_compute_worst_case_storage_size(); (void)storage_max_size, (void)worst_case_size_needed; // to silence unused variable warnings MEMFAULT_LOG_WARN("Event storage (%d) smaller than largest reset reason (%d)", (int)storage_max_size, (int)worst_case_size_needed); return MEMFAULT_REBOOT_TRACKING_STORAGE_TOO_SMALL; } memfault_reboot_tracking_clear_reset_info(); return 0; } ================================================ FILE: components/core/src/memfault_sdk_assert.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/platform/core.h" #include "memfault/core/sdk_assert.h" MEMFAULT_WEAK void memfault_sdk_assert_func_noreturn(void) { while (1) { } } void memfault_sdk_assert_func(void) { void *return_address; MEMFAULT_GET_LR(return_address); (void)return_address; MEMFAULT_LOG_ERROR("ASSERT! LR: 0x%" PRIx32, (uint32_t)(uintptr_t)return_address); memfault_platform_halt_if_debugging(); memfault_sdk_assert_func_noreturn(); } ================================================ FILE: components/core/src/memfault_self_test.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Memfault SDK self test functions to verify SDK integration #include #include #include #include #include "memfault/core/build_info.h" #include "memfault/core/data_export.h" #include "memfault/core/debug_log.h" #include "memfault/core/event_storage.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/device_info.h" #include "memfault/core/platform/system_time.h" #include "memfault/core/reboot_tracking.h" #include "memfault/core/self_test.h" #include "memfault/core/trace_event.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" #include "memfault_self_test_private.h" // Wrap this definition to prevent unused macro warning #if !MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE #ifndef MEMFAULT_SELF_TEST_COREDUMP_STORAGE_DISABLE_MSG #define MEMFAULT_SELF_TEST_COREDUMP_STORAGE_DISABLE_MSG \ "Set MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE in memfault_platform_config.h" #endif // !MEMFAULT_SELF_TEST_COREDUMP_STORAGE_DISABLE_MSG #endif // !MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE #if !defined(MEMFAULT_UNITTEST_SELF_TEST) typedef enum { kDeviceInfoField_DeviceSerial = 0, kDeviceInfoField_SoftwareType, kDeviceInfoField_SoftwareVersion, kDeviceInfoField_HardwareVersion, kDeviceInfoField_BuildId, kDeviceInfoField_Max, } eDeviceInfoField; static const char *s_device_info_field_names[] = { [kDeviceInfoField_DeviceSerial] = "Device Serial", [kDeviceInfoField_SoftwareType] = "Software Type", [kDeviceInfoField_SoftwareVersion] = "Software Version", [kDeviceInfoField_HardwareVersion] = "Hardware Version", [kDeviceInfoField_BuildId] = "Build ID", }; typedef bool (*FieldCharValidator)(unsigned char c); static const FieldCharValidator s_device_info_str_validators[] = { [kDeviceInfoField_DeviceSerial] = memfault_self_test_valid_device_serial, [kDeviceInfoField_SoftwareType] = memfault_self_test_valid_hw_version_sw_type, [kDeviceInfoField_SoftwareVersion] = memfault_self_test_valid_sw_version, [kDeviceInfoField_HardwareVersion] = memfault_self_test_valid_hw_version_sw_type, NULL, }; MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_device_info_field_names) == kDeviceInfoField_Max, "Mismatch in field name table, must be equal"); MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_device_info_str_validators) == kDeviceInfoField_Max, "Mismatch in string validator table, must be equal"); static bool prv_validate_string(const char *str, size_t len, FieldCharValidator is_valid) { for (size_t idx = 0; idx < len; ++idx) { unsigned char c = (unsigned char)str[idx]; if (!is_valid(c)) { MEMFAULT_LOG_ERROR("Invalid char %c, found in %s", c, str); return false; } } return true; } static bool is_field_valid(const char *str, eDeviceInfoField field) { if (str == NULL) { return false; } // First validate min and max length // Max + 1 needed determine if we exceeded bounds before NULL found size_t len = memfault_strnlen(str, MEMFAULT_DEVICE_INFO_MAX_STRING_SIZE + 1); if ((len < 1) || (len > MEMFAULT_DEVICE_INFO_MAX_STRING_SIZE)) { (void)s_device_info_field_names; // Avoid unused variable warning when logging is disabled MEMFAULT_LOG_ERROR("Invalid length %zu for %s", len, s_device_info_field_names[field]); return false; } if (field < kDeviceInfoField_Max) { FieldCharValidator validator = s_device_info_str_validators[field]; return (validator != NULL) ? prv_validate_string(str, len, validator) : false; } else { MEMFAULT_LOG_ERROR("Invalid device info string type %u for %s", field, str); return false; } } static uint32_t prv_validate_device_info_field(const char *str, eDeviceInfoField field) { uint32_t result = 0; if (!is_field_valid(str, field)) { result = (uint32_t)(1 << field); } return result; } static uint32_t prv_validate_device_info(void) { sMemfaultDeviceInfo device_info = { 0 }; memfault_platform_get_device_info(&device_info); uint32_t results = 0; // Validate each field in device_info results |= prv_validate_device_info_field(device_info.device_serial, kDeviceInfoField_DeviceSerial); results |= prv_validate_device_info_field(device_info.hardware_version, kDeviceInfoField_HardwareVersion); results |= prv_validate_device_info_field(device_info.software_version, kDeviceInfoField_SoftwareVersion); results |= prv_validate_device_info_field(device_info.software_type, kDeviceInfoField_SoftwareType); return results; } static uint32_t prv_validate_build_id(void) { char build_id_str[(MEMFAULT_BUILD_ID_LEN * 2) + 1] = { 0 }; uint32_t result = 0; if (!memfault_build_id_get_string(build_id_str, MEMFAULT_ARRAY_SIZE(build_id_str))) { result = (uint32_t)(1 << kDeviceInfoField_BuildId); } return result; } static void prv_device_info_test_describe(uint32_t results) { if (results == 0) { MEMFAULT_SELF_TEST_OUTPUT_LOG("All fields valid"); return; } MEMFAULT_LOG_ERROR("One or more fields is invalid. Check for correct length and contents"); for (size_t i = 0; i < kDeviceInfoField_Max; i++) { // Check if bit cleared, cleared bits indicate an invalid field if ((results & (1u << i))) { MEMFAULT_LOG_ERROR("%s invalid", s_device_info_field_names[i]); } } } uint32_t memfault_self_test_device_info_test(void) { uint32_t results = 0; MEMFAULT_SELF_TEST_PRINT_HEADER("Device Info Test"); // Validate the build ID results |= prv_validate_build_id(); // Valid device info fields results |= prv_validate_device_info(); prv_device_info_test_describe(results); MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return results; } typedef enum MfltBootComponent { kMfltBootComponent_EventStorage = 0, kMfltBootComponent_Logging, kMfltBootComponent_RebootTracking, kMfltBootComponent_TraceEvent, kMfltBootComponent_NumTypes, } eMfltBootComponent; static const struct { char *component_name; bool (*booted)(void); } s_boot_components[kMfltBootComponent_NumTypes] = { [kMfltBootComponent_EventStorage] = { .component_name = "Event Storage", memfault_event_storage_booted, }, [kMfltBootComponent_Logging] = { .component_name = "Logging", memfault_log_booted, }, [kMfltBootComponent_RebootTracking] = { .component_name = "Reboot Tracking", memfault_reboot_tracking_booted, }, [kMfltBootComponent_TraceEvent] = { .component_name = "Trace Event", memfault_trace_event_booted, }, }; uint32_t memfault_self_test_component_boot_test(void) { uint32_t result = 0; MEMFAULT_SELF_TEST_PRINT_HEADER("Component Boot Test"); MEMFAULT_SELF_TEST_OUTPUT_LOG("%-16s|%8s|", "Component", "Booted?"); MEMFAULT_SELF_TEST_OUTPUT_LOG("-----------------------------"); for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_boot_components); i++) { bool booted = s_boot_components[i].booted(); if (booted) { MEMFAULT_SELF_TEST_OUTPUT_LOG("%-16s|%8s|", s_boot_components[i].component_name, "yes"); } else { MEMFAULT_LOG_ERROR("%-16s|%8s|", s_boot_components[i].component_name, "no"); result |= (1 << i); } } if (result == 0) { MEMFAULT_SELF_TEST_OUTPUT_LOG("All components booted"); } MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return result; } void memfault_self_test_data_export_test(void) { char test_line[MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN] = { 0 }; // Set every char to '+' except for the last char before null terminator memset(test_line, '+', MEMFAULT_ARRAY_SIZE(test_line) - 2); // Last character before null should be '1' test_line[MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN - 2] = '1'; MEMFAULT_SELF_TEST_PRINT_HEADER("Data Export Line Test"); MEMFAULT_SELF_TEST_OUTPUT_LOG( "Printing %u characters, confirm line ends with '1' and is not split", (unsigned)(MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN - 1)); MEMFAULT_SELF_TEST_OUTPUT_LOG("%s", test_line); MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); } static void prv_print_region_group_info(const char *group_name, const sMfltCoredumpRegion *regions, const size_t num_regions) { (void)group_name; MEMFAULT_SELF_TEST_OUTPUT_LOG("Coredump group: %s", group_name); MEMFAULT_SELF_TEST_OUTPUT_LOG("-----------------------------"); MEMFAULT_SELF_TEST_OUTPUT_LOG("%10s|%10s|%6s|", "Address", "Length", "Type"); MEMFAULT_SELF_TEST_OUTPUT_LOG("-----------------------------"); for (size_t i = 0; (regions != NULL) && (i < num_regions); i++) { sMfltCoredumpRegion region = regions[i]; (void)region; // to silence compiler warning when logs are disabled MEMFAULT_SELF_TEST_OUTPUT_LOG("0x%08" PRIxPTR "|%10" PRIu32 "|%6u|", (uintptr_t)region.region_start, region.region_size, region.type); } MEMFAULT_SELF_TEST_OUTPUT_LOG("-----------------------------"); } uint32_t memfault_self_test_coredump_regions_test(void) { uint32_t result = 0; const sMfltCoredumpRegion *platform_regions = NULL; size_t num_platform_regions = 0; // Set stack address to current sp and unknown reboot reason // These are dummy values so we can get as close as possible to the regions // that memfault_platform_coredump_get_regions will return sCoredumpCrashInfo info = { .stack_address = &num_platform_regions, .trace_reason = kMfltRebootReason_Unknown, }; // Call memfault_platform_coredump_get_regions platform_regions = memfault_platform_coredump_get_regions(&info, &num_platform_regions); // Get arch and SDK regions size_t num_arch_regions = 0; const sMfltCoredumpRegion *arch_regions = memfault_coredump_get_arch_regions(&num_arch_regions); size_t num_sdk_regions = 0; const sMfltCoredumpRegion *sdk_regions = memfault_coredump_get_sdk_regions(&num_sdk_regions); MEMFAULT_SELF_TEST_PRINT_HEADER("Coredump Regions Test"); if (num_platform_regions == 0) { MEMFAULT_LOG_ERROR("Number of platform regions = 0"); result = 1; } if (platform_regions == NULL) { MEMFAULT_LOG_ERROR("Platform regions was NULL"); result = 1; } prv_print_region_group_info("Platform Regions", platform_regions, num_platform_regions); prv_print_region_group_info("Arch Regions", arch_regions, num_arch_regions); prv_print_region_group_info("SDK Regions", sdk_regions, num_sdk_regions); MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return result; } MEMFAULT_NORETURN void memfault_self_test_reboot_reason_test(void) { // Set a known reason and allow the device to reboot MEMFAULT_SELF_TEST_PRINT_HEADER("Reboot Reason Test"); MEMFAULT_SELF_TEST_OUTPUT_LOG( "This test will now reboot the device to test reboot reason tracking"); MEMFAULT_SELF_TEST_OUTPUT_LOG("After the device reboots, please run with reboot_verify argument"); MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_SelfTest); memfault_platform_reboot(); } uint32_t memfault_self_test_reboot_reason_test_verify(void) { // Use explicit initializer to avoid ti-armcl warning sMfltRebootReason reboot_reason = { .prior_stored_reason = kMfltRebootReason_Unknown, .reboot_reg_reason = kMfltRebootReason_Unknown, }; int result = memfault_reboot_tracking_get_reboot_reason(&reboot_reason); bool success = (result == 0) && (reboot_reason.prior_stored_reason == kMfltRebootReason_SelfTest); MEMFAULT_SELF_TEST_PRINT_HEADER("Reboot Reason Test"); if (success) { MEMFAULT_SELF_TEST_OUTPUT_LOG("Reboot reason test successful"); } else { MEMFAULT_LOG_ERROR("Reboot reason test failed:"); MEMFAULT_LOG_ERROR("get_reboot_reason result: %d, " "prior_stored_reason: 0x%08" PRIx32, result, (uint32_t)reboot_reason.prior_stored_reason); } MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return success ? 0 : 1; } // Use MEMFAULT_NO_OPT to prevent busy loop from being removed MEMFAULT_NO_OPT static uint32_t prv_get_time_since_boot_test(void) { uint64_t start_time_ms = memfault_platform_get_time_since_boot_ms(); if (start_time_ms == 0) { MEMFAULT_LOG_ERROR("Time since boot reported as 0"); return (1 << 0); } // Force a 100ms delay to ensure enough time has passed to yield a different timestamp memfault_self_test_platform_delay(100); uint64_t end_time_ms = memfault_platform_get_time_since_boot_ms(); if ((end_time_ms <= start_time_ms)) { MEMFAULT_LOG_ERROR("Time since boot not monotonically increasing: start[%" PRIu32 "] vs end[%" PRIu32 "]", (uint32_t)start_time_ms, (uint32_t)end_time_ms); return (1 << 1); } MEMFAULT_SELF_TEST_OUTPUT_LOG("Time since boot test succeeded"); return 0; } // Arbitrary point in recent history // The time test did not exist before 2024/01/29 UTC #define MEMFAULT_SELF_TEST_TIMESTAMP_ANCHOR (1706486400) static uint32_t prv_platform_time_get_current_test(void) { sMemfaultCurrentTime mflt_time = { .type = kMemfaultCurrentTimeType_Unknown, .info = { .unix_timestamp_secs = 0, }, }; bool result = memfault_platform_time_get_current(&mflt_time); if (!result) { MEMFAULT_LOG_ERROR("Current timestamp could not be recovered"); return (1 << 2); } if (mflt_time.type != kMemfaultCurrentTimeType_UnixEpochTimeSec) { MEMFAULT_LOG_ERROR("Invalid time type returned: %u", mflt_time.type); return (1 << 3); } if (mflt_time.info.unix_timestamp_secs < MEMFAULT_SELF_TEST_TIMESTAMP_ANCHOR) { MEMFAULT_LOG_ERROR("Timestamp too far in the past: %" PRIu32, (uint32_t)mflt_time.info.unix_timestamp_secs); return (1 << 4); } MEMFAULT_SELF_TEST_OUTPUT_LOG( "Verify received timestamp for accuracy. Timestamp received %" PRIu32, (uint32_t)mflt_time.info.unix_timestamp_secs); return 0; } uint32_t memfault_self_test_time_test(void) { MEMFAULT_SELF_TEST_PRINT_HEADER("Time Test"); uint32_t result = prv_get_time_since_boot_test(); result |= prv_platform_time_get_current_test(); MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return result; } uint32_t memfault_self_test_coredump_storage_capacity_test(void) { MEMFAULT_SELF_TEST_PRINT_HEADER("Coredump Storage Capacity Test"); bool capacity_ok = memfault_coredump_storage_check_size(); if (capacity_ok) { size_t total_size = 0; size_t capacity = 0; memfault_coredump_size_and_storage_capacity(&total_size, &capacity); MEMFAULT_SELF_TEST_OUTPUT_LOG("Total size required: %u bytes", (unsigned)total_size); MEMFAULT_SELF_TEST_OUTPUT_LOG("Storage capacity: %u bytes", (unsigned)capacity); } MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return capacity_ok ? 0 : 1; } uint32_t memfault_self_test_coredump_storage_test(void) { MEMFAULT_SELF_TEST_PRINT_HEADER("Coredump Storage Test"); if (memfault_coredump_has_valid_coredump(NULL)) { MEMFAULT_LOG_ERROR("Aborting test, valid coredump present"); MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return (1 << 0); } // Wrap test with calls to disable/enable irqs to allow test to run uninterrupted // Abort if we cannot disable irqs bool irqs_disabled = memfault_self_test_platform_disable_irqs(); if (!irqs_disabled) { MEMFAULT_LOG_ERROR("Aborting test, could not disable interrupts"); MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return (1 << 1); } memfault_coredump_storage_debug_test_begin(); if (!memfault_self_test_platform_enable_irqs()) { MEMFAULT_LOG_WARN("Failed to enable interrupts after test completed"); } bool result = memfault_coredump_storage_debug_test_finish(); MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_END_OUTPUT); return result ? 0 : (1 << 2); } #endif // defined(MEMFAULT_UNITTEST_SELF_TEST) int memfault_self_test_run(uint32_t run_flags) { uint32_t result = 0; if (run_flags & kMemfaultSelfTestFlag_DeviceInfo) { result |= memfault_self_test_device_info_test(); } if (run_flags & kMemfaultSelfTestFlag_ComponentBoot) { result |= memfault_self_test_component_boot_test(); } if (run_flags & kMemfaultSelfTestFlag_CoredumpRegions) { result |= memfault_self_test_coredump_regions_test(); } if (run_flags & kMemfaultSelfTestFlag_DataExport) { memfault_self_test_data_export_test(); } if (run_flags & kMemfaultSelfTestFlag_RebootReason) { memfault_self_test_reboot_reason_test(); } if (run_flags & kMemfaultSelfTestFlag_RebootReasonVerify) { result = memfault_self_test_reboot_reason_test_verify(); } if (run_flags & kMemfaultSelfTestFlag_PlatformTime) { result |= memfault_self_test_time_test(); } if (run_flags & kMemfaultSelfTestFlag_CoredumpStorage) { #if MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE result |= memfault_self_test_coredump_storage_test(); #else MEMFAULT_LOG_ERROR("Coredump storage test not enabled"); MEMFAULT_LOG_ERROR(MEMFAULT_SELF_TEST_COREDUMP_STORAGE_DISABLE_MSG); result = 1; #endif } if (run_flags & kMemfaultSelfTestFlag_CoredumpStorageCapacity) { result |= memfault_self_test_coredump_storage_capacity_test(); } return (result == 0) ? 0 : 1; } ================================================ FILE: components/core/src/memfault_self_test_private.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #include #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #ifdef __cplusplus extern "C" { #endif //! The below macros are exposed for verification in unit tests only, and are //! not intended for use outside of the self test module. //! Macro for beginning of test string #define MEMFAULT_SELF_TEST_END_OUTPUT "=============================\n" //! Macro for end of test string #define MEMFAULT_SELF_TEST_BEGIN_OUTPUT "=============================" // Default to INFO level for the self test output #if !defined(MEMFAULT_SELF_TEST_OUTPUT_LOG) #define MEMFAULT_SELF_TEST_OUTPUT_LOG MEMFAULT_LOG_INFO #endif #define MEMFAULT_SELF_TEST_PRINT_HEADER(test_name) \ do { \ MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_BEGIN_OUTPUT); \ MEMFAULT_SELF_TEST_OUTPUT_LOG(test_name); \ MEMFAULT_SELF_TEST_OUTPUT_LOG(MEMFAULT_SELF_TEST_BEGIN_OUTPUT); \ } while (0) // Checks if char fits regex: [-a-zA-z0-0_] bool memfault_self_test_valid_device_serial(unsigned char c); //! Checks if char fits regex: [-a-zA-Z0-9_\.\+:] bool memfault_self_test_valid_hw_version_sw_type(unsigned char c); //! Simple wrapper for isprint, software version does not have any regex to match bool memfault_self_test_valid_sw_version(unsigned char c); //! Runs device info and build ID test uint32_t memfault_self_test_device_info_test(void); //! Runs component boot test uint32_t memfault_self_test_component_boot_test(void); //! Data export test void memfault_self_test_data_export_test(void); //! Runs coredump regions test uint32_t memfault_self_test_coredump_regions_test(void); //! Runs reboot reason test //! //! This function begins the test by setting a known reboot reason and then rebooting the device //! After the device reboots, memfault_self_test_reboot_reason_test_verify should be used to verify //! the test results MEMFAULT_NORETURN void memfault_self_test_reboot_reason_test(void); //! Verifies reboot reason test results uint32_t memfault_self_test_reboot_reason_test_verify(void); //! Runs tests against platform time functions uint32_t memfault_self_test_time_test(void); //! Runs tests against platform coredump storage implementation //! //! This test will check for the existence of a valid coredump before proceeding and abort if one is //! found to prevent overwriting valid data uint32_t memfault_self_test_coredump_storage_test(void); //! Runs test to check capacity of coredump storage against worst case uint32_t memfault_self_test_coredump_storage_capacity_test(void); //! Internal implementation of strnlen //! //! Support for strnlen is inconsistent across a lot of libc implementations so we implement this //! here for compatibility reasons. See //! https://pubs.opengroup.org/onlinepubs/9699919799/functions/strlen.html //! @param str Pointer to a C string. Pointer must not be NULL //! @param n Maximum number of characters to examine when determining the length of the string //! @returns The strnlen() function shall return the number of bytes preceding the first null byte //! in the array to which s points, if s contains a null byte within the first maxlen bytes; //! otherwise, it shall return maxlen size_t memfault_strnlen(const char *str, size_t n); #ifdef __cplusplus } #endif ================================================ FILE: components/core/src/memfault_self_test_utils.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include #include #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/sdk_assert.h" #include "memfault/core/self_test.h" #include "memfault_self_test_private.h" bool memfault_self_test_valid_device_serial(unsigned char c) { return (isalnum(c) > 0) || (c == '_') || (c == '-'); } bool memfault_self_test_valid_hw_version_sw_type(unsigned char c) { return (memfault_self_test_valid_device_serial(c)) || (c == '.') || (c == '+') || (c == ':'); } bool memfault_self_test_valid_sw_version(unsigned char c) { return (isprint(c) > 0); } static const struct { char *name; uint32_t value; } s_test_flags[] = { { .name = "reboot", .value = kMemfaultSelfTestFlag_RebootReason, }, { .name = "reboot_verify", .value = kMemfaultSelfTestFlag_RebootReasonVerify, }, { .name = "coredump_storage", .value = kMemfaultSelfTestFlag_CoredumpStorage, }, }; #define SELF_TEST_MAX_NAME_LEN 30 uint32_t memfault_self_test_arg_to_flag(const char *arg) { MEMFAULT_SDK_ASSERT(arg != NULL); for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_test_flags); i++) { if (strncmp(arg, s_test_flags[i].name, SELF_TEST_MAX_NAME_LEN) == 0) { return (uint32_t)s_test_flags[i].value; } } MEMFAULT_LOG_WARN("No test type found for arg: %s. Default tests selected", arg); return kMemfaultSelfTestFlag_Default; } // Include an implementation of strnlen, based on picolibc/newlib // For reference: https://github.com/picolibc/picolibc/blob/main/newlib/libc/string/strnlen.c size_t memfault_strnlen(const char *str, size_t n) { size_t count = 0; // Must check n first to prevent out of bounds access while (n-- && *str) { count++; str++; } return count; } MEMFAULT_WEAK void memfault_self_test_platform_delay(MEMFAULT_UNUSED uint32_t delay_ms) { MEMFAULT_LOG_ERROR("%s not implemented for this platform", __func__); } MEMFAULT_WEAK bool memfault_self_test_platform_disable_irqs(void) { MEMFAULT_LOG_ERROR("%s not implemented for this platform", __func__); return false; } MEMFAULT_WEAK bool memfault_self_test_platform_enable_irqs(void) { MEMFAULT_LOG_ERROR("%s not implemented for this platform", __func__); return false; } ================================================ FILE: components/core/src/memfault_serializer_helper.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! See header for more details #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/event_storage_implementation.h" #include "memfault/core/platform/device_info.h" #include "memfault/core/platform/system_time.h" #include "memfault/core/serializer_helper.h" #include "memfault/core/serializer_key_ids.h" #include "memfault/util/cbor.h" #if MEMFAULT_EVENT_INCLUDE_BUILD_ID #include "memfault/core/build_info.h" #include "memfault_build_id_private.h" #endif //! The number of messages dropped since the last successful send static uint32_t s_num_storage_drops = 0; //! A running sum of total messages dropped since memfault_serializer_helper_read_drop_count() was //! last called static uint32_t s_last_drop_count = 0; static bool prv_encode_event_key_string_pair(sMemfaultCborEncoder *encoder, eMemfaultEventKey key, const char *value) { return memfault_cbor_encode_unsigned_integer(encoder, key) && memfault_cbor_encode_string(encoder, value); } static bool prv_encode_device_version_info(sMemfaultCborEncoder *e) { // Encoding something like: // // (Optional) "device_serial": "ABCD1234", // "software_type": "main-fw", // "software_version": "1.0.0", // "hardware_version": "hwrev1", // // NOTE: int keys are used instead of strings to minimize the wire payload. sMemfaultDeviceInfo info = { 0 }; memfault_platform_get_device_info(&info); #if MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL if (!prv_encode_event_key_string_pair(e, kMemfaultEventKey_DeviceSerial, info.device_serial)) { return false; } #endif if (!prv_encode_event_key_string_pair(e, kMemfaultEventKey_SoftwareType, info.software_type)) { return false; } if (!prv_encode_event_key_string_pair(e, kMemfaultEventKey_SoftwareVersion, info.software_version)) { return false; } if (!prv_encode_event_key_string_pair(e, kMemfaultEventKey_HardwareVersion, info.hardware_version)) { return false; } return true; } bool memfault_serializer_helper_encode_uint32_kv_pair(sMemfaultCborEncoder *encoder, uint32_t key, uint32_t value) { return memfault_cbor_encode_unsigned_integer(encoder, key) && memfault_cbor_encode_unsigned_integer(encoder, value); } bool memfault_serializer_helper_encode_int32_kv_pair(sMemfaultCborEncoder *encoder, uint32_t key, int32_t value) { return memfault_cbor_encode_unsigned_integer(encoder, key) && memfault_cbor_encode_signed_integer(encoder, value); } bool memfault_serializer_helper_encode_byte_string_kv_pair(sMemfaultCborEncoder *encoder, uint32_t key, const void *buf, size_t buf_len) { return memfault_cbor_encode_unsigned_integer(encoder, key) && memfault_cbor_encode_byte_string(encoder, buf, buf_len); } static bool prv_encode_event_key_uint32_pair(sMemfaultCborEncoder *encoder, eMemfaultEventKey key, uint32_t value) { return memfault_cbor_encode_unsigned_integer(encoder, key) && memfault_cbor_encode_unsigned_integer(encoder, value); } bool memfault_serializer_helper_encode_metadata(sMemfaultCborEncoder *encoder, eMemfaultEventType type) { sMemfaultCurrentTime time; if (!memfault_platform_time_get_current(&time)) { time.type = kMemfaultCurrentTimeType_Unknown; } return memfault_serializer_helper_encode_metadata_with_time(encoder, type, &time); } bool memfault_serializer_helper_encode_metadata_with_time(sMemfaultCborEncoder *encoder, eMemfaultEventType type, const sMemfaultCurrentTime *time) { const bool unix_timestamp_available = (time != NULL) && (time->type == kMemfaultCurrentTimeType_UnixEpochTimeSec); #if MEMFAULT_EVENT_INCLUDE_BUILD_ID sMemfaultBuildInfo info; const bool has_build_id = memfault_build_info_read(&info); #else const bool has_build_id = false; #endif const size_t top_level_num_pairs = 1 /* type */ + (unix_timestamp_available ? 1 : 0) + #if MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL 1 + #endif 3 /* sw version, sw type, hw version */ + (has_build_id ? 1 : 0) + 1 /* cbor schema version */ + 1 /* event_info */; memfault_cbor_encode_dictionary_begin(encoder, top_level_num_pairs); if (!prv_encode_event_key_uint32_pair(encoder, kMemfaultEventKey_Type, type)) { return false; } if (!memfault_serializer_helper_encode_uint32_kv_pair( encoder, kMemfaultEventKey_CborSchemaVersion, MEMFAULT_CBOR_SCHEMA_VERSION_V1)) { return false; } if (!prv_encode_device_version_info(encoder)) { return false; } #if MEMFAULT_EVENT_INCLUDE_BUILD_ID MEMFAULT_STATIC_ASSERT( MEMFAULT_EVENT_INCLUDED_BUILD_ID_SIZE_BYTES >= 5 && MEMFAULT_EVENT_INCLUDED_BUILD_ID_SIZE_BYTES <= sizeof(info.build_id), "MEMFAULT_EVENT_INCLUDED_BUILD_ID_SIZE_BYTES must be between 5 and 20 (inclusive)"); if (has_build_id && !memfault_serializer_helper_encode_byte_string_kv_pair( encoder, kMemfaultEventKey_BuildId, info.build_id, MEMFAULT_EVENT_INCLUDED_BUILD_ID_SIZE_BYTES)) { return false; } #endif return !unix_timestamp_available || prv_encode_event_key_uint32_pair(encoder, kMemfaultEventKey_CapturedDateUnixTimestamp, (uint32_t)time->info.unix_timestamp_secs); } bool memfault_serializer_helper_encode_trace_event(sMemfaultCborEncoder *e, const sMemfaultTraceEventHelperInfo *info) { if (!memfault_serializer_helper_encode_metadata(e, kMemfaultEventType_Trace)) { return false; } const size_t num_entries = 1 /* reason */ + ((info->pc != 0) ? 1 : 0) + ((info->lr != 0) ? 1 : 0) + info->extra_event_info_pairs; if (!memfault_cbor_encode_unsigned_integer(e, kMemfaultEventKey_EventInfo) || !memfault_cbor_encode_dictionary_begin(e, num_entries)) { return false; } if (!memfault_serializer_helper_encode_uint32_kv_pair(e, info->reason_key, info->reason_value)) { return false; } bool success = true; if (info->pc) { success = memfault_serializer_helper_encode_uint32_kv_pair( e, kMemfaultTraceInfoEventKey_ProgramCounter, info->pc); } if (success && info->lr) { success = memfault_serializer_helper_encode_uint32_kv_pair( e, kMemfaultTraceInfoEventKey_LinkRegister, info->lr); } return success; } typedef struct { const sMemfaultEventStorageImpl *storage_impl; } sMemfaultSerializerHelperEncoderCtx; static void prv_encoder_write_cb(void *ctx, MEMFAULT_UNUSED uint32_t offset, const void *buf, size_t buf_len) { const sMemfaultEventStorageImpl *storage_impl = ((sMemfaultSerializerHelperEncoderCtx *)ctx)->storage_impl; storage_impl->append_data_cb(buf, buf_len); } bool memfault_serializer_helper_encode_to_storage( sMemfaultCborEncoder *encoder, const sMemfaultEventStorageImpl *storage_impl, MemfaultSerializerHelperEncodeCallback encode_callback, void *ctx) { const size_t space_available = storage_impl->begin_write_cb(); bool success; { sMemfaultSerializerHelperEncoderCtx encoder_ctx = { .storage_impl = storage_impl, }; memfault_cbor_encoder_init(encoder, prv_encoder_write_cb, &encoder_ctx, space_available); success = encode_callback(encoder, ctx); memfault_cbor_encoder_deinit(encoder); } const bool rollback = !success; storage_impl->finish_write_cb(rollback); if (!success) { if (s_num_storage_drops == 0) { MEMFAULT_LOG_ERROR("Event storage full"); } s_num_storage_drops++; } else if (s_num_storage_drops != 0) { MEMFAULT_LOG_INFO("Event saved successfully after %d drops", (int)s_num_storage_drops); s_last_drop_count += s_num_storage_drops; s_num_storage_drops = 0; } return success; } uint32_t memfault_serializer_helper_read_drop_count(void) { const uint32_t drop_count = s_last_drop_count + s_num_storage_drops; s_last_drop_count = 0; s_num_storage_drops = 0; return drop_count; } size_t memfault_serializer_helper_compute_size( sMemfaultCborEncoder *encoder, MemfaultSerializerHelperEncodeCallback encode_callback, void *ctx) { memfault_cbor_encoder_size_only_init(encoder); encode_callback(encoder, ctx); return memfault_cbor_encoder_deinit(encoder); } bool memfault_serializer_helper_check_storage_size(const sMemfaultEventStorageImpl *storage_impl, size_t(compute_worst_case_size)(void), const char *event_type) { (void)event_type; // Check to see if the backing storage can hold at least one event // and return an error code in this situation so it's easier for an end user to catch it: const size_t storage_max_size = storage_impl->get_storage_size_cb(); const size_t worst_case_size_needed = compute_worst_case_size(); if (worst_case_size_needed > storage_max_size) { MEMFAULT_LOG_WARN("Event storage (%d) smaller than largest %s event (%d)", (int)storage_max_size, event_type, (int)worst_case_size_needed); return false; } return true; } ================================================ FILE: components/core/src/memfault_task_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Task watchdog implementation. #include "memfault/core/task_watchdog.h" #include "memfault/core/task_watchdog_impl.h" //! non-module definitions #include #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/panics/assert.h" #if MEMFAULT_TASK_WATCHDOG_ENABLE sMemfaultTaskWatchdogInfo g_memfault_task_channel_info; //! Catch if the timeout is set to an incompatible literal static const uint32_t s_watchdog_timeout_ms = MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS; void memfault_task_watchdog_init(void) { g_memfault_task_channel_info = (struct MemfaultTaskWatchdogInfo){ 0 }; } static bool prv_memfault_task_watchdog_expired(struct MemfaultTaskWatchdogChannel channel, uint64_t current_time_ms) { const bool active = channel.state == kMemfaultTaskWatchdogChannelState_Started; // const bool expired = (channel.fed_time_ms + s_watchdog_timeout_ms) < current_time_ms; const bool expired = (current_time_ms - channel.fed_time_ms) > s_watchdog_timeout_ms; return active && expired; } static size_t prv_memfault_task_watchdog_do_check(void) { const uint32_t time_since_boot_ms = memfault_platform_get_time_since_boot_ms(); size_t expired_channels_count = 0; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_task_channel_info.channels); i++) { if (prv_memfault_task_watchdog_expired(g_memfault_task_channel_info.channels[i], time_since_boot_ms)) { expired_channels_count++; } } return expired_channels_count; } void memfault_task_watchdog_check_all(void) { size_t expired_channels_count = prv_memfault_task_watchdog_do_check(); // if any channel reached expiration, trigger a panic if (expired_channels_count > 0) { MEMFAULT_TASK_WATCHDOG(); } else { memfault_task_watchdog_platform_refresh_callback(); } } void memfault_task_watchdog_bookkeep(void) { // only update the internal structure, don't trigger callbacks (void)prv_memfault_task_watchdog_do_check(); } void memfault_task_watchdog_start(eMemfaultTaskWatchdogChannel channel_id) { g_memfault_task_channel_info.channels[channel_id].fed_time_ms = memfault_platform_get_time_since_boot_ms(); g_memfault_task_channel_info.channels[channel_id].state = kMemfaultTaskWatchdogChannelState_Started; } void memfault_task_watchdog_feed(eMemfaultTaskWatchdogChannel channel_id) { g_memfault_task_channel_info.channels[channel_id].fed_time_ms = memfault_platform_get_time_since_boot_ms(); } void memfault_task_watchdog_stop(eMemfaultTaskWatchdogChannel channel_id) { g_memfault_task_channel_info.channels[channel_id].state = kMemfaultTaskWatchdogChannelState_Stopped; } //! Callback which is called when there are no expired tasks; can be used for //! example to reset a hardware watchdog MEMFAULT_WEAK void memfault_task_watchdog_platform_refresh_callback(void) { } #else // MEMFAULT_TASK_WATCHDOG_ENABLE void memfault_task_watchdog_bookkeep(void) { } #endif // MEMFAULT_TASK_WATCHDOG_ENABLE ================================================ FILE: components/core/src/memfault_trace_event.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include #include #include #include "memfault/config.h" #include "memfault/core/arch.h" #include "memfault/core/compact_log_serializer.h" #include "memfault/core/debug_log.h" #include "memfault/core/event_storage.h" #include "memfault/core/event_storage_implementation.h" #include "memfault/core/math.h" #include "memfault/core/serializer_helper.h" #include "memfault/core/serializer_key_ids.h" #include "memfault/core/trace_event.h" #include "memfault_trace_event_private.h" #define MEMFAULT_TRACE_EVENT_STORAGE_UNINITIALIZED (-1) #define MEMFAULT_TRACE_EVENT_STORAGE_OUT_OF_SPACE (-2) #define MEMFAULT_TRACE_EVENT_STORAGE_TOO_SMALL (-3) #define MEMFAULT_TRACE_EVENT_BAD_PARAM (-4) #define TRACE_EVENT_OPT_FIELD_STATUS_MASK (1 << 0) #define TRACE_EVENT_OPT_FIELD_LOG_MASK (1 << 1) typedef struct { eMfltTraceReasonUser reason; void *pc_addr; void *return_addr; //! A bitmask which tracks the optional fields have been captured. uint32_t opt_fields; // // Optional fields which can be captured // //! A status / error code to record alongside a trace event int32_t status_code; //! A null terminated log captured alongside a trace event const void *log; size_t log_len; } sMemfaultTraceEventInfo; static struct { const sMemfaultEventStorageImpl *storage_impl; } s_memfault_trace_event_ctx; int memfault_trace_event_boot(const sMemfaultEventStorageImpl *storage_impl) { if (storage_impl == NULL) { return MEMFAULT_TRACE_EVENT_BAD_PARAM; } if (!memfault_serializer_helper_check_storage_size( storage_impl, memfault_trace_event_compute_worst_case_storage_size, "trace")) { return MEMFAULT_TRACE_EVENT_STORAGE_TOO_SMALL; } s_memfault_trace_event_ctx.storage_impl = storage_impl; return 0; } static bool prv_encode_cb(sMemfaultCborEncoder *encoder, void *ctx) { const sMemfaultTraceEventInfo *info = (const sMemfaultTraceEventInfo *)ctx; uint32_t extra_event_info_pairs = 0; const bool status_present = (info->opt_fields & TRACE_EVENT_OPT_FIELD_STATUS_MASK) != 0; if (status_present) { extra_event_info_pairs++; } const bool log_present = (info->opt_fields & TRACE_EVENT_OPT_FIELD_LOG_MASK) != 0; if (log_present) { extra_event_info_pairs++; } sMemfaultTraceEventHelperInfo helper_info = { .reason_key = kMemfaultTraceInfoEventKey_UserReason, .reason_value = info->reason, .pc = (uint32_t)(uintptr_t)info->pc_addr, .lr = (uint32_t)(uintptr_t)info->return_addr, .extra_event_info_pairs = extra_event_info_pairs, }; bool success = memfault_serializer_helper_encode_trace_event(encoder, &helper_info); if (success && status_present) { success = memfault_serializer_helper_encode_int32_kv_pair( encoder, kMemfaultTraceInfoEventKey_StatusCode, info->status_code); } if (success && log_present) { #if !MEMFAULT_COMPACT_LOG_ENABLE success = memfault_serializer_helper_encode_byte_string_kv_pair( encoder, kMemfaultTraceInfoEventKey_Log, info->log, info->log_len); #else success = memfault_cbor_encode_unsigned_integer(encoder, kMemfaultTraceInfoEventKey_CompactLog) && memfault_cbor_join(encoder, info->log, info->log_len); #endif } return success; } typedef struct { uint32_t reason; sMemfaultTraceEventInfo info; #if MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED char log[MEMFAULT_TRACE_EVENT_MAX_LOG_LEN]; #endif } sMemfaultIsrTraceEvent; static sMemfaultIsrTraceEvent s_isr_trace_event; // To keep the number of cycles spent logging a trace from an ISR to a minimum we just copy the // values into a storage area and then flush the data after the system has returned from an ISR static int prv_trace_event_capture_from_isr(sMemfaultTraceEventInfo *trace_info) { uint32_t expected_reason = MEMFAULT_TRACE_REASON(Unknown); const uint32_t desired_reason = (uint32_t)trace_info->reason; if (s_isr_trace_event.reason != expected_reason) { return MEMFAULT_TRACE_EVENT_STORAGE_OUT_OF_SPACE; } // NOTE: It's perfectly fine to be interrupted by a higher priority interrupt at this point. // In the unlikely scenario where that exception also logged a trace event we will just wind // up overwriting it. The actual update of the reason (32 bit write) is an atomic op s_isr_trace_event.reason = desired_reason; s_isr_trace_event.info = *trace_info; if (s_isr_trace_event.info.log != NULL) { #if MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED memcpy(s_isr_trace_event.log, trace_info->log, trace_info->log_len); s_isr_trace_event.info.log = &s_isr_trace_event.log[0]; #endif } return 0; } static int prv_trace_event_capture(sMemfaultTraceEventInfo *info) { sMemfaultCborEncoder encoder = { 0 }; const bool success = memfault_serializer_helper_encode_to_storage( &encoder, s_memfault_trace_event_ctx.storage_impl, prv_encode_cb, info); if (!success) { return MEMFAULT_TRACE_EVENT_STORAGE_OUT_OF_SPACE; } return 0; } int memfault_trace_event_try_flush_isr_event(void) { if (s_isr_trace_event.reason == MEMFAULT_TRACE_REASON(Unknown)) { return 0; } const int rv = prv_trace_event_capture(&s_isr_trace_event.info); if (rv == 0) { // we successfully flushed the ISR event, mark the space as free to use again s_isr_trace_event.reason = MEMFAULT_TRACE_REASON(Unknown); } return rv; } static int prv_capture_trace_event_info(sMemfaultTraceEventInfo *info) { if (s_memfault_trace_event_ctx.storage_impl == NULL) { return MEMFAULT_TRACE_EVENT_STORAGE_UNINITIALIZED; } if (memfault_arch_is_inside_isr()) { return prv_trace_event_capture_from_isr(info); } // NOTE: We flush any ISR pended events here so that the order in which events are captured is // preserved. An user of the trace event API can also flush ISR events at anytime by explicitly // calling memfault_trace_event_try_flush_isr_event() const int rv = memfault_trace_event_try_flush_isr_event(); if (rv != 0) { return rv; } return prv_trace_event_capture(info); } int memfault_trace_event_capture(eMfltTraceReasonUser reason, void *pc_addr, void *lr_addr) { sMemfaultTraceEventInfo event_info = { .reason = reason, .pc_addr = pc_addr, .return_addr = lr_addr, }; return prv_capture_trace_event_info(&event_info); } int memfault_trace_event_with_status_capture(eMfltTraceReasonUser reason, void *pc_addr, void *lr_addr, int32_t status) { sMemfaultTraceEventInfo event_info = { .reason = reason, .pc_addr = pc_addr, .return_addr = lr_addr, .opt_fields = TRACE_EVENT_OPT_FIELD_STATUS_MASK, .status_code = status, }; return prv_capture_trace_event_info(&event_info); } #if !MEMFAULT_COMPACT_LOG_ENABLE int memfault_trace_event_with_log_capture(eMfltTraceReasonUser reason, void *pc_addr, void *lr_addr, const char *fmt, ...) { #if !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED // If a log capture takes place while in an ISR we just record a normal trace event if (memfault_arch_is_inside_isr()) { return memfault_trace_event_capture(reason, pc_addr, lr_addr); } #endif /* !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED */ // Note: By performing the vsnprintf in this function (rather than forward vargs in event_info), // the stdlib dependency only gets pulled in when using trace event logs and not for all trace // event types char log[MEMFAULT_TRACE_EVENT_MAX_LOG_LEN] = { 0 }; va_list args; va_start(args, fmt); int rv = vsnprintf(log, sizeof(log), fmt, args); size_t log_len = rv < 0 ? 0 : MEMFAULT_MIN(sizeof(log) - 1, (size_t)rv); va_end(args); sMemfaultTraceEventInfo event_info = { .reason = reason, .pc_addr = pc_addr, .return_addr = lr_addr, .opt_fields = TRACE_EVENT_OPT_FIELD_LOG_MASK, .log = &log[0], .log_len = log_len, }; return prv_capture_trace_event_info(&event_info); } #else int memfault_trace_event_with_compact_log_capture(eMfltTraceReasonUser reason, void *lr_addr, uint32_t log_id, uint32_t compressed_fmt, ...) { #if !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED // If a log capture takes place while in an ISR we just record a normal trace event if (memfault_arch_is_inside_isr()) { return memfault_trace_event_capture(reason, 0, lr_addr); } #endif /* !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED */ va_list args; va_start(args, compressed_fmt); uint8_t log[MEMFAULT_TRACE_EVENT_MAX_LOG_LEN] = { 0 }; sMemfaultCborEncoder encoder; memfault_cbor_encoder_init(&encoder, memfault_cbor_encoder_memcpy_write, log, sizeof(log)); memfault_vlog_compact_serialize(&encoder, log_id, compressed_fmt, args); size_t log_len = memfault_cbor_encoder_deinit(&encoder); sMemfaultTraceEventInfo event_info = { .reason = reason, // Note: pc is recovered from file/line encoded in compact log so no need to collect! .pc_addr = 0, .return_addr = lr_addr, .opt_fields = TRACE_EVENT_OPT_FIELD_LOG_MASK, .log = &log[0], .log_len = log_len, }; va_end(args); return prv_capture_trace_event_info(&event_info); } #endif size_t memfault_trace_event_compute_worst_case_storage_size(void) { sMemfaultTraceEventInfo event_info = { .reason = kMfltTraceReasonUser_NumReasons, .pc_addr = (void *)(uintptr_t)UINT32_MAX, .return_addr = (void *)(uintptr_t)UINT32_MAX, .opt_fields = TRACE_EVENT_OPT_FIELD_STATUS_MASK, .status_code = INT32_MAX, }; sMemfaultCborEncoder encoder = { 0 }; return memfault_serializer_helper_compute_size(&encoder, prv_encode_cb, &event_info); } void memfault_trace_event_reset(void) { s_memfault_trace_event_ctx.storage_impl = NULL; s_isr_trace_event = (sMemfaultIsrTraceEvent){ 0 }; } bool memfault_trace_event_booted(void) { return (s_memfault_trace_event_ctx.storage_impl != NULL); } ================================================ FILE: components/core/src/memfault_trace_event_private.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Internal utilities used for trace event module #ifdef __cplusplus extern "C" { #endif //! Resets the Trace Event storage for unit testing purposes. void memfault_trace_event_reset(void); #ifdef __cplusplus } #endif ================================================ FILE: components/demo/README.md ================================================ # demo Common code that is used by demo apps for the various platforms. See documentation in header files for details on the API itself. The component contains a collection of cli commands which can also be useful for bringup and initial integrations. You can find the list of available cli commands at [include/memfault/demo/cli.h](include/memfault/demo/cli.h). CLI commands specific to an optional component are found in `src/${component}/*.c`.If you are using our CMake or Make [build system helpers](README.md#add-sources-to-build-system) and enable the demo component, these commands will only be included if the component has been selected. ================================================ FILE: components/demo/src/http/memfault_demo_http.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! CLI commands which require integration of the "http" component. #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/errors.h" #include "memfault/demo/cli.h" #include "memfault/demo/util.h" #include "memfault/http/http_client.h" const char *memfault_demo_get_chunks_url(void) { static char s_chunks_url[MEMFAULT_HTTP_URL_BUFFER_SIZE]; memfault_http_build_url(s_chunks_url, MEMFAULT_HTTP_CHUNKS_API_SUBPATH); return s_chunks_url; } const char *memfault_demo_get_api_project_key(void) { return g_mflt_http_client_config.api_key; } int memfault_demo_cli_cmd_post_core(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { MEMFAULT_LOG_INFO("Posting Memfault Data..."); return memfault_http_client_post_chunk(); } ================================================ FILE: components/demo/src/memfault_demo_cli_drain_chunks.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! This file contains an example implementation of the pseudocode included in the Memfault Docs //! https://mflt.io/data-transport-example //! //! This CLI command can be used with the Memfault GDB command "memfault install_chunk_handler" to //! "drain" chunks up to the Memfault cloud directly from gdb. //! //! This can be useful when working on integrations and initially getting a transport path in place. //! (gdb) source $MEMFAULT_SDK/scripts/memfault_gdb.py //! (gdb) memfault install_chunk_handler -pk //! or for more details //! (gdb) memfault install_chunk_handler --help //! //! For more details see https://mflt.io/posting-chunks-with-gdb //! #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer.h" #include "memfault/demo/cli.h" // Note: We mark the function as weak so an end user can override this with a real implementation // and we disable optimizations so the parameters don't get stripped away MEMFAULT_NO_OPT MEMFAULT_WEAK void user_transport_send_chunk_data( MEMFAULT_UNUSED void *chunk_data, MEMFAULT_UNUSED size_t chunk_data_len) { } static bool prv_try_send_memfault_data(void) { // buffer to copy chunk data into uint8_t buf[MEMFAULT_DEMO_CLI_USER_CHUNK_SIZE]; size_t buf_len = sizeof(buf); bool data_available = memfault_packetizer_get_chunk(buf, &buf_len); if (!data_available) { return false; // no more data to send } // send payload collected to chunks/ endpoint user_transport_send_chunk_data(buf, buf_len); return true; } int memfault_demo_drain_chunk_data(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { while (prv_try_send_memfault_data()) { } return 0; } ================================================ FILE: components/demo/src/memfault_demo_cli_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! CLI commands that exercises the MEMFAULT_LOG_... and //! memfault_log_trigger_collection() APIs. See debug_log.h and log.h for more info. #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/log.h" #include "memfault/demo/cli.h" int memfault_demo_cli_cmd_test_log(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { MEMFAULT_LOG_RAW("Raw log!"); MEMFAULT_LOG_DEBUG("Debug log!"); MEMFAULT_LOG_INFO("Info log!"); MEMFAULT_LOG_WARN("Warning log!"); MEMFAULT_LOG_ERROR("Error log!"); // ESP-IDF will still save logs when MEMFAULT_SDK_LOG_SAVE_DISABLE is set #if MEMFAULT_SDK_LOG_SAVE_DISABLE && !defined(ESP_PLATFORM) // MEMFAULT_LOGs are not written to the ram backed log buffer so do // it explicitly for testing purposes MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Debug, "Debug log!"); MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, "Info log!"); MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Warning, "Warning log!"); MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Error, "Error log!"); #endif return 0; } int memfault_demo_cli_cmd_trigger_logs(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { MEMFAULT_LOG_RAW("Triggering log collection"); memfault_log_trigger_collection(); return 0; } ================================================ FILE: components/demo/src/memfault_demo_cli_trace_event.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! CLI command that exercises the MEMFAULT_TRACE_EVENT API, capturing a //! Trace Event with the error reason set to "MemfaultDemoCli_Error". #include "memfault/core/trace_event.h" #include "memfault/demo/cli.h" int memfault_demo_cli_cmd_trace_event_capture(int argc, char *argv[]) { // For more information on user-defined error reasons, see // the MEMFAULT_TRACE_REASON_DEFINE macro in trace_reason_user.h . if (argc < 2) { MEMFAULT_TRACE_EVENT_WITH_LOG(MemfaultCli_Test, "Example Trace Event. Num Args %d", argc); } else { MEMFAULT_TRACE_EVENT_WITH_LOG(MemfaultCli_Test, "%s", argv[1]); } return 0; } ================================================ FILE: components/demo/src/memfault_demo_core.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! CLI commands used by demo applications to exercise the Memfault SDK #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/device_info.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/device_info.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/reboot_tracking.h" #include "memfault/demo/cli.h" int memfault_demo_cli_cmd_get_device_info(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { memfault_device_info_dump(); return 0; } int memfault_demo_cli_cmd_system_reboot(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_UserReset); memfault_platform_reboot(); return 0; } ================================================ FILE: components/demo/src/memfault_demo_shell.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Minimal shell/console implementation for platforms that do not include one. //! NOTE: For simplicity, ANSI escape sequences are not dealt with! #include #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/demo/shell.h" #include "memfault/demo/shell_commands.h" #define MEMFAULT_SHELL_MAX_ARGS (16) #define MEMFAULT_SHELL_PROMPT "mflt> " #if defined(MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS) // When the extension list is enabled, iterate over both the core commands and // the extension commands. This construct, despite being pretty intricate, // saves about ~28 bytes of code space over running the iteration twice in a // row, and keeps the iterator in one macro, instead of two. #define MEMFAULT_SHELL_FOR_EACH_COMMAND(command) \ const sMemfaultShellCommand *command = g_memfault_shell_commands; \ for (size_t i = 0; i < g_memfault_num_shell_commands + s_mflt_shell.num_extension_commands; \ ++i, command = (i < g_memfault_num_shell_commands) ? \ &g_memfault_shell_commands[i] : \ (s_mflt_shell.extension_commands ? \ &s_mflt_shell.extension_commands[i - g_memfault_num_shell_commands] : \ NULL)) #else #define MEMFAULT_SHELL_FOR_EACH_COMMAND(command) \ for (const sMemfaultShellCommand *command = g_memfault_shell_commands; \ command < &g_memfault_shell_commands[g_memfault_num_shell_commands]; ++command) #endif static struct MemfaultShellContext { int (*send_char)(char c); size_t rx_size; // the char we will ignore when received end-of-line sequences char eol_ignore_char; char rx_buffer[MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE]; #if defined(MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS) const sMemfaultShellCommand *extension_commands; size_t num_extension_commands; #endif } s_mflt_shell; static bool prv_booted(void) { return s_mflt_shell.send_char != NULL; } static void prv_send_char(char c) { if (!prv_booted()) { return; } s_mflt_shell.send_char(c); } static void prv_echo(char c) { if (c == '\n') { prv_send_char('\r'); prv_send_char('\n'); } else if ('\b' == c) { prv_send_char('\b'); prv_send_char(' '); prv_send_char('\b'); } else { prv_send_char(c); } } static char prv_last_char(void) { return s_mflt_shell.rx_buffer[s_mflt_shell.rx_size - 1]; } static bool prv_is_rx_buffer_full(void) { return s_mflt_shell.rx_size >= MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE; } static void prv_reset_rx_buffer(void) { memset(s_mflt_shell.rx_buffer, 0, sizeof(s_mflt_shell.rx_buffer)); s_mflt_shell.rx_size = 0; } static void prv_echo_str(const char *str) { for (const char *c = str; *c != '\0'; ++c) { prv_echo(*c); } } static void prv_send_prompt(void) { prv_echo_str(MEMFAULT_SHELL_PROMPT); } static const sMemfaultShellCommand *prv_find_command(const char *name) { MEMFAULT_SHELL_FOR_EACH_COMMAND (command) { if (strcmp(command->command, name) == 0) { return command; } } return NULL; } static void prv_process(void) { if (prv_last_char() != '\n' && !prv_is_rx_buffer_full()) { return; } char *argv[MEMFAULT_SHELL_MAX_ARGS] = { 0 }; int argc = 0; char *next_arg = NULL; for (size_t i = 0; i < s_mflt_shell.rx_size && argc < MEMFAULT_SHELL_MAX_ARGS; ++i) { char *const c = &s_mflt_shell.rx_buffer[i]; if (*c == ' ' || *c == '\n' || i == s_mflt_shell.rx_size - 1) { *c = '\0'; if (next_arg) { argv[argc++] = next_arg; next_arg = NULL; } } else if (!next_arg) { next_arg = c; } } if (s_mflt_shell.rx_size == MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE) { prv_echo('\n'); } if (argc >= 1) { const sMemfaultShellCommand *command = prv_find_command(argv[0]); if (!command) { prv_echo_str("Unknown command: "); prv_echo_str(argv[0]); prv_echo('\n'); prv_echo_str("Type 'help' to list all commands\n"); } else { command->handler(argc, argv); } } prv_reset_rx_buffer(); prv_send_prompt(); } void memfault_demo_shell_boot(const sMemfaultShellImpl *impl) { s_mflt_shell.eol_ignore_char = 0; s_mflt_shell.send_char = impl->send_char; prv_reset_rx_buffer(); prv_echo_str("\n" MEMFAULT_SHELL_PROMPT); } #if defined(MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS) void memfault_shell_command_set_extensions(const sMemfaultShellCommand *const commands, size_t num_commands) { s_mflt_shell.extension_commands = commands; s_mflt_shell.num_extension_commands = num_commands; } #endif //! Logic to deal with CR, LF, CRLF, or LFCR end-of-line (EOL) sequences //! @return true if the character should be ignored, false otherwise static bool prv_should_ignore_eol_char(char c) { if (s_mflt_shell.eol_ignore_char != 0) { return (c == s_mflt_shell.eol_ignore_char); } // // Check to see if we have encountered our first newline character since the shell was booted // (either a CR ('\r') or LF ('\n')). Once found, we will use this character as our EOL delimiter // and ignore the opposite character if we see it in the future. // if (c == '\r') { s_mflt_shell.eol_ignore_char = '\n'; } else if (c == '\n') { s_mflt_shell.eol_ignore_char = '\r'; } return false; } void memfault_demo_shell_receive_char(char c) { if (prv_should_ignore_eol_char(c) || prv_is_rx_buffer_full() || !prv_booted()) { return; } const bool is_backspace = (c == '\b'); if (is_backspace && s_mflt_shell.rx_size == 0) { return; // nothing left to delete so don't echo the backspace } // CR are our EOL delimiter. Remap as a LF here since that's what internal handling logic expects if (c == '\r') { c = '\n'; } prv_echo(c); if (is_backspace) { s_mflt_shell.rx_buffer[--s_mflt_shell.rx_size] = '\0'; return; } s_mflt_shell.rx_buffer[s_mflt_shell.rx_size++] = c; prv_process(); } int memfault_shell_help_handler(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { MEMFAULT_SHELL_FOR_EACH_COMMAND (command) { prv_echo_str(command->command); prv_echo_str(": "); prv_echo_str(command->help); prv_echo('\n'); } return 0; } ================================================ FILE: components/demo/src/memfault_demo_shell_commands.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Command definitions for the minimal shell/console implementation. #include #include #include "memfault/core/compiler.h" #include "memfault/core/data_export.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/self_test.h" #include "memfault/demo/cli.h" #include "memfault/demo/shell_commands.h" #include "memfault/metrics/metrics.h" static int prv_panics_component_required(void) { MEMFAULT_LOG_RAW("Disabled. panics component integration required"); return -1; } MEMFAULT_WEAK int memfault_demo_cli_cmd_get_core(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { return prv_panics_component_required(); } MEMFAULT_WEAK int memfault_demo_cli_cmd_clear_core(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { return prv_panics_component_required(); } MEMFAULT_WEAK int memfault_demo_cli_cmd_crash(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { return prv_panics_component_required(); } int memfault_demo_cli_cmd_export(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { memfault_data_export_dump_chunks(); return 0; } // Provide weak implementations in the case where the metrics component is not enabled MEMFAULT_WEAK void memfault_metrics_heartbeat_debug_print(void) { MEMFAULT_LOG_RAW("Disabled. metrics component integration required"); } MEMFAULT_WEAK void memfault_metrics_heartbeat_debug_trigger(void) { MEMFAULT_LOG_RAW("Disabled. metrics component integration required"); } MEMFAULT_WEAK void memfault_metrics_all_sessions_debug_print(void) { MEMFAULT_LOG_RAW("Disabled. metrics component integration required"); } static int memfault_demo_cli_cmd_metrics_dump(int argc, char *argv[]) { if (argc < 2) { MEMFAULT_LOG_RAW("Enter 'heartbeat' or 'sessions'"); return 0; } if (!strncmp(argv[1], "sessions", sizeof("sessions"))) { memfault_metrics_all_sessions_debug_print(); } else if (!strncmp(argv[1], "heartbeat", sizeof("heartbeat"))) { memfault_metrics_heartbeat_debug_print(); } else { MEMFAULT_LOG_RAW("Unknown option. Enter 'heartbeat' or 'sessions'"); return 0; } return 0; } int memfault_demo_cli_cmd_heartbeat(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { memfault_metrics_heartbeat_debug_trigger(); return 0; } #if MEMFAULT_DEMO_CLI_SELF_TEST int memfault_demo_cli_cmd_self_test(int argc, char *argv[]) { uint32_t run_flags = kMemfaultSelfTestFlag_Default; // If we got an arg, translate to a test flag if (argc >= 2) { run_flags = memfault_self_test_arg_to_flag(argv[1]); } return memfault_self_test_run(run_flags); } #endif static const sMemfaultShellCommand s_memfault_shell_commands[] = { { "clear_core", memfault_demo_cli_cmd_clear_core, "Clear an existing coredump" }, { "drain_chunks", memfault_demo_drain_chunk_data, "Flushes queued Memfault data. To upload data see https://mflt.io/posting-chunks-with-gdb" }, { "export", memfault_demo_cli_cmd_export, "Export base64-encoded chunks. To upload data see https://mflt.io/chunk-data-export" }, { "get_core", memfault_demo_cli_cmd_get_core, "Get coredump info" }, { "get_device_info", memfault_demo_cli_cmd_get_device_info, "Get device info" }, { "coredump_size", memfault_demo_cli_cmd_coredump_size, "Print the coredump storage capacity" }, { "heartbeat", memfault_demo_cli_cmd_heartbeat, "Trigger a heartbeat" }, { "metrics_dump", memfault_demo_cli_cmd_metrics_dump, "Dump current heartbeat or session metrics" }, // // Test commands for validating SDK functionality: https://mflt.io/mcu-test-commands // { "test_assert", memfault_demo_cli_cmd_assert, "Trigger memfault assert" }, { "test_cassert", memfault_demo_cli_cmd_cassert, "Trigger C assert" }, #if MEMFAULT_COMPILER_ARM_CORTEX_M { "test_busfault", memfault_demo_cli_cmd_busfault, "Trigger a busfault" }, { "test_hardfault", memfault_demo_cli_cmd_hardfault, "Trigger a hardfault" }, { "test_memmanage", memfault_demo_cli_cmd_memmanage, "Trigger a memory management fault" }, { "test_usagefault", memfault_demo_cli_cmd_usagefault, "Trigger a usage fault" }, #endif #if MEMFAULT_COMPILER_ARM_V7_A_R { "test_dataabort", memfault_demo_cli_cmd_dataabort, "Trigger a data abort" }, { "test_prefetchabort", memfault_demo_cli_cmd_prefetchabort, "Trigger a prefetch abort" }, #endif { "test_log", memfault_demo_cli_cmd_test_log, "Writes test logs to log buffer" }, { "test_log_capture", memfault_demo_cli_cmd_trigger_logs, "Trigger capture of current log buffer contents" }, { "test_reboot", memfault_demo_cli_cmd_system_reboot, "Force system reset and track it with a trace event" }, { "test_trace", memfault_demo_cli_cmd_trace_event_capture, "Capture an example trace event" }, #if MEMFAULT_DEMO_CLI_SELF_TEST { "self_test", memfault_demo_cli_cmd_self_test, "Run a self test to check integration with the SDK" }, #endif #if MEMFAULT_DEMO_CLI_WATCHDOG { "wdog_enable", memfault_demo_cli_cmd_software_watchdog_enable, "Enable the software watchdog" }, { "wdog_disable", memfault_demo_cli_cmd_software_watchdog_disable, "Disable the software watchdog" }, { "wdog_update", memfault_demo_cli_cmd_software_watchdog_update_timeout, "Update the software watchdog timeout" }, #endif { "help", memfault_shell_help_handler, "Lists all commands" }, }; // Note: Declared as weak so an end user can override the command table MEMFAULT_WEAK const sMemfaultShellCommand *const g_memfault_shell_commands = s_memfault_shell_commands; MEMFAULT_WEAK const size_t g_memfault_num_shell_commands = MEMFAULT_ARRAY_SIZE(s_memfault_shell_commands); ================================================ FILE: components/demo/src/memfault_demo_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! CLI commands used by demo applications to exercise the Memfault software watchdog #include "memfault/config.h" #if MEMFAULT_DEMO_CLI_WATCHDOG #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/ports/watchdog.h" int memfault_demo_cli_cmd_software_watchdog_enable(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { int rv = memfault_software_watchdog_enable(); if (rv < 0) { MEMFAULT_LOG_RAW("Failed to enable software watchdog: %d", rv); return -1; } return 0; } int memfault_demo_cli_cmd_software_watchdog_disable(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { int rv = memfault_software_watchdog_disable(); if (rv < 0) { MEMFAULT_LOG_RAW("Failed to disable software watchdog: %d", rv); return -1; } return 0; } int memfault_demo_cli_cmd_software_watchdog_update_timeout(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { if (argc < 2) { MEMFAULT_LOG_RAW("Usage: wdog_update "); return -1; } int timeout_ms = atoi(argv[1]); if (timeout_ms <= 0) { MEMFAULT_LOG_RAW("Invalid timeout value: %d", timeout_ms); return -1; } int rv = memfault_software_watchdog_update_timeout(timeout_ms); if (rv < 0) { MEMFAULT_LOG_RAW("Failed to update software watchdog timeout: %d", rv); return -1; } MEMFAULT_LOG_RAW("Software watchdog timeout updated to %d ms", timeout_ms); return 0; } #endif // MEMFAULT_DEMO_CLI_WATCHDOG ================================================ FILE: components/demo/src/panics/memfault_demo_cli_aux.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Some variables that are used in demo applications to force certain crashes //! We put them in their own compilation unit so the compiler can't figure out #include #include "memfault/core/compiler.h" #include "memfault_demo_cli_aux_private.h" // Jump through some hoops to trick the compiler into doing an unaligned 64 bit access MEMFAULT_ALIGNED(4) static uint8_t s_test_buffer[16]; void *g_memfault_unaligned_buffer = &s_test_buffer[1]; // Also jump through some more hoops to trick the compiler into executing a bad function void (*g_bad_func_call)(void) = (void (*)(void))0xbadcafe; ================================================ FILE: components/demo/src/panics/memfault_demo_cli_aux_private.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Some globals we use to create different types of crashes from the memfault_demo_core.c CLI //! commands. We put the variables in a separate file / compilation unit to prevent the compiler //! from detecting / error'ing out on the crashes we are creating for test purposes. #ifdef __cplusplus extern "C" { #endif extern void *g_memfault_unaligned_buffer; extern void (*g_bad_func_call)(void); #ifdef __cplusplus } #endif ================================================ FILE: components/demo/src/panics/memfault_demo_panics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! CLI commands which require integration of the "panic" component. #include #include #include "memfault/core/arch.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/errors.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/device_info.h" #include "memfault/core/reboot_tracking.h" #include "memfault/demo/cli.h" #include "memfault/panics/assert.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "memfault_demo_cli_aux_private.h" // Allow opting out of the cassert demo. Some platforms may not have a libc // assert implementation. #if !defined(MEMFAULT_DEMO_DISABLE_CASSERT) #include #define MEMFAULT_DEMO_DISABLE_CASSERT 0 #endif MEMFAULT_NO_OPT static void do_some_work_base(char *argv[]) { // An assert that is guaranteed to fail. We perform // the check against argv so that the compiler can't // perform any optimizations MEMFAULT_ASSERT((uint32_t)(uintptr_t)argv == 0xdeadbeef); } MEMFAULT_NO_OPT static void do_some_work1(char *argv[]) { do_some_work_base(argv); } MEMFAULT_NO_OPT static void do_some_work2(char *argv[]) { do_some_work1(argv); } MEMFAULT_NO_OPT static void do_some_work3(char *argv[]) { do_some_work2(argv); } MEMFAULT_NO_OPT static void do_some_work4(char *argv[]) { do_some_work3(argv); } MEMFAULT_NO_OPT static void do_some_work5(char *argv[]) { do_some_work4(argv); } int memfault_demo_cli_cmd_crash(int argc, char *argv[]) { int crash_type = 0; if (argc >= 2) { crash_type = atoi(argv[1]); } switch (crash_type) { case 0: MEMFAULT_ASSERT(0); break; case 1: g_bad_func_call(); break; case 2: { uint64_t *buf = (uint64_t *)g_memfault_unaligned_buffer; *buf = 0xbadcafe0000; } break; case 3: do_some_work5(argv); break; case 4: MEMFAULT_SOFTWARE_WATCHDOG(); break; default: // this should only ever be reached if crash_type is invalid MEMFAULT_LOG_ERROR("Usage: \"crash\" or \"crash \" where n is 0..4"); return -1; } // Should be unreachable. If we get here, trigger an assert and record the crash_type which // failed to trigger a crash MEMFAULT_ASSERT_RECORD((uint32_t)crash_type); return -1; } int memfault_demo_cli_cmd_get_core(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { size_t total_size = 0; if (!memfault_coredump_has_valid_coredump(&total_size)) { MEMFAULT_LOG_INFO("No coredump present!"); return 0; } MEMFAULT_LOG_INFO("Has coredump with size: %d", (int)total_size); return 0; } int memfault_demo_cli_cmd_clear_core(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { MEMFAULT_LOG_INFO("Invalidating coredump"); memfault_platform_coredump_storage_clear(); return 0; } int memfault_demo_cli_cmd_coredump_size(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { size_t total_size = 0; size_t capacity = 0; memfault_coredump_size_and_storage_capacity(&total_size, &capacity); MEMFAULT_LOG_INFO("Coredump size: %d, capacity: %d", (int)total_size, (int)capacity); return 0; } int memfault_demo_cli_cmd_assert(int argc, char *argv[]) { // permit running with a user-provided "extra" value for testing that path if (argc > 1) { MEMFAULT_ASSERT_RECORD(atoi(argv[1])); } else { MEMFAULT_ASSERT(0); } return -1; } int memfault_demo_cli_cmd_cassert(int argc, char *argv[]) { (void)argc, (void)argv; #if MEMFAULT_DEMO_DISABLE_CASSERT MEMFAULT_LOG_ERROR("C assert demo disabled"); #else assert(0); #endif return -1; } #if MEMFAULT_COMPILER_ARM_CORTEX_M int memfault_demo_cli_cmd_hardfault(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { memfault_arch_disable_configurable_faults(); uint64_t *buf = (uint64_t *)g_memfault_unaligned_buffer; *buf = 0xdeadbeef0000; return -1; } int memfault_demo_cli_cmd_memmanage(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { // Per "Relation of the MPU to the system memory map" in ARMv7-M reference manual: // // "The MPU is restricted in how it can change the default memory map attributes associated with // System space, that is, for addresses 0xE0000000 and higher. System space is always marked as // XN, Execute Never." // // So we can trip a MemManage exception by simply attempting to execute any address >= // 0xE000.0000 void (*bad_func)(void) = (void (*)(void))0xEEEEDEAD; bad_func(); // We should never get here -- platforms MemManage or HardFault handler should be tripped return -1; } int memfault_demo_cli_cmd_busfault(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { // Trigger a BusFault by attempting to load and jump to an address from the "RAM" (0x60000000 - // 0x7FFFFFFF) region, described as: "Memory with write-back, write allocate cache attribute for // L2/L3 cache support." in the ARMv7-M architecture reference manual. This is not guaranteed to // trigger a BusFault, if the address is both loadable and executable, but on many devices it // will. void (*busfault_func)(void) = (void (*)(void))0x60000001; busfault_func(); // We should never get here -- platforms BusFault or HardFault handler should be tripped // with a precise error due to unaligned execution return -1; } int memfault_demo_cli_cmd_usagefault(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { uint64_t *buf = (uint64_t *)g_memfault_unaligned_buffer; *buf = 0xbadcafe0000; // We should never get here -- platforms UsageFault or HardFault handler should be tripped due to // unaligned access return -1; } #endif // MEMFAULT_COMPILER_ARM_CORTEX_M #if MEMFAULT_COMPILER_ARM_V7_A_R int memfault_demo_cli_cmd_dataabort(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { // try to write to a read-only address volatile int explode = *(int *)0xFFFFFFFF; return explode | 1; } int memfault_demo_cli_cmd_prefetchabort(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { // We can trip a PrefetchAbort exception by simply attempting to execute any address >= // 0xE000.0000 void (*bad_func)(void) = (void (*)(void))0xEEEEDEAD; bad_func(); // We should never get here -- platforms PrefetchAbort handler should be tripped return -1; } #endif // MEMFAULT_COMPILER_ARM_V7_A_R int memfault_demo_cli_loadaddr(int argc, char *argv[]) { if (argc < 2) { MEMFAULT_LOG_ERROR("Usage: loadaddr "); return -1; } uint32_t addr = (uint32_t)strtoul(argv[1], NULL, 0); uint32_t val = *(uint32_t *)(uintptr_t)addr; (void)addr, (void)val; // to silence unused variable warnings if logging is disabled MEMFAULT_LOG_INFO("Read 0x%08" PRIx32 " from 0x%08" PRIx32, val, (uint32_t)(uintptr_t)addr); return 0; } ================================================ FILE: components/http/README.md ================================================ # http HTTP client API, to post coredumps and events directly to the Memfault service from devices. See documentation in header files for details on the API itself. ================================================ FILE: components/http/src/memfault_http_client.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Memfault HTTP Client implementation which can be used to send data to the Memfault cloud for //! processing #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/errors.h" #include "memfault/core/platform/device_info.h" #include "memfault/http/http_client.h" #include "memfault/http/platform/http_client.h" #include "memfault/http/utils.h" bool memfault_http_build_url(char url_buffer[MEMFAULT_HTTP_URL_BUFFER_SIZE], const char *subpath) { sMemfaultDeviceInfo device_info; memfault_http_get_device_info(&device_info); const int rv = snprintf(url_buffer, MEMFAULT_HTTP_URL_BUFFER_SIZE, "%s://%s" MEMFAULT_HTTP_CHUNKS_API_PREFIX "%s/%s", MEMFAULT_HTTP_GET_SCHEME(), MEMFAULT_HTTP_GET_CHUNKS_API_HOST(), subpath, device_info.device_serial); return (rv < MEMFAULT_HTTP_URL_BUFFER_SIZE); } sMfltHttpClient *memfault_http_client_create(void) { return memfault_platform_http_client_create(); } static void prv_handle_post_data_response(const sMfltHttpResponse *response, MEMFAULT_UNUSED void *ctx) { if (!response) { return; // Request failed } uint32_t http_status = 0; const int rv = memfault_platform_http_response_get_status(response, &http_status); if (rv != 0) { MEMFAULT_LOG_ERROR("Request failed. No HTTP status: %d", rv); return; } if (http_status < 200 || http_status >= 300) { // Redirections are expected to be handled by the platform implementation MEMFAULT_LOG_ERROR("Request failed. HTTP Status: %" PRIu32, http_status); return; } } int memfault_http_client_post_data(sMfltHttpClient *client) { if (!client) { return MemfaultInternalReturnCode_InvalidInput; } return memfault_platform_http_client_post_data(client, prv_handle_post_data_response, NULL); } int memfault_http_client_wait_until_requests_completed(sMfltHttpClient *client, uint32_t timeout_ms) { if (!client) { return MemfaultInternalReturnCode_InvalidInput; } return memfault_platform_http_client_wait_until_requests_completed(client, timeout_ms); } int memfault_http_client_destroy(sMfltHttpClient *client) { if (!client) { return MemfaultInternalReturnCode_InvalidInput; } return memfault_platform_http_client_destroy(client); } ================================================ FILE: components/http/src/memfault_http_client_post_chunk.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Implements conveninece API for posting a single chunk of Memfault data #include "memfault/core/data_packetizer.h" #include "memfault/core/debug_log.h" #include "memfault/core/errors.h" #include "memfault/http/http_client.h" int memfault_http_client_post_chunk(void) { // A pre-flight check before we attempt to setup an HTTP client // If there's no data to send, just early return bool more_data = memfault_packetizer_data_available(); if (!more_data) { // no new data to post return kMfltPostDataStatus_NoDataFound; } sMfltHttpClient *http_client = memfault_http_client_create(); if (!http_client) { MEMFAULT_LOG_ERROR("Failed to create HTTP client"); return MemfaultInternalReturnCode_Error; } const int rv = memfault_http_client_post_data(http_client); if ((eMfltPostDataStatus)rv != kMfltPostDataStatus_Success) { MEMFAULT_LOG_ERROR("Failed to post chunk: rv=%d", rv); } const uint32_t timeout_ms = 30 * 1000; memfault_http_client_wait_until_requests_completed(http_client, timeout_ms); memfault_http_client_destroy(http_client); return rv; } ================================================ FILE: components/http/src/memfault_http_utils.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! See header for more details #include #include #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/platform/device_info.h" #include "memfault/http/http_client.h" #include "memfault/http/utils.h" #include "memfault/version.h" //! Default buffer size for the URL-encoded device info parameters. This may //! need to be set higher by the user if there are particularly long device info //! strings #ifndef MEMFAULT_DEVICE_INFO_URL_ENCODED_MAX_LEN #define MEMFAULT_DEVICE_INFO_URL_ENCODED_MAX_LEN (48) #endif static bool prv_write_msg(MfltHttpClientSendCb write_callback, void *ctx, const char *msg, size_t msg_len, size_t max_len) { if (msg_len >= max_len) { return false; } return write_callback(msg, msg_len, ctx); } static bool prv_write_crlf(MfltHttpClientSendCb write_callback, void *ctx) { #define END_HEADER_SECTION "\r\n" const size_t end_hdr_section_len = MEMFAULT_STATIC_STRLEN(END_HEADER_SECTION); return write_callback(END_HEADER_SECTION, end_hdr_section_len, ctx); } // NB: All HTTP/1.1 requests must provide a Host Header // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host static bool prv_write_host_hdr(MfltHttpClientSendCb write_callback, void *ctx, const void *host, size_t host_len) { #define HOST_HDR_BEGIN "Host:" const size_t host_hdr_begin_len = MEMFAULT_STATIC_STRLEN(HOST_HDR_BEGIN); if (!write_callback(HOST_HDR_BEGIN, host_hdr_begin_len, ctx)) { return false; } if (!write_callback(host, host_len, ctx)) { return false; } return prv_write_crlf(write_callback, ctx); } static bool prv_write_user_agent_hdr(MfltHttpClientSendCb write_callback, void *ctx) { #define USER_AGENT_HDR "User-Agent:MemfaultSDK/" MEMFAULT_SDK_VERSION_STR "\r\n" const size_t user_agent_hdr_len = MEMFAULT_STATIC_STRLEN(USER_AGENT_HDR); return write_callback(USER_AGENT_HDR, user_agent_hdr_len, ctx); } static bool prv_write_project_key_hdr(MfltHttpClientSendCb write_callback, void *ctx) { #define PROJECT_KEY_HDR_BEGIN "Memfault-Project-Key:" const size_t project_key_begin_len = MEMFAULT_STATIC_STRLEN(PROJECT_KEY_HDR_BEGIN); if (!write_callback(PROJECT_KEY_HDR_BEGIN, project_key_begin_len, ctx)) { return false; } const char *project_key = g_mflt_http_client_config.api_key; size_t project_key_len = strlen(project_key); if (!write_callback(project_key, project_key_len, ctx)) { return false; } return prv_write_crlf(write_callback, ctx); } bool memfault_http_start_chunk_post(MfltHttpClientSendCb write_callback, void *ctx, size_t content_body_length) { // Request built will look like this: // POST /api/v0/chunks/ HTTP/1.1\r\n // Host:chunks.memfault.com\r\n // User-Agent: MemfaultSDK/0.4.2\r\n // Memfault-Project-Key:\r\n // Content-Type:application/octet-stream\r\n // Content-Length:\r\n // \r\n sMemfaultDeviceInfo device_info; memfault_http_get_device_info(&device_info); char buffer[100]; const size_t max_msg_len = sizeof(buffer); size_t msg_len = (size_t)snprintf(buffer, sizeof(buffer), "POST /api/v0/chunks/%s HTTP/1.1\r\n", device_info.device_serial); if (!prv_write_msg(write_callback, ctx, buffer, msg_len, max_msg_len)) { return false; } const char *host = MEMFAULT_HTTP_GET_CHUNKS_API_HOST(); const size_t host_len = strlen(host); if (!prv_write_host_hdr(write_callback, ctx, host, host_len)) { return false; } if (!prv_write_user_agent_hdr(write_callback, ctx)) { return false; } if (!prv_write_project_key_hdr(write_callback, ctx)) { return false; } #define CONTENT_TYPE "Content-Type:application/octet-stream\r\n" const size_t content_type_len = MEMFAULT_STATIC_STRLEN(CONTENT_TYPE); if (!write_callback(CONTENT_TYPE, content_type_len, ctx)) { return false; } msg_len = (size_t)snprintf(buffer, sizeof(buffer), "Content-Length:%d\r\n", (int)content_body_length); return prv_write_msg(write_callback, ctx, buffer, msg_len, max_msg_len) && prv_write_crlf(write_callback, ctx); } static bool prv_write_qparam(MfltHttpClientSendCb write_callback, void *ctx, const void *name, size_t name_strlen, const char *value) { return write_callback("&", 1, ctx) && write_callback(name, name_strlen, ctx) && write_callback("=", 1, ctx) && write_callback(value, strlen(value), ctx); } bool memfault_http_get_latest_ota_payload_url(MfltHttpClientSendCb write_callback, void *ctx) { // Request built will look like this: // GET // /api/v0/releases/latest/url&device_serial=<>&hardware_version=<>&software_type=<>¤t_version=<> // HTTP/1.1\r\n Host:\r\n User-Agent: MemfaultSDK/0.4.2\r\n // Memfault-Project-Key:\r\n // \r\n #define LATEST_REQUEST_LINE_BEGIN "GET /api/v0/releases/latest/url?" const size_t request_line_begin_len = MEMFAULT_STATIC_STRLEN(LATEST_REQUEST_LINE_BEGIN); if (!write_callback(LATEST_REQUEST_LINE_BEGIN, request_line_begin_len, ctx)) { return false; } sMemfaultDeviceInfo device_info = { 0 }; memfault_http_get_device_info(&device_info); #define DEVICE_SERIAL_QPARAM "device_serial" #define HARDWARE_VERSION_QPARAM "hardware_version" #define SOFTWARE_TYPE_QPARAM "software_type" #define CURRENT_VERSION_QPARAM "current_version" #define MEMFAULT_STATIC_STRLEN(s) (sizeof(s) - 1) const struct qparam_values_s { const char *name; size_t name_strlen; const char *value; } qparam_values[] = { { DEVICE_SERIAL_QPARAM, MEMFAULT_STATIC_STRLEN(DEVICE_SERIAL_QPARAM), device_info.device_serial, }, { HARDWARE_VERSION_QPARAM, MEMFAULT_STATIC_STRLEN(HARDWARE_VERSION_QPARAM), device_info.hardware_version, }, { SOFTWARE_TYPE_QPARAM, MEMFAULT_STATIC_STRLEN(SOFTWARE_TYPE_QPARAM), device_info.software_type, }, { CURRENT_VERSION_QPARAM, MEMFAULT_STATIC_STRLEN(CURRENT_VERSION_QPARAM), device_info.software_version, }, }; // URL encode the qparam values before writing them for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(qparam_values); i++) { const struct qparam_values_s *qparam_value = &qparam_values[i]; char qparam_encoded_buffer[MEMFAULT_DEVICE_INFO_URL_ENCODED_MAX_LEN]; int rv = memfault_http_urlencode(qparam_value->value, strlen(qparam_value->value), qparam_encoded_buffer, sizeof(qparam_encoded_buffer)); if (rv != 0) { MEMFAULT_LOG_ERROR("Failed to URL encode qparam value: %s", qparam_value->value); return false; } if (!prv_write_qparam(write_callback, ctx, qparam_value->name, qparam_value->name_strlen, qparam_encoded_buffer)) { return false; } } #define LATEST_REQUEST_LINE_END " HTTP/1.1\r\n" const size_t request_line_end_len = MEMFAULT_STATIC_STRLEN(LATEST_REQUEST_LINE_END); if (!write_callback(LATEST_REQUEST_LINE_END, request_line_end_len, ctx)) { return false; } const char *host = MEMFAULT_HTTP_GET_DEVICE_API_HOST(); const size_t host_len = strlen(host); if (!prv_write_host_hdr(write_callback, ctx, host, host_len)) { return false; } if (!prv_write_user_agent_hdr(write_callback, ctx)) { return false; } if (!prv_write_project_key_hdr(write_callback, ctx)) { return false; } return prv_write_crlf(write_callback, ctx); } static bool prv_is_number(char c) { return ((c) >= '0' && (c) <= '9'); } static size_t prv_count_spaces(const char *line, size_t start_offset, size_t total_len) { size_t spaces_found = 0; for (; start_offset < total_len; start_offset++) { if (line[start_offset] != ' ') { break; } spaces_found++; } return spaces_found; } static char prv_lower(char in) { char maybe_lower_c = in | 0x20; if (maybe_lower_c >= 'a' && maybe_lower_c <= 'z') { // return lower case if value is actually in A-Z, a-z range return maybe_lower_c; } return in; } // depending on the libc used, strcasecmp isn't always available so let's // use a simple variant here static bool prv_strcasecmp(const char *line, const char *search_str, size_t str_len) { for (size_t idx = 0; idx < str_len; idx++) { char lower_c = prv_lower(line[idx]); if (lower_c != search_str[idx]) { return false; } } return true; } static int prv_str_to_dec(const char *buf, size_t buf_len, int *value_out) { int result = 0; size_t idx = 0; for (; idx < buf_len; idx++) { char c = buf[idx]; if (c == ' ') { break; } if (!prv_is_number(c)) { // unexpected character encountered return -1; } int digit = c - '0'; // there's no limit to the size of a Content-Length value per specification: // https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 // // status code is required to be 3 digits per: // https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 // // any value that we can't fit in our variable is an error if ((INT_MAX / 10) < (result + digit)) { return -1; // result will overflow } result = (result * 10) + digit; } *value_out = result; return (int)idx; } //! @return true if parsing was successful, false if a parse error occurred //! @note The only header we are interested in for the simple memfault response //! parser is "Content-Length" to figure out how long the body is so that's all //! this parser looks for static bool prv_parse_header(char *line, size_t len, int *content_length_out) { #define CONTENT_LENGTH "content-length" const size_t content_hdr_len = MEMFAULT_STATIC_STRLEN(CONTENT_LENGTH); if (len < content_hdr_len) { return true; } size_t idx = 0; if (!prv_strcasecmp(line, CONTENT_LENGTH, content_hdr_len)) { return true; } idx += content_hdr_len; idx += prv_count_spaces(line, idx, len); if (line[idx] != ':') { return false; } idx++; idx += prv_count_spaces(line, idx, len); const int bytes_processed = prv_str_to_dec(&line[idx], len - idx, content_length_out); // should find at least one digit return (bytes_processed > 0); } static bool prv_parse_status_line(char *line, size_t len, int *http_status) { #define HTTP_VERSION "HTTP/1." const size_t http_ver_len = MEMFAULT_STATIC_STRLEN(HTTP_VERSION); if (len < http_ver_len) { return false; } if (memcmp(line, HTTP_VERSION, http_ver_len) != 0) { return false; } size_t idx = http_ver_len; if (len < idx + 1) { return false; } // now we should have single byte minor version if (!prv_is_number(line[idx])) { return false; } idx++; // now we should have at least one space const size_t num_spaces = prv_count_spaces(line, idx, len); if (num_spaces == 0) { return false; } idx += num_spaces; const size_t status_code_num_digits = 3; size_t status_code_end = idx + status_code_num_digits; if (len < status_code_end) { return false; } const int bytes_processed = prv_str_to_dec(&line[idx], status_code_end, http_status); // NB: the remainder line is the "Reason-Phrase" which we don't care about return (bytes_processed == (int)status_code_num_digits); } static bool prv_is_cr_lf(char *buf) { return buf[0] == '\r' && buf[1] == '\n'; } static bool prv_parse_http_response(sMemfaultHttpResponseContext *ctx, const void *data, size_t data_len, bool parse_header_only) { ctx->data_bytes_processed = 0; const char *chars = (const char *)data; char *line_buf = &ctx->line_buf[0]; for (size_t i = 0; i < data_len; i++, ctx->data_bytes_processed++) { const char c = chars[i]; if (ctx->phase == kMfltHttpParsePhase_ExpectingBody) { if (parse_header_only) { return true; } // Just eat the message body so we can handle response lengths of arbitrary size ctx->content_received++; if (ctx->line_len < (sizeof(ctx->line_buf) - 1)) { line_buf[ctx->line_len] = c; ctx->line_len++; } bool done = (ctx->content_received == ctx->content_length); if (!done) { continue; } ctx->line_buf[ctx->line_len] = '\0'; ctx->http_body = ctx->line_buf; ctx->data_bytes_processed++; return done; } if (ctx->line_len >= sizeof(ctx->line_buf)) { if (ctx->phase == kMfltHttpParsePhase_ExpectingHeader) { // We want to truncate headers at index sizeof(line_buf)-2 // so we can place the source's CR/LF sequence at the end // when the source is finally exhausted. static const size_t lf_idx = sizeof(ctx->line_buf) - 2; static const size_t cr_idx = sizeof(ctx->line_buf) - 1; if (c == '\r') { ctx->line_buf[lf_idx] = c; } else if (c == '\n') { ctx->line_buf[cr_idx] = c; } } else { // It's too long so set a parse error and return done. ctx->parse_error = MfltHttpParseStatus_HeaderTooLongError; return true; } } else { line_buf[ctx->line_len] = c; ctx->line_len++; } if (ctx->line_len < 2) { continue; } const size_t len = ctx->line_len - 2; if (prv_is_cr_lf(&line_buf[len])) { ctx->line_len = 0; line_buf[len] = '\0'; // The first line in a http response is the HTTP "Status-Line" if (ctx->phase == kMfltHttpParsePhase_ExpectingStatusLine) { if (!prv_parse_status_line(line_buf, len, &ctx->http_status_code)) { ctx->parse_error = MfltHttpParseStatus_ParseStatusLineError; return true; } ctx->phase = kMfltHttpParsePhase_ExpectingHeader; } else if (ctx->phase == kMfltHttpParsePhase_ExpectingHeader) { if (!prv_parse_header(line_buf, len, &ctx->content_length)) { ctx->parse_error = MfltHttpParseStatus_ParseHeaderError; return true; } if (len != 0) { continue; } // We've reached the end of headers marker if (ctx->content_length == 0) { // no body to read return true; } ctx->phase = kMfltHttpParsePhase_ExpectingBody; } } } return false; } bool memfault_http_parse_response(sMemfaultHttpResponseContext *ctx, const void *data, size_t data_len) { const bool parse_header_only = false; return prv_parse_http_response(ctx, data, data_len, parse_header_only); } bool memfault_http_parse_response_header(sMemfaultHttpResponseContext *ctx, const void *data, size_t data_len) { const bool parse_header_only = true; return prv_parse_http_response(ctx, data, data_len, parse_header_only); } static bool prv_find_first_occurrence(const char *line, size_t total_len, char c, size_t *offset_out) { for (size_t offset = 0; offset < total_len; offset++) { if (line[offset] == c) { *offset_out = offset; return true; } } return false; } static bool prv_find_last_occurrence(const char *line, size_t total_len, char c, size_t *offset_out) { for (int offset = (int)total_len - 1; offset >= 0; offset--) { if (line[offset] == c) { *offset_out = (size_t)offset; return true; } } return false; } static bool prv_startswith_str(const char *match, size_t match_len, const void *uri, size_t total_len) { if (total_len < match_len) { return false; } return prv_strcasecmp((const char *)uri, match, match_len); } static size_t prv_is_http_or_https_scheme(const void *uri, size_t total_len, eMemfaultUriScheme *scheme, uint32_t *default_port) { #define HTTPS_SCHEME_WITH_AUTHORITY "https://" const size_t https_scheme_with_authority_len = MEMFAULT_STATIC_STRLEN(HTTPS_SCHEME_WITH_AUTHORITY); if (prv_startswith_str(HTTPS_SCHEME_WITH_AUTHORITY, https_scheme_with_authority_len, uri, total_len)) { *scheme = kMemfaultUriScheme_Https; *default_port = 443; return https_scheme_with_authority_len; } #define HTTP_SCHEME_WITH_AUTHORITY "http://" const size_t http_scheme_with_authority_len = MEMFAULT_STATIC_STRLEN(HTTP_SCHEME_WITH_AUTHORITY); if (prv_startswith_str(HTTP_SCHEME_WITH_AUTHORITY, http_scheme_with_authority_len, uri, total_len)) { *scheme = kMemfaultUriScheme_Http; *default_port = 80; return http_scheme_with_authority_len; } *scheme = kMemfaultUriScheme_Unrecognized; return 0; } bool memfault_http_parse_uri(const char *uri, size_t uri_len, sMemfaultUriInfo *uri_info_out) { size_t host_len = uri_len; // we will adjust as we parse the uri eMemfaultUriScheme scheme; uint32_t port = 0; const size_t bytes_consumed = prv_is_http_or_https_scheme(uri, host_len, &scheme, &port); if (bytes_consumed == 0) { return false; } host_len -= bytes_consumed; const char *authority = uri + bytes_consumed; // Authority ends with a "/" when followed by a "path" or is the length of the string size_t offset; const char *path = NULL; size_t path_len = 0; if (prv_find_first_occurrence(authority, host_len, '/', &offset)) { path = &authority[offset]; path_len = host_len - offset; host_len = offset; } if (prv_find_first_occurrence(authority, host_len, '@', &offset)) { offset++; // skip past the '@' - no use for username/password today if (host_len == offset) { return false; } authority += offset; host_len -= offset; } // are we dealing with an IP-Literal? size_t port_begin_search_offset = 0; if (authority[0] == '[') { if (!prv_find_last_occurrence(authority, host_len, ']', &port_begin_search_offset)) { return false; } } // was a port number included? if (prv_find_last_occurrence(authority, host_len, ':', &offset)) { if (offset >= port_begin_search_offset) { const size_t port_begin_offset = offset + 1 /* skip ':' */; // recover the port int dec_num; const int bytes_processed = prv_str_to_dec(&authority[port_begin_offset], host_len - port_begin_offset, &dec_num); if (bytes_processed <= 0) { return false; } port = (uint32_t)dec_num; host_len = offset; } } if (host_len == 0) { return false; // no host name located! } *uri_info_out = (sMemfaultUriInfo){ .scheme = scheme, .host = authority, .host_len = host_len, .path = path, .path_len = path_len, .port = port, }; return true; } bool memfault_http_get_ota_payload(MfltHttpClientSendCb write_callback, void *ctx, const char *url, size_t url_len) { // Request built will look like this: // GET HTTP/1.1\r\n // Host:\r\n // User-Agent: MemfaultSDK/0.4.2\r\n // \r\n sMemfaultUriInfo info; if (!memfault_http_parse_uri(url, url_len, &info)) { return false; } #define DOWNLOAD_REQUEST_LINE_BEGIN "GET " const size_t download_line_begin_len = MEMFAULT_STATIC_STRLEN(DOWNLOAD_REQUEST_LINE_BEGIN); if (!write_callback(DOWNLOAD_REQUEST_LINE_BEGIN, download_line_begin_len, ctx)) { return false; } bool success = (info.path != NULL) ? write_callback(info.path, info.path_len, ctx) : write_callback("/", 1, ctx); if (!success) { return false; } #define DOWNLOAD_REQUEST_LINE_END " HTTP/1.1\r\n" const size_t download_request_line_end_len = MEMFAULT_STATIC_STRLEN(DOWNLOAD_REQUEST_LINE_END); if (!write_callback(DOWNLOAD_REQUEST_LINE_END, download_request_line_end_len, ctx)) { return false; } if (!prv_write_host_hdr(write_callback, ctx, info.host, info.host_len)) { return false; } if (!prv_write_user_agent_hdr(write_callback, ctx)) { return false; } return prv_write_crlf(write_callback, ctx); } static bool prv_is_unreserved(char c) { return isalnum((uint8_t)c) || c == '-' || c == '_' || c == '.' || c == '~'; } bool memfault_http_needs_escape(const char *str, size_t len) { for (size_t i = 0; i < len; i++) { if (!prv_is_unreserved(str[i])) { return true; } } return false; } int memfault_http_urlencode(const char *inbuf, size_t inbuf_len, char *outbuf, size_t outbuf_len) { // null check if (!inbuf || !outbuf || !inbuf_len || !outbuf_len) { return -1; } while (inbuf_len--) { if (outbuf_len < 2) { // ran out of room before encoding the full input, error. there needs to // be 1 spare character for null term return -1; } char c = *inbuf++; if (prv_is_unreserved(c)) { *outbuf++ = c; outbuf_len--; } else { if (outbuf_len < 4) { // not enough room for encoded character and null term return -1; } // paste encoding (+ null term) snprintf(outbuf, 4, "%%%02X", (uint8_t)c); outbuf += 3; outbuf_len -= 3; } } // null terminate *outbuf = '\0'; return 0; } void memfault_http_get_device_info(sMemfaultDeviceInfo *info) { // Use the user provided callback if available, otherwise use the standard // platform implementation if (g_mflt_http_client_config.get_device_info != NULL) { g_mflt_http_client_config.get_device_info(info); return; } memfault_platform_get_device_info(info); // note: HTTP functions should always call this } bool memfault_http_build_latest_ota_url(char *buf, size_t buf_len) { if ((buf == NULL) || (buf_len == 0)) { return false; } sMemfaultDeviceInfo device_info; memfault_http_get_device_info(&device_info); // Write the base URL int rv = snprintf(buf, buf_len, "%s://%s/api/v0/releases/latest/url?", MEMFAULT_HTTP_GET_SCHEME(), MEMFAULT_HTTP_GET_DEVICE_API_HOST()); if ((rv < 0) || ((size_t)rv >= buf_len)) { return false; } size_t offset = (size_t)rv; // Append each query parameter, URL-encoding values one at a time to save stack space const struct { const char *name; const char *value; } params[] = { { "device_serial", device_info.device_serial }, { "hardware_version", device_info.hardware_version }, { "software_type", device_info.software_type }, { "current_version", device_info.software_version }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(params); i++) { // Write separator and param name const char *sep = (i == 0) ? "" : "&"; rv = snprintf(&buf[offset], buf_len - offset, "%s%s=", sep, params[i].name); if (rv < 0 || (size_t)rv >= buf_len - offset) { return false; } offset += (size_t)rv; // URL-encode value directly into remaining buffer space if (memfault_http_urlencode(params[i].value, strlen(params[i].value), &buf[offset], buf_len - offset) != 0) { MEMFAULT_LOG_ERROR("Failed to URL encode device info param"); return false; } offset += strlen(&buf[offset]); } return true; } bool memfault_http_build_chunk_post_url(char *buf, size_t buf_len) { if ((buf == NULL) || (buf_len == 0)) { return false; } sMemfaultDeviceInfo device_info; memfault_http_get_device_info(&device_info); const int rv = snprintf(buf, buf_len, "%s://%s/api/v0/chunks/%s", MEMFAULT_HTTP_GET_SCHEME(), MEMFAULT_HTTP_GET_CHUNKS_API_HOST(), device_info.device_serial); if ((rv < 0) || ((size_t)rv >= buf_len)) { MEMFAULT_LOG_ERROR("Chunk URL buffer too small. Was %dB but need %dB", (int)buf_len, rv); return false; } return true; } ================================================ FILE: components/http/src/memfault_root_certs_der.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Representation of Root Certs Memfault uses in DER format #include #include "memfault/http/root_certs.h" const uint8_t g_memfault_cert_digicert_global_root_ca[] = { 0x30, 0x82, 0x03, 0xaf, 0x30, 0x82, 0x02, 0x97, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x08, 0x3b, 0xe0, 0x56, 0x90, 0x42, 0x46, 0xb1, 0xa1, 0x75, 0x6a, 0xc9, 0x59, 0x91, 0xc7, 0x4a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe2, 0x3b, 0xe1, 0x11, 0x72, 0xde, 0xa8, 0xa4, 0xd3, 0xa3, 0x57, 0xaa, 0x50, 0xa2, 0x8f, 0x0b, 0x77, 0x90, 0xc9, 0xa2, 0xa5, 0xee, 0x12, 0xce, 0x96, 0x5b, 0x01, 0x09, 0x20, 0xcc, 0x01, 0x93, 0xa7, 0x4e, 0x30, 0xb7, 0x53, 0xf7, 0x43, 0xc4, 0x69, 0x00, 0x57, 0x9d, 0xe2, 0x8d, 0x22, 0xdd, 0x87, 0x06, 0x40, 0x00, 0x81, 0x09, 0xce, 0xce, 0x1b, 0x83, 0xbf, 0xdf, 0xcd, 0x3b, 0x71, 0x46, 0xe2, 0xd6, 0x66, 0xc7, 0x05, 0xb3, 0x76, 0x27, 0x16, 0x8f, 0x7b, 0x9e, 0x1e, 0x95, 0x7d, 0xee, 0xb7, 0x48, 0xa3, 0x08, 0xda, 0xd6, 0xaf, 0x7a, 0x0c, 0x39, 0x06, 0x65, 0x7f, 0x4a, 0x5d, 0x1f, 0xbc, 0x17, 0xf8, 0xab, 0xbe, 0xee, 0x28, 0xd7, 0x74, 0x7f, 0x7a, 0x78, 0x99, 0x59, 0x85, 0x68, 0x6e, 0x5c, 0x23, 0x32, 0x4b, 0xbf, 0x4e, 0xc0, 0xe8, 0x5a, 0x6d, 0xe3, 0x70, 0xbf, 0x77, 0x10, 0xbf, 0xfc, 0x01, 0xf6, 0x85, 0xd9, 0xa8, 0x44, 0x10, 0x58, 0x32, 0xa9, 0x75, 0x18, 0xd5, 0xd1, 0xa2, 0xbe, 0x47, 0xe2, 0x27, 0x6a, 0xf4, 0x9a, 0x33, 0xf8, 0x49, 0x08, 0x60, 0x8b, 0xd4, 0x5f, 0xb4, 0x3a, 0x84, 0xbf, 0xa1, 0xaa, 0x4a, 0x4c, 0x7d, 0x3e, 0xcf, 0x4f, 0x5f, 0x6c, 0x76, 0x5e, 0xa0, 0x4b, 0x37, 0x91, 0x9e, 0xdc, 0x22, 0xe6, 0x6d, 0xce, 0x14, 0x1a, 0x8e, 0x6a, 0xcb, 0xfe, 0xcd, 0xb3, 0x14, 0x64, 0x17, 0xc7, 0x5b, 0x29, 0x9e, 0x32, 0xbf, 0xf2, 0xee, 0xfa, 0xd3, 0x0b, 0x42, 0xd4, 0xab, 0xb7, 0x41, 0x32, 0xda, 0x0c, 0xd4, 0xef, 0xf8, 0x81, 0xd5, 0xbb, 0x8d, 0x58, 0x3f, 0xb5, 0x1b, 0xe8, 0x49, 0x28, 0xa2, 0x70, 0xda, 0x31, 0x04, 0xdd, 0xf7, 0xb2, 0x16, 0xf2, 0x4c, 0x0a, 0x4e, 0x07, 0xa8, 0xed, 0x4a, 0x3d, 0x5e, 0xb5, 0x7f, 0xa3, 0x90, 0xc3, 0xaf, 0x27, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, 0x66, 0xf0, 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, 0x66, 0xf0, 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xcb, 0x9c, 0x37, 0xaa, 0x48, 0x13, 0x12, 0x0a, 0xfa, 0xdd, 0x44, 0x9c, 0x4f, 0x52, 0xb0, 0xf4, 0xdf, 0xae, 0x04, 0xf5, 0x79, 0x79, 0x08, 0xa3, 0x24, 0x18, 0xfc, 0x4b, 0x2b, 0x84, 0xc0, 0x2d, 0xb9, 0xd5, 0xc7, 0xfe, 0xf4, 0xc1, 0x1f, 0x58, 0xcb, 0xb8, 0x6d, 0x9c, 0x7a, 0x74, 0xe7, 0x98, 0x29, 0xab, 0x11, 0xb5, 0xe3, 0x70, 0xa0, 0xa1, 0xcd, 0x4c, 0x88, 0x99, 0x93, 0x8c, 0x91, 0x70, 0xe2, 0xab, 0x0f, 0x1c, 0xbe, 0x93, 0xa9, 0xff, 0x63, 0xd5, 0xe4, 0x07, 0x60, 0xd3, 0xa3, 0xbf, 0x9d, 0x5b, 0x09, 0xf1, 0xd5, 0x8e, 0xe3, 0x53, 0xf4, 0x8e, 0x63, 0xfa, 0x3f, 0xa7, 0xdb, 0xb4, 0x66, 0xdf, 0x62, 0x66, 0xd6, 0xd1, 0x6e, 0x41, 0x8d, 0xf2, 0x2d, 0xb5, 0xea, 0x77, 0x4a, 0x9f, 0x9d, 0x58, 0xe2, 0x2b, 0x59, 0xc0, 0x40, 0x23, 0xed, 0x2d, 0x28, 0x82, 0x45, 0x3e, 0x79, 0x54, 0x92, 0x26, 0x98, 0xe0, 0x80, 0x48, 0xa8, 0x37, 0xef, 0xf0, 0xd6, 0x79, 0x60, 0x16, 0xde, 0xac, 0xe8, 0x0e, 0xcd, 0x6e, 0xac, 0x44, 0x17, 0x38, 0x2f, 0x49, 0xda, 0xe1, 0x45, 0x3e, 0x2a, 0xb9, 0x36, 0x53, 0xcf, 0x3a, 0x50, 0x06, 0xf7, 0x2e, 0xe8, 0xc4, 0x57, 0x49, 0x6c, 0x61, 0x21, 0x18, 0xd5, 0x04, 0xad, 0x78, 0x3c, 0x2c, 0x3a, 0x80, 0x6b, 0xa7, 0xeb, 0xaf, 0x15, 0x14, 0xe9, 0xd8, 0x89, 0xc1, 0xb9, 0x38, 0x6c, 0xe2, 0x91, 0x6c, 0x8a, 0xff, 0x64, 0xb9, 0x77, 0x25, 0x57, 0x30, 0xc0, 0x1b, 0x24, 0xa3, 0xe1, 0xdc, 0xe9, 0xdf, 0x47, 0x7c, 0xb5, 0xb4, 0x24, 0x08, 0x05, 0x30, 0xec, 0x2d, 0xbd, 0x0b, 0xbf, 0x45, 0xbf, 0x50, 0xb9, 0xa9, 0xf3, 0xeb, 0x98, 0x01, 0x12, 0xad, 0xc8, 0x88, 0xc6, 0x98, 0x34, 0x5f, 0x8d, 0x0a, 0x3c, 0xc6, 0xe9, 0xd5, 0x95, 0x95, 0x6d, 0xde }; const uint8_t g_memfault_cert_digicert_global_root_g2[] = { 0x30, 0x82, 0x03, 0x8e, 0x30, 0x82, 0x02, 0x76, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x03, 0x3a, 0xf1, 0xe6, 0xa7, 0x11, 0xa9, 0xa0, 0xbb, 0x28, 0x64, 0xb1, 0x1d, 0x09, 0xfa, 0xe5, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x38, 0x30, 0x31, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x38, 0x30, 0x31, 0x31, 0x35, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbb, 0x37, 0xcd, 0x34, 0xdc, 0x7b, 0x6b, 0xc9, 0xb2, 0x68, 0x90, 0xad, 0x4a, 0x75, 0xff, 0x46, 0xba, 0x21, 0x0a, 0x08, 0x8d, 0xf5, 0x19, 0x54, 0xc9, 0xfb, 0x88, 0xdb, 0xf3, 0xae, 0xf2, 0x3a, 0x89, 0x91, 0x3c, 0x7a, 0xe6, 0xab, 0x06, 0x1a, 0x6b, 0xcf, 0xac, 0x2d, 0xe8, 0x5e, 0x09, 0x24, 0x44, 0xba, 0x62, 0x9a, 0x7e, 0xd6, 0xa3, 0xa8, 0x7e, 0xe0, 0x54, 0x75, 0x20, 0x05, 0xac, 0x50, 0xb7, 0x9c, 0x63, 0x1a, 0x6c, 0x30, 0xdc, 0xda, 0x1f, 0x19, 0xb1, 0xd7, 0x1e, 0xde, 0xfd, 0xd7, 0xe0, 0xcb, 0x94, 0x83, 0x37, 0xae, 0xec, 0x1f, 0x43, 0x4e, 0xdd, 0x7b, 0x2c, 0xd2, 0xbd, 0x2e, 0xa5, 0x2f, 0xe4, 0xa9, 0xb8, 0xad, 0x3a, 0xd4, 0x99, 0xa4, 0xb6, 0x25, 0xe9, 0x9b, 0x6b, 0x00, 0x60, 0x92, 0x60, 0xff, 0x4f, 0x21, 0x49, 0x18, 0xf7, 0x67, 0x90, 0xab, 0x61, 0x06, 0x9c, 0x8f, 0xf2, 0xba, 0xe9, 0xb4, 0xe9, 0x92, 0x32, 0x6b, 0xb5, 0xf3, 0x57, 0xe8, 0x5d, 0x1b, 0xcd, 0x8c, 0x1d, 0xab, 0x95, 0x04, 0x95, 0x49, 0xf3, 0x35, 0x2d, 0x96, 0xe3, 0x49, 0x6d, 0xdd, 0x77, 0xe3, 0xfb, 0x49, 0x4b, 0xb4, 0xac, 0x55, 0x07, 0xa9, 0x8f, 0x95, 0xb3, 0xb4, 0x23, 0xbb, 0x4c, 0x6d, 0x45, 0xf0, 0xf6, 0xa9, 0xb2, 0x95, 0x30, 0xb4, 0xfd, 0x4c, 0x55, 0x8c, 0x27, 0x4a, 0x57, 0x14, 0x7c, 0x82, 0x9d, 0xcd, 0x73, 0x92, 0xd3, 0x16, 0x4a, 0x06, 0x0c, 0x8c, 0x50, 0xd1, 0x8f, 0x1e, 0x09, 0xbe, 0x17, 0xa1, 0xe6, 0x21, 0xca, 0xfd, 0x83, 0xe5, 0x10, 0xbc, 0x83, 0xa5, 0x0a, 0xc4, 0x67, 0x28, 0xf6, 0x73, 0x14, 0x14, 0x3d, 0x46, 0x76, 0xc3, 0x87, 0x14, 0x89, 0x21, 0x34, 0x4d, 0xaf, 0x0f, 0x45, 0x0c, 0xa6, 0x49, 0xa1, 0xba, 0xbb, 0x9c, 0xc5, 0xb1, 0x33, 0x83, 0x29, 0x85, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x42, 0x30, 0x40, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x4e, 0x22, 0x54, 0x20, 0x18, 0x95, 0xe6, 0xe3, 0x6e, 0xe6, 0x0f, 0xfa, 0xfa, 0xb9, 0x12, 0xed, 0x06, 0x17, 0x8f, 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x60, 0x67, 0x28, 0x94, 0x6f, 0x0e, 0x48, 0x63, 0xeb, 0x31, 0xdd, 0xea, 0x67, 0x18, 0xd5, 0x89, 0x7d, 0x3c, 0xc5, 0x8b, 0x4a, 0x7f, 0xe9, 0xbe, 0xdb, 0x2b, 0x17, 0xdf, 0xb0, 0x5f, 0x73, 0x77, 0x2a, 0x32, 0x13, 0x39, 0x81, 0x67, 0x42, 0x84, 0x23, 0xf2, 0x45, 0x67, 0x35, 0xec, 0x88, 0xbf, 0xf8, 0x8f, 0xb0, 0x61, 0x0c, 0x34, 0xa4, 0xae, 0x20, 0x4c, 0x84, 0xc6, 0xdb, 0xf8, 0x35, 0xe1, 0x76, 0xd9, 0xdf, 0xa6, 0x42, 0xbb, 0xc7, 0x44, 0x08, 0x86, 0x7f, 0x36, 0x74, 0x24, 0x5a, 0xda, 0x6c, 0x0d, 0x14, 0x59, 0x35, 0xbd, 0xf2, 0x49, 0xdd, 0xb6, 0x1f, 0xc9, 0xb3, 0x0d, 0x47, 0x2a, 0x3d, 0x99, 0x2f, 0xbb, 0x5c, 0xbb, 0xb5, 0xd4, 0x20, 0xe1, 0x99, 0x5f, 0x53, 0x46, 0x15, 0xdb, 0x68, 0x9b, 0xf0, 0xf3, 0x30, 0xd5, 0x3e, 0x31, 0xe2, 0x8d, 0x84, 0x9e, 0xe3, 0x8a, 0xda, 0xda, 0x96, 0x3e, 0x35, 0x13, 0xa5, 0x5f, 0xf0, 0xf9, 0x70, 0x50, 0x70, 0x47, 0x41, 0x11, 0x57, 0x19, 0x4e, 0xc0, 0x8f, 0xae, 0x06, 0xc4, 0x95, 0x13, 0x17, 0x2f, 0x1b, 0x25, 0x9f, 0x75, 0xf2, 0xb1, 0x8e, 0x99, 0xa1, 0x6f, 0x13, 0xb1, 0x41, 0x71, 0xfe, 0x88, 0x2a, 0xc8, 0x4f, 0x10, 0x20, 0x55, 0xd7, 0xf3, 0x14, 0x45, 0xe5, 0xe0, 0x44, 0xf4, 0xea, 0x87, 0x95, 0x32, 0x93, 0x0e, 0xfe, 0x53, 0x46, 0xfa, 0x2c, 0x9d, 0xff, 0x8b, 0x22, 0xb9, 0x4b, 0xd9, 0x09, 0x45, 0xa4, 0xde, 0xa4, 0xb8, 0x9a, 0x58, 0xdd, 0x1b, 0x7d, 0x52, 0x9f, 0x8e, 0x59, 0x43, 0x88, 0x81, 0xa4, 0x9e, 0x26, 0xd5, 0x6f, 0xad, 0xdd, 0x0d, 0xc6, 0x37, 0x7d, 0xed, 0x03, 0x92, 0x1b, 0xe5, 0x77, 0x5f, 0x76, 0xee, 0x3c, 0x8d, 0xc4, 0x5d, 0x56, 0x5b, 0xa2, 0xd9, 0x66, 0x6e, 0xb3, 0x35, 0x37, 0xe5, 0x32, 0xb6 }; const uint8_t g_memfault_cert_amazon_root_ca1[] = { 0x30, 0x82, 0x03, 0x41, 0x30, 0x82, 0x02, 0x29, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x06, 0x6c, 0x9f, 0xcf, 0x99, 0xbf, 0x8c, 0x0a, 0x39, 0xe2, 0xf0, 0x78, 0x8a, 0x43, 0xe6, 0x96, 0x36, 0x5b, 0xca, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x39, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x06, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x35, 0x32, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x38, 0x30, 0x31, 0x31, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x39, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x06, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb2, 0x78, 0x80, 0x71, 0xca, 0x78, 0xd5, 0xe3, 0x71, 0xaf, 0x47, 0x80, 0x50, 0x74, 0x7d, 0x6e, 0xd8, 0xd7, 0x88, 0x76, 0xf4, 0x99, 0x68, 0xf7, 0x58, 0x21, 0x60, 0xf9, 0x74, 0x84, 0x01, 0x2f, 0xac, 0x02, 0x2d, 0x86, 0xd3, 0xa0, 0x43, 0x7a, 0x4e, 0xb2, 0xa4, 0xd0, 0x36, 0xba, 0x01, 0xbe, 0x8d, 0xdb, 0x48, 0xc8, 0x07, 0x17, 0x36, 0x4c, 0xf4, 0xee, 0x88, 0x23, 0xc7, 0x3e, 0xeb, 0x37, 0xf5, 0xb5, 0x19, 0xf8, 0x49, 0x68, 0xb0, 0xde, 0xd7, 0xb9, 0x76, 0x38, 0x1d, 0x61, 0x9e, 0xa4, 0xfe, 0x82, 0x36, 0xa5, 0xe5, 0x4a, 0x56, 0xe4, 0x45, 0xe1, 0xf9, 0xfd, 0xb4, 0x16, 0xfa, 0x74, 0xda, 0x9c, 0x9b, 0x35, 0x39, 0x2f, 0xfa, 0xb0, 0x20, 0x50, 0x06, 0x6c, 0x7a, 0xd0, 0x80, 0xb2, 0xa6, 0xf9, 0xaf, 0xec, 0x47, 0x19, 0x8f, 0x50, 0x38, 0x07, 0xdc, 0xa2, 0x87, 0x39, 0x58, 0xf8, 0xba, 0xd5, 0xa9, 0xf9, 0x48, 0x67, 0x30, 0x96, 0xee, 0x94, 0x78, 0x5e, 0x6f, 0x89, 0xa3, 0x51, 0xc0, 0x30, 0x86, 0x66, 0xa1, 0x45, 0x66, 0xba, 0x54, 0xeb, 0xa3, 0xc3, 0x91, 0xf9, 0x48, 0xdc, 0xff, 0xd1, 0xe8, 0x30, 0x2d, 0x7d, 0x2d, 0x74, 0x70, 0x35, 0xd7, 0x88, 0x24, 0xf7, 0x9e, 0xc4, 0x59, 0x6e, 0xbb, 0x73, 0x87, 0x17, 0xf2, 0x32, 0x46, 0x28, 0xb8, 0x43, 0xfa, 0xb7, 0x1d, 0xaa, 0xca, 0xb4, 0xf2, 0x9f, 0x24, 0x0e, 0x2d, 0x4b, 0xf7, 0x71, 0x5c, 0x5e, 0x69, 0xff, 0xea, 0x95, 0x02, 0xcb, 0x38, 0x8a, 0xae, 0x50, 0x38, 0x6f, 0xdb, 0xfb, 0x2d, 0x62, 0x1b, 0xc5, 0xc7, 0x1e, 0x54, 0xe1, 0x77, 0xe0, 0x67, 0xc8, 0x0f, 0x9c, 0x87, 0x23, 0xd6, 0x3f, 0x40, 0x20, 0x7f, 0x20, 0x80, 0xc4, 0x80, 0x4c, 0x3e, 0x3b, 0x24, 0x26, 0x8e, 0x04, 0xae, 0x6c, 0x9a, 0xc8, 0xaa, 0x0d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x42, 0x30, 0x40, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x84, 0x18, 0xcc, 0x85, 0x34, 0xec, 0xbc, 0x0c, 0x94, 0x94, 0x2e, 0x08, 0x59, 0x9c, 0xc7, 0xb2, 0x10, 0x4e, 0x0a, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x98, 0xf2, 0x37, 0x5a, 0x41, 0x90, 0xa1, 0x1a, 0xc5, 0x76, 0x51, 0x28, 0x20, 0x36, 0x23, 0x0e, 0xae, 0xe6, 0x28, 0xbb, 0xaa, 0xf8, 0x94, 0xae, 0x48, 0xa4, 0x30, 0x7f, 0x1b, 0xfc, 0x24, 0x8d, 0x4b, 0xb4, 0xc8, 0xa1, 0x97, 0xf6, 0xb6, 0xf1, 0x7a, 0x70, 0xc8, 0x53, 0x93, 0xcc, 0x08, 0x28, 0xe3, 0x98, 0x25, 0xcf, 0x23, 0xa4, 0xf9, 0xde, 0x21, 0xd3, 0x7c, 0x85, 0x09, 0xad, 0x4e, 0x9a, 0x75, 0x3a, 0xc2, 0x0b, 0x6a, 0x89, 0x78, 0x76, 0x44, 0x47, 0x18, 0x65, 0x6c, 0x8d, 0x41, 0x8e, 0x3b, 0x7f, 0x9a, 0xcb, 0xf4, 0xb5, 0xa7, 0x50, 0xd7, 0x05, 0x2c, 0x37, 0xe8, 0x03, 0x4b, 0xad, 0xe9, 0x61, 0xa0, 0x02, 0x6e, 0xf5, 0xf2, 0xf0, 0xc5, 0xb2, 0xed, 0x5b, 0xb7, 0xdc, 0xfa, 0x94, 0x5c, 0x77, 0x9e, 0x13, 0xa5, 0x7f, 0x52, 0xad, 0x95, 0xf2, 0xf8, 0x93, 0x3b, 0xde, 0x8b, 0x5c, 0x5b, 0xca, 0x5a, 0x52, 0x5b, 0x60, 0xaf, 0x14, 0xf7, 0x4b, 0xef, 0xa3, 0xfb, 0x9f, 0x40, 0x95, 0x6d, 0x31, 0x54, 0xfc, 0x42, 0xd3, 0xc7, 0x46, 0x1f, 0x23, 0xad, 0xd9, 0x0f, 0x48, 0x70, 0x9a, 0xd9, 0x75, 0x78, 0x71, 0xd1, 0x72, 0x43, 0x34, 0x75, 0x6e, 0x57, 0x59, 0xc2, 0x02, 0x5c, 0x26, 0x60, 0x29, 0xcf, 0x23, 0x19, 0x16, 0x8e, 0x88, 0x43, 0xa5, 0xd4, 0xe4, 0xcb, 0x08, 0xfb, 0x23, 0x11, 0x43, 0xe8, 0x43, 0x29, 0x72, 0x62, 0xa1, 0xa9, 0x5d, 0x5e, 0x08, 0xd4, 0x90, 0xae, 0xb8, 0xd8, 0xce, 0x14, 0xc2, 0xd0, 0x55, 0xf2, 0x86, 0xf6, 0xc4, 0x93, 0x43, 0x77, 0x66, 0x61, 0xc0, 0xb9, 0xe8, 0x41, 0xd7, 0x97, 0x78, 0x60, 0x03, 0x6e, 0x4a, 0x72, 0xae, 0xa5, 0xd1, 0x7d, 0xba, 0x10, 0x9e, 0x86, 0x6c, 0x1b, 0x8a, 0xb9, 0x59, 0x33, 0xf8, 0xeb, 0xc4, 0x90, 0xbe, 0xf1, 0xb9 }; const size_t g_memfault_cert_digicert_global_root_ca_len = sizeof(g_memfault_cert_digicert_global_root_ca); const size_t g_memfault_cert_digicert_global_root_g2_len = sizeof(g_memfault_cert_digicert_global_root_g2); const size_t g_memfault_cert_amazon_root_ca1_len = sizeof(g_memfault_cert_amazon_root_ca1); ================================================ FILE: components/include/memfault/components.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A convenience header to pick up all the possible Memfault SDK APIs available //! //! For example, in a platform port file, a user of the SDK can pick up this single header and //! start implementing dependencies without needed any other memfault component headers: //! //! #include memfault/components.h //! #include "memfault/config.h" #include "memfault/core/arch.h" #include "memfault/core/batched_events.h" #include "memfault/core/build_info.h" #include "memfault/core/compiler.h" #include "memfault/core/custom_data_recording.h" #include "memfault/core/data_export.h" #include "memfault/core/data_packetizer.h" #include "memfault/core/debug_log.h" #include "memfault/core/device_info.h" #include "memfault/core/errors.h" #include "memfault/core/event_storage.h" #include "memfault/core/heap_stats.h" #include "memfault/core/log.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/crc32.h" #include "memfault/core/platform/debug_log.h" #include "memfault/core/platform/device_info.h" #include "memfault/core/platform/nonvolatile_event_storage.h" #include "memfault/core/platform/overrides.h" #include "memfault/core/platform/reboot_tracking.h" #include "memfault/core/platform/system_time.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/reboot_tracking.h" #include "memfault/core/sdk_assert.h" #include "memfault/core/self_test.h" #include "memfault/core/task_watchdog.h" #include "memfault/core/trace_event.h" #include "memfault/demo/cli.h" #include "memfault/demo/shell.h" #include "memfault/demo/shell_commands.h" #include "memfault/demo/util.h" #include "memfault/http/http_client.h" #include "memfault/http/platform/http_client.h" #include "memfault/http/root_certs.h" #include "memfault/http/utils.h" #include "memfault/metrics/battery.h" #include "memfault/metrics/connectivity.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/battery.h" #include "memfault/metrics/platform/overrides.h" #include "memfault/metrics/platform/timer.h" #include "memfault/metrics/reliability.h" #include "memfault/metrics/utils.h" #include "memfault/panics/assert.h" #include "memfault/panics/coredump.h" #include "memfault/panics/fault_handling.h" #include "memfault/panics/platform/coredump.h" #include "memfault/util/banner.h" #include "memfault/util/base64.h" #include "memfault/util/cbor.h" #include "memfault/util/circular_buffer.h" #include "memfault/util/crc16.h" #include "memfault/util/rle.h" #include "memfault/util/varint.h" #include "memfault/version.h" ================================================ FILE: components/include/memfault/config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Configuration settings available in the SDK. //! //! All settings can be set from the platform configuration file, //! "memfault_platform_config.h". If no setting is specified, the default values //! below will get picked up. //! //! The configuration file has three settings which can be overridden by adding a //! compiler time define to your CFLAG list: //! 1. MEMFAULT_PLATFORM_CONFIG_FILE can be used to change the default name of //! the platform configuration file, "memfault_platform_config.h" //! 2. MEMFAULT_PLATFORM_CONFIG_DISABLED can be used to disable inclusion of //! a platform configuration file. //! 3. MEMFAULT_PLATFORM_CONFIG_STRICT can be used to force a user of the SDK to //! explicitly define every configuration constant rather than pick up defaults. //! When the Wundef compiler option is used, any undefined configuration will //! be caught at compilation time. #ifdef __cplusplus extern "C" { #endif #ifndef MEMFAULT_PLATFORM_CONFIG_FILE #define MEMFAULT_PLATFORM_CONFIG_FILE "memfault_platform_config.h" #endif #ifndef MEMFAULT_PLATFORM_CONFIG_DISABLED #include MEMFAULT_PLATFORM_CONFIG_FILE #endif #ifndef MEMFAULT_PLATFORM_CONFIG_STRICT #define MEMFAULT_PLATFORM_CONFIG_STRICT 0 #endif #if !MEMFAULT_PLATFORM_CONFIG_STRICT #define MEMFAULT_PLATFORM_CONFIG_INCLUDE_DEFAULTS #include "memfault/default_config.h" #undef MEMFAULT_PLATFORM_CONFIG_INCLUDE_DEFAULTS #endif #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/arch.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! APIs for MCU architecture specifics #include #ifdef __cplusplus extern "C" { #endif //! Return true if the code is currently running in an interrupt context, false otherwise bool memfault_arch_is_inside_isr(void); //! Disable any configurable fault handlers supported by the platform void memfault_arch_disable_configurable_faults(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/batched_events.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Helpers used for serializing multiple events into a single message #include #include #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_BATCHED_EVENTS_MAX_HEADER_LENGTH 5 typedef struct { size_t length; uint8_t data[MEMFAULT_BATCHED_EVENTS_MAX_HEADER_LENGTH]; } sMemfaultBatchedEventsHeader; //! Builds the header used when events are sent in one message //! //! @num_events The number events that will be sent in one message //! @header_out Populated with the header that needs to lead the events to send. //! If no header is needed (i.e num_events <= 1), the length can be 0. void memfault_batched_events_build_header(size_t num_events, sMemfaultBatchedEventsHeader *header_out); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/build_info.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! The Memfault SDK can automatically track information about the build it was compiled into, such //! as a unique "build id" for your firmware project binary. //! //! There are two ways a unique "build id" can be generated and tracked by the SDK. The modes //! are controlled by setting the MEMFAULT_USE_GNU_BUILD_ID CFLAG to either 0 or 1, where: //! //! Memfault Generated Build ID (MEMFAULT_USE_GNU_BUILD_ID=0) //! //! This is the _default_ option and works with any compiler (GCC, IAR, ARM MDK, etc). For this //! configuration, a unique "build id" can be patched into the ELF file generated by your build //! by running the following as a post-compilation step: //! python $MEMFAULT_FIRMWARE_SDK/scripts/fw_build_id.py //! //! Running this script is not required but if it if not run Memfault will be unable to to track a //! unique "build id" for your binary. //! //! GNU Build ID (MEMFAULT_USE_GNU_BUILD_ID=1) //! //! This mode makes use of the GNU Build ID generated by the GNU linker //! (https://mflt.io/gnu-build-id). If you are compiling with GCC, we _recommend_ using this mode. //! To do this, you will need to make the following two changes: //! //! 1. Pass the "--build-id" argument to GNU LD. //! ("-Wl,--build-id" when invoking the linker via GCC) //! //! 2. Add the -DMEMFAULT_USE_GNU_BUILD_ID=1 CFLAG to your CC invocation //! //! 3. Add the following snippet to your projects linker script (.ld file) //! where "FLASH" below will match the name of the MEMORY section //! read-only data and text is placed in. //! //! .note.gnu.build-id : //! { //! __start_gnu_build_id_start = .; //! KEEP(*(.note.gnu.build-id)) //! } > FLASH #include #include #include #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_BUILD_ID_LEN 20 typedef struct { // A unique build identifier for the current binary uint8_t build_id[MEMFAULT_BUILD_ID_LEN]; } sMemfaultBuildInfo; //! Returns build info tracked by the Memfault SDK //! //! @param[out] info When available, populated with information about the current build //! //! @return true if a unique id was found and populated in info or false otherwise bool memfault_build_info_read(sMemfaultBuildInfo *info); //! Copies the build id as a hex string into the buffer provided //! //! @param[out] out_buf The buffer to copy the build id into //! //! @param buf_len The length of the buffer. This routine will copy up to buf_len - 1 bytes of the //! build id. For example, to copy only the first 5 hex characters of a build id, the buffer only //! needs to be 6 bytes long. bool memfault_build_id_get_string(char *out_buf, size_t buf_len); //! Dump version info tracked by Memfault to the console using an MEMFAULT_LOG_INFO() //! //! You will see something like one of the following printed //! depending on the configuration in use: //! No Build ID available //! Memfault Build ID: 000102030405060708090a0b0c0d0e0f10111213 //! GNU Build ID: 58faeeb01696afbfb4f790c63b77c80f89972989 void memfault_build_info_dump(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/compact_log_compile_time_checks.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! //! Compile time validity checks run on compact logs: //! 1) Enables printf() style Wformat checking //! 2) Verifies that number of args passed is <= the maximum number supported (15) #include "memfault/config.h" #ifdef __cplusplus extern "C" { #endif #include #if MEMFAULT_COMPACT_LOG_ENABLE #include "memfault/core/compiler.h" #include "memfault/core/preprocessor.h" #define MEMFAULT_LOGGING_MAX_SUPPORTED_ARGS 15 static void run_printf_like_func_check_(const char *format, ...) MEMFAULT_PRINTF_LIKE_FUNC(1, 2); //! Mark the function as used to prevent warnings in situations where this header is included but //! no logging is actually used MEMFAULT_USED static void run_printf_like_func_check_(MEMFAULT_UNUSED const char *format, ...) { } //! Compilation time checks on log formatter //! //! - static asserts that argument list does not exceed allowed length. //! - Runs printf() style format checking (behind a "if (false)" so that //! the actual code gets optimized away) #define MEMFAULT_LOGGING_RUN_COMPILE_TIME_CHECKS(format, ...) \ do { \ MEMFAULT_STATIC_ASSERT( \ MEMFAULT_ARG_COUNT_UP_TO_32(__VA_ARGS__) <= MEMFAULT_LOGGING_MAX_SUPPORTED_ARGS, \ MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_ARG_COUNT_UP_TO_32( \ __VA_ARGS__)) " args > MEMFAULT_LOGGING_MAX_SUPPORTED_ARGS " \ "(" MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_LOGGING_MAX_SUPPORTED_ARGS) ")" \ "!"); \ if (false) { \ run_printf_like_func_check_(format, ##__VA_ARGS__); \ } \ } while (0) #endif /* MEMFAULT_COMPACT_LOG_ENABLE */ #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/compact_log_helpers.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! //! Utilities used to generate "compact" logs by replacing the string formatter with an integer //! "id" at compilation time. This allows arbitrary length string formatters to be stripped from a //! binary which has several key benefits: //! - Less codespace usage (a uint32_t instead of an arbitrary length string) //! - Less bandwidth so quicker to transmit and consequently lower power //! - Better obfuscation (i.e with formatters stripped, running "strings" on the binary reveals //! less) //! //! //! Note: Some of the macros in this file make use of `##` in `, ## __VA_ARGS__` to support calls //! with empty __VA_ARGS__. While this is a GNU extension, it's widely supported in most other //! modern compilers (ARM Compiler 5, Clang, iccarm). When using GCC this means you should be //! compiling with -std=gnu11 (default), -std=gnu99, etc. We check for this support at compile //! time via a static assert in "memfault/core/preprocessor.h" #include #include #include "memfault/config.h" #if MEMFAULT_COMPACT_LOG_ENABLE #include "memfault/core/compiler.h" #include "memfault/core/preprocessor.h" #define MEMFAULT_LOG_ARG_PROMOTED_TO_INT32 0 #define MEMFAULT_LOG_ARG_PROMOTED_TO_INT64 1 #define MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE 2 #define MEMFAULT_LOG_ARG_PROMOTED_TO_STR 3 #ifdef __cplusplus //! C++ type promotion logic //! //! This code defines the MemfaultLogArgPromotionType template structure, responsible for //! determining the promotion type of different argument types passed to Memfault's logging //! functions. This is important because certain types may need to be promoted to larger or //! different types, ensuring consistent handling in logging The code uses C++11 features, //! including SFINAE (Substitution Failure Is Not An Error) with std::enable_if, and the GNU ## va //! args extension. It assumes the --std=gnu++11 compiler flag or newer. //! //! Structure definition: //! 1. Default promotion to int64: //! - By default, if no other promotion rule applies, MemfaultLogArgPromotionType promotes the //! type to //! MEMFAULT_LOG_ARG_PROMOTED_TO_INT64, indicating that any unhandled type defaults to a //! 64-bit integer. //! //! 2. Conditional promotion to int32: //! - If the size of the type T is less than or equal to 4 bytes (e.g., smaller integer types), //! MemfaultLogArgPromotionType promotes the type to MEMFAULT_LOG_ARG_PROMOTED_TO_INT32, //! allowing consistent handling for smaller integers. //! //! 3. Specialized promotions: //! - Specific types receive unique promotion constants: //! - Floating-point types (float, double, long double): Promoted to //! MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE. //! - String types (char* variants): Promoted to MEMFAULT_LOG_ARG_PROMOTED_TO_STR. //! //! Macro definition: //! - _MEMFAULT_LOG_ARG_PROMOTION_TYPE(arg): This macro determines the promotion type of an //! argument. //! The (arg) + 0 operation is used to enforce integer promotion for non-integer types, helping //! the compiler resolve the appropriate type for MemfaultLogArgPromotionType. #include // Default integer type is int64 template struct MemfaultLogArgPromotionType : std::integral_constant { }; // If sizeof(T) <= 32, then it's int32 template struct MemfaultLogArgPromotionType::type> : std::integral_constant { }; // More specific types template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; template <> struct MemfaultLogArgPromotionType : std::integral_constant { }; #define _MEMFAULT_LOG_ARG_PROMOTION_TYPE(arg) \ MemfaultLogArgPromotionType::value #else // C Code implementation //! C type promotion logic //! //! Utilizes the rules around "default argument promotion" (specifically for va_args) defined in //! the C specification (http://www.iso-9899.info/wiki/The_Standard) to encode information about //! the width of arguments. (For more details see "6.5.2.2 Function calls" in the C11 Spec). //! //! Promotion Rules: //! - Floating-point types (float, double, long double): Promoted to //! MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE. //! - String types (char* variants): Promoted to MEMFAULT_LOG_ARG_PROMOTED_TO_STR. //! - Default case: //! - If the size of the argument is less than or equal to the size of an int, the type is //! promoted to //! MEMFAULT_LOG_ARG_PROMOTED_TO_INT32. //! - Otherwise, it defaults to MEMFAULT_LOG_ARG_PROMOTED_TO_INT64, for larger integer types. //! - Notably, bool and _BitInt(N) are safely promoted to int by falling into this case. //! //! NOTE 1: We use a special type for "strings" (i.e char *) so we know to encode the value //! pointed to (the actual NUL terminated string) in this situation rather than the pointer //! itself. //! //! NOTE 2: We use "(arg) + 0" in _Generic() below to explicitly force implicit type conversion on //! char[N] to (char *) and bitfields. There was an ambiguity in the spec when the feature was //! introduced in C11 about how arrays should be handled. This has since been fixed but not all //! compilers (i.e IAR) have picked that up. See notes at: //! https://en.cppreference.com/w/c/language/generic //! //! NOTE 3: While _Generic was formally added as part of C11, it has been backported as an //! extension to many older C standard implementations in compilers so we don't explicitly check //! for C11. #ifdef __clang__ // Clangs "Wsizeof-array-decay" generates a false positive with our usage of sizeof() // inside _Generic so just disable it when the file is used #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsizeof-array-decay" // Similarly for "-Wpre-c11-compat", since this implementation relies on C11 _Generic #pragma clang diagnostic ignored "-Wpre-c11-compat" #endif // clang-format off #define _MEMFAULT_LOG_ARG_PROMOTION_TYPE(arg) \ _Generic((arg) + 0, \ float: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \ double: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \ long double: MEMFAULT_LOG_ARG_PROMOTED_TO_DOUBLE, \ char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \ const char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \ signed char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \ const signed char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \ unsigned char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \ const unsigned char*: MEMFAULT_LOG_ARG_PROMOTED_TO_STR, \ default: sizeof((arg) + 0) <= sizeof(int) ? \ MEMFAULT_LOG_ARG_PROMOTED_TO_INT32 : MEMFAULT_LOG_ARG_PROMOTED_TO_INT64) // clang-format on #endif // __cplusplus #ifdef __cplusplus extern "C" { #endif #define _MF_FMT_0(fmt_op) 0 #define _MF_FMT_1(fmt_op, X) fmt_op(X, 0) #define _MF_FMT_2(fmt_op, X, ...) fmt_op(X, 2) | _MF_FMT_1(fmt_op, __VA_ARGS__) #define _MF_FMT_3(fmt_op, X, ...) fmt_op(X, 4) | _MF_FMT_2(fmt_op, __VA_ARGS__) #define _MF_FMT_4(fmt_op, X, ...) fmt_op(X, 6) | _MF_FMT_3(fmt_op, __VA_ARGS__) #define _MF_FMT_5(fmt_op, X, ...) fmt_op(X, 8) | _MF_FMT_4(fmt_op, __VA_ARGS__) #define _MF_FMT_6(fmt_op, X, ...) fmt_op(X, 10) | _MF_FMT_5(fmt_op, __VA_ARGS__) #define _MF_FMT_7(fmt_op, X, ...) fmt_op(X, 12) | _MF_FMT_6(fmt_op, __VA_ARGS__) #define _MF_FMT_8(fmt_op, X, ...) fmt_op(X, 14) | _MF_FMT_7(fmt_op, __VA_ARGS__) #define _MF_FMT_9(fmt_op, X, ...) fmt_op(X, 16) | _MF_FMT_8(fmt_op, __VA_ARGS__) #define _MF_FMT_10(fmt_op, X, ...) fmt_op(X, 18) | _MF_FMT_9(fmt_op, __VA_ARGS__) #define _MF_FMT_11(fmt_op, X, ...) fmt_op(X, 20) | _MF_FMT_10(fmt_op, __VA_ARGS__) #define _MF_FMT_12(fmt_op, X, ...) fmt_op(X, 22) | _MF_FMT_11(fmt_op, __VA_ARGS__) #define _MF_FMT_13(fmt_op, X, ...) fmt_op(X, 24) | _MF_FMT_12(fmt_op, __VA_ARGS__) #define _MF_FMT_14(fmt_op, X, ...) fmt_op(X, 26) | _MF_FMT_13(fmt_op, __VA_ARGS__) #define _MF_FMT_15(fmt_op, X, ...) fmt_op(X, 28) | _MF_FMT_14(fmt_op, __VA_ARGS__) #define _MF_GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \ NAME, ...) \ NAME #define _MF_FOR_EACH(action, ...) \ _MF_GET_MACRO(_0, ##__VA_ARGS__, _MF_FMT_15, _MF_FMT_14, _MF_FMT_13, _MF_FMT_12, _MF_FMT_11, \ _MF_FMT_10, _MF_FMT_9, _MF_FMT_8, _MF_FMT_7, _MF_FMT_6, _MF_FMT_5, _MF_FMT_4, \ _MF_FMT_3, _MF_FMT_2, _MF_FMT_1, _MF_FMT_0) \ (action, ##__VA_ARGS__) #define MEMFAULT_FMT_ENCODE_OP(_arg, _shift) (_MEMFAULT_LOG_ARG_PROMOTION_TYPE(_arg) << _shift) //! Generates a compressed representation of the va_arg format (up to 15 arguments) //! //! The value itself resolves at compilation time so there is 0 overhead at runtime //! //! Encoding scheme: //! - Bits following the most significant bit set to 1 are in use //! - 2 bits per argument encode the promotion type. For arg0 - argN, most significant 2 bits in //! use correspond to argument 0 format, least significant 2 bits correspond to argument N //! format //! //! Examples: //! 0b0000.0001 => 0 arguments //! 0b0000.0110 => 1 argument (arg0=0b10=double) //! 0b0111.0100 => 3 arguments (arg0=0b11=str, arg1=0b01=int64, arg2=0b00=int) #define MFLT_GET_COMPRESSED_LOG_FMT(...) \ ((_MF_FOR_EACH(MEMFAULT_FMT_ENCODE_OP, ##__VA_ARGS__)) | \ (1 << (2 * MEMFAULT_ARG_COUNT_UP_TO_32(__VA_ARGS__)))) //! The "special" ELF section all compact log format information is placed in //! //! Since data in this section is _never_ read by the firmware, it does not need //! to be placed in the binary flashed on device. //! //! With GNU GCC's ld format, the "INFO" attribute can be used to mark a section as //! non-allocated. The following addition will need to be made to the .ld script: //! //! log_fmt 0xF0000000 (INFO): //! { //! KEEP(*(*.log_fmt_hdr)) //! KEEP(*(log_fmt)) //! } #define MEMFAULT_LOG_FMT_ELF_SECTION MEMFAULT_PUT_IN_SECTION("log_fmt") //! The metadata we track in the ELF for each compact log in the code. //! This is used to recover the original information #define MEMFAULT_LOG_FMT_ELF_SECTION_ENTRY(format, ...) \ MEMFAULT_LOG_FMT_ELF_SECTION \ MEMFAULT_NO_ALLOC static const char _memfault_log_fmt_ptr[] = MEMFAULT_EXPAND_AND_QUOTE( \ MEMFAULT_ARG_COUNT_UP_TO_32(__VA_ARGS__)) ";" __FILE__ \ ";" MEMFAULT_EXPAND_AND_QUOTE(__LINE__) ";" format #define MEMFAULT_LOG_FMT_ELF_SECTION_ENTRY_PTR ((uint32_t)(uintptr_t)&_memfault_log_fmt_ptr[0]) typedef struct MemfaultLogFmtElfSectionHeader { uint32_t magic; uint8_t version; uint8_t rsvd[3]; } sMemfaultLogFmtElfSectionHeader; extern const sMemfaultLogFmtElfSectionHeader g_memfault_log_fmt_elf_section_hdr; #ifdef __cplusplus } #endif #endif /* MEMFAULT_COMPACT_LOG_ENABLE */ ================================================ FILE: components/include/memfault/core/compact_log_serializer.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A backend which serializes logs that have been compressed using //! "memfault/core/compact_log_helpers.h" into an array of CBOR encoded arguments. //! This enables logs to be easily inserted into Memfault events or base64/hexdump'd out //! over the CLI. #include #include "memfault/util/cbor.h" #ifdef __cplusplus extern "C" { #endif //! Serializes a "compact" log into a CBOR array representation //! //! @param encoder The cbor encoder to write into //! @param log_id A unique identifier for the log being serialized //! @param compressed_fmt The formatters for the log. See MFLT_GET_COMPRESSED_LOG_FMT in //! "memfault/core/compact_log_helpers.h" for more context //! @param args The list of arguments to encode //! //! @return true if serialization succeeded, false otherwise bool memfault_vlog_compact_serialize(sMemfaultCborEncoder *encoder, uint32_t log_id, uint32_t compressed_fmt, va_list args); //! Serializes a fallback entry, when the compact log is too large to fit the //! provided log entry limit. //! //! @param encoder The cbor encoder to write into //! @param log_id A unique identifier for the log being serialized //! @param serialized_len The computed serialized length of the original compact log //! //! @return true if serialization succeeded, false otherwise bool memfault_vlog_compact_serialize_fallback_entry(sMemfaultCborEncoder *encoder, uint32_t log_id, uint32_t serialized_len); //! Same as "memfault_vlog_compact_serialize" but takes a variable list of arguments bool memfault_log_compact_serialize(sMemfaultCborEncoder *encoder, uint32_t log_id, uint32_t compressed_fmt, ...); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/compiler.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Wrappers for common macros & compiler specifics #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_QUOTE(str) #str #define MEMFAULT_EXPAND_AND_QUOTE(str) MEMFAULT_QUOTE(str) #define MEMFAULT_CONCAT_(x, y) x##y #define MEMFAULT_CONCAT(x, y) MEMFAULT_CONCAT_(x, y) // Given a static string definition, compute the strlen equivalent // (i.e MEMFAULT_STATIC_STRLEN("abcd") == 4) #define MEMFAULT_STATIC_STRLEN(s) (sizeof(s) - 1) // Convenience macros for distinguishing ARM variants #if defined(__arm__) || defined(__ICCARM__) || defined(__TI_ARM__) #define MEMFAULT_COMPILER_ARM 1 #else #define MEMFAULT_COMPILER_ARM 0 #endif // Check for ARM Cortex-A/R target architecture (v7 and v8) #if MEMFAULT_COMPILER_ARM #if defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || \ defined(__ARM_ARCH_8R__) #define MEMFAULT_COMPILER_ARM_V7_A_R 1 #else #define MEMFAULT_COMPILER_ARM_V7_A_R 0 #endif // Pre-Cortex ARM families: ARM7TDMI (ARMv4T), ARM9 (ARMv5TE/TEJ), ARM11 (ARMv6). // These lack Cortex-M system registers (SCB, NVIC, etc.) so must not be treated // as Cortex-M even though MEMFAULT_COMPILER_ARM_V7_A_R is also 0 for them. #if defined(__ARM_ARCH_4T__) || defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5TE__) || \ defined(__ARM_ARCH_5TEJ__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || \ defined(__ARM_ARCH_6T2__) #define MEMFAULT_COMPILER_ARM_LEGACY 1 #else #define MEMFAULT_COMPILER_ARM_LEGACY 0 #endif // Prefer positive detection via __ARM_ARCH_PROFILE (GCC/Clang define this as // 'A', 'R', or 'M'). Fall back to negation when unavailable, with explicit // A/R and legacy exclusions to avoid misclassifying unknown ARM variants. #if defined(__ARM_ARCH_PROFILE) #define MEMFAULT_COMPILER_ARM_CORTEX_M (__ARM_ARCH_PROFILE == 'M') #else #define MEMFAULT_COMPILER_ARM_CORTEX_M \ (!MEMFAULT_COMPILER_ARM_V7_A_R && !MEMFAULT_COMPILER_ARM_LEGACY) #endif #else #define MEMFAULT_COMPILER_ARM_V7_A_R 0 #define MEMFAULT_COMPILER_ARM_LEGACY 0 #define MEMFAULT_COMPILER_ARM_CORTEX_M 0 #endif // // Pick up compiler specific macro definitions // #if defined(__CC_ARM) #include "compiler_armcc.h" #elif defined(__TI_ARM__) #include "compiler_ti_arm.h" #elif defined(__GNUC__) || defined(__clang__) #include "compiler_gcc.h" #elif defined(__ICCARM__) #include "compiler_iar.h" #else #error "New compiler to add support for!" #endif #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/compiler_armcc.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Wrappers for common macros & compiler specifics //! //! Note: This file should never be included directly but rather picked up through the compiler.h //! header #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_PACKED __packed #define MEMFAULT_PACKED_STRUCT MEMFAULT_PACKED struct #define MEMFAULT_NORETURN __declspec(noreturn) #define MEMFAULT_NAKED_FUNC __asm #define MEMFAULT_UNREACHABLE while (1) #define MEMFAULT_NO_OPT #define MEMFAULT_ALIGNED(x) __attribute__((aligned(x))) #define MEMFAULT_UNUSED __attribute__((unused)) #define MEMFAULT_USED __attribute__((used)) #define MEMFAULT_WEAK __attribute__((weak)) #define MEMFAULT_PRINTF_LIKE_FUNC(a, b) #define MEMFAULT_CLZ(a) __clz(a) //! Non-loaded symbols are specified by linker section, not compiler attribute #define MEMFAULT_NO_ALLOC #define MEMFAULT_GET_LR(_a) _a = ((void *)__return_address()) #define MEMFAULT_GET_PC(_a) _a = (void *)__current_pc() #define MEMFAULT_PUT_IN_SECTION(x) __attribute__((section(x), zero_init)) #define MEMFAULT_BREAKPOINT(val) __breakpoint(val) #define MEMFAULT_STATIC_ASSERT(expr, msg) \ enum { MEMFAULT_CONCAT(MEMFAULT_ASSERTION_AT_, __LINE__) = sizeof(char[(expr) ? 1 : -1]) } #define MEMFAULT_DISABLE_WARNING(warning) #define MEMFAULT_ENABLE_WARNING(warning) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/compiler_gcc.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Wrappers for common macros & compiler specifics //! //! Note: This file should never be included directly but rather picked up through the compiler.h //! header #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_PACKED __attribute__((packed)) #define MEMFAULT_PACKED_STRUCT struct MEMFAULT_PACKED //! MEMFAULT_NORETURN is only intended to be overridden in unit tests, if needed #if !defined(MEMFAULT_NORETURN) #define MEMFAULT_NORETURN __attribute__((noreturn)) #endif #define MEMFAULT_NAKED_FUNC __attribute__((naked)) #define MEMFAULT_UNREACHABLE __builtin_unreachable() #if defined(__clang__) #define MEMFAULT_NO_OPT __attribute__((optnone)) #else // GCC 12 changed to no longer enable the var-tracking debug option when // compiling without optimizations. Explicitly enable it so we always get a nice // variable loading experience in GDB, even when the function prologue hasn't // run yet. This is required when using the memfault_gdb.py hook to upload data. #define MEMFAULT_NO_OPT \ __attribute__((optimize("O0", "var-tracking", "var-tracking-assignments"))) #endif #define MEMFAULT_ALIGNED(x) __attribute__((aligned(x))) #define MEMFAULT_UNUSED __attribute__((unused)) #define MEMFAULT_USED __attribute__((used)) #define MEMFAULT_WEAK __attribute__((weak)) #define MEMFAULT_PRINTF_LIKE_FUNC(a, b) __attribute__((format(__printf__, a, b))) #define MEMFAULT_ALIGNOF(x) (__alignof__(x)) //! From https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html, //! If x is 0, the result is undefined. #define MEMFAULT_CLZ(a) ((a == 0) ? 32UL : (uint32_t)__builtin_clz(a)) //! Non-loaded symbols are specified by linker section, not compiler attribute #define MEMFAULT_NO_ALLOC #if defined(__arm__) #define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0) #define MEMFAULT_GET_PC(_a) __asm volatile("mov %0, pc" : "=r"(_a)) #define MEMFAULT_BREAKPOINT(val) __asm volatile("bkpt " #val) #elif defined(__aarch64__) #define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0) #define MEMFAULT_GET_PC(_a) __asm volatile("adr %0, ." : "=r"(_a)) #define MEMFAULT_BREAKPOINT(val) __asm volatile("brk %0" : : "I"(val)) #elif defined(__XTENSA__) #define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0) #define MEMFAULT_GET_PC(_a) \ _a = ({ \ __label__ _l; \ _l: \ &&_l; \ }); #define MEMFAULT_BREAKPOINT(val) __asm__("break 0,0") #elif defined(__riscv) #define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0) // Take advantage of "Locally Declared Labels" to get a PC // https://gcc.gnu.org/onlinedocs/gcc/Local-Labels.html#Local-Labels #define MEMFAULT_GET_PC(_a) \ _a = ({ \ __label__ _l; \ _l: \ &&_l; \ }); // Trigger a breakpoint exception (similar to bkpt instruction in ARM) #define MEMFAULT_BREAKPOINT(val) __asm volatile("ebreak") #elif defined(MEMFAULT_UNITTEST) || \ defined(__APPLE__) // Memfault iOS SDK also #includes this header #define MEMFAULT_GET_LR(_a) _a = 0 #define MEMFAULT_GET_PC(_a) _a = 0 #define MEMFAULT_BREAKPOINT(val) (void)0 #else #define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0) // Take advantage of "Locally Declared Labels" to get a PC // https://gcc.gnu.org/onlinedocs/gcc/Local-Labels.html#Local-Labels #define MEMFAULT_GET_PC(_a) \ _a = ({ \ __label__ _l; \ _l: \ &&_l; \ }); #define MEMFAULT_BREAKPOINT(val) (void)0 #endif /* defined(__GNUC__) && defined(__arm__) */ #if defined(__APPLE__) && defined(MEMFAULT_UNITTEST) // NB: OSX linker has slightly different placement syntax and requirements // // Notably, // 1. Comma separated where first item is top level section (i.e __DATA), i.e // __attribute__((section("__DATA," x))) // 2. total length of name must be between 1-16 characters // // For now we just stub it out since unit tests don't make use of section locations. #define MEMFAULT_PUT_IN_SECTION(x) #else #define MEMFAULT_PUT_IN_SECTION(x) __attribute__((section(x))) #endif #if defined(__cplusplus) #define MEMFAULT_STATIC_ASSERT(cond, msg) static_assert(cond, msg) #else // For clang builds, disable -Wpre-c11-compat, which prohibits use of _Static_assert #if defined(__clang__) #define MEMFAULT_STATIC_ASSERT(cond, msg) \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wpre-c11-compat\"") _Static_assert(cond, msg) \ _Pragma("clang diagnostic pop") #else #define MEMFAULT_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) #endif #endif // Macro for disabling specific warnings. There must be quotes in the argument // to expand correctly to eg _Pragma("GCC diagnostic ignored \"warning-name\"") // Example: // MEMFAULT_DISABLE_WARNING("-Wunused-macros") #if defined(__clang__) #define MEMFAULT_PRAGMA_PREFIX clang #else #define MEMFAULT_PRAGMA_PREFIX GCC #endif #define MEMFAULT_DISABLE_WARNING(warning) \ _Pragma(MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_PRAGMA_PREFIX diagnostic push)) \ _Pragma(MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_PRAGMA_PREFIX diagnostic ignored warning)) #define MEMFAULT_ENABLE_WARNING(warning) \ _Pragma(MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_PRAGMA_PREFIX diagnostic pop)) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/compiler_iar.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Wrappers for common macros & compiler specifics //! //! Note: This file should never be included directly but rather picked up through the compiler.h //! header #ifdef __cplusplus extern "C" { #endif #include "intrinsics.h" #define MEMFAULT_PACKED __packed #define MEMFAULT_PACKED_STRUCT MEMFAULT_PACKED struct #define MEMFAULT_NORETURN __noreturn #define MEMFAULT_NAKED_FUNC #define MEMFAULT_UNREACHABLE while (1) #define MEMFAULT_NO_OPT _Pragma("optimize=none") #define MEMFAULT_ALIGNED(x) _Pragma(MEMFAULT_QUOTE(data_alignment=x)) #define MEMFAULT_UNUSED #define MEMFAULT_USED __root #define MEMFAULT_WEAK __weak #define MEMFAULT_PRINTF_LIKE_FUNC(a, b) #define MEMFAULT_CLZ(a) __iar_builtin_CLZ(a) #define MEMFAULT_NO_ALLOC __no_alloc #define MEMFAULT_GET_LR(_a) __asm volatile("mov %0, lr" : "=r"(_a)) #define MEMFAULT_GET_PC(_a) __asm volatile("mov %0, pc" : "=r"(_a)) #define MEMFAULT_PUT_IN_SECTION(x) _Pragma(MEMFAULT_QUOTE(location=x)) #define MEMFAULT_BREAKPOINT(val) __asm volatile("bkpt %0" : : "i"(val)) #define MEMFAULT_STATIC_ASSERT(cond, msg) static_assert(cond, msg) #define MEMFAULT_DISABLE_WARNING(warning) #define MEMFAULT_ENABLE_WARNING(warning) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/compiler_ti_arm.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Wrappers for common macros & compiler specifics //! //! Note: This file should never be included directly but rather picked up through the compiler.h //! header #ifdef __cplusplus extern "C" { #endif #include #define MEMFAULT_PACKED __attribute__((packed)) #define MEMFAULT_PACKED_STRUCT struct MEMFAULT_PACKED #define MEMFAULT_NORETURN __attribute__((noreturn)) #define MEMFAULT_NAKED_FUNC __attribute__((naked)) #define MEMFAULT_UNREACHABLE while (1) #define MEMFAULT_NO_OPT #define MEMFAULT_PUT_IN_SECTION(x) __attribute__((section(x))) #define MEMFAULT_ALIGNED(x) __attribute__((aligned(x))) #define MEMFAULT_UNUSED __attribute__((unused)) #define MEMFAULT_USED __attribute__((used)) #define MEMFAULT_WEAK __attribute__((weak)) #define MEMFAULT_PRINTF_LIKE_FUNC(a, b) __attribute__((format(__printf__, a, b))) #define MEMFAULT_CLZ(a) ((a == 0) ? 32UL : (uint32_t)__clz(a)) //! Non-loaded symbols are specified by linker section, not compiler attribute #define MEMFAULT_NO_ALLOC // Compiler incorrectly thinks return value is missing for pure asm function // disable the check for them #pragma diag_push #pragma diag_suppress 994 #pragma FUNC_CANNOT_INLINE(memfault_get_pc) MEMFAULT_NAKED_FUNC static void *memfault_get_pc(void) { __asm(" mov r0, lr \n" " bx lr"); } #pragma FUNC_CANNOT_INLINE(__get_PSP) MEMFAULT_NAKED_FUNC static uint32_t __get_PSP(void) { __asm(" mrs r0, psp\n" " bx lr"); } #pragma diag_pop //! __builtin_return_address() currently always returns no value (0) but //! there is a ticket tracking a real implementation: //! https://sir.ext.ti.com/jira/browse/EXT_EP-9303 #define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0) //! TI Compiler has an intrinsic __curpc() but it does not work when compiling against Cortex-M //! variants (i.e --silicon_version=7M4) #define MEMFAULT_GET_PC(_a) _a = memfault_get_pc(); #define MEMFAULT_BREAKPOINT(val) __asm(" bkpt #" #val) #if defined(__cplusplus) #define MEMFAULT_STATIC_ASSERT(cond, msg) static_assert(cond, msg) #else #define MEMFAULT_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) #endif #define MEMFAULT_DISABLE_WARNING(warning) #define MEMFAULT_ENABLE_WARNING(warning) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/custom_data_recording.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Many embedded projects have custom data formats which can be useful to analyze when debugging //! issue (i.e trace data, proprietary coredumps, logs). Memfault allows this arbitrary information //! to be bundled as a "Custom Data Recording" (CDR). The following API provides a flexible way //! for this type of data to be published to the Memfault cloud. Within the Memfault cloud, //! recorded data can then easily be looked up by reason, time, and device. //! //! To start reporting custom data a user of the SDK must do the following: //! //! 1. Implement the sMemfaultCdrSourceImpl API for the data to be captured: //! static sMemfaultCdrSourceImpl s_my_custom_data_recording_source = { //! [... fill me in ...] //! } //! 2. Register the implementation with this module by calling: //! memfault_cdr_register_source(&s_my_custom_data_recording_source) //! //! See documentation below for more tips on how to implement the API. #include #include #include #include "memfault/core/platform/system_time.h" #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_CDR_BINARY "application/octet-stream" #define MEMFAULT_CDR_TEXT "text/plain" #define MEMFAULT_CDR_CSV "text/csv" typedef struct MemfaultCdrMetadata { //! The time the recording was started: //! .start_time = (sMemfaultCurrentTime) { //! .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, //! .info = { .unix_timestamp_secs = ... }, //! } //! //! If no start_time is known, simply set the type to kMemfaultCurrentTimeType_Unknown //! and Memfault will use the time the data arrives on the server - duration_ms to approximate //! the start. sMemfaultCurrentTime start_time; //! The format of the CDR. Memfault uses this as a hint for visualization in the UI //! //! Typically this will be one of the MEMFAULT_CDR_* defines from above: //! //! const char *mimetypes = { MEMFAULT_CDR_BINARY }; //! MemfaultCdrMetadata my_cdr = (MemfaultCdrMetadata) { //! [...] //! .mimetypes = mimetypes, //! .num_mimetypes = MEMFAULT_ARRAY_SIZE(mimetypes), //! } //! //! @note List is expected to be ordered from most specific to most generic description when //! multiple types are listed. const char **mimetypes; size_t num_mimetypes; //! The total size of the CDR data (in bytes) uint32_t data_size_bytes; //! The duration of time the recording tracks (can be 0 if unknown). uint32_t duration_ms; // optional //! The reason the data was captured (i.e "ble connection failure", "wifi stack crash") //! //! @note A project can have at most 100 unique reasons and the length of a given reason can not //! exceed 100 characters. const char *collection_reason; } sMemfaultCdrMetadata; typedef struct MemfaultCdrSourceImpl { //! Called to determine if a CDR is available //! //! @param metadata Metadata associated with recording. See comments within //! sMemfaultCdrMetadata typedef for more info. //! //! @note If a recording is available, metadata must be populated. //! //! @return true if a recording is available, false otherwise bool (*has_cdr_cb)(sMemfaultCdrMetadata *metadata); //! Called to read the data within a CDR. //! //! @note It is expected the data being returned here is from the CDR //! returned in the last "has_cdr_cb" call and that it is //! safe to call this function multiple times //! //! @param offset The offset within the CDR data to read //! @param data The buffer to populate with CDR data //! @param data_len The size of data to fill in the buffer //! //! return true if read was successful and buf was entirely filled, false otherwise bool (*read_data_cb)(uint32_t offset, void *data, size_t data_len); //! Called once the recording has been processed //! //! At this point, the recording should be cleared and a subsequent call to has_cdr_cb //! should either return metadata for a new recording or false void (*mark_cdr_read_cb)(void); } sMemfaultCdrSourceImpl; //! Register a Custom Data Recording generator to publish data to the Memfault cloud //! //! @param impl The CDR source to register //! //! @note We recommend invoking this function at bootup and expect that the "impl" passed //! is of static or global scope //! //! @return true if registration was successful or false if the storage was full. If false is //! returned MEMFAULT_CDR_MAX_DATA_SOURCES needs to be increased. bool memfault_cdr_register_source(const sMemfaultCdrSourceImpl *impl); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/data_export.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! //! Utilities for exporting data collected by the Memfault SDK ("chunks") to a file or a log //! stream for upload to the Memfault cloud. //! //! This can be used for production use-cases where data is extracted over pre-existing logging //! facilities or during initial bringup before another transport is in place. //! //! The extracted data can be published to the Memfault cloud using the memfault-cli: //! $ memfault --project-key ${YOUR_PROJECT_KEY} post-chunk --encoding sdk_data_export //! your_exported_data.txt //! //! A step-by-step integration guide with more details can be found at: //! https://mflt.io/chunk-data-export #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/util/base64.h" #ifdef __cplusplus extern "C" { #endif //! A utility for dumping all the currently collected Memfault Data //! //! While there is still data available to send, this API makes calls to //! 'memfault_data_export_chunk()' void memfault_data_export_dump_chunks(void); //! Called by 'memfault_data_export_chunk' once a chunk has been formatted as a string //! //! @note Defined as a weak function so an end user can override it and control where the chunk is //! written. The default implementation prints the "chunk" by calling MEMFAULT_LOG_INFO() but an //! end user could override to save chunks elsewhere (for example a file). //! //! @param chunk_str - A NUL terminated string with a base64 encoded Memfault "chunk" with a "MC:" //! as a header and ":" as a footer. void memfault_data_export_base64_encoded_chunk(const char *chunk_str); //! Encodes a Memfault "chunk" as a string and calls memfault_data_export_base64_encoded_chunk //! //! @note The string is formatted as 'MC:CHUNK_DATA_BASE64_ENCODED:'. We wrap the base64 encoded //! chunk in a prefix ("MC:") and suffix (":") so the chunks can be even be extracted from logs with //! other data //! //! @note This command can also be used with the Memfault GDB command "memfault //! install_chunk_handler" to "drain" chunks up to the Memfault cloud directly from GDB. This can //! be useful when working on integrations and initially getting a transport path in place: //! //! (gdb) source $MEMFAULT_FIRMWARE_SDK/scripts/memfault_gdb.py //! (gdb) memfault install_chunk_handler --help //! (gdb) memfault install_chunk_handler -pk //! //! For more details see https://mflt.io/posting-chunks-with-gdb //! //! @param chunk_data The binary chunk data to send //! @param chunk_data_len The length of the chunk data to send. Must be less than //! or equal to MEMFAULT_DATA_EXPORT_CHUNK_MAX_LEN void memfault_data_export_chunk(void *chunk_data, size_t chunk_data_len); #define MEMFAULT_DATA_EXPORT_BASE64_CHUNK_PREFIX "MC:" // *M*emfault *C*hunk #define MEMFAULT_DATA_EXPORT_BASE64_CHUNK_PREFIX_LEN \ MEMFAULT_STATIC_STRLEN(MEMFAULT_DATA_EXPORT_BASE64_CHUNK_PREFIX) #define MEMFAULT_DATA_EXPORT_BASE64_CHUNK_SUFFIX ":" #define MEMFAULT_DATA_EXPORT_BASE64_CHUNK_SUFFIX_LEN \ MEMFAULT_STATIC_STRLEN(MEMFAULT_DATA_EXPORT_BASE64_CHUNK_SUFFIX) #define MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN \ (MEMFAULT_DATA_EXPORT_BASE64_CHUNK_PREFIX_LEN + \ MEMFAULT_BASE64_ENCODE_LEN(MEMFAULT_DATA_EXPORT_CHUNK_MAX_LEN) + \ MEMFAULT_DATA_EXPORT_BASE64_CHUNK_SUFFIX_LEN + 1 /* '\0' */) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/data_packetizer.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! API for packetizing the data stored by the Memfault SDK (such as coredumps) //! so that the data can be transported up to the Memfault cloud //! //! For a step-by-step walkthrough of the API, check out the documentation: //! https://mflt.io/data-to-cloud #include #include #include #ifdef __cplusplus extern "C" { #endif //! 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 //! with the amount of data, in bytes, that was copied into the buffer. If a buffer with a length //! less than MEMFAULT_PACKETIZER_MIN_BUF_LEN is passed, no data will be copied and the size //! returned will be 0. //! //! @return true if the buffer was filled, false otherwise bool memfault_packetizer_get_chunk(void *buf, size_t *buf_len); typedef enum { //! Indicates there is no more data to be sent at this time kMemfaultPacketizerStatus_NoMoreData = 0, //! Indicates that an entire chunk has been returned. By default, every call to //! memfault_packetizer_get_next() that returns data will be a complete "Chunk" kMemfaultPacketizerStatus_EndOfChunk, //! Indicates there is more data to be received for the chunk. This will _only_ be returned //! as a value if multi packet chunking has been enabled by setting the boolean option //! enable_multi_packet_chunk to true in the packetizer config (sPacketizerConfig) kMemfaultPacketizerStatus_MoreDataForChunk, } eMemfaultPacketizerStatus; //! The _absolute_ minimum a buffer passed into memfault_packetizer_get_next() can be in order to //! receive data. However, it's recommended you use a buffer size that matches the MTU of your //! transport path. #define MEMFAULT_PACKETIZER_MIN_BUF_LEN 9 //! 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. //! //! @return true if there is data available to send, false otherwise bool memfault_packetizer_data_available(void); typedef struct { //! When false, memfault_packetizer_get_next() will always return a single "chunk" //! when data is available that can be pushed directly to the Memfault cloud //! //! When true, memfault_packetizer_get_next() may have to be called multiple times to return a //! single chunk. This can be used as an optimization for system which support sending or //! re-assembling larger messages over their transport. //! //! @note You can find a reference example in the reference example using the WICED http stack //! (wiced/libraries/memfault/platform_reference_impl/memfault_platform_http_client.c) //! @note In this mode, it's the API users responsibility to make sure they push the chunk data //! only when a kMemfaultPacketizerStatus_EndOfChunk is received bool enable_multi_packet_chunk; } sPacketizerConfig; typedef struct { //! true if the packetizer has partially sent an underlying message. Calls to //! memfault_packetizer_get_next() will continue to return the next packets of the message to //! send. false if no parts of a message have been sent yet. bool send_in_progress; //! The size of the message when sent as a single chunk. This can be useful when //! using transports which require the entire size of the binary blob be known up front //! (i.e the "Content-Length" in an http request) 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); //! Fills the provided buffer with data to be sent. //! //! @note It's expected that memfault_packetizer_begin() is called prior to invoking this call for //! the first time and each time kMemfaultPacketizerStatus_EndOfChunk is returned. If it is not //! called this routine will return kMemfaultPacketizerStatus_NoMoreData //! @note It's expected that this API will be called periodically (i.e when a connection to the //! internet becomes available) to drain data collected by Memfault up to the cloud. //! @note To completely drain the data "memfault_packetizer_get_next()" must be called until the //! function returns kMemfaultPacketizerStatus_NoMoreData. It is _not_ required that all the data be //! drained at once. It is perfectly fine to drain data a little bit at a time each time a //! connection to the internet becomes available on your device. //! //! @note It is expected that the customer has a mechanism to send the packets returned from //! 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 //! with the amount of data, in bytes, that was copied into the buffer. If a buffer with a length //! less than MEMFAULT_PACKETIZER_MIN_BUF_LEN is passed, no data will be copied and the size //! returned will be 0. //! //! @return The status of the packetization. See comments in enum for more details. eMemfaultPacketizerStatus memfault_packetizer_get_next(void *buf, size_t *buf_len); //! Abort any in-progress message packetizations //! //! For example, if packets being sent got dropped or failed to send, it would make sense to abort //! the transaction and re-send the data //! //! @note This will cause any partially written pieces of data to be re-transmitted in their //! entirety (i.e coredump) //! @note This is a no-op when called and memfault_packetizer_get_next() is already returning //! kMemfaultPacketizerStatus_NoMoreData or memfault_packetizer_get_chunk() is already returning //! false void memfault_packetizer_abort(void); typedef enum { kMfltDataSourceMask_None = (1 << 0), // Coredumps recorded when the system crashes kMfltDataSourceMask_Coredump = (1 << 1), // All "events" collected by the SDK (reboot, traces, heartbeats) kMfltDataSourceMask_Event = (1 << 2), // Any "triggered" log captures: https://mflt.io/logging kMfltDataSourceMask_Log = (1 << 3), // Any Custom Data Recording captured kMfltDataSourceMask_Cdr = (1 << 4), // A convenience mask which enables all active sources kMfltDataSourceMask_All = (kMfltDataSourceMask_Coredump | kMfltDataSourceMask_Event | kMfltDataSourceMask_Log | kMfltDataSourceMask_Cdr) } eMfltDataSourceMask; //! Set the data sources which will be drained by the packetizer //! //! @param mask A mask representing the data sources to drain //! //! @note By default, all data sources are active. //! //! @note This API can be used to prioritize the data source drained from the packetizer. //! This can be useful for use cases such as: //! - Devices with multi connectivity toplogies (i.e BLE & WiFi) For example, in this situation a //! user could choose to only enable Event and Log transfer when connected to BLE and enable all //! sources when connected to WiFi. //! - Devices with extended periods where there is no connection to the internet. In this //! situation, an end user may want to store data buffered in RAM (i.e events & logs) on flash //! to minimize the RAM footprint and prevent data from being lost by an unexpected reset. //! If an end user is already saving coredumps in a dedicated flash region, pre-encoded chunks //! can of just events can be saved as follows: //! //! 1. Only enable draining of events with the following API call: //! memfault_packetizer_set_active_sources(kMfltDataSourceMask_Event); //! 2. Using memfault_packetizer_get_chunk() API, now read out events and //! save to flash or a filesystem (https://mflt.io/data-to-cloud) //! 3. When a connection becomes available //! a) send pre-saved chunks from flash over your transport //! b) call memfault_packetizer_get_chunk() and send data until no more data is available //! c) re-enable all sources with memfault_packetizer_get_chunk(kMfltDataSourceMask_All) and //! then call memfault_packetizer_get_chunk() to drain and send any data from other //! sources that is now available (i.e coredumps) //! //! @note Calling this API invokes memfault_packetizer_abort(). This means any in-progress message //! packetization will be restarted when you next attempt to drain data. void memfault_packetizer_set_active_sources(uint32_t mask); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/data_packetizer_source.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Various data providers for the "data_packetizer" which packetizes data collected by the //! Memfault SDK into payloads that can be sent over the transport used up to the cloud //! //! @note A data source must implement three functions which are documented in the function typedefs //! below //! //! @note A weak function implementation of all the provider functions is defined within the //! memfault data packetizer. This way a user can easily add or remove provider functionality by //! compiling or not-compiling certain "components" in the SDK //! //! @note These APIs are only for use within the SDK itself, a user of the SDK should _never_ need //! to call them directly #include #include #include #ifdef __cplusplus extern "C" { #endif //! Check if there is another message in the data source available for reading //! //! @note This function is idempotent and thus should be safe to call multiple times //! //! @param total_size On return, populated with total size of the next message //! available for reading or 0 if there is no new message available //! //! @return true if there is a message which can be read typedef bool(MemfaultDataSourceHasMoreMessagesCallback)(size_t *next_msg_size); //! Read the requested bytes for the currently queued up message //! //! @param The offset to begin reading at //! @param buf The buffer to copy the read data into //! @param buf_len The size of the result buffer //! //! @return true if the read was successful, false otherwise (for example if the //! read goes past the next_msg_size returned from MemfaultDataSourceHasMoreMessagesCallback typedef bool(MemfaultDataSourceReadMessageCallback)(uint32_t offset, void *buf, size_t buf_len); //! Delete the currently queued up message being read //! //! A subsequent call to the paired MemfaultDataSourceHasMoreMessagesCallback will return //! a info about a new message or nothing if there are no more messages to read typedef void(MemfaultDataSourceMarkMessageReadCallback)(void); typedef struct MemfaultDataSourceImpl { MemfaultDataSourceHasMoreMessagesCallback *has_more_msgs_cb; MemfaultDataSourceReadMessageCallback *read_msg_cb; MemfaultDataSourceMarkMessageReadCallback *mark_msg_read_cb; } sMemfaultDataSourceImpl; //! "Coredump" data source provided as part of "panics" component extern const sMemfaultDataSourceImpl g_memfault_coredump_data_source; //! Events (i.e "Heartbeat Metrics" & "Reset Reasons") provided as part of "metrics" and "panics" //! component, respectively extern const sMemfaultDataSourceImpl g_memfault_event_data_source; //! Logging data source provided as part of "core" component (memfault/core/log.h) extern const sMemfaultDataSourceImpl g_memfault_log_data_source; //! Custom Data Recording (CDR) source provided as part of "core" component //! (memfault/core/custom_data_recording.h) extern const sMemfaultDataSourceImpl g_memfault_cdr_source; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/data_source_rle.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A generic data source implementation that can wrap a pre-existing data source //! (e.g. g_memfault_coredump_data_source) and compress the stream using Run-length Encoding (RLE). //! //! The feature is enabled by default but can be disabled by simply excluding //! 'memfault_data_source_rle.c' from your compilation list or adding the define //! MEMFAULT_DATA_SOURCE_RLE_ENABLED=0 as a define to your build system. //! //! @note If your setup relies on accessing data sources asynchronously //! (https://mflt.io/data-to-cloud-async-mode), you will need to disable this feature. #include #include #include #include "memfault/core/data_packetizer_source.h" #ifdef __cplusplus extern "C" { #endif bool memfault_data_source_rle_encoder_set_active(const sMemfaultDataSourceImpl *active_source); bool memfault_data_source_rle_has_more_msgs(size_t *total_size); bool memfault_data_source_rle_read_msg(uint32_t offset, void *buf, size_t buf_len); void memfault_data_source_rle_mark_msg_read(void); extern const sMemfaultDataSourceImpl g_memfault_data_rle_source; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/debug_log.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Log utility used within the memfault SDK. When enabled, logs will be emitted to help a user //! understand what is happening in the library. //! //! The Memfault SDK uses logs sparingly to better call out glaring configuration issues and //! runtime errors. It is recommended to enable all levels of Memfault logs. //! //! If your system does not have logging infrastructure in place, the subsystem can also be //! leveraged for logging within your platform. In that situation, we suggest making your own log.h //! file for the platform and calling the Memfault macros from there: //! //! #include "memfault/core/debug_log.h" //! #define YOUR_PLATFORM_LOG_DEBUG(...) MEMAULT_LOG_DEBUG(__VA_ARGS__) //! #define YOUR_PLATFORM_LOG_INFO(...) MEMAULT_LOG_INFO(__VA_ARGS__) //! #define YOUR_PLATFORM_LOG_WARN(...) MEMAULT_LOG_WARN(__VA_ARGS__) //! #define YOUR_PLATFORM_LOG_ERROR(...) MEMAULT_LOG_ERROR(__VA_ARGS__) #include "memfault/config.h" #include "memfault/core/log.h" #include "memfault/core/platform/debug_log.h" #ifdef __cplusplus extern "C" { #endif #if !MEMFAULT_SDK_LOG_SAVE_DISABLE // Note that this call will be a no-op if the system has not initialized the log module // by calling memfault_log_boot(). See ./log.h for more details. #define MEMFAULT_SDK_LOG_SAVE MEMFAULT_LOG_SAVE #else #define MEMFAULT_SDK_LOG_SAVE(...) #endif #if MEMFAULT_PLATFORM_HAS_LOG_CONFIG #include "memfault_platform_log_config.h" #else #define _MEMFAULT_LOG_IMPL(_level, ...) \ do { \ MEMFAULT_SDK_LOG_SAVE(_level, __VA_ARGS__); \ memfault_platform_log(_level, __VA_ARGS__); \ } while (0) #define MEMFAULT_LOG_DEBUG(...) _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Debug, __VA_ARGS__) #define MEMFAULT_LOG_INFO(...) _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Info, __VA_ARGS__) #define MEMFAULT_LOG_WARN(...) _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Warning, __VA_ARGS__) #define MEMFAULT_LOG_ERROR(...) _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Error, __VA_ARGS__) //! Only needs to be implemented when using demo component #define MEMFAULT_LOG_RAW(...) memfault_platform_log_raw(__VA_ARGS__) #endif #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/device_info.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif //! Print device info to console using memfault logging void memfault_device_info_dump(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/errors.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! All Memfault APIs return: //! 0 on success //! < 0 on error //! //! This way a memfault API call can be checked with a simple "if (rv != 0) { }" to decide if //! a call was successful or not //! //! For APIs that need to convey more than just success/failure, an API-specific enum will be //! defined and mapped to values > 0 #ifdef __cplusplus extern "C" { #endif #include #include "memfault/core/math.h" //! @brief //! //! Internal error codes used within the Memfault components. An external caller should never need //! to use these Error codes. typedef enum MemfaultInternalReturnCode { MemfaultInternalReturnCode_Error = -1, MemfaultInternalReturnCode_InvalidInput = -2, MemfaultReturnCode_PlatformBase = -1000 } MemfaultInternalReturnCode; //! A convenience macro for mapping a platform specific error code to a reserved range. For //! example, this could be used to map the error codes returned from calls to a MCUs flash driver //! implementation. This way, meaningful error information can still be surfaced for diagnostics. //! The goal here is to allow a way to avoid the following pattern where a useful error code //! always gets mapped down to one value, i.e //! //! int rv = some_platform_specific_api() //! if (rv != 0) { //! return -1; //! } //! //! Note: We mask off the top bit and take the absolute value of the original error code to avoid //! the chance of an overflow. This should leave enough useful info to assist narrowing down where //! platform specific errors occurred #define MEMFAULT_PLATFORM_SPECIFIC_ERROR(e) \ (MemfaultReturnCode_PlatformBase - ((int32_t)MEMFAULT_ABS(e) & 0x7fffffff)) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/event_storage.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Stores serialized event information that is ready to be sent up to the Memfault cloud (i.e //! Heartbeat Metrics, Reboot Reasons & Trace events). Must be initialized on system boot. //! //! @note If calls to data_packetizer.c are made on a different task than the one //! MemfaultPlatformTimerCallback is invoked on, memfault_lock() & memfault_unlock() should also //! be implemented by the platform //! //! @note Recorded events are always written into RAM for minimal latency. Users of the API can //! (optionally) implement the non-volatile event storage platform API and periodically flush //! events to a non-volatile storage medium. More details can be found in //! "memfault/core/platform/nonvolatile_event_storage.h" #include #include #include #include #include "memfault/config.h" // The unity test framework (http://www.throwtheswitch.org/unity) fails to generate mocks when // opaque pointers are used in a header. To work around, the problem, we pull in the full internal // definition for "sMemfaultEventStorageImpl" when the unity framework is being used. #if defined(UNITY_INCLUDE_CONFIG_H) #include "memfault/core/event_storage_implementation.h" #endif #ifdef __cplusplus extern "C" { #endif typedef struct MemfaultEventStorageImpl sMemfaultEventStorageImpl; //! Must be called by the customer on boot to setup event storage. //! //! This is where serialized event data like heartbeat metrics, reboot tracking info, and //! trace event data is stored as it waits to be drained (via call's //! to memfault_packetizer_get_next()). //! //! For any module using the event store the worst case size needed can be computed using the //! exported APIs: //! memfault_reboot_tracking_compute_worst_case_storage_size() //! memfault_metrics_heartbeat_compute_worst_case_storage_size() //! memfault_trace_event_compute_worst_case_storage_size() //! //! For example, if a device connects to the Internet once/hour and collects a heartbeat at this //! interval as well, the buffer size to allocate can easily be computed as: //! buf_len = memfault_metrics_heartbeat_compute_worst_case_storage_size() * 1 //! //! When a module using the event store is initialized a WARNING will print on boot and an error //! code will return if it is not appropriately sized to hold at least one event. //! //! @param buf The buffer to use for event storage //! @param buf_len The length of the buffer to use for event storage //! //! @return a handle to the event storage implementation on success & false on failure. //! This handle will need to be provided to modules which use the event store on initialization const sMemfaultEventStorageImpl *memfault_events_storage_boot(void *buf, size_t buf_len); typedef struct MemfaultEventStorageInfo { size_t bytes_used; size_t bytes_free; } sMemfaultEventStorageInfo; #if MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED typedef struct MemfaultEventStoragePersistCbStatus { //! Summarizes the utilization of the RAM buffer passed in memfault_events_storage_boot() sMemfaultEventStorageInfo volatile_storage; } sMemfaultEventStoragePersistCbStatus; //! Invoked to request that events be saved to non-volatile storage //! //! @param status Summarizes the current state of event storage. The user of the SDK //! can optionally use this information to decide whether or not to persist events to //! non-volatile storage. //! //! @note By default this is a weak function which behaves as a no-op. //! //! @note The intended use of the API is to provide platforms which implement non-volatile event //! storage a "hook" to help them determine when to flush events from volatile to non-volatile //! storage. This function is invoked each time an event: //! - is saved into the RAM-backed buffer //! - is consumed from non-volatile storage _and_ there are still events in //! volatile storage. (i.e If non-volatile storage is full, reading events will free up space //! and then events residing in RAM can be persisted into the new freed up space) //! //! @note It is safe to call "memfault_event_storage_persist()" both synchronously and //! asynchronously from this callback void memfault_event_storage_request_persist_callback( const sMemfaultEventStoragePersistCbStatus *status); //! Saves events which have been collected into non-volatile storage //! //! @return number of events saved or <0 for unexpected errors int memfault_event_storage_persist(void); #endif //! 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); //! 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); typedef struct { // Event storage uses 2 separate buffers, one for context and one for the // circular buffer storage. The context size is fixed, but the storage // size is user-configurable. void *context; size_t context_len; void *storage; size_t storage_len; } sMfltEventStorageSaveState; //! Provide a define for the size of the context storage state. This can be used //! to appropriately size the full state storage buffer. #if (INTPTR_MAX == 0x7fffffff) #define MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES 44 #elif (INTPTR_MAX == 0x7fffffffffffffff) #define MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES 80 #endif //! Return a pointer to the event storage state. This is used to save the state //! prior to loss of system power, if the platform needs to restore the state //! after restart. //! //! @note If event storage is accessed after this function is called, the event //! storage state may be undefined. This function should only be called during a //! planned shutdown or deep sleep entry, when the rest of the system is //! inactive. sMfltEventStorageSaveState memfault_event_storage_get_state(void); //! Restore the event storage state. This is used to restore the state after //! system restarts, and is called from memfault_events_storage_boot. A platform //! can implement this function to restore the event storage state after a deep //! sleep or power loss. //! //! Must be implemented if MEMFAULT_EVENT_STORAGE_RESTORE_STATE is enabled. //! //! @param state The state to restore- should match the state returned from //! memfault_event_storage_get_state. This function assumes the state is valid //! (uncorrupted). //! //! @return true if the state was restored successfully, false otherwise. extern bool memfault_event_storage_restore_state(sMfltEventStorageSaveState *state); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/event_storage_implementation.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! The API an event storage implementation must adhere to. A user of the SDK should never need to //! include this header #include #include #include #include "memfault/core/event_storage.h" #ifdef __cplusplus extern "C" { #endif struct MemfaultEventStorageImpl { //! Opens a session to begin writing a heartbeat event to storage //! //! @note To close the session, memfault_events_storage_finish_write() must be called //! //! @return the free space in storage for the write size_t (*begin_write_cb)(void); //! Called to append more data to the current event //! //! @note This function can be called multiple times to make it easy for an event to //! be stored in chunks //! //! @param bytes Buffer of data to add to the current event //! @param num_bytes The total number of bytes to store //! //! @return true if the write was successful, false otherwise bool (*append_data_cb)(const void *bytes, size_t num_bytes); //! Called to close a heartbeat event session //! //! @param rollback If false, the event being stored is committed meaning a future call //! to "g_memfault_event_data_source.has_more_msgs_cb" will return the event. If true, //! the event that was being stored is discarded void (*finish_write_cb)(bool rollback); //! Returns the _total_ size that can be used by event storage size_t (*get_storage_size_cb)(void); }; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/heap_stats.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! //! A minimal set of functions to track statistics on heap allocations. //! //! @note To integrate with your system heap, add the intrumentation functions //! to your platforms malloc + free implementations. //! //! For example, when using malloc & free from a library with GCC or Clang, you can use the //! compiler flag `-Wl,--wrap=malloc,--wrap=free` to shim around the library malloc- in that case, //! these functions should be added to your platform port: //! //! #include memfault/components.h //! //! extern void *__real_malloc(size_t size); //! extern void __real_free(void *ptr); //! //! void *__wrap_malloc(size_t size) { //! void *ptr = __real_malloc(size); //! MEMFAULT_HEAP_STATS_MALLOC(ptr, size); //! return ptr; //! } //! //! void __wrap_free(void *ptr) { //! MEMFAULT_HEAP_STATS_FREE(ptr); //! __real_free(ptr); //! } //! //! @note By default, the thread-safety of the module depends on memfault_lock/unlock() API. //! If calls to the malloc/free stats functions can be made from multiple tasks, //! these APIs must be implemented. Locks are held while updating the internal //! stats tracking data structures, which is quick and has a bounded worst-case //! runtime. //! @note If the functions you are calling MEMFAULT_HEAP_STATS_MALLOC/FREE from //! already use a lock of their own, the use of memfault_lock/unlock can be disabled from //! memfault_platform_config.h: //! //! #define MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE 0 #include #include #include #include "memfault/core/compiler.h" #ifdef __cplusplus extern "C" { #endif //! Record a single malloc. Called from within malloc handler (eg __wrap_malloc). //! //! @note This function usually should not be called directly, but instead via the macro //! MEMFAULT_HEAP_STATS_MALLOC, which will retrieve the LR for the frame where malloc was called. //! //! @param lr Link register from the malloc function frame, stored for minimal context around this //! @param ptr The pointer returned by the malloc call (can be NULL) //! @param size The size value passed to the malloc call //! allocation void memfault_heap_stats_malloc(const void *lr, const void *ptr, size_t size); #define MEMFAULT_HEAP_STATS_MALLOC(ptr_, size_) \ do { \ void *lr_; \ MEMFAULT_GET_LR(lr_); \ memfault_heap_stats_malloc(lr_, ptr_, size_); \ } while (0) //! Record a single free. Called from within free handler (eg __wraps_free). //! //! @note A macro MEMFAULT_HEAP_STATS_FREE is provided for visual symmetry with //! MEMFAULT_HEAP_STATS_MALLOC //! //! @param ptr The pointer being passed to free (can be NULL) void memfault_heap_stats_free(const void *ptr); #define MEMFAULT_HEAP_STATS_FREE(ptr_) memfault_heap_stats_free(ptr_) //! These functions can be used to manually increment and decrement the //! in_use_block_count. This is useful if the application has custom memory //! tracing that provides more detailed information than just malloc/free. void memfault_heap_stats_decrement_in_use_block_count(void); void memfault_heap_stats_increment_in_use_block_count(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/heap_stats_impl.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! //! Heap tracking APIs intended for use within the memfault-firmware-sdk #include #include #include #include "memfault/config.h" #ifdef __cplusplus extern "C" { #endif //! Number of regions used by heap stats #define MEMFAULT_HEAP_STATS_NUM_RAM_REGIONS 2 //! Value used to indicate entry is the end of the list #define MEMFAULT_HEAP_STATS_LIST_END UINT16_MAX //! The heap stats data structure, which is exported when capturing a core. typedef struct MfltHeapStatEntry { // LR at time of allocation const void *lr; // The pointer returned by the actual allocation function const void *ptr; // Size of this allocation. 0 means the entry is invalid and should be ignored struct { // 31 bits to represent the size passed to malloc uint32_t size:31; // 1 bit indicating whether this entry is still in use or has been freed uint32_t in_use:1; // Index to next oldest entry in heap stats entry array, uint16_t next_entry_index; } info; } sMfltHeapStatEntry; typedef struct MfltHeapStats { uint8_t version; // Number of blocks currently allocated and not freed uint32_t in_use_block_count; // Track the max value of 'in_use_block_count' uint32_t max_in_use_block_count; // Allocation entry list pointer uint16_t stats_pool_head; } sMfltHeapStats; extern sMfltHeapStats g_memfault_heap_stats; extern sMfltHeapStatEntry g_memfault_heap_stats_pool[MEMFAULT_HEAP_STATS_MAX_COUNT]; //! Reset the tracked stats. void memfault_heap_stats_reset(void); //! Check if no heap stats have been collected bool memfault_heap_stats_empty(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/log.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! //! A lightweight set of log utilities which can be wrapped around pre-existing logging //! infrastructure to capture events or errors that transpired leading up to an issue. //! See https://mflt.io/logging for detailed integration steps. //! //! @note These utilities are already integrated into memfault/core/debug_log.h module. If your //! project does not have a logging subsystem, see the notes in that header about how to leverage //! the debug_log.h module for that! //! //! @note The thread-safety of the module depends on memfault_lock/unlock() API. If calls can be //! made from multiple tasks, these APIs must be implemented. Locks are _only_ held while copying //! data into the backing circular buffer so durations will be very quick. #include #include #include #include #include "memfault/config.h" #include "memfault/core/compact_log_compile_time_checks.h" #include "memfault/core/compact_log_helpers.h" #include "memfault/core/compiler.h" #include "memfault/core/platform/debug_log.h" // For eMemfaultPlatformLogLevel #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_LOG_EXPORT_BASE64_CHUNK_PREFIX "ML:" // *M*emfault *L*og #define MEMFAULT_LOG_EXPORT_BASE64_CHUNK_PREFIX_LEN \ MEMFAULT_STATIC_STRLEN(MEMFAULT_LOG_EXPORT_BASE64_CHUNK_PREFIX) #define MEMFAULT_LOG_EXPORT_BASE64_CHUNK_SUFFIX ":" #define MEMFAULT_LOG_EXPORT_BASE64_CHUNK_SUFFIX_LEN \ MEMFAULT_STATIC_STRLEN(MEMFAULT_LOG_EXPORT_BASE64_CHUNK_SUFFIX) #define MEMFAULT_LOG_EXPORT_BASE64_CHUNK_MAX_LEN \ (MEMFAULT_LOG_EXPORT_BASE64_CHUNK_PREFIX_LEN + \ MEMFAULT_BASE64_ENCODE_LEN(MEMFAULT_LOG_EXPORT_CHUNK_MAX_LEN) + \ MEMFAULT_LOG_EXPORT_BASE64_CHUNK_SUFFIX_LEN + 1 /* '\0' */) //! Must be called on boot to initialize the Memfault logging module //! //! @param buffer The ram buffer to save logs into //! @param buf_len The length of the buffer. There's no length restriction but //! the more space that is available, the longer the trail of breadcrumbs that will //! be available upon crash //! //! @note Until this function is called, all other calls to the module will be no-ops //! @return true if call was successful and false if the parameters were bad or //! memfault_log_boot has already been called bool memfault_log_boot(void *buffer, size_t buf_len); //! Change the minimum level log saved to the circular buffer //! //! By default, any logs >= kMemfaultPlatformLogLevel_Info can be saved void memfault_log_set_min_save_level(eMemfaultPlatformLogLevel min_log_level); //! The MEMFAULT_LOG_SAVE macro backs the built-in SDK MEMFAULT_LOG_x logging //! macros, but can also be called from a platforms pre-existing logging //! macro. //! //! For example, if your platform already has something like this //! //! #define YOUR_PLATFORM_LOG_ERROR(...) //! your_platform_log_error(__VA_ARGS__) //! //! the error data could be automatically recorded by making the //! following modification //! //! #define YOUR_PLATFORM_LOG_ERROR(...) //! do { //! MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Error, __VA_ARGS__); //! your_platform_log_error(__VA_ARGS__) //! } while (0) //! //! The macro will save the log data in the normal or compact form depending on //! SDK configuration. #if MEMFAULT_COMPACT_LOG_ENABLE //! Same as MEMFAULT_LOG_SAVE except logs use Memfault's "compact" log strategy which offloads //! formatting to the Memfault cloud to reduce on device codespace and cpu consumption. See //! https://mflt.io/compact-logs for more details. #define MEMFAULT_COMPACT_LOG_SAVE(level, format, ...) \ do { \ MEMFAULT_LOGGING_RUN_COMPILE_TIME_CHECKS(format, ##__VA_ARGS__); \ MEMFAULT_LOG_FMT_ELF_SECTION_ENTRY(format, ##__VA_ARGS__); \ memfault_compact_log_save(level, MEMFAULT_LOG_FMT_ELF_SECTION_ENTRY_PTR, \ MFLT_GET_COMPRESSED_LOG_FMT(__VA_ARGS__), ##__VA_ARGS__); \ } while (0) #define MEMFAULT_LOG_SAVE MEMFAULT_COMPACT_LOG_SAVE //! Serializes the provided compact log and saves it to backing storage //! //! @note: Should only be called via MEMFAULT_COMPACT_LOG_SAVE macro void memfault_compact_log_save(eMemfaultPlatformLogLevel level, uint32_t log_id, uint32_t compressed_fmt, ...); #else // !MEMFAULT_COMPACT_LOG_ENABLE #define MEMFAULT_LOG_SAVE(_level, ...) memfault_log_save(_level, __VA_ARGS__) #endif /* MEMFAULT_COMPACT_LOG_ENABLE */ //! Function which can be called to save a log after it has been formatted //! //! Typically a user should be able to use the MEMFAULT_LOG_SAVE macro but if your platform does //! not have logging macros and you are just using newlib or dlib & printf, you could make //! the following changes to the _write dependency function: //! //! int _write(int fd, char *ptr, int len) { //! // ... other code such as printing to console ... //! eMemfaultPlatformLogLevel level = //! (fd == 2) ? kMemfaultPlatformLogLevel_Error : kMemfaultPlatformLogLevel_Info; //! memfault_log_save_preformatted(level, ptr, len); //! // ... //! } void memfault_log_save_preformatted(eMemfaultPlatformLogLevel level, const char *log, size_t log_len); //! As above, but do not acquire a lock internally. void memfault_log_save_preformatted_nolock(eMemfaultPlatformLogLevel level, const char *log, size_t log_len); typedef enum { kMemfaultLogRecordType_Preformatted = 0, kMemfaultLogRecordType_Compact = 1, kMemfaultLogRecordType_NumTypes, } eMemfaultLogRecordType; typedef struct { // the level of the message eMemfaultPlatformLogLevel level; // the log returned is a binary "compact log" // See https://mflt.io/compact-logs for more details eMemfaultLogRecordType type; // the length of the msg (not including NUL character) uint32_t msg_len; #if MEMFAULT_LOG_TIMESTAMPS_ENABLE // if non-zero, the timestamp of the log uint32_t timestamp; #endif // the message to print which will always be NUL terminated when a preformatted log is returned // (so it is always safe to call printf without copying the log into another buffer yourself) char msg[MEMFAULT_LOG_MAX_LINE_SAVE_LEN + 1 /* '\0' */]; } sMemfaultLog; //! Returns the oldest unread log in memfault log storage //! //! @param log[out] When a new log is available, populated with its info //! @return true if a log was found, false if there were no logs to read. //! //! @note For some timing sensitive applications, logs may be written into RAM and later dumped out //! over UART and/or saved to flash on a lower priority background task. The memfault_log_read() //! API is designed to be easy to utilize in these situations. For example: //! //! Any task: //! Call MEMFAULT_SAVE_LOG() to quickly write a log into RAM. //! //! Optional: Anytime a new log is saved, memfault_log_handle_saved_callback is called by the //! memfault log module. A platform can choose to implement something like: //! //! void memfault_log_handle_saved_callback(void) { //! my_rtos_schedule_log_read() //! } //! //! Task responsible for flushing logs out to slower mediums (UART, NOR/EMMC Flash, etc): //! // .. RTOS code to wait for log read event .. //! sMemfaultLog log = { 0 }; //! const bool log_found = memfault_log_read(&log); //! if (log_found && (log.type == kMemfaultLogRecordType_Preformatted)) { //! my_platform_uart_println(log.level, log, log.msg_len); //! } bool memfault_log_read(sMemfaultLog *log); //! Utility that formats and outputs a log via the output medium defined in //! `memfault_log_export_msg`. //! //! @note In case the log message is stored in the binary "compact log" format, the message is first //! formatted as 'ML:COMPACT_LOG_DATA_BASE64_ENCODED:' //! A python library for decoding compact logs can be found here: //! https://pypi.org/project/mflt-compact-log/ void memfault_log_export_log(sMemfaultLog *log); //! Helper function that repeatedly calls memfault_log_read() until there are no more logs //! available. For each log `memfault_log_export_log` is called. void memfault_log_export_logs(void); //! Called as part of memfault_log_export() for every time a log message needs to be exported. //! //! @note This is a weak function that by default calls `memfault_platform_log_raw`. It can be //! overridden to change the formatting of the output, as well as where it is stored. extern void memfault_log_export_msg(eMemfaultPlatformLogLevel level, const char *msg, size_t msg_len); //! Invoked every time a new log has been saved //! //! @note By default this is a weak function which behaves as a no-op. Platforms which dispatch //! console logging to a low priority thread can implement this callback to have a "hook" from //! where a job to drain new logs can easily be scheduled extern void memfault_log_handle_saved_callback(void); //! Formats the provided string and saves it to backing storage //! //! @note: Should only be called via MEMFAULT_LOG_SAVE macro MEMFAULT_PRINTF_LIKE_FUNC(2, 3) void memfault_log_save(eMemfaultPlatformLogLevel level, const char *fmt, ...); //! Formats the provided string from a variable argument list //! //! @note Prefer saving logs via MEMFAULT_LOG_SAVE() when possible MEMFAULT_PRINTF_LIKE_FUNC(2, 0) void memfault_vlog_save(eMemfaultPlatformLogLevel level, const char *fmt, va_list args); //! Freezes the contents of the log buffer in preparation of uploading the logs to Memfault. //! //! Once the log buffer contents have been uploaded, the buffer is unfrozen. While the buffer is //! frozen, logs can still be added, granted enough space is available in the buffer. If the buffer //! is full, newly logs will get dropped. Once the buffer is unfrozen again, the oldest logs will be //! expunged again upon writing new logs that require the space. //! @note This function must not be called from an ISR context. void memfault_log_trigger_collection(void); //! Check if log component has booted //! //! @returns true if log component has booted or false if not bool memfault_log_booted(void); //! Return the count of lines that were not recorded into the logging buffer due //! to lack of space. Monotonically incrementing from boot. uint32_t memfault_log_get_dropped_count(void); //! Return the count of lines that have been written to the logging buffer. uint32_t memfault_log_get_recorded_count(void); typedef struct { size_t num_logs; size_t bytes; } sMfltLogUnsentCount; //! Return the number of unsent logs and their total size sMfltLogUnsentCount memfault_log_get_unsent_count(void); typedef struct { // Logging uses 2 separate buffers, one for context and one for the circular // buffer storage. The context size is fixed, but the storage size is // user-configurable. void *context; size_t context_len; void *storage; size_t storage_len; } sMfltLogSaveState; //! Provide a define for the size of the context storage state. This can be used //! to appropriately size the full state storage buffer. #if (INTPTR_MAX == 0x7fffffff) #define MEMFAULT_LOG_STATE_SIZE_BYTES (44 + ((sizeof(eMemfaultPlatformLogLevel) == 4) ? 4 : 0)) #elif (INTPTR_MAX == 0x7fffffffffffffff) #define MEMFAULT_LOG_STATE_SIZE_BYTES (76 + ((sizeof(eMemfaultPlatformLogLevel) == 4) ? 4 : 0)) #endif //! Return a pointer to the log state. This is used to save the state prior to //! loss of system power, if the platform needs to restore the state after //! restart. //! //! @note the state data will change if logs are captured or exported. It's //! recommended to call this function when the system is idle, just before deep //! sleep power down. sMfltLogSaveState memfault_log_get_state(void); //! Restore the event storage state. This is used to restore the state after //! system restarts, and is called from memfault_events_storage_boot. A platform //! can implement this function to restore the event storage state after a deep //! sleep or power loss. //! //! Must be implemented if MEMFAULT_EVENT_STORAGE_RESTORE_STATE is enabled. //! //! @param state The state to restore- should match the state returned from //! memfault_event_storage_get_state. This function assumes the state is valid //! (uncorrupted). //! //! @return true if the state was restored successfully, false otherwise. extern bool memfault_log_restore_state(sMfltLogSaveState *state); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/log_impl.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! //! Logging module APIs intended for use inside the SDK. #include #include #include #ifdef __cplusplus extern "C" { #endif //! Reset the state of log tracking //! //! @note Internal function only intended for use with unit tests void memfault_log_reset(void); #define MEMFAULT_LOG_NUM_RAM_REGIONS 2 typedef struct { const void *region_start; uint32_t region_size; } sMemfaultLogMemoryRegion; typedef struct { sMemfaultLogMemoryRegion region[MEMFAULT_LOG_NUM_RAM_REGIONS]; } sMemfaultLogRegions; //! @note Internal function used to recover the memory regions which need to be collected in order //! for logs //! to get decoded in a coredump (https://mflt.io/logging) bool memfault_log_get_regions(sMemfaultLogRegions *regions); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/math.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Math helpers #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) #define MEMFAULT_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MEMFAULT_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MEMFAULT_FLOOR(a, align) (((a) / (align)) * (align)) #define MEMFAULT_ABS(a) (((a) < 0) ? -(a) : (a)) #define MEMFAULT_CEIL_DIV(x, y) (((x) + (y) - 1) / (y)) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/platform/core.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! APIs the platform must implement for using the SDK. These routines are needed by all //! subcomponents of the library one can include #include #include #include "memfault/core/compiler.h" #ifdef __cplusplus extern "C" { #endif //! Initialize Memfault SDK modules used in the port //! //! @note The expectation is a user of the SDK will implement this function and call it once on //! boot. It should be used to setup anything needed by the SDK (such as event & coredump //! storage) and then start up any of the memfault subsystems being used //! @note This is also a good time to run any runtime assertion checks (such as checking //! that coredump storage is large enough to hold the coredump regions being collected) //! @note A reference usage can be found in nrf5/libraries/memfault/memfault_platform_core.c //! //! @return 0 if initialization completed, else error code int memfault_platform_boot(void); //! Invoked after memfault fault handling has run. //! //! The platform should do any final cleanup and reboot the system #if defined(__ICCARM__) || defined(__CC_ARM) //! IAR and armcc will optimize away link register stores from callsites, which //! makes it impossible for a reliable backtrace to be resolved, so for those //! compilers don't apply the NORETURN attribute #else MEMFAULT_NORETURN #endif void memfault_platform_reboot(void); //! Invoked after faults occur so the user can debug locally if a debugger is attached void memfault_platform_halt_if_debugging(void); //! @return elapsed time since the device was last restarted (in milliseconds) uint64_t memfault_platform_get_time_since_boot_ms(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/platform/crc32.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Platform API for CRC APIs #include #include #ifdef __cplusplus extern "C" { #endif //! Computes the CRC32 for the provided data. //! //! While the actual CRC polynomial used does not matter to Memfault, the recommendation is to //! compute the standard ZIP file checksum. This is the standard "CRC32" computed by libraries //! implemented in most languages //! //! @param data data buffer to compute the crc over //! @param data_len length of the buffer to compute the crc32 over //! //! @return the crc over the provided buffer uint32_t memfault_platform_crc32(const void *data, size_t data_len); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/platform/debug_log.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! APIs that need to be implemented in order to enable logging within the memfault SDK //! //! The memfault SDK uses logs sparingly to communicate useful diagnostic information to the //! integrator of the library #include #include "memfault/core/compiler.h" #ifdef __cplusplus extern "C" { #endif typedef enum { kMemfaultPlatformLogLevel_Debug = 0, kMemfaultPlatformLogLevel_Info, kMemfaultPlatformLogLevel_Warning, kMemfaultPlatformLogLevel_Error, // Convenience definition to get the number of possible levels kMemfaultPlatformLogLevel_NumLevels, } eMemfaultPlatformLogLevel; //! Routine for displaying (or capturing) a log. //! //! @note it's expected that the implementation will terminate the log with a newline //! @note Even if there is no UART or RTT Console, it's worth considering adding a logging //! implementation that writes to RAM or flash which allows for post-mortem analysis MEMFAULT_PRINTF_LIKE_FUNC(2, 3) void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...); //! Routine for printing a log line as-is, only appending a newline, but without suffixing or //! appending timestamps, log level info, etc. This is used for debug console-commands where //! it would degrade the developer experience when the string would be suffixed/post-fixed with //! other characters. //! //! @note it's expected that the implementation will terminate the log with a newline //! but NOT suffix or append anything other than that. MEMFAULT_PRINTF_LIKE_FUNC(1, 2) void memfault_platform_log_raw(const char *fmt, ...); //! Routine for displaying (or capturing) hexdumps void memfault_platform_hexdump(eMemfaultPlatformLogLevel level, const void *data, size_t data_len); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/platform/device_info.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @details //! //! @brief //! Platform APIs used to get information about the device and its components. #include #include #ifdef __cplusplus extern "C" { #endif //! Maximum string size of a field in MemfaultDeviceInfo. Does not include NULL terminator #if !defined(MEMFAULT_DEVICE_INFO_MAX_STRING_SIZE) #define MEMFAULT_DEVICE_INFO_MAX_STRING_SIZE 128 #endif // !defined(MEMFAULT_DEVICE_INFO_MAX_STRING_SIZE) typedef struct MemfaultDeviceInfo { //! The device's serial number or unique identifier. //! Regular expression defining valid device serials: ^[-a-zA-Z0-9_]+$ const char *device_serial; //! The "Software Type" is used to identify the separate pieces of software running on a given //! device. This can be images running on different MCUs (i.e "main-mcu-app" & "bluetooth-mcu") or //! different images running on the same MCU (i.e "main-mcu-bootloader" & "main-mcu-app"). To //! learn more, check out the documentation: https://mflt.io/34PyNGQ const char *software_type; //! Version of the currently running software. //! We recommend using Semantic Version V2 strings. const char *software_version; //! Hardware version (sometimes also called "board revision") that the software is currently //! running on. Regular expression defining valid hardware versions: ^[-a-zA-Z0-9_\.\+]+$ const char *hardware_version; } sMemfaultDeviceInfo; //! Invoked by memfault library to query the device information //! //! It's expected the strings returned will be valid for the lifetime of the application //! @note This function must be safe to call from an interrupt void memfault_platform_get_device_info(sMemfaultDeviceInfo *info); //! Allows caller to get a pointer to a unique version string //! starting with their supplied version. Will insert a plus //! sign between the supplied version and the unique suffix //! to help ensure Semantic (SemVer) compliance. //! //! @note The unique version string is generated using the configured build id //! (https://mflt.io/unique-build-id) //! //! @return System version with suffix or null on error. For example, //! if the input version is "1.0.0" and the build id is "123456....", the string //! returned will be "1.0.0+123456" const char *memfault_create_unique_version_string(const char *const version); //! Convenience function to get a pointer to a previously created version string. const char *memfault_get_unique_version_string(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/platform/nonvolatile_event_storage.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Dependencies which must be implemented to persist events collected by the Memfault SDK into //! non-volatile storage. This can be useful when a device: //! - has prolonged periods without connectivity causing many events to get batched up //! - is likely to reboot in between connections (i.e. due to low battery, user initiated //! resets, etc) #include #include #include #ifdef __cplusplus extern "C" { #endif //! Callback passed into the non-volatile storage write() dependency to read an event typedef bool(MemfaultEventReadCallback)(uint32_t offset, void *buf, size_t buf_len); typedef struct MemfaultNonVolatileEventStorageImpl { //! @return if true, the Memfault SDK will persist events here when //! "memfault_event_storage_persist" is called. if false, none of the other //! dependencies will be called and events will not be saved in non-volatile storage. bool (*enabled)(void); //! Check if there is an event ready to be consumed from non-volatile storage //! //! @note This function is idempotent and thus must be safe to call multiple times //! //! @param[out] event_length_out The size of the next stored event to read //! //! @return true if there is at least one event stored bool (*has_event)(size_t *event_length_out); //! Read the requested bytes for the currently queued up message //! @return true if the read was successful, false otherwise bool (*read)(uint32_t offset, void *buf, size_t buf_len); //! Delete the currently queued up message being read //! //! @note The next call to "has_event" should return info about the next queued event to be //! "read". void (*consume)(void); //! Write the event provided into storage //! //! @param reader_callback Helper for reading event to be written. //! @param total_size The total size of the event to save bool (*write)(MemfaultEventReadCallback reader_callback, size_t total_size); } sMemfaultNonVolatileEventStorageImpl; //! By default a weak definition of this structure is provided and the feature is disabled //! //! @note May optionally be implemented by a SDK user to save events to non-volatile storage //! //! @note It's up to the end user to decide when to flush events since that is use case specific //! The easiest way to achieve this is to add the following to your port: //! //! #include "memfault/core/event_storage.h" //! void memfault_event_storage_request_persist_callback( //! const sMemfaultEventStoragePersistCbStatus *status) { //! memfault_event_storage_persist(); //! } extern const sMemfaultNonVolatileEventStorageImpl g_memfault_platform_nv_event_storage_impl; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/platform/overrides.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Functions that can be optionally overridden by the user of the SDK. //! //! Default "weak function" stub definitions are provided for each of these functions //! in the SDK itself #ifdef __cplusplus extern "C" { #endif //! Locking APIs used within the Memfault SDK //! //! The lock dependencies can (optionally) be implemented by the application to enable locking //! around accesses to Memfault APIs. This is required if calls are made to Memfault APIs from //! multiple tasks _and_ tasks do _not_ run to completion (can be interrupted by other tasks). //! //! @note By default, a weak version of this function is implemented which always returns false (no //! time available) //! //! @note It's expected that the mutexes implemented are recursive mutexes. void memfault_lock(void); void memfault_unlock(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/platform/reboot_tracking.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif //! A placeholder for initializing reboot tracking //! //! @note A user of the SDK can chose to implement this function and call it during bootup. //! //! @note A reference implementation can be found in the NRF52 demo application: //! ports/nrf5_sdk/resetreas_reboot_tracking.c void memfault_platform_reboot_tracking_boot(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/platform/system_time.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Dependency functions which can optionally be implemented for time tracking within the Memfault //! SDK. #include #include #ifdef __cplusplus extern "C" { #endif typedef enum { kMemfaultCurrentTimeType_Unknown = 0, //! the number of seconds that have elapsed since the Unix epoch, //! (00:00:00 UTC on 1 January 1970) kMemfaultCurrentTimeType_UnixEpochTimeSec = 1, } eMemfaultCurrentTimeType; typedef struct { eMemfaultCurrentTimeType type; union { uint64_t unix_timestamp_secs; } info; } sMemfaultCurrentTime; //! Returns the current system time //! //! This dependency can (optionally) be implemented if a device keeps track of time and wants to //! track the exact time events occurred on device. If no time is provided, the Memfault backend //! will automatically create a timestamp based on when an event is received by the chunks endpoint. //! //! This function should not issue any logs, because it is called from within the Memfault logging //! system when log timestamps are enabled. //! //! @note By default, a weak version of this function is implemented which always returns false (no //! time available) //! //! @param return true if a time could be recovered, false otherwise bool memfault_platform_time_get_current(sMemfaultCurrentTime *time); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/preprocessor.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/compiler.h" #ifdef __cplusplus extern "C" { #endif //! Counts the number of __VA_ARGS__ (provided the length is <= 32) //! //! Runs as early preprocess phase which makes it easy to stringify the result and embed //! it in something like a static assert: //! MEMFAULT_STATIC_ASSERT( , //! "Too many args: " MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_ARG_COUNT_UP_TO_32(__VA_ARGS__))) #define MEMFAULT_ARG_COUNT_UP_TO_32(...) \ _MEMFAULT_ARG_COUNT_UP_TO_32_IMPL(dummy, ##__VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, \ 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, \ 6, 5, 4, 3, 2, 1, 0) #define _MEMFAULT_ARG_COUNT_UP_TO_32_IMPL( \ dummy, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, _22, _21, _20, _19, _18, _17, _16, _15, \ _14, _13, _12, _11, _10, _09, _08, _07, _06, _05, _04, _03, _02, _01, count, ...) \ count //! Note: We sanity check that "the ‘##’ token paste operator has a special meaning when placed //! between a comma and a variable argument" //! (https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html) where //! ", ##__VA_ARGS__" evaluates to "" if __VA_ARGS__ is empty. //! //! While this feature is a GNU extension, it's widely supported in most other modern compilers //! (ARM Compiler 5, Clang, iccarm). When using GCC this means you should be compiling with //! -std=gnu11 (default), -std=gnu99, etc, and with other compilers you may need to enable GNU //! extensions. //! //! The check itself is pretty simple. //! When supported, "(, ##__VA_ARGS__)" expands to () //! When not supported "(, ##__VA_ARGS__)" expands to (,) //! If we count the arguments via a preprocessor macro we will consequently get 0 when the //! feature is supported and 1 when it is not (due to the ',' being left in the expansion) //! //! The C++20 introduced __VA_OPT__ for this behavior but that is still not widely available yet MEMFAULT_STATIC_ASSERT(MEMFAULT_ARG_COUNT_UP_TO_32() == 0, "## operator not supported, enable gnu extensions on your compiler"); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/reboot_reason_types.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Different types describing information collected as part of "Trace Events" #ifdef __cplusplus extern "C" { #endif #include "memfault/config.h" #include "memfault/core/compiler.h" #define MEMFAULT_REBOOT_REASON_EXPECTED_CUSTOM_BASE 0x1000 #define MEMFAULT_REBOOT_REASON_EXPECTED_CUSTOM_MAX 0x1100 #define MEMFAULT_REBOOT_REASON_UNEXPECTED_CUSTOM_BASE 0xA000 #define MEMFAULT_REBOOT_REASON_UNEXPECTED_CUSTOM_MAX 0xA100 #undef MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE #undef MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE typedef enum 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, // // Expected Resets // kMfltRebootReason_UserShutdown = 0x0001, kMfltRebootReason_UserReset = 0x0002, kMfltRebootReason_FirmwareUpdate = 0x0003, kMfltRebootReason_LowPower = 0x0004, kMfltRebootReason_DebuggerHalted = 0x0005, kMfltRebootReason_ButtonReset = 0x0006, kMfltRebootReason_PowerOnReset = 0x0007, kMfltRebootReason_SoftwareReset = 0x0008, // MCU went through a full reboot due to exit from lowest power state kMfltRebootReason_DeepSleep = 0x0009, // MCU reset pin was toggled kMfltRebootReason_PinReset = 0x000A, // Self Test generated reboot kMfltRebootReason_SelfTest = 0x000B, // // User Defined Expected Resets // #if MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE == 1 kMfltRebootReason_ExpectedBase = MEMFAULT_REBOOT_REASON_EXPECTED_CUSTOM_BASE, #define MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(name) kMfltRebootReason_##name, #define MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(name) #include MEMFAULT_REBOOT_REASON_USER_DEFS_FILE #undef MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE #undef MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE #endif // // Unexpected Resets // // Can be used to flag an unexpected reset path. i.e NVIC_SystemReset() // being called without any reboot logic getting invoked. kMfltRebootReason_UnknownError = 0x8000, kMfltRebootReason_Assert = 0x8001, // Deprecated in favor of HardwareWatchdog & SoftwareWatchdog. This way, // the amount of watchdogs not caught by software can be easily tracked kMfltRebootReason_WatchdogDeprecated = 0x8002, kMfltRebootReason_BrownOutReset = 0x8003, kMfltRebootReason_Nmi = 0x8004, // Non-Maskable Interrupt // More details about nomenclature in https://mflt.io/root-cause-watchdogs kMfltRebootReason_HardwareWatchdog = 0x8005, kMfltRebootReason_SoftwareWatchdog = 0x8006, // A reset triggered due to the MCU losing a stable clock. This can happen, // for example, if power to the clock is cut or the lock for the PLL is lost. kMfltRebootReason_ClockFailure = 0x8007, // A software reset triggered when the OS or RTOS end-user code is running on top of identifies // a fatal error condition. kMfltRebootReason_KernelPanic = 0x8008, // A reset triggered when an attempt to upgrade to a new OTA image has failed and a rollback // to a previous version was initiated kMfltRebootReason_FirmwareUpdateError = 0x8009, // A software reset triggered due to a dynamic memory (heap) allocation failure. kMfltRebootReason_OutOfMemory = 0x800A, // A reset due to stack overflow kMfltRebootReason_StackOverflow = 0x800B, // Task Watchdog reset type kMfltRebootReason_TaskWatchdog = 0x800C, // Resets from Arm Faults kMfltRebootReason_BusFault = 0x9100, kMfltRebootReason_MemFault = 0x9200, kMfltRebootReason_UsageFault = 0x9300, kMfltRebootReason_HardFault = 0x9400, // A reset which is triggered when the processor faults while already // executing from a fault handler. kMfltRebootReason_Lockup = 0x9401, // A reset triggered due to a security violation kMfltRebootReason_SecurityViolation = 0x9402, // A reset triggered due to a parity error (i.e. memory integrity check) kMfltRebootReason_ParityError = 0x9403, // A reset triggered due to a temperature error kMfltRebootReason_Temperature = 0x9404, // A reset due to some other hardware error kMfltRebootReason_Hardware = 0x9405, // // User Defined Unexpected Resets // #if MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE == 1 kMfltRebootReason_UnexpectedBase = MEMFAULT_REBOOT_REASON_UNEXPECTED_CUSTOM_BASE, #define MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(name) #define MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(name) kMfltRebootReason_##name, #include MEMFAULT_REBOOT_REASON_USER_DEFS_FILE #undef MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE #undef MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE #endif } eMemfaultRebootReason; #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) \ MEMFAULT_STATIC_ASSERT(kMfltRebootReason_##name < MEMFAULT_REBOOT_REASON_EXPECTED_CUSTOM_MAX, \ "User defined expected reboot is out of range"); #define MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(name) \ MEMFAULT_STATIC_ASSERT( \ kMfltRebootReason_##name < MEMFAULT_REBOOT_REASON_UNEXPECTED_CUSTOM_MAX, \ "User defined unexpected reboot is out of range"); #include MEMFAULT_REBOOT_REASON_USER_DEFS_FILE #undef MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE #undef MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE #endif #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/reboot_tracking.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A subsystem which can (optionally) be used to trace _all_ reboots taking place on the system //! //! The Memfault "panics" component will automatically save coredumps anytime the system crashes. //! However, it can sometimes be useful to track other types of reset reasons such as a software //! initiated reset to complete an OTA, a brown out reset, a hardware watchdog reset, etc //! //! To track these types of resets, the "panics" SDK component also exposes a lightweight "reboot //! tracking" module. More details can be found in the function descriptions below or a //! step-by-step setup tutorial is available at https://mflt.io/2QlOlgH //! //! A user may also (optionally) use two APIs for catching & reacting to reboot loops: //! memfault_reboot_tracking_reset_crash_count() //! memfault_reboot_tracking_get_crash_count() #include #include #include "memfault/core/compiler.h" #include "memfault/core/event_storage.h" #include "memfault/core/reboot_reason_types.h" #ifdef __cplusplus extern "C" { #endif //! Additional information that can optionally be collected at bootup and appended to the current //! reset information typedef struct BootupInfo { //! Most MCUs have an always-on register that will report why the device rebooted (i.e normal //! reset, brown out reset, watchdog, etc). This value can be provided here to attach the current //! value of the register to the reboot information or be 0 otherwise uint32_t reset_reason_reg; //! If the reason for the current reboot is not already tracked, this value will be used. //! //! @note This can useful in situations where no code executes from the main application prior to //! reboot (for example, a reset button is pressed or another MCU pulls power to the rail) but //! info is made available to the application after bootup as to why it was reset (i.e bootloader //! passes additional state about reset to main app). //! //! @note If there is not additional info available about the reset, this should be set to 0 //! (kMfltRebootReason_Unknown). eMemfaultRebootReason reset_reason; } sResetBootupInfo; //! Helper structure for storing/retrieving the device's reboot reason typedef struct MfltRebootType { //! Stores the reboot reason determined from hardware during the current boot eMemfaultRebootReason reboot_reg_reason; //! Stores the reboot reason as read from s_mflt_reboot_info. This could be set in //! the prior boot from either: //! * the application using memfault_reboot_tracking_mark_reset_imminent (fault handler, firmware //! update, etc) //! * a reason determined from the reboot register at bootup eMemfaultRebootReason prior_stored_reason; } sMfltRebootReason; #define MEMFAULT_REBOOT_TRACKING_REGION_SIZE 64 //! Sets the memory region used for reboot tracking. //! //! @note This region should _not_ initialized by your bootloader or application. //! //! To achieve this behavior, some compilers have NOINIT directives or with GCC LD //! this behavior can be easily achieved with something like: //! //! // In a C File //! #include "memfault/core/compiler.h" //! MEMFAULT_PUT_IN_SECTION(".mflt_reboot_info") //! static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; //! //! // In device LD file //! NOINIT (rw) : ORIGIN = , LENGTH = 0x20 //! .noinit (NOLOAD): { KEEP(*(*.mflt_reboot_info)) } > NOINIT //! //! @note The size of the region should be MEMFAULT_REBOOT_TRACKING_REGION_SIZE //! @note This should be called once on bootup of the system prior to making any other //! reboot_tracking calls //! @param start_addr The location where reboot tracking is located //! @param bootup_info See struct for more details. Can be NULL if there is no info //! to provide void memfault_reboot_tracking_boot(void *start_addr, const sResetBootupInfo *bootup_info); typedef struct MfltRebootTrackingRegInfo { uint32_t pc; uint32_t lr; } sMfltRebootTrackingRegInfo; //! Flag that a reboot is about to take place //! //! This is automatically called by the Memfault API for fault handlers and when //! memfault_fault_handling_assert() is invoked //! //! It can also be called for happy-path reboots such as a reboot due to a user clicking //! a button or a reboot due to an OTA update taking place. It's up to the user of the SDK //! to call the API in these scenarios //! @param reboot_reason The reason for the reboot. See eMemfaultRebootReason for options //! @param reg Register state at the time the reboot was initiated or NULL if no state is available void memfault_reboot_tracking_mark_reset_imminent(eMemfaultRebootReason reboot_reason, const sMfltRebootTrackingRegInfo *reg); //! Helper macro to capture the current pc & lr and call //! memfault_reboot_tracking_mark_reset_imminent #define MEMFAULT_REBOOT_MARK_RESET_IMMINENT(reason_) \ do { \ void *mflt_pc; \ MEMFAULT_GET_PC(mflt_pc); \ void *mflt_lr; \ MEMFAULT_GET_LR(mflt_lr); \ sMfltRebootTrackingRegInfo mflt_reg_info = { \ .pc = (uint32_t)(uintptr_t)mflt_pc, \ .lr = (uint32_t)(uintptr_t)mflt_lr, \ }; \ memfault_reboot_tracking_mark_reset_imminent(reason_, &mflt_reg_info); \ } while (0) //! Helper macro that behaves the same as `MEMFAULT_REBOOT_MARK_RESET_IMMINENT` but allows //! for a custom reboot reason to be specified without needed to use the //! `MEMFAULT_REBOOT_REASON_KEY` macro #define MEMFAULT_REBOOT_MARK_RESET_IMMINENT_CUSTOM(reason_) \ MEMFAULT_REBOOT_MARK_RESET_IMMINENT(MEMFAULT_REBOOT_REASON_KEY(reason_)) //! Collects recent reset info and pushes it to memfault_event_storage so that the data can //! can be sent out using the Memfault data packetizer //! //! @param storage_impl The event storage implementation being used (returned from //! memfault_events_storage_boot()) //! @return 0 on success or if there was nothing to collect, error code otherwise int memfault_reboot_tracking_collect_reset_info(const sMemfaultEventStorageImpl *storage_impl); //! Compute the worst case number of bytes required to serialize Memfault data //! //! @return the worst case amount of space needed to serialize an event size_t memfault_reboot_tracking_compute_worst_case_storage_size(void); //! Get the current crash count //! //! Every time the device resets due to a Reason of Unknown or Error, the crash count //! is incremented. A user of the SDK may (optionally) use this information to determine //! if the device is crash looping and if so take recovery action. //! //! @return crash count size_t memfault_reboot_tracking_get_crash_count(void); //! Reset the crash count to 0 void memfault_reboot_tracking_reset_crash_count(void); //! Flags that a coredump has been collected as part of this reboot //! //! @note This is called by the "panics" component coredump integration automatically and should //! never need to be called by an end user directly void memfault_reboot_tracking_mark_coredump_saved(void); //! Get the reported reboot reason from boot //! //! Each time the device boots, the reboot reason mapped from the platform reboot register is //! stored. This can be used either by other subsystems or users of the SDK. //! //! @param reboot_reason Pointer to store the reboot reason from boot //! @return 0 on success or 1 if the reboot reason is invalid //! or the input parameter is NULL int memfault_reboot_tracking_get_reboot_reason(sMfltRebootReason *reboot_reason); //! Returns a boolean representing whether an unexpected reboot occurred from boot //! //! This function uses a reboot reason from a reboot register and the prior reboot reason (if //! present) to determine if a reboot was unexpected. //! //! @param unexpected_reboot_occurred Pointer to store boolean marking an unexpected reboot //! @return 0 on success, or 1 if the result is invalid or the input parameter is NULL int memfault_reboot_tracking_get_unexpected_reboot_occurred(bool *unexpected_reboot_occurred); //! Checks if reboot tracking component has booted //! //! @returns true if reboot tracking component booted or false if not bool memfault_reboot_tracking_booted(void); //! Set or clear the "active" property for a Metrics Session. This is used when //! a session starts and stops, for tracking which sessions were active when a //! reboot occurred. //! //! @param activate True if the session is active, false if it is not //! @param index The index of the session void memfault_reboot_tracking_metrics_session(bool activate, uint32_t index); //! Clear the reboot tracking data for "active" Metrics Sessions. This is used //! on reboot to reset the persisted active state of all sessions. void memfault_reboot_tracking_clear_metrics_sessions(void); //! Check if a Metrics Session was active when a reboot occurred //! //! @param index The index of the session bool memfault_reboot_tracking_metrics_session_was_active(uint32_t index); #if MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE == 1 //! Defines a customer specific reboot reason. //! //! These allow for custom reboot reasons to be defined which can be used to track //! the root cause of a reboot that is not captured by the default set of reboot reasons. //! //! There are two types of reboot reasons: //! 1. Expected: These are reboots which are expected to happen as part of normal operation. //! For example, a user initiated reboot. These can be specified using //! MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE. //! 2. Unexpected: These are reboots which are not expected to happen as part of normal operation. //! For example, a watchdog reset, or overcurrent event. These can be specified using //! MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE. #define MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(_name) MEMFAULT_REBOOT_REASON_DEFINE_TRAP_() #define MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(_name) MEMFAULT_REBOOT_REASON_DEFINE_TRAP_() //! Stub define to detect accidental usage outside of the user reboot reason file #define MEMFAULT_REBOOT_REASON_DEFINE_TRAP_() \ MEMFAULT_STATIC_ASSERT(false, "MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE should only be used " \ "in " MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE); #endif //! Convenience macro to use a custom reboot reason key //! //! This macro is used to convert a custom reboot reason name to a key that can be used to //! track the reboot reason since //! @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 ================================================ FILE: components/include/memfault/core/sdk_assert.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Assert implementation used internally by the Memfault SDK. //! //! Asserts are _only_ used for API misuse and configuration issues (i.e a NULL function pointer //! as for a function in a *StorageImpl context). #include "memfault/config.h" #include "memfault/core/compiler.h" #ifdef __cplusplus extern "C" { #endif #if MEMFAULT_SDK_ASSERT_ENABLED //! A user of the SDK may optionally override the default assert macro by adding a CFLAG such as: //! -DMEMFAULT_SDK_ASSERT=MY_PLATFORM_ASSERT #ifndef MEMFAULT_SDK_ASSERT //! An end user can override the implementation to trigger a fault/reboot the system. //! //! @note The default implementation is a weak function that is a while (1) {} loop. MEMFAULT_NORETURN void memfault_sdk_assert_func_noreturn(void); //! Handler that is invoked when the MEMFULT_SDK_ASSERT() check fails //! //! @note The handler: //! - logs the return address that tripped the assert //! - halts the platform by calling "memfault_platform_halt_if_debugging" //! - calls memfault_sdk_assert_func_noreturn void memfault_sdk_assert_func(void); #define MEMFAULT_SDK_ASSERT(expr) ((expr) ? (void)0 : memfault_sdk_assert_func()) #endif /* MEMFAULT_SDK_ASSERT */ #else #define MEMFAULT_SDK_ASSERT(expr) (void)0 #endif /* MEMFAULT_SDK_ASSERT_ENABLED */ #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/self_test.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Public interface for self_test component #pragma once #ifdef __cplusplus extern "C" { #endif #include //! A bitfield-based enum used to select which self tests to run typedef enum { kMemfaultSelfTestFlag_DeviceInfo = (1 << 0), kMemfaultSelfTestFlag_ComponentBoot = (1 << 1), kMemfaultSelfTestFlag_CoredumpRegions = (1 << 2), kMemfaultSelfTestFlag_DataExport = (1 << 3), kMemfaultSelfTestFlag_RebootReason = (1 << 4), kMemfaultSelfTestFlag_RebootReasonVerify = (1 << 5), kMemfaultSelfTestFlag_PlatformTime = (1 << 6), kMemfaultSelfTestFlag_CoredumpStorage = (1 << 7), kMemfaultSelfTestFlag_CoredumpStorageCapacity = (1 << 8), // A convenience mask which runs the default tests kMemfaultSelfTestFlag_Default = (kMemfaultSelfTestFlag_DeviceInfo | kMemfaultSelfTestFlag_ComponentBoot | kMemfaultSelfTestFlag_CoredumpRegions | kMemfaultSelfTestFlag_DataExport | kMemfaultSelfTestFlag_PlatformTime | kMemfaultSelfTestFlag_CoredumpStorageCapacity), } eMemfaultSelfTestFlag; //! Main entry point to performing a self test //! //! This function will carry out all component tests and return after completing all tests //! See output for test results and feedback on any errors detected //! //! @returns 0 on success or 1 if a subtest failed int memfault_self_test_run(uint32_t run_flags); //! Translates a string arg to flag used to select which test to run //! //! @returns eMemfaultSelfTestFlag mapped to argument, or kMemfaultSelfTestFlag_Default if no //! matches found. uint32_t memfault_self_test_arg_to_flag(const char *arg); //! Delays or puts to sleep current context for specified milliseconds //! //! @note This function must be implemented by your platform to perform platform time test void memfault_self_test_platform_delay(uint32_t delay_ms); //! Disables all interrupts //! //! @note This function must be implemented by your platform to perform coredump storage test //! @returns True if interrupts were disabled, otherwise false bool memfault_self_test_platform_disable_irqs(void); //! Enables all interrupts //! //! @note This function must be implemented by your platform to perform coredump storage test //! @returns True if interrupts were enabled, otherwise false bool memfault_self_test_platform_enable_irqs(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/serializer_helper.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Internal helper functions that are used when serializing Memfault Event based data //! A user of the sdk should never have to call these routines directly. #include #include #include #include "memfault/core/event_storage.h" #include "memfault/core/platform/system_time.h" #include "memfault/core/serializer_key_ids.h" #include "memfault/util/cbor.h" #ifdef __cplusplus extern "C" { #endif bool memfault_serializer_helper_encode_metadata_with_time(sMemfaultCborEncoder *encoder, eMemfaultEventType type, const sMemfaultCurrentTime *time); bool memfault_serializer_helper_encode_metadata(sMemfaultCborEncoder *encoder, eMemfaultEventType type); bool memfault_serializer_helper_encode_uint32_kv_pair(sMemfaultCborEncoder *encoder, uint32_t key, uint32_t value); bool memfault_serializer_helper_encode_int32_kv_pair(sMemfaultCborEncoder *encoder, uint32_t key, int32_t value); bool memfault_serializer_helper_encode_byte_string_kv_pair(sMemfaultCborEncoder *encoder, uint32_t key, const void *buf, size_t buf_len); typedef struct MemfaultTraceEventHelperInfo { eMemfaultTraceInfoEventKey reason_key; uint32_t reason_value; uint32_t pc; uint32_t lr; size_t extra_event_info_pairs; } sMemfaultTraceEventHelperInfo; bool memfault_serializer_helper_encode_trace_event(sMemfaultCborEncoder *e, const sMemfaultTraceEventHelperInfo *info); //! @return false if encoding was not successful and the write session needs to be rolled back. typedef bool(MemfaultSerializerHelperEncodeCallback)(sMemfaultCborEncoder *encoder, void *ctx); //! Helper to initialize a CBOR encoder, prepare the storage for writing, call the encoder_callback //! to encode and write any data and finally commit the write to the storage (or rollback in case //! of an error). //! @return the value returned from encode_callback bool memfault_serializer_helper_encode_to_storage( sMemfaultCborEncoder *encoder, const sMemfaultEventStorageImpl *storage_impl, MemfaultSerializerHelperEncodeCallback encode_callback, void *ctx); //! Helper to compute the size of encoding operations performed by encode_callback. //! @return the computed size required to store the encoded data. size_t memfault_serializer_helper_compute_size( sMemfaultCborEncoder *encoder, MemfaultSerializerHelperEncodeCallback encode_callback, void *ctx); bool memfault_serializer_helper_check_storage_size(const sMemfaultEventStorageImpl *storage_impl, size_t(compute_worst_case_size)(void), const char *event_type); //! Return the number of events that were dropped since last call //! //! @note Calling this function resets the counters. uint32_t memfault_serializer_helper_read_drop_count(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/serializer_key_ids.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Internal keys used for serializing different types of event ids. Users of the SDK should never //! need to use any of these directly. #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_CBOR_SCHEMA_VERSION_V1 (1) // NOTE: implies "sdk_version": "0.5.0" typedef enum { kMemfaultEventKey_CapturedDateUnixTimestamp = 1, kMemfaultEventKey_Type = 2, kMemfaultEventKey_CborSchemaVersion = 3, kMemfaultEventKey_EventInfo = 4, kMemfaultEventKey_UserInfo = 5, kMemfaultEventKey_HardwareVersion = 6, kMemfaultEventKey_DeviceSerial = 7, kMemfaultEventKey_ReleaseVersionDeprecated = 8, kMemfaultEventKey_SoftwareVersion = 9, kMemfaultEventKey_SoftwareType = 10, kMemfaultEventKey_BuildId = 11, } eMemfaultEventKey; //! Possible values for the kMemfaultEventKey_Type field. typedef enum { kMemfaultEventType_Heartbeat = 1, kMemfaultEventType_Trace = 2, // Deprecated: kMemfaultEventType_LogError = 3, use trace_with_log feature instead kMemfaultEventType_Logs = 4, kMemfaultEventType_Cdr = 5, kMemfaultEventType_LogsTimestamped = 6, } eMemfaultEventType; //! EventInfo dictionary keys for events with type kMemfaultEventType_Heartbeat. typedef enum { kMemfaultHeartbeatInfoKey_Metrics = 1, kMemfaultHeartbeatInfoKey_Session = 2, } eMemfaultHeartbeatInfoKey; //! EventInfo dictionary keys for events with type kMemfaultEventType_Trace. typedef enum { kMemfaultTraceInfoEventKey_Reason = 1, kMemfaultTraceInfoEventKey_ProgramCounter = 2, kMemfaultTraceInfoEventKey_LinkRegister = 3, kMemfaultTraceInfoEventKey_McuReasonRegister = 4, kMemfaultTraceInfoEventKey_CoredumpSaved = 5, kMemfaultTraceInfoEventKey_UserReason = 6, kMemfaultTraceInfoEventKey_StatusCode = 7, kMemfaultTraceInfoEventKey_Log = 8, kMemfaultTraceInfoEventKey_CompactLog = 9, } eMemfaultTraceInfoEventKey; //! EventInfo dictionary keys for events with type kMemfaultEventType_LogError. typedef enum { kMemfaultLogErrorInfoKey_Log = 1, } eMemfaultLogErrorInfoKey; //! For events with type kMemfaultEventType_Logs, the EventInfo contains a single array containing //! all logs: [lvl1, msg1, lvl2, msg2, ...] //! EventInfo dictionary keys for events with type kMemfaultEventType_Cdr. typedef enum { kMemfaultCdrInfoKey_DurationMs = 1, kMemfaultCdrInfoKey_Mimetypes = 2, kMemfaultCdrInfoKey_Reason = 3, kMemfaultCdrInfoKey_Data = 4, } eMemfaultCdrInfoKey; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/task_watchdog.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Task watchdog API. //! //! This module implements a task watchdog that can be used to detect stuck RTOS //! tasks. //! //! Example usage: //! //! // Initialize the task watchdog system on system start //! memfault_task_watchdog_init(); //! //! // In a task loop //! void example_task_loop(void) { //! while(1) { //! rtos_wait_for_task_to_wake_up(); //! //! // Example: Use the task watchdog to monitor a block of work //! MEMFAULT_TASK_WATCHDOG_START(example_task_loop_channel_id); //! // do work that should be monitored by the watchdog //! MEMFAULT_TASK_WATCHDOG_STOP(example_task_loop_channel_id); //! //! // Example: Use the task watchdog to monitor a repeated operation //! MEMFAULT_TASK_WATCHDOG_START(example_task_loop_channel_id); //! // feeding the watchdog is only necessary if in some long-running task //! // that runs the risk of expiring the timeout, but is not expected to be //! // stuck in between calls //! for (size_t i = 0; i < 100; i++) { //! MEMFAULT_TASK_WATCHDOG_FEED(example_task_loop_channel_id); //! run_slow_repeated_task(); //! } //! MEMFAULT_TASK_WATCHDOG_STOP(example_task_loop_channel_id); //! //! rtos_put_task_to_sleep(); //! } //! } //! //! // In either a standalone timer, or a periodic background task, call the //! // memfault_task_watchdog_check_all function //! void example_task_watchdog_timer_cb(void) { //! memfault_task_watchdog_check_all(); //! } //! //! // Call memfault_task_watchdog_bookkeep() in //! // memfault_platform_coredump_save_begin()- this updates all the task //! // channel timeouts, in the event that the task monitor wasn't able to run //! // for some reason. Also might be appropriate to refresh other system //! // watchdogs prior to executing the coredump save. //! bool memfault_platform_coredump_save_begin(void) { //! memfault_task_watchdog_bookkeep(); //! // may also need to refresh other watchdogs here, for example: //! memfault_task_watchdog_platform_refresh_callback(); //! return true; //! } #include #include "memfault/config.h" #ifdef __cplusplus extern "C" { #endif //! Declare the task watchdog channel IDs. These shouldn't be used directly, but //! should be passed to the MEMFAULT_TASK_WATCHDOG_START etc. functions with the //! name declared in the memfault_task_watchdog_config.def file. #define MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_) kMemfaultTaskWatchdogChannel_##channel_, typedef enum { #if MEMFAULT_TASK_WATCHDOG_ENABLE #include "memfault_task_watchdog_config.def" #else // define one channel to prevent the compiler from complaining about a zero-length array MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(placeholder_) #endif kMemfaultTaskWatchdogChannel_NumChannels, } eMemfaultTaskWatchdogChannel; #undef MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE #define MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_) kMemfaultTaskWatchdogChannel_##channel_ //! Initialize (or reset) the task watchdog system. This will stop and reset all //! internal bookkeeping for all channels. void memfault_task_watchdog_init(void); //! This function should be called periodically (for example, around the //! MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS period). It checks if any task //! watchdog channel has reached the timeout interval. If so, it will trigger a //! task watchdog assert. void memfault_task_watchdog_check_all(void); //! As in `memfault_task_watchdog_check_all`, but only updates the internal //! bookkeeping, and does not trigger any callbacks or asserts. //! //! Intended to be called just prior to coredump capture when the system is in //! the fault_handler. void memfault_task_watchdog_bookkeep(void); //! Start a task watchdog channel. After being started, a channel will now be //! eligible for expiration. Also resets the timeout interval for the channel. #define MEMFAULT_TASK_WATCHDOG_START(channel_id_) \ memfault_task_watchdog_start(MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_id_)) //! Reset the timeout interval for a task watchdog channel. #define MEMFAULT_TASK_WATCHDOG_FEED(channel_id_) \ memfault_task_watchdog_feed(MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_id_)) //! Stop a task watchdog channel. After being stopped, a channel will no longer //! be eligible for expiration and is reset #define MEMFAULT_TASK_WATCHDOG_STOP(channel_id_) \ memfault_task_watchdog_stop(MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_id_)) //! These functions should not be used directly, but instead through the macros //! above. void memfault_task_watchdog_start(eMemfaultTaskWatchdogChannel channel_id); void memfault_task_watchdog_feed(eMemfaultTaskWatchdogChannel channel_id); void memfault_task_watchdog_stop(eMemfaultTaskWatchdogChannel channel_id); //! Optional weakly defined function to perform any additional actions during //! `memfault_task_watchdog_check_all` when no channels have expired void memfault_task_watchdog_platform_refresh_callback(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/task_watchdog_impl.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! //! Task watchdog APIs intended for use within the memfault-firmware-sdk #include #include #include #include "memfault/config.h" // Keep this after config.h #include "memfault/core/task_watchdog.h" #ifdef __cplusplus extern "C" { #endif enum MemfaultTaskWatchdogChannelState { kMemfaultTaskWatchdogChannelState_Stopped = 0, kMemfaultTaskWatchdogChannelState_Started, kMemfaultTaskWatchdogChannelState_Expired, }; struct MemfaultTaskWatchdogChannel { // Using a 32-bit value to track fed_time. This is enough for 49 days, which // should be much longer than any watchdog timeout. uint32_t fed_time_ms; enum MemfaultTaskWatchdogChannelState state; }; typedef struct MemfaultTaskWatchdogInfo { struct MemfaultTaskWatchdogChannel channels[kMemfaultTaskWatchdogChannel_NumChannels]; } sMemfaultTaskWatchdogInfo; //! Bookkeeping for the task watchdog channels. If the structure needs to change //! in the future, rename it to keep analyzer compatibility (eg "_v2") extern sMemfaultTaskWatchdogInfo g_memfault_task_channel_info; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/trace_event.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Subsystem to trace errors in a way that requires less storage than full coredump traces and //! also allows the system to continue running after capturing the event. //! //! @note For a step-by-step guide about how to integrate and leverage the Trace Event component //! check out https://mflt.io/error-tracing //! //! @note To capture a full snapshot of an error condition (all tasks, logs, and local & //! global variable state), you can integrate memfault coredumps: https://mflt.io/coredumps #include #include "memfault/config.h" #include "memfault/core/compact_log_compile_time_checks.h" #include "memfault/core/compact_log_helpers.h" #include "memfault/core/event_storage.h" #include "memfault/core/trace_event_impl.h" #include "memfault/core/trace_reason_user.h" #ifdef __cplusplus extern "C" { #endif //! Initializes trace event module //! //! @note This must be called before using MEMFAULT_TRACE_EVENT / MEMFAULT_TRACE_EVENT_WITH_STATUS //! //! @param storage_impl The event storage implementation being used (returned from //! memfault_events_storage_boot()) //! @return 0 on success, else error code. int memfault_trace_event_boot(const sMemfaultEventStorageImpl *storage_impl); //! Records a "Trace Event" with given reason, pc, & lr. //! //! The current program counter and return address are collected as part of the macro. //! //! @param reason The error reason. See MEMFAULT_TRACE_REASON_DEFINE in trace_reason_user.h for //! information on how to define reason values. //! @see memfault_trace_event_capture //! @see MEMFAULT_TRACE_REASON_DEFINE //! @note Ensure memfault_trace_event_boot() has been called before using this API. On Zephyr and //! ESP-IDF platforms, this will be done automatically. Other platforms may need to call this during //! system initialization. #define MEMFAULT_TRACE_EVENT(reason) \ do { \ void *mflt_pc; \ MEMFAULT_GET_PC(mflt_pc); \ void *mflt_lr; \ MEMFAULT_GET_LR(mflt_lr); \ memfault_trace_event_capture(MEMFAULT_TRACE_REASON(reason), mflt_pc, mflt_lr); \ } while (0) //! Records same info as MEMFAULT_TRACE_EVENT as well as a "status_code" //! //! @note The status code allows one to disambiguate traces of the same "reason" class //! and record additional diagnostic info. //! //! Some example use cases: //! reason=UnexpectedBluetoothDisconnect, status_codes = bluetooth error code //! reason=LibCFileIoError, status_code = value of errno when operation failed //! //! @note Trace events with the same 'reason' but a different 'status_code' are classified as //! unique issues in the Memfault UI #define MEMFAULT_TRACE_EVENT_WITH_STATUS(reason, status_code) \ do { \ void *mflt_pc; \ MEMFAULT_GET_PC(mflt_pc); \ void *mflt_lr; \ MEMFAULT_GET_LR(mflt_lr); \ memfault_trace_event_with_status_capture(MEMFAULT_TRACE_REASON(reason), mflt_pc, mflt_lr, \ status_code); \ } while (0) #if !MEMFAULT_COMPACT_LOG_ENABLE //! Records same info as MEMFAULT_TRACE_EVENT as well as a log //! //! @note The log allows one to capture arbitrary metadata when a system error is detected //! such as the value of several registers or current state machine state. //! //! @note Trace events with the same 'reason' but a different 'log' are classified as //! grouped together by default in the UI #define MEMFAULT_TRACE_EVENT_WITH_LOG(reason, ...) \ do { \ void *mflt_pc; \ MEMFAULT_GET_PC(mflt_pc); \ void *mflt_lr; \ MEMFAULT_GET_LR(mflt_lr); \ memfault_trace_event_with_log_capture(MEMFAULT_TRACE_REASON(reason), mflt_pc, mflt_lr, \ __VA_ARGS__); \ } while (0) #else #define MEMFAULT_TRACE_EVENT_WITH_LOG(reason, format, ...) \ do { \ void *lr; \ MEMFAULT_GET_LR(lr); \ MEMFAULT_LOGGING_RUN_COMPILE_TIME_CHECKS(format, ##__VA_ARGS__); \ MEMFAULT_LOG_FMT_ELF_SECTION_ENTRY(format, ##__VA_ARGS__); \ memfault_trace_event_with_compact_log_capture( \ MEMFAULT_TRACE_REASON(reason), lr, MEMFAULT_LOG_FMT_ELF_SECTION_ENTRY_PTR, \ MFLT_GET_COMPRESSED_LOG_FMT(__VA_ARGS__), ##__VA_ARGS__); \ } while (0) #endif //! Flushes an ISR trace event capture out to event storage //! //! @note If a user is logging events from ISRs, it's recommended this API is called //! prior to draining data from the packetizer. //! @note This API is automatically called when a new trace event is recorded. int memfault_trace_event_try_flush_isr_event(void); //! Compute the worst case number of bytes required to serialize a Trace Event. //! //! @return the worst case amount of space needed to serialize a Trace Event. size_t memfault_trace_event_compute_worst_case_storage_size(void); //! Checks if trace event component has booted //! //! @returns true if trace event component booted or false if not bool memfault_trace_event_booted(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/trace_event_impl.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! APIs used internally by the SDK for trace event collection. An user //! of the SDK should never have to call these routines directly. #include #include "memfault/core/compiler.h" #include "memfault/core/trace_reason_user.h" #ifdef __cplusplus extern "C" { #endif int memfault_trace_event_capture(eMfltTraceReasonUser reason, void *pc_addr, void *lr_addr); int memfault_trace_event_with_status_capture(eMfltTraceReasonUser reason, void *pc_addr, void *lr_addr, int32_t status); MEMFAULT_PRINTF_LIKE_FUNC(4, 5) int memfault_trace_event_with_log_capture( eMfltTraceReasonUser reason, void *pc_addr, void *lr_addr, const char *fmt, ...); int memfault_trace_event_with_compact_log_capture(eMfltTraceReasonUser reason, void *lr_addr, uint32_t log_id, uint32_t fmt, ...); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/core/trace_reason_user.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Helpers to add user-defined reasons for "Trace Events" //! //! NOTE: The internals of the MEMFAULT_TRACE_REASON APIs make use of "X-Macros" to enable more //! flexibility improving and extending the internal implementation without impacting the externally //! facing API. //! //! More details about error tracing in general can be found at https://mflt.io/error-tracing #ifdef __cplusplus extern "C" { #endif #include "memfault/config.h" #include "memfault/core/compiler.h" //! Defines a user-defined trace reason. //! //! User-defined error reasons are expected to be defined in a separate *.def file. Aside of //! creating the file, you will need to add the preprocessor definition //! MEMFAULT_TRACE_REASON_USER_DEFS_FILE to point to the file. For example, passing the compiler //! flag -DMEMFAULT_TRACE_REASON_USER_DEFS_FILE=\"memfault_trace_reason_user_config.def\" indicates //! that the file "memfault_trace_reason_user_config.def" will be included to pick up user-defined //! error trace reasons. Please ensure the file can be found in the header search paths. //! //! The contents of the .def file could look like: //! //! // memfault_trace_reason_user_config.def //! MEMFAULT_TRACE_REASON_DEFINE(bluetooth_cmd_buffer_full) //! MEMFAULT_TRACE_REASON_DEFINE(sensor_ack_timeout) //! //! @param reason The name of reason, without quotes. This gets surfaced in the Memfault UI, so //! it's useful to make these names human readable. C variable naming rules apply. //! @note reason must be unique #define MEMFAULT_TRACE_REASON_DEFINE(reason) kMfltTraceReasonUser_##reason, //! Uses a user-defined trace reason. Before you can use a user-defined trace reason, it should //! defined using MEMFAULT_TRACE_REASON_DEFINE in memfault_trace_reason_user_config.def //! @param reason The name of the reason, without quotes, as defined using //! MEMFAULT_TRACE_REASON_DEFINE. #define MEMFAULT_TRACE_REASON(reason) kMfltTraceReasonUser_##reason //! If trace events are not being used, including the user config file can be disabled //! by adding -DMEMFAULT_DISABLE_USER_TRACE_REASONS=1 to CFLAGs #if !defined(MEMFAULT_DISABLE_USER_TRACE_REASONS) #define MEMFAULT_DISABLE_USER_TRACE_REASONS 0 #endif /* MEMFAULT_DISABLE_USER_TRACE_REASONS */ //! For compilers which support the __has_include macro display a more friendly error message //! when the user defined header is not found on the include path //! //! NB: ARMCC and IAR define __has_include but they don't work as expected #if !MEMFAULT_DISABLE_USER_TRACE_REASONS #if !defined(__CC_ARM) && !defined(__ICCARM__) #if defined(__has_include) && !__has_include(MEMFAULT_TRACE_REASON_USER_DEFS_FILE) #pragma message("ERROR: " MEMFAULT_EXPAND_AND_QUOTE( \ MEMFAULT_TRACE_REASON_USER_DEFS_FILE) " must be in header search path") #error "See trace_reason_user.h for more details" #endif #endif #endif typedef enum MfltTraceReasonUser { MEMFAULT_TRACE_REASON_DEFINE(Unknown) #if (MEMFAULT_DISABLE_USER_TRACE_REASONS == 0) // Pick up any user specified trace reasons #include MEMFAULT_TRACE_REASON_USER_DEFS_FILE #endif // A precanned reason which is used by the "demo" component // (memfault_demo_cli_cmd_trace_event.c) and can be used for a user test command as well. MEMFAULT_TRACE_REASON_DEFINE(MemfaultCli_Test) kMfltTraceReasonUser_NumReasons, } eMfltTraceReasonUser; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/default_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Default configuration settings for the Memfault SDK //! This file should always be picked up through "memfault/config.h" //! and never included directly #ifdef __cplusplus extern "C" { #endif #ifndef MEMFAULT_PLATFORM_CONFIG_INCLUDE_DEFAULTS #error "Include "memfault/config.h" instead of "memfault/default_config.h" directly" #endif // // Core Components // //! Controls the use of the Gnu build ID feature. #ifndef MEMFAULT_USE_GNU_BUILD_ID #define MEMFAULT_USE_GNU_BUILD_ID 0 #endif //! Controls the name used to reference the GNU build ID data #ifndef MEMFAULT_GNU_BUILD_ID_SYMBOL #define MEMFAULT_GNU_BUILD_ID_SYMBOL __start_gnu_build_id_start #endif //! Allows users to dial in the correct amount of storage for their //! software version + build ID string. #ifndef MEMFAULT_UNIQUE_VERSION_MAX_LEN #define MEMFAULT_UNIQUE_VERSION_MAX_LEN 32 #endif //! While it is recommended that MEMFAULT_SDK_ASSERT be left enabled, they can //! be disabled by adding -DMEMFAULT_SDK_ASSERT_ENABLED=0 to the CFLAGS used to //! compile the SDK #ifndef MEMFAULT_SDK_ASSERT_ENABLED #define MEMFAULT_SDK_ASSERT_ENABLED 1 #endif //! Controls whether or not memfault_platform_halt_if_debugging() will be called //! at the beginning of an assert handler prior to trapping into the fault handler. //! //! This can make viewing the stack trace prior to exception easier when debugging locally #ifndef MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED #define MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED 0 #endif //! Install a low-level hook into the C stdlib assert() library call, to capture //! coredumps when asserts occur. #ifndef MEMFAULT_ASSERT_CSTDLIB_HOOK_ENABLED #define MEMFAULT_ASSERT_CSTDLIB_HOOK_ENABLED 1 #endif //! Controls whether or not device serial is encoded in messages. Applies to all //! Data Sources exported by the SDK: //! //! - Coredumps //! - Events (Reboots, Heartbeats, Trace Events) //! - Logs //! - CDR //! //! When disabled (default), the device serial is derived from the API route //! used to post data to the cloud #ifndef MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL #define MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL 0 #endif //! Controls whether or not the Build Id is encoded in events //! //! The Build Id can be used by Memfault to reliably find the corresponding //! symbol file to process the event. When disabled, the software version & type //! are used instead to find the symbol file. #ifndef MEMFAULT_EVENT_INCLUDE_BUILD_ID #define MEMFAULT_EVENT_INCLUDE_BUILD_ID 1 #endif //! Controls whether or not the Build Id is encoded in coredumps //! //! The Build Id can be used by Memfault to reliably find the corresponding //! symbol file while processing a coredump. When disabled, the software version & type //! are used instead to find the symbol file. #ifndef MEMFAULT_COREDUMP_INCLUDE_BUILD_ID #define MEMFAULT_COREDUMP_INCLUDE_BUILD_ID 1 #endif //! Some architectures will support multiple cores. This is used by the //! architecture-specific coredump handling code to determine how many //! register sets to save. #ifndef MEMFAULT_COREDUMP_CPU_COUNT #define MEMFAULT_COREDUMP_CPU_COUNT 1 #endif //! Controls the truncation of the Build Id that is encoded in events //! //! The full Build Id hash is 20 bytes, but is truncated by default to save space. The //! default truncation to 6 bytes (48 bits) has a 0.1% chance of collisions given //! 7.5 * 10 ^ 5 (750,000) Build Ids. //! See https://en.wikipedia.org/wiki/Birthday_problem#Probability_table #ifndef MEMFAULT_EVENT_INCLUDED_BUILD_ID_SIZE_BYTES #define MEMFAULT_EVENT_INCLUDED_BUILD_ID_SIZE_BYTES 6 #endif //! Controls whether or not run length encoding (RLE) is used when packetizing //! data //! //! Significantly reduces transmit size of data with repeated patterns such as //! coredumps at the cost of ~1kB of codespace #ifndef MEMFAULT_DATA_SOURCE_RLE_ENABLED #define MEMFAULT_DATA_SOURCE_RLE_ENABLED 1 #endif //! Controls default log level that will be saved to https://mflt.io/logging #ifndef MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL #define MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL kMemfaultPlatformLogLevel_Info #endif //! Controls whether or not to include the memfault_log_trigger_collection() API //! and the module that is responsible for sending collected logs. #ifndef MEMFAULT_LOG_DATA_SOURCE_ENABLED #define MEMFAULT_LOG_DATA_SOURCE_ENABLED 1 #endif //! Maximum length of a compact log export chunk. //! //! Controls the maximum chunk length when exporting compact logs with memfault_log_export. #ifndef MEMFAULT_LOG_EXPORT_CHUNK_MAX_LEN #define MEMFAULT_LOG_EXPORT_CHUNK_MAX_LEN 80 #endif //! Maximum length a log record can occupy //! //! Structs holding this log may be allocated on the stack so care should be taken //! to make the size is not to large to blow through the allocated space for the stack. #ifndef MEMFAULT_LOG_MAX_LINE_SAVE_LEN #define MEMFAULT_LOG_MAX_LINE_SAVE_LEN 128 #endif //! Control whether or automatic persisting of MEMFAULT_LOG_*'s is enabled #ifndef MEMFAULT_SDK_LOG_SAVE_DISABLE #define MEMFAULT_SDK_LOG_SAVE_DISABLE 0 #endif // Allows for the MEMFAULT_LOG APIs to be re-mapped to another macro rather // than the memfault_platform_log dependency function // // In this mode a user of the SDK will need to define the following macros: // MEMFAULT_LOG_DEBUG(...) // MEMFAULT_LOG_INFO(...) // MEMFAULT_LOG_WARN(...) // MEMFAULT_LOG_ERROR(...) // // And if the "demo" component is being used: // MEMFAULT_LOG_RAW(...) #ifndef MEMFAULT_PLATFORM_HAS_LOG_CONFIG #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 0 #endif //! Enables use of "compact" logs. //! //! Compact logs convert format strings to an integer index at compile time and serialize an "id" //! and format arguments on the device. The Memfault cloud will decode the log and format the full //! log greatly reducing the storage and overhead on the device side. #ifndef MEMFAULT_COMPACT_LOG_ENABLE #define MEMFAULT_COMPACT_LOG_ENABLE 0 #endif //! Enable log line timestamps, when memfault_platform_time_get_current() is //! available. Log timestamps are included in all log entries when platform //! time is enabled. #ifndef MEMFAULT_LOG_TIMESTAMPS_ENABLE #define MEMFAULT_LOG_TIMESTAMPS_ENABLE 1 #endif //! Enable save/restore of log state. This is useful for systems that //! enter a deep sleep state, and need to persist the log state for //! later restoration. #ifndef MEMFAULT_LOG_RESTORE_STATE #define MEMFAULT_LOG_RESTORE_STATE 0 #endif //! Controls whether or not multiple events will be batched into a single //! message when reading information via the event storage data source. //! //! This can be a useful strategy when trying to maximize the amount of data //! sent in a single transmission unit. //! //! To enable, you will need to update the compiler flags for your project, i.e //! CFLAGS += -DMEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED=1 #ifndef MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED #define MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED 0 #endif //! Enables support for non-volatile event storage. At run-time the non-volatile //! storage must be enabled before use. See nonvolatile_event_storage.h for //! details. #ifndef MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED #define MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED 0 #endif #if MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED != 0 //! When batching is enabled, controls the maximum amount of event data bytes //! that will be in a single message. //! //! By default, there is no limit. To set a limit you will need to update the //! compiler flags for your project the following would cap the data payload //! size at 1024 bytes //! CFLAGS += -DMEMFAULT_EVENT_STORAGE_READ_BATCHING_MAX_BYTES=1024 #ifndef MEMFAULT_EVENT_STORAGE_READ_BATCHING_MAX_BYTES #define MEMFAULT_EVENT_STORAGE_READ_BATCHING_MAX_BYTES UINT32_MAX #endif #endif /* MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED */ //! Include a stub, weakly defined implementation of //! memfault_platform_time_get_current(). Most build systems will be able to //! override the symbol with a custom implementation at link time, but some will //! need to exclude the weak definition entirely. #ifndef MEMFAULT_EVENT_STORAGE_STUB_SYSTEM_TIME #define MEMFAULT_EVENT_STORAGE_STUB_SYSTEM_TIME 1 #endif //! Enable save/restore of event storage state, similar to //! MEMFAULT_LOG_RESTORE_STATE. This is not necessary if non-volatile event //! storage is enabled, as the event storage data will be saved to non-volatile //! storage automatically. #ifndef MEMFAULT_EVENT_STORAGE_RESTORE_STATE #define MEMFAULT_EVENT_STORAGE_RESTORE_STATE 0 #endif //! The max size of a chunk. Should be a size suitable to write to transport //! data is being dumped over. #ifndef MEMFAULT_DATA_EXPORT_CHUNK_MAX_LEN #define MEMFAULT_DATA_EXPORT_CHUNK_MAX_LEN 80 #endif #ifndef MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS #define MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS 0 #endif //! Insert the Memfault Project Key in the message header. This will override //! the Project Key used in any requests sent to chunks.memfault.com . When //! enabled, a '#define MEMFAULT_PROJECT_KEY ""' must be //! provided in the project's memfault_platform_config.h . This is intended for //! systems where the Project Key in the data uploader cannot be modified. #ifndef MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY #define MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY 0 #endif // // Heap Statistics Configuration // //! When the FreeRTOS port is being used, controls whether or not heap //! allocation tracking is enabled. //! Note: When using this feature, MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE 0 //! is also required #ifndef MEMFAULT_FREERTOS_PORT_HEAP_STATS_ENABLE #define MEMFAULT_FREERTOS_PORT_HEAP_STATS_ENABLE 0 #endif //! Enable this flag to collect the heap stats information on coredump #ifndef MEMFAULT_COREDUMP_COLLECT_HEAP_STATS #define MEMFAULT_COREDUMP_COLLECT_HEAP_STATS 0 #endif //! Max number of recent outstanding heap allocations to track. //! oldest tracked allocations are expired (by allocation order) #ifndef MEMFAULT_HEAP_STATS_MAX_COUNT #define MEMFAULT_HEAP_STATS_MAX_COUNT 32 #endif //! Controls whether or not memfault_lock() will be used in the heap stats module. If the //! allocation implementation in use already enables locks of it's own (i.e FreeRTOS heap_*.c //! implementations), the recommendation is to disable memfault locking #ifndef MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE #define MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE 1 #endif // // Task Watchdog Configuration // //! Use this flag to enable the task watchdog component. See the header file at //! components/include/memfault/core/task_watchdog.h for usage details. #ifndef MEMFAULT_TASK_WATCHDOG_ENABLE #define MEMFAULT_TASK_WATCHDOG_ENABLE 0 #endif //! Configure the task watchdog timeout value in milliseconds. #ifndef MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS #define MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS 1000 #endif //! Enable this flag to collect the task watchdog information on coredump. Only //! needed if the entire memory contents are not already collected on coredump. #ifndef MEMFAULT_COREDUMP_COLLECT_TASK_WATCHDOG_REGION #define MEMFAULT_COREDUMP_COLLECT_TASK_WATCHDOG_REGION 0 #endif // // Trace Configuration // //! Set the name for the user trace reason config file, which is where custom //! trace reasons can be defined #ifndef MEMFAULT_TRACE_REASON_USER_DEFS_FILE #define MEMFAULT_TRACE_REASON_USER_DEFS_FILE "memfault_trace_reason_user_config.def" #endif //! By default, the Memfault SDK allows for trace events with logs to be //! captured for trace events from ISRs. //! //! However, this can be disabled to reduce static RAM usage by //! MEMFAULT_TRACE_EVENT_MAX_LOG_LEN #ifndef MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED #define MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED 1 #endif //! The maximum size allocated for a trace event log #ifndef MEMFAULT_TRACE_EVENT_MAX_LOG_LEN #define MEMFAULT_TRACE_EVENT_MAX_LOG_LEN 80 #endif // // Custom Reboot Reason Configuration // //! Enables the use of custom reboot reasons #ifndef MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE #define MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE 0 #endif //! Defines the user file containing custom reboots #ifndef MEMFAULT_REBOOT_REASON_USER_DEFS_FILE #define MEMFAULT_REBOOT_REASON_USER_DEFS_FILE "memfault_reboot_reason_user_config.def" #endif // // Metrics Component Configurations // //! The frequency in seconds to collect heartbeat metrics. The suggested //! interval is once per hour but the value can be overridden to be as low as //! once every 15 minutes. #ifndef MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS #define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS 3600 #endif #ifndef MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #define MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE "memfault_metrics_heartbeat_config.def" #endif //! Enable the sync_successful metric API #ifndef MEMFAULT_METRICS_SYNC_SUCCESS #define MEMFAULT_METRICS_SYNC_SUCCESS 0 #endif //! Enable the sync_memfault_successful metric API #ifndef MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS #define MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS 0 #endif //! Enable the connectivity_connected_time_ms metric API #ifndef MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME #define MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME 0 #endif //! Enable the battery metrics API #ifndef MEMFAULT_METRICS_BATTERY_ENABLE #define MEMFAULT_METRICS_BATTERY_ENABLE 0 #endif //! Set a scale factor to be used with battery_soc_pct and battery_soc_pct_drop. On ingestion, //! Memfault will scale down the received integer value of these metrics by //! MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE. The default value is 1 (i.e. no change) #ifndef MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE #define MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE 1 #endif //! Enable tracking the number of log lines dropped in this interval #ifndef MEMFAULT_METRICS_LOGS_ENABLE #define MEMFAULT_METRICS_LOGS_ENABLE 1 #endif //! Enable built in uptime metric tracking #ifndef MEMFAULT_METRICS_UPTIME_ENABLE #define MEMFAULT_METRICS_UPTIME_ENABLE 1 #endif //! Disable Metrics Sessions at compile time. This saves a small amount of //! memory but prevents the use of Metrics Sessions. #ifndef MEMFAULT_METRICS_SESSIONS_ENABLED #define MEMFAULT_METRICS_SESSIONS_ENABLED 1 #endif //! Enable save/restore of metrics component state. This is useful for systems //! that enter a deep sleep state, and want to persist the metrics state //! across sleep cycles. #ifndef MEMFAULT_METRICS_RESTORE_STATE #define MEMFAULT_METRICS_RESTORE_STATE 0 #endif // // Panics Component Configs // // By default, enable collection of interrupt state at the time the coredump is // collected. User of the SDK can disable collection by adding the following // compile flag: -DMEMFAULT_COLLECT_INTERRUPT_STATE=0 // // NB: The default Interrupt collection configuration requires ~150 bytes of // coredump storage space to save. #ifndef MEMFAULT_COLLECT_INTERRUPT_STATE #define MEMFAULT_COLLECT_INTERRUPT_STATE 1 #endif // ARMv7-M supports at least 32 external interrupts in the NVIC and a maximum of // 496. // // By default, only collect the minimum set. For a typical application this will // generally cover all the interrupts in use. // // If there are more interrupts implemented than analyzed a note will appear in // the "ISR Analysis" tab for the Issue analyzed in the Memfault UI about the // appropriate setting needed. // // NB: For each additional 32 NVIC interrupts analyzed, 44 extra bytes are // needed for coredump storage. #ifndef MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT #define MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT 32 #endif // Enables the collection of fault registers information // // When fault registers are collected, an analysis will be presented on the // "issues" page of the Memfault UI. #ifndef MEMFAULT_COLLECT_FAULT_REGS #define MEMFAULT_COLLECT_FAULT_REGS 1 #endif // ARMv7-M can support an IMPLEMENTATION DEFINED number of MPU regions if the MPU // is implemented. If MPU_TYPE register is non-zero then the MPU is implemented and the // number of regions will be indicated in MPU_TYPE[DREGIONS] since the ARMv7-m // uses a unified MPU, e.g. MPU_TYPE[SEPARATE] = 0. // // By default, we disable MPU collection since it requires more RAM and not // every MCU has an MPU implemented or, if it is, may not be employed. Users // should set MEMFAULT_COLLECT_MPU_STATE and verify MEMFAULT_MPU_REGIONS_TO_COLLECT // matches their needs to collect the number of regions they actually configure // in the MPU. // // For ideas on how to make use of the MPU in your application check out // https://mflt.io/mpu-stack-overflow-debug #ifndef MEMFAULT_COLLECT_MPU_STATE // This controls allocations and code need to collect the MPU state. #define MEMFAULT_COLLECT_MPU_STATE 0 #endif #ifndef MEMFAULT_CACHE_FAULT_REGS // Controls whether memfault_coredump_get_arch_regions() will collect // the HW registers as-is insert a pre-cached memory copy of them. // This entails calling memfault_coredump_cache_fault_regs() before // the OS processes the fault. #define MEMFAULT_CACHE_FAULT_REGS 0 #endif #ifndef MEMFAULT_MPU_REGIONS_TO_COLLECT // This is used to define the sMfltMpuRegs structure. #define MEMFAULT_MPU_REGIONS_TO_COLLECT 8 #endif // By default, exception handlers use CMSIS naming conventions. By default, the // CMSIS library provides a weak implementation for each handler that is // implemented as an infinite loop. By using the same name, the Memfault SDK can // override this default behavior to capture crash information when a fault // handler runs. // // However, if needed, each handler can be renamed using the following // preprocessor defines: #ifndef MEMFAULT_EXC_HANDLER_HARD_FAULT #define MEMFAULT_EXC_HANDLER_HARD_FAULT HardFault_Handler #endif #ifndef MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT #define MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT MemManage_Handler #endif #ifndef MEMFAULT_EXC_HANDLER_BUS_FAULT #define MEMFAULT_EXC_HANDLER_BUS_FAULT BusFault_Handler #endif #ifndef MEMFAULT_EXC_HANDLER_USAGE_FAULT #define MEMFAULT_EXC_HANDLER_USAGE_FAULT UsageFault_Handler #endif #ifndef MEMFAULT_EXC_HANDLER_NMI #define MEMFAULT_EXC_HANDLER_NMI NMI_Handler #endif #ifndef MEMFAULT_EXC_HANDLER_WATCHDOG #define MEMFAULT_EXC_HANDLER_WATCHDOG MemfaultWatchdog_Handler #endif #ifndef MEMFAULT_PLATFORM_FAULT_HANDLER_CUSTOM #define MEMFAULT_PLATFORM_FAULT_HANDLER_CUSTOM 0 #endif // If enabled, memfault_fault_handler will return back to OS // exception handling code instead of rebooting the device. #ifndef MEMFAULT_FAULT_HANDLER_RETURN #define MEMFAULT_FAULT_HANDLER_RETURN 0 #endif // Enable this flag to return from the Watchdog ISR instead of rebooting the // device. Depending on the hardware, the platform may need to clear any // watchdog or timer interrupt flags in memfault_platform_fault_handler() // (or equivalent) to prevent the interrupt from immediately retriggering. #ifndef MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN #define MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN 0 #endif // // Http Configuration Options // #ifndef MEMFAULT_HTTP_CHUNKS_API_HOST #define MEMFAULT_HTTP_CHUNKS_API_HOST "chunks.memfault.com" #endif #ifndef MEMFAULT_HTTP_DEVICE_API_HOST #define MEMFAULT_HTTP_DEVICE_API_HOST "device.memfault.com" #endif #ifndef MEMFAULT_HTTP_APIS_DEFAULT_PORT #define MEMFAULT_HTTP_APIS_DEFAULT_PORT (443) #endif #ifndef MEMFAULT_HTTP_APIS_DEFAULT_SCHEME #if (MEMFAULT_HTTP_APIS_DEFAULT_PORT == 80) #define MEMFAULT_HTTP_APIS_DEFAULT_SCHEME "http" #else #define MEMFAULT_HTTP_APIS_DEFAULT_SCHEME "https" #endif #endif // // Util Configuration Options // // Set this to 0 to disable the built-in CRC16 implementation. The platform will // need to provide a compatible implementation of memfault_crc16_compute(), as // defined in memfault/util/crc16.h. #ifndef MEMFAULT_CRC16_BUILTIN #define MEMFAULT_CRC16_BUILTIN 1 #endif // Enables the use of a (512 bytes) lookup table for CRC16 computation to improve performance // // For extremely constrained environments where a small amount of data is being sent anyway the // lookup table can be disabled to save ~500 bytes of flash space #ifndef MEMFAULT_CRC16_LOOKUP_TABLE_ENABLE #define MEMFAULT_CRC16_LOOKUP_TABLE_ENABLE 1 #endif // // Demo Configuration Options // #ifndef MEMFAULT_CLI_LOG_BUFFER_MAX_SIZE_BYTES #define MEMFAULT_CLI_LOG_BUFFER_MAX_SIZE_BYTES (80) #endif #ifndef MEMFAULT_DEMO_CLI_USER_CHUNK_SIZE // Note: Arbitrary default size for CLI command. Can be as small as 9 bytes. #define MEMFAULT_DEMO_CLI_USER_CHUNK_SIZE 1024 #endif //! The maximum length supported for a single CLI command #ifndef MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE #define MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE 64 #endif //! Enable the Self Test command through the Demo CLI component //! //! Setting this config to 1 will add a new command to trigger self test functionality. //! The self test can be used to validate a Memfault integration #ifndef MEMFAULT_DEMO_CLI_SELF_TEST #define MEMFAULT_DEMO_CLI_SELF_TEST 0 #endif //! Enable the experimental coredump storage self test //! //! Setting this config will add a new self test to exercise the platform's coredump storage //! implementation. This test requires implementing `memfault_self_test_platform_disable_irqs` and //! `memfault_self_test_platform_enable_irqs` to run. Additionally, you should disable any //! watchdogs (hardware, or software) on your system to prevent interrupting or restarting //! your device while running this test. #ifndef MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE #define MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE 0 #endif //! Enable testing of the software watchdog through the Demo CLI component //! //! Setting this config to 1 will add new commands to enable, disable, and update the timeout //! of the software watchdog. To use this feature, a software watchdog implementation must be //! provided. See https://docs.memfault.com/docs/mcu/watchdogs for more details. #ifndef MEMFAULT_DEMO_CLI_WATCHDOG #define MEMFAULT_DEMO_CLI_WATCHDOG 0 #endif // // Custom Data Recording configuration options [EXPERIMENTAL] // //! Controls whether or not publishing Custom Data Recordings is enabled. //! For severely constrained environments, this option can be disabled to save //! a little flash/data space. #ifndef MEMFAULT_CDR_ENABLE #define MEMFAULT_CDR_ENABLE 0 #endif //! Controls the maximum number of Custom Data Recording sources a project can register. //! The overhead per allocation is 4 bytes (size of a pointer) #ifndef MEMFAULT_CDR_MAX_DATA_SOURCES #define MEMFAULT_CDR_MAX_DATA_SOURCES 4 #endif //! Controls the maximum space budgeted for a Custom Data Recording header. #ifndef MEMFAULT_CDR_MAX_ENCODED_METADATA_LEN #define MEMFAULT_CDR_MAX_ENCODED_METADATA_LEN 128 #endif // // Port Configuration Options // //! Controls whether or not MCU reboot info is printed when //! memfault_reboot_reason_get() is called #ifndef MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_ENABLE_REBOOT_DIAG_DUMP 1 #endif //! Controls whether or not MCU reboot info is reset when //! memfault_reboot_reason_get() is called #ifndef MEMFAULT_REBOOT_REASON_CLEAR #define MEMFAULT_REBOOT_REASON_CLEAR 1 #endif //! Timeout to set Software Watchdog expiration for #ifndef MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS #define MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS 10 #endif //! The maximum number of tasks which can be tracked by this subsystem at one //! time. If your system has more tasks than this, the number will need to be //! bumped in order for the stacks to be fully recovered #ifndef MEMFAULT_PLATFORM_MAX_TRACKED_TASKS #define MEMFAULT_PLATFORM_MAX_TRACKED_TASKS 16 #endif //! The default amount of stack for each task to collect in bytes. The larger //! the size, the more stack frames Memfault will be able to unwind when the //! coredump is uploaded. #ifndef MEMFAULT_PLATFORM_TASK_STACK_SIZE_TO_COLLECT #define MEMFAULT_PLATFORM_TASK_STACK_SIZE_TO_COLLECT 256 #endif //! The default amount of stack to collect for the stack that was active leading up to a crash #ifndef MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT #define MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT 512 #endif // // Controls whether RAM or FLASH coredump storage port is picked up. // #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH 0 #endif #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_RAM #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_RAM (!MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH) #endif #if MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH && MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_RAM #error "Configuration error. Only one of _USE_FLASH or _USE_RAM can be set!" #endif // // RAM backed coredump configuration options // //! When set to 1, end user must allocate and provide pointer to //! coredump storage noinit region and define MEMFAULT_PLATFORM_COREDUMP_RAM_START_ADDR //! in memfault_platform_config.h #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_CUSTOM #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_CUSTOM 0 #endif //! Controls the section name used for the noinit region a RAM backed coredump is saved to //! Some vendor SDKs have pre-defined no-init regions in which case this can be overridden #ifndef MEMFAULT_PLATFORM_COREDUMP_NOINIT_SECTION_NAME #define MEMFAULT_PLATFORM_COREDUMP_NOINIT_SECTION_NAME ".noinit.mflt_coredump" #endif //! Controls the size of the RAM region allocated for coredump storage #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE 1024 #endif //! The RAM port will by default collect the active stack at the time of crash. //! Alternatively, an end user can define custom regions by implementing their own //! version of memfault_platform_coredump_get_regions() #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_REGIONS_CUSTOM #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_REGIONS_CUSTOM 0 #endif //! Set the capture size for FreeRTOS TCB structures in the coredump, when //! memfault_freertos_ram_regions.c is used. //! //! Set this to 0 to collect the entire TCB structure. //! //! The default value captures the required structure fields in each TCB used //! for RTOS Awareness by the Memfault backend, but truncates unused fields- for //! example, if FreeRTOS is configured with configUSE_NEWLIB_REENTRANT, the TCBs //! contain extra fields that are not needed for thread decode and take up space //! in the coredump. //! //! See more details here: https://docs.memfault.com/docs/mcu/rtos-analysis //! //! And see ports/freertos/src/memfault_freertos_ram_regions.c for information //! on the implementation #ifndef MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE #define MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE 100 #endif // // Platform-specific metrics configuration options // //! Enable this flag to cause metrics_boot() to call a platform-implemented //! memfault_platform_metrics_connectivity_boot() function #ifndef MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT #define MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT 0 #endif #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/demo/cli.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief CLI console commands for Memfault demo apps //! //! The first element in argv is expected to be the command name that was invoked. //! The elements following after that are expected to be the arguments to the command. #include #ifdef __cplusplus extern "C" { #endif #include "memfault/core/compiler.h" //! Command to crash the device, for example, to trigger a coredump to be captured. //! It takes one number argument, which is the crash type: //! - 0: crash by MEMFAULT_ASSERT(0) //! - 1: crash by jumping to 0xbadcafe //! - 2: crash by unaligned memory store //! - 3: crash by MEMFAULT_ASSERT() deep in the callstack //! - 4: crash by MEMFAULT_SOFTWARE_WATCHDOG() int memfault_demo_cli_cmd_crash(int argc, char *argv[]); #if MEMFAULT_COMPILER_ARM_CORTEX_M //! Command which will generate a HardFault int memfault_demo_cli_cmd_hardfault(int argc, char *argv[]); //! Command which will generate a BusFault on Cortex-M hardware int memfault_demo_cli_cmd_busfault(int argc, char *argv[]); //! Command which will generate a Memory Management fault on Cortex-M hardware int memfault_demo_cli_cmd_memmanage(int argc, char *argv[]); //! Command which will generate a UsageFault on Cortex-M hardware int memfault_demo_cli_cmd_usagefault(int argc, char *argv[]); #endif // MEMFAULT_COMPILER_ARM_CORTEX_M //! Read a 32-bit memory address and print the value. Can be used to test //! specific faults due to protected regions int memfault_demo_cli_loadaddr(int argc, char *argv[]); #if MEMFAULT_COMPILER_ARM_V7_A_R //! Trigger a data abort on an ARMv7-A/R chip int memfault_demo_cli_cmd_dataabort(int argc, char *argv[]); //! Trigger a prefetch abort on an ARMv7-A/R chip int memfault_demo_cli_cmd_prefetchabort(int argc, char *argv[]); #endif // MEMFAULT_COMPILER_ARM_V7_A_R //! Command which will generate an assert int memfault_demo_cli_cmd_assert(int argc, char *argv[]); //! Command which will generate a libc assert() int memfault_demo_cli_cmd_cassert(int argc, char *argv[]); //! Command to exercise the MEMFAULT_TRACE_EVENT API, capturing a //! Trace Event with the error reason set to "MemfaultDemoCli_Error". int memfault_demo_cli_cmd_trace_event_capture(int argc, char *argv[]); //! Command to insert test logs into the RAM log buffer. One log for each log level //! is written. The command takes no arguments. //! @note By default, the minimum save level is >= Info. Use the //! memfault_log_set_min_save_level() API to control this. int memfault_demo_cli_cmd_test_log(int argc, char *argv[]); //! Command to trigger "freeze" the current contents of the RAM log buffer and //! allow them to be collected through the data transport (see memfault_demo_drain_chunk_data()). //! It takes no arguments. int memfault_demo_cli_cmd_trigger_logs(int argc, char *argv[]); //! Command to get whether a coredump is currently stored and how large it is. //! It takes no arguments. int memfault_demo_cli_cmd_get_core(int argc, char *argv[]); //! Command to post a coredump using the http client. //! It takes no arguments. int memfault_demo_cli_cmd_post_core(int argc, char *argv[]); //! Command to clear a coredump. //! It takes no arguments. int memfault_demo_cli_cmd_clear_core(int argc, char *argv[]); //! Print coredump size and storage capacity int memfault_demo_cli_cmd_coredump_size(int argc, char *argv[]); //! Command to print device info, as obtained through memfault_platform_get_device_info(). //! It takes no arguments. int memfault_demo_cli_cmd_get_device_info(int argc, char *argv[]); //! Reboots the system //! //! This command takes no arguments and demonstrates how to use the reboot tracking module to //! track the occurrence of intentional reboots. int memfault_demo_cli_cmd_system_reboot(int argc, char *argv[]); //! Drains _all_ queued up chunks by calling user_transport_send_chunk_data //! //! @note user_transport_send_chunk_data is defined as a weak function so it can be overridden. //! The default implementation is a no-op. //! @note When "memfault install_chunk_handler" has been run, this can be used as a way to post //! chunks to the Memfault cloud directly from GDB. See https://mflt.io/posting-chunks-with-gdb //! for more details int memfault_demo_drain_chunk_data(int argc, char *argv[]); void user_transport_send_chunk_data(void *chunk_data, size_t chunk_data_len); //! Output base64 encoded chunks. Chunks can be uploaded via the Memfault CLI or //! manually via the Chunks Debug in the UI. int memfault_demo_cli_cmd_export(int argc, char *argv[]); //! Print current heartbeat metrics int memfault_demo_cli_cmd_heartbeat_dump(int argc, char *argv[]); //! Trigger a heartbeat int memfault_demo_cli_cmd_heartbeat(int argc, char *argv[]); //! Run a self test //! //! This command triggers a test which exercises different subsystems int memfault_demo_cli_cmd_self_test(int argc, char *argv[]); //! Enable the software watchdog int memfault_demo_cli_cmd_software_watchdog_enable(int argc, char *argv[]); //! Disable the software watchdog int memfault_demo_cli_cmd_software_watchdog_disable(int argc, char *argv[]); //! Update the software watchdog timeout int memfault_demo_cli_cmd_software_watchdog_update_timeout(int argc, char *argv[]); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/demo/shell.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Minimal shell/console implementation for platforms that do not include one. //! NOTE: For simplicity, ANSI escape sequences are not dealt with! #ifdef __cplusplus extern "C" { #endif typedef struct MemfaultShellImpl { //! Function to call whenever a character needs to be sent out. int (*send_char)(char c); } sMemfaultShellImpl; //! Initializes the demo shell. To be called early at boot. void memfault_demo_shell_boot(const sMemfaultShellImpl *impl); //! Call this when a character is received. The character is processed synchronously. void memfault_demo_shell_receive_char(char c); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/demo/shell_commands.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Command definitions for the minimal shell/console implementation. #include #include "memfault/config.h" #ifdef __cplusplus extern "C" { #endif typedef struct MemfaultShellCommand { const char *command; int (*handler)(int argc, char *argv[]); const char *help; } sMemfaultShellCommand; extern const sMemfaultShellCommand *const g_memfault_shell_commands; extern const size_t g_memfault_num_shell_commands; int memfault_shell_help_handler(int argc, char *argv[]); #if defined(MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS) //! Set extension command list. //! //! @arg commands: Pointer to the command list to set //! @arg num_commands: Number of commands in the list. void memfault_shell_command_set_extensions(const sMemfaultShellCommand *const commands, size_t num_commands); #endif #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/demo/util.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Utilities used within the "demo" component. #ifdef __cplusplus extern "C" { #endif //! Returns the URL to the chunks endpoint //! //! @note A non-weak implementation is exposed in demo/src/http/memfault_demo_https.c and picked up //! automatically by the build system helpers when the "http" component is enabled. const char *memfault_demo_get_chunks_url(void); //! Returns the current Project Key //! //! @note A non-weak implementation is exposed in demo/src/http/memfault_demo_https.c and picked up //! automatically by the build system helpers when the "http" component is enabled. const char *memfault_demo_get_api_project_key(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/http/http_client.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! API when using the Memfault HTTP Client #include #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/platform/device_info.h" #define MEMFAULT_HTTP_URL_BUFFER_SIZE (128) #define MEMFAULT_HTTP_CHUNKS_API_PREFIX "/api/v0/" #define MEMFAULT_HTTP_CHUNKS_API_SUBPATH "chunks" #define MEMFAULT_HTTP_PROJECT_KEY_HEADER "Memfault-Project-Key" #ifdef __cplusplus extern "C" { #endif typedef struct { //! The API host to use, NULL to use the default host. const char *host; //! The TCP port to use or 0 to use the default port as defined by MEMFAULT_HTTP_CHUNKS_API_PORT. uint16_t port; } sMemfaultHttpApi; //! Configuration of the Memfault HTTP client. typedef struct MfltHttpClientConfig { //! The project key. This is a mandatory field. //! Go to app.memfault.com, then navigate to "Settings" to find your key. const char *api_key; //! When false, TLS/https will be used, otherwise plain text http will be used. bool disable_tls; //! Route used to send packetized data ("chunks") to the Memfault cloud for reassembly and //! processing. See https://mflt.io/data-to-cloud for more details. sMemfaultHttpApi chunks_api; //! Route used to get information from the Memfault cloud pertaining to a device in your fleet. //! For example, the latest firmware release available. sMemfaultHttpApi device_api; //! Callback for fetching device info, used when uploading chunks and //! performing OTA checks. Typically this callback can be left unset, but if //! the request is on behalf of a downstream device, can be substituted with //! the downstream device's information. The function signature is the same as //! memfault_platform_get_device_info(). If unset (the default), //! memfault_platform_get_device_info() will be used. void (*get_device_info)(sMemfaultDeviceInfo *info); } sMfltHttpClientConfig; //! Global configuration of the Memfault HTTP client. //! See @ref sMfltHttpClientConfig for information about each of the fields. extern sMfltHttpClientConfig g_mflt_http_client_config; //! If on a GNUC-compatible compiler, perform a type-compatibility check with //! the get_device_info callback. This will fail to compile if the callback //! signature does not match the expected signature. #if defined(__GNUC__) && !defined(__cplusplus) MEMFAULT_STATIC_ASSERT( __builtin_types_compatible_p(__typeof__(g_mflt_http_client_config.get_device_info), __typeof__(&memfault_platform_get_device_info)), "get_device_info callback signature does not match expected signature"); #endif //! Convenience macros to get the currently configured Chunks API hostname & Port #define MEMFAULT_HTTP_GET_CHUNKS_API_HOST() \ (g_mflt_http_client_config.chunks_api.host ? g_mflt_http_client_config.chunks_api.host : \ MEMFAULT_HTTP_CHUNKS_API_HOST) #define MEMFAULT_HTTP_GET_CHUNKS_API_PORT() \ (g_mflt_http_client_config.chunks_api.port ? g_mflt_http_client_config.chunks_api.port : \ MEMFAULT_HTTP_APIS_DEFAULT_PORT) //! Convenience macros to get the currently configured Device API hostname & Port #define MEMFAULT_HTTP_GET_DEVICE_API_HOST() \ (g_mflt_http_client_config.device_api.host ? g_mflt_http_client_config.device_api.host : \ MEMFAULT_HTTP_DEVICE_API_HOST) #define MEMFAULT_HTTP_GET_DEVICE_API_PORT() \ (g_mflt_http_client_config.device_api.port ? g_mflt_http_client_config.device_api.port : \ MEMFAULT_HTTP_APIS_DEFAULT_PORT) //! Returns the "scheme" part of the URI based on client configuration #define MEMFAULT_HTTP_GET_SCHEME() (g_mflt_http_client_config.disable_tls ? "http" : "https") //! Forward declaration of a HTTP client. typedef struct MfltHttpClient sMfltHttpClient; //! Writes a Memfault API URL for the specified subpath. //! @param url_buffer Buffer where the URL should be written. //! @param subpath Subpath to use. //! @return true if the buffer was filled, false otherwise bool memfault_http_build_url(char url_buffer[MEMFAULT_HTTP_URL_BUFFER_SIZE], const char *subpath); //! Creates a new HTTP client. A client can be reused across requests. //! This way, the cost of connection set up to the server will be shared with multiple requests. //! @return The newly created client or NULL in case of an error. //! @note Make sure to call memfault_platform_http_client_destroy() to close and clean up resources. sMfltHttpClient *memfault_http_client_create(void); typedef enum { kMfltPostDataStatus_Success = 0, kMfltPostDataStatus_NoDataFound = 1, } eMfltPostDataStatus; //! Posts Memfault data that is pending transmission to Memfault's services over HTTP. //! //! @return kMfltPostDataStatus_Success on success, kMfltPostDataStatus_NoDataFound //! if no data was found or else an error code. int memfault_http_client_post_data(sMfltHttpClient *client); //! Create a http client, post a chunk of data and then teardown the connection //! //! @return kMfltPostDataStatus_Success on success, kMfltPostDataStatus_NoDataFound //! if no data was found or else an error code. int memfault_http_client_post_chunk(void); //! Waits until pending requests have been completed. //! @param client The http client. //! @return 0 on success, else error code int memfault_http_client_wait_until_requests_completed(sMfltHttpClient *client, uint32_t timeout_ms); //! Destroys a HTTP client that was previously created using memfault_platform_http_client_create(). int memfault_http_client_destroy(sMfltHttpClient *client); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/http/platform/http_client.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Dependency functions required in order to send data directly to Memfault's //! servers via HTTPS, using memfault_http_client_post_data(). #include #include #include "memfault/http/http_client.h" #ifdef __cplusplus extern "C" { #endif //! Creates a new HTTP client. A client can be reused across requests. //! This way, the cost of connection set up to the server will be shared with multiple requests. //! //! @return The newly created client or NULL in case of an error. //! @note Make sure to call memfault_platform_http_client_destroy() to close and clean up resources. sMfltHttpClient *memfault_platform_http_client_create(void); //! Forward declaration of a HTTP response. typedef struct MfltHttpResponse sMfltHttpResponse; //! HTTP response callback function pointer type. //! @param response The response object or NULL if the request failed (i.e. due to a networking //! error, timeout, etc.). After returning from the callback, this pointer is no longer valid. //! @param ctx The user pointer that was passed when performing the request. typedef void (*MemfaultHttpClientResponseCallback)(const sMfltHttpResponse *response, void *ctx); //! Get the HTTP status code of a response object. //! @param response The response object. Guaranteed to be non-NULL. //! @param[out] status_out Pointer to variable to which to write the HTTP status code. //! @return 0 on success, else error code int memfault_platform_http_response_get_status(const sMfltHttpResponse *response, uint32_t *status_out); //! Posts data that is pending transmission to Memfault's services over HTTPS to the API path //! defined by MEMFAULT_HTTP_CHUNKS_API_SUBPATH. The implementation is expected to set the project //! key header (see MEMFAULT_HTTP_PROJECT_KEY_HEADER) as well as the "Content-Type: //! application/octet-stream" header. See https://docs.memfault.com/ for HTTP API documentation. //! HTTP redirects are expected to be handled by the implementation -- in other words, when //! receiving 3xx responses, be called should only be called after following the redirection(s). //! //! @param client The client to use to post the request. Guaranteed to be non-NULL. //! @param callback The callback to call with the response object. This callback MUST ALWAYS be //! called when this function returned kMfltPostDataStatus_Success! //! @param ctx Pointer to user data that is expected to be passed into the callback. //! @return 0 on success, otherwise an error code int memfault_platform_http_client_post_data(sMfltHttpClient *client, MemfaultHttpClientResponseCallback callback, void *ctx); //! Waits until pending requests have been completed. //! @param client The http client. Guaranteed to be non-NULL. //! @return 0 on success. int memfault_platform_http_client_wait_until_requests_completed(sMfltHttpClient *client, uint32_t timeout_ms); //! Destroys a HTTP client that was previously created using memfault_platform_http_client_create(). //! @param client The client to destroy. Guaranteed to be non-NULL. //! @return 0 iff the client was destroyed successfully. int memfault_platform_http_client_destroy(sMfltHttpClient *client); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/http/root_certs.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! All the Root Certs are provided below in the PEM & DER formats. Depending on the TLS stack //! being used a cert in one format or the other may be more favorable. For example, when using //! Mbed TLS, parsing certificates in DER format is _always_ enabled whereas PEM parsing is only //! available when MBEDTLS_PEM_PARSE_C has been added to the config. #include #include #ifdef __cplusplus extern "C" { #endif //! DigiCert Global Root CA. SHA-1/RSA-256. Valid until 2031-10-11. #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA \ "-----BEGIN CERTIFICATE-----\n" \ "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \ "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \ "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \ "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \ "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \ "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \ "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \ "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \ "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \ "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \ "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \ "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \ "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \ "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \ "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \ "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \ "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \ "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \ "-----END CERTIFICATE-----\n" //! DigiCert Global Root G2. SHA-1/RSA-256. Valid until 2038-01-15. #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2 \ "-----BEGIN CERTIFICATE-----\n" \ "MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\n" \ "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n" \ "MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\n" \ "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \ "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n" \ "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n" \ "2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n" \ "1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n" \ "q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\n" \ "tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\n" \ "vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\n" \ "BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n" \ "5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n" \ "1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\n" \ "NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\n" \ "Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n" \ "8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n" \ "pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\n" \ "MrY=\n" \ "-----END CERTIFICATE-----\n" //! Amazon Root CA 1. Valid until 2038-01-17. #define MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1 \ "-----BEGIN CERTIFICATE-----\n" \ "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" \ "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" \ "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" \ "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" \ "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" \ "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" \ "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" \ "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" \ "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" \ "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" \ "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" \ "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" \ "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" \ "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" \ "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" \ "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" \ "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" \ "rqXRfboQnoZsG4q5WTP468SQvvG5\n" \ "-----END CERTIFICATE-----\n" // In decreasing order of likelihood of success #define MEMFAULT_ROOT_CERTS_PEM \ MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2 \ MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1 \ MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA // The certs are also available in their binary DER format via extern const uint8_t g_memfault_cert_digicert_global_root_ca[]; extern const size_t g_memfault_cert_digicert_global_root_ca_len; extern const uint8_t g_memfault_cert_digicert_global_root_g2[]; extern const size_t g_memfault_cert_digicert_global_root_g2_len; extern const uint8_t g_memfault_cert_amazon_root_ca1[]; extern const size_t g_memfault_cert_amazon_root_ca1_len; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/http/utils.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A collection of HTTP utilities *solely* for interacting with the Memfault REST API including //! - An API for generating HTTP Requests to Memfault REST API //! - An API for incrementally parsing HTTP Response Data [1] //! //! @note The expectation is that a typical application making use of HTTP will have a HTTP client //! and parsing implementation already available to leverage. This module is provided as a //! reference and convenience implementation for very minimal environments. //! //! [1]: For more info on embedded-C HTTP parsing options, the following commit //! in the Zephyr RTOS is a good starting point: https://mflt.io/35RWWwp #include #include #include #include "memfault/core/platform/device_info.h" #ifdef __cplusplus extern "C" { #endif //! Writer invoked by calls to "memfault_http_start_chunk_post" //! //! For example, this would be where a user of the API would make a call to send() to push data //! over a socket typedef bool (*MfltHttpClientSendCb)(const void *data, size_t data_len, void *ctx); //! Builds the HTTP 'Request-Line' and Headers for a POST to the Memfault Chunk Endpoint //! //! @note Upon completion of this call, a caller then needs to send the raw data received from //! memfault_packetizer_get_next() out over the active connection. //! //! @param callback The callback invoked to send post request data. //! @param ctx A user specific context that gets passed to 'callback' invocations. //! @param content_body_length The length of the chunk payload to be sent. This value //! will be populated in the HTTP "Content-Length" header. //! //! @return true if the post was successful, false otherwise bool memfault_http_start_chunk_post(MfltHttpClientSendCb callback, void *ctx, size_t content_body_length); //! Builds the HTTP GET request to query the Memfault cloud to see if a new OTA Payload is available //! //! For more details about release management and OTA payloads in general, check out: //! https://mflt.io/release-mgmt //! //! @param callback The callback invoked to send post request data. //! @param ctx A user specific context that gets passed to 'callback' invocations. //! //! @return true if sending the request was successful, false otherwise. On success, //! the request response body can be read where the following HTTP Status Codes //! are expected: //! 200: A new firmware (OTA Payload) is available and the response contains a url for it //! 204: No new firmware is available //! 4xx, 5xx: Error bool memfault_http_get_latest_ota_payload_url(MfltHttpClientSendCb write_callback, void *ctx); //! Builds the HTTP GET request to download a Firmware OTA Payload //! //! @param callback The callback invoked to send post request data. //! @param ctx A user specific context that gets passed to 'callback' invocations. //! @param url The ota payload url returned in the body of the HTTP request constructed with //! memfault_http_get_latest_ota_payload_url(). //! @param url_len The string length of the url param //! //! @return true if sending the request was successful, false otherwise. On success, //! the request response body can be read where the Content-Length will contain the size //! of the OTA payload and the message-body will be the OTA Payload that was uploaded via //! the Memfault UI bool memfault_http_get_ota_payload(MfltHttpClientSendCb write_callback, void *ctx, const char *url, size_t url_len); typedef enum MfltHttpParseStatus { kMfltHttpParseStatus_Ok = 0, MfltHttpParseStatus_ParseStatusLineError, MfltHttpParseStatus_ParseHeaderError, MfltHttpParseStatus_HeaderTooLongError, } eMfltHttpParseStatus; typedef enum MfltHttpParsePhase { kMfltHttpParsePhase_ExpectingStatusLine = 0, kMfltHttpParsePhase_ExpectingHeader, kMfltHttpParsePhase_ExpectingBody, } eMfltHttpParsePhase; typedef struct { //! true if a error occurred trying to parse the response eMfltHttpParseStatus parse_error; //! populated with the status code returned as part of the response int http_status_code; //! Pointer to http_body, may be truncated but always NULL terminated. //! This should only be used for debug purposes const char *http_body; //! The number of bytes processed by the last invocation of //! "memfault_http_parse_response" or "memfault_http_parse_response_header" int data_bytes_processed; //! Populated with the Content-Length found in the HTTP Response Header //! Valid upon parsing completion if no parse_error was returned int content_length; // For internal use only eMfltHttpParsePhase phase; int content_received; size_t line_len; char line_buf[128]; } sMemfaultHttpResponseContext; //! A *minimal* HTTP response parser for Memfault API calls //! //! @param ctx The context to be used while a parsing is in progress. It's //! expected that when a user first calls this function the context will be //! zero initialized. //! @param data The data to parse //! @param data_len The length of the data to parse //! @return True if parsing completed or false if more data is needed for the response //! Upon completion the 'parse_error' & 'http_status_code' fields can be checked //! within the 'ctx' for the results bool memfault_http_parse_response(sMemfaultHttpResponseContext *ctx, const void *data, size_t data_len); //! Same as memfault_http_parse_response but only parses the response header //! //! This API can be useful when the message body contains information further action //! is taken on (i.e an OTA Payload) //! //! @note Specifically, all data up to the "message-body" is consumed by the parser //! Response = Status-Line //! *(( general-header //! | response-header //! | entity-header ) CRLF) //! CRLF //! [ message-body ] ; **NOT Consumed** bool memfault_http_parse_response_header(sMemfaultHttpResponseContext *ctx, const void *data, size_t data_len); typedef enum { kMemfaultUriScheme_Unrecognized = 0, kMemfaultUriScheme_Http, kMemfaultUriScheme_Https, } eMemfaultUriScheme; typedef struct { //! Protocol detected (Either HTTPS or HTTP) eMemfaultUriScheme scheme; //! The 'host' component of the uri: //! https://tools.ietf.org/html/rfc3986#section-3.2.2 //! @note: NOT nul-terminated const void *host; size_t host_len; //! Port to use for connection //! @note if no port is specified in URI, default for scheme will be //! populated (i.e 80 for http & 443 for https) uint32_t port; //! The 'path' component of the uri: //! https://tools.ietf.org/html/rfc3986#section-3.3 //! @note: Path will be NULL when empty in URI. Like host, when //! path is populated, it is not terminated with a nul character. const void *path; size_t path_len; } sMemfaultUriInfo; //! A *minimal* parser for HTTP/HTTPS URIs //! //! @note API performs no copies or mallocs. Instead uri_info_out contains //! pointers back to the original uri provided and lengths of fields parsed. //! //! @note For more details about URIs in general, check out the RFC //! https://tools.ietf.org/html/rfc3986#section-1.1.3 //! @note this function parses URIs with 'scheme's of "http" or "https" //! URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] //! //! @param uri The URI to try and parse. //! @param uri_len The string length of the uri //! @param[out] uri_info_out On successful parse, populated with various pieces of information //! See 'sMemfaultUriInfo' for more details. //! //! @return true if parse was successful and uri_info_out is populated, false otherwise bool memfault_http_parse_uri(const char *uri, size_t uri_len, sMemfaultUriInfo *uri_info_out); //! Check if a string contains any characters that require URL escaping //! //! @param str the string to check //! @param len length of str //! //! @return true if any characters in str require URL escaping, false otherwise bool memfault_http_needs_escape(const char *str, size_t len); //! URL encode a string. See https://www.ietf.org/rfc/rfc3986.html#section-2.1 //! //! @param inbuf String to encode; must be null-terminated //! @param[out] outbuf Encoded string. Should be sized to fit possible encoding //! overhead, eg 3 * strlen(inbuf) //! @param outbuf_len Size of outbuf //! //! @return 0 if encoding was successful, non zero otherwise int memfault_http_urlencode(const char *inbuf, size_t inbuf_len, char *outbuf, size_t outbuf_len); //! Read the device information for building an HTTP request. This uses the //! callback installed into g_mflt_http_client_config.get_device_info if //! available, or falls back onto the default platform implementation otherwise. //! //! @param[out] info The device information to populate void memfault_http_get_device_info(sMemfaultDeviceInfo *info); //! Builds the URL for querying the Memfault latest OTA release endpoint //! //! The URL will be in the format: //! https:///api/v0/releases/latest/url?device_serial=<>&hardware_version=<>&software_type=<>¤t_version=<> //! //! @param buf The buffer to write the URL into //! @param buf_len The size of the buffer //! //! @return true if the URL was successfully written to the buffer, false if the buffer was too //! small or an encoding error occurred bool memfault_http_build_latest_ota_url(char *buf, size_t buf_len); //! Builds the URL for posting chunks to the Memfault chunks endpoint //! //! The URL will be in the format: //! https:///api/v0/chunks/ //! //! @param buf The buffer to write the URL into //! @param buf_len The size of the buffer //! //! @return true if the URL was successfully written to the buffer, false if the buffer was too //! small bool memfault_http_build_chunk_post_url(char *buf, size_t buf_len); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/battery.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Memfault built-in battery metrics. #ifdef __cplusplus extern "C" { #endif //! This function must be called when the battery stops discharging, eg: //! //! 1. charge mode is entered //! 2. battery is disconnected //! //! @note This function must be called for the system to correctly report soc //! drop and discharging time only during discharging. If it's not called //! correctly, the metric data will be invalid. void memfault_metrics_battery_stopped_discharging(void); //! Record battery metric values. This is normally called automatically during //! the end of heartbeat timer callback, and should not be called by the user. void memfault_metrics_battery_collect_data(void); //! Initialize the battery metrics module. Call exactly once on boot, after the //! device's battery subsystem is initialized: the battery platform dependency //! functions need to be valid when this function is called. void memfault_metrics_battery_boot(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/connectivity.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief Connectivity metrics implementation. //! //! This module contains the implementation of the built in connectivity metrics //! API. These metric pairs are available for use: //! //! - sync_successful/sync_failure: These metrics are used to count "sync" //! operation successes and failures, which Memfault will use to report sync //! success rate. A "sync" has an implementation-specific meaning. //! //! - sync_memfault_successful/sync_memfault_failure: These metrics are used to //! track syncs of data to Memfault. Platform ports like Zephyr and ESP-IDF //! may implement this metric by default. //! //! - connectivity_connected_time_ms/expected_connected_time_ms: These metrics //! are used for tracking "connection uptime" rate, for devices that are //! expected to have a continuous connection. "Connection" is device-specific. //! //! All metrics are implemented as heartbeat metrics, and are opt-in (disabled //! by default). Use the following flags in memfault_platform_config.h to enable //! the metrics: //! //! - MEMFAULT_METRICS_SYNC_SUCCESS //! - MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS //! - MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME #include "memfault/config.h" #ifdef __cplusplus extern "C" { #endif #if MEMFAULT_METRICS_SYNC_SUCCESS //! Record a successful or failed sync event. //! //! This API can be used to count "sync" successes and failures. A "sync" has an //! implementation-specific meaning. Some examples of sync events: //! //! - transferring data over BLE //! - uploading data over MQTT //! - sending data over a cellular modem //! //! Using this standard metric will enable some automatic "sync reliability" //! analysis by the Memfault backend. void memfault_metrics_connectivity_record_sync_success(void); void memfault_metrics_connectivity_record_sync_failure(void); #endif // MEMFAULT_METRICS_SYNC_SUCCESS #if MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS //! Record a successful or failed Memfault sync event. //! //! This metric measures a specific sync type, "Memfault syncs", which are //! uploads of data to Memfault itself. void memfault_metrics_connectivity_record_memfault_sync_success(void); void memfault_metrics_connectivity_record_memfault_sync_failure(void); #endif // MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS #if MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME //! Connectivity states: //! | State | Should be connected? | Is connected? | //! |--------------|----------------------|---------------| //! | Stopped | No | No | //! | Started | Yes | No | //! | Connected | Yes | Yes | //! | Disconnected | Yes | No | typedef enum { kMemfaultMetricsConnectivityState_Stopped, kMemfaultMetricsConnectivityState_Started, kMemfaultMetricsConnectivityState_Connected, kMemfaultMetricsConnectivityState_ConnectionLost, kMemfaultMetricsConnectivityState_NumStates, } eMemfaultMetricsConnectivityState; //! Signal a connectivity state change event. For a device that should be always //! connected, this would be a typical example state sequence: //! //! - Started (device boots, and starts attempting to connect) //! - Connected (device connects) //! - Connection Lost (some problem, eg wifi AP out of range; device disconnects) //! - Connected (device reconnects) //! //! This API is thread-safe for supported platforms, but is not interrupt-safe: //! it uses the Memfault Metrics Timer API, which is not interrupt-safe on most //! platforms. //! //! @param state The new connectivity state. void memfault_metrics_connectivity_connected_state_change(eMemfaultMetricsConnectivityState state); #endif // MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/heartbeat_config.def ================================================ //! The Memfault SDK tracks a few select heartbeat metrics by default to compute //! default health statistics for the fleet. All other metrics are provided by the user //! of the sdk via the MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE file #include "memfault/config.h" // The time since the last heartbeat was collected in ms MEMFAULT_METRICS_KEY_DEFINE(MemfaultSdkMetric_IntervalMs, kMemfaultMetricType_Timer) // The number of reboots that have taken place since the last heartbeat was collected MEMFAULT_METRICS_KEY_DEFINE(MemfaultSdkMetric_UnexpectedRebootCount, kMemfaultMetricType_Unsigned) #if MEMFAULT_METRICS_SYNC_SUCCESS MEMFAULT_METRICS_KEY_DEFINE(sync_successful, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(sync_failure, kMemfaultMetricType_Unsigned) #endif #if MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS MEMFAULT_METRICS_KEY_DEFINE(sync_memfault_successful, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(sync_memfault_failure, kMemfaultMetricType_Unsigned) #endif #if MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME MEMFAULT_METRICS_KEY_DEFINE(connectivity_connected_time_ms, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(connectivity_expected_time_ms, kMemfaultMetricType_Timer) #endif // Operational hours accumulated since the last heartbeat MEMFAULT_METRICS_KEY_DEFINE(operational_hours, kMemfaultMetricType_Unsigned) // Operational hours without a crash since the last heartbeat MEMFAULT_METRICS_KEY_DEFINE(operational_crashfree_hours, kMemfaultMetricType_Unsigned) #if MEMFAULT_METRICS_BATTERY_ENABLE // State of Charge drop since the last heartbeat was collected, use scale factor for non 0-100% representations MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(battery_soc_pct_drop, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE) MEMFAULT_METRICS_KEY_DEFINE(battery_discharge_duration_ms, kMemfaultMetricType_Unsigned) // Intentionally leave the range unset for this metric. Some platforms will // require a 0-100% range, some will require 0-1000 (0-100.0%), etc. Use scale factor for non 0-100% representations MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(battery_soc_pct, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE) #endif #if MEMFAULT_METRICS_LOGS_ENABLE MEMFAULT_METRICS_KEY_DEFINE(MemfaultSDKMetric_log_dropped_lines, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(MemfaultSDKMetric_log_recorded_lines, kMemfaultMetricType_Unsigned) #endif #if MEMFAULT_METRICS_UPTIME_ENABLE MEMFAULT_METRICS_KEY_DEFINE(uptime_s, kMemfaultMetricType_Unsigned) #endif ================================================ FILE: components/include/memfault/metrics/ids_impl.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! NOTE: The internals of the metric APIs make use of "X-Macros" to enable more flexibility //! improving and extending the internal implementation without impacting the externally facing API #ifdef __cplusplus extern "C" { #endif #include #include "memfault/config.h" // Clear any potential issues from transitive dependencies in these files by // including them one time, with stubs for the macros we need to define. This // set up any multiple-include guards, and we can safely include the x-macro // definitions later. #define MEMFAULT_METRICS_KEY_DEFINE(...) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(...) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(...) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(...) #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(...) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(...) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(...) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(...) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(...) #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE #define MEMFAULT_METRICS_KEY_DEFINE_(session_name, key_name) \ kMfltMetricsIndex_##session_name##__##key_name, //! Generate an enum for all IDs (used for indexing into values) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, _) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_name) \ MEMFAULT_METRICS_KEY_DEFINE_(session_name, key_name) //! Sessions have the following built-in keys: //! - "__MemfaultSdkMetric_IntervalMs" //! - "__operational_crashes" #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(session_name) \ kMfltMetricsIndex_##session_name##__##MemfaultSdkMetric_IntervalMs, \ kMfltMetricsIndex_##session_name##__##operational_crashes, #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_name) \ MEMFAULT_METRICS_KEY_DEFINE_(session_name, key_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_name) \ MEMFAULT_METRICS_KEY_DEFINE_(session_name, key_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE_(session_key, key_name) #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) \ MEMFAULT_METRICS_KEY_DEFINE_(heartbeat, key_name) typedef enum MfltMetricsIndex { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE kMfltMetricsIndexV2_COUNT, } eMfltMetricsIndexV2; typedef eMfltMetricsIndexV2 eMfltMetricsIndex; //! Count the number of user-defined timer metrics, for computing context size //! lower down. #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_ #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Unsigned(_name) #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Signed(_name) #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Timer(_name) +1 #define MEMFAULT_METRICS_KEY_DEFINE(_name, _type) MEMFAULT_METRICS_STATE_HELPER_##_type(_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(_name, _type, _session_name) \ MEMFAULT_METRICS_STATE_HELPER_##_type(_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(_name, _type, _session_name, \ _scale_value) \ MEMFAULT_METRICS_STATE_HELPER_##_type(_name) extern uint8_t g_memfault_metrics_timer_count_unused[0 #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE ]; #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) kMfltMetricsSessionKey_##key_name, #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) typedef enum MfltMetricSessionIndex { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE MEMFAULT_METRICS_SESSION_KEY_DEFINE(heartbeat) #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE kMfltMetricsSessionKey_COUNT, } eMfltMetricsSessionIndex; //! Compute the total size of string key metric storage, by tallying up the //! individual string metric sizes. Use a uint8_t array to compute the size, //! because we can't x-macro within a #define. Set it to 1 byte to account for //! the placeholder byte. #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) +(max_length + 1) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) \ +(max_length + 1) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) extern uint8_t g_memfault_metrics_string_storage[1 #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE ]; // MEMFAULT_IS_SET_FLAGS_PER_BYTE must be a power of 2 // MEMFAULT_IS_SET_FLAGS_DIVIDER must be equal to log2(MEMFAULT_IS_SET_FLAGS_PER_BYTE) #define MEMFAULT_IS_SET_FLAGS_PER_BYTE 8 #define MEMFAULT_IS_SET_FLAGS_DIVIDER 3 #if MEMFAULT_METRICS_LOGS_ENABLE #define MEMFAULT_METRICS_CONTEXT_LOG_SIZE_BYTES \ /* last log counts */ \ 2 * sizeof(uint32_t) #else #define MEMFAULT_METRICS_CONTEXT_LOG_SIZE_BYTES 0 #endif //! Compute the amount of space needed to store the metrics context structure, //! used for backing up across deep sleep. Tally each structure member size //! and add them up. //! 1. Sessions: each session implements: //! - a start callback //! - an end callback //! - a timer + operational crashes metric (these are counted in the metrics count later) //! 2. Metrics: each metric needs: //! - space for its current value //! - a bit in the "is set" bitmap (needs to include padding for alignment) //! 3. Timer metrics: need an additional metadata storage //! - 4 bytes //! 4. String metrics: need additional storage for the string value //! - user-specified size for each metric //! - padding for alignment //! 5. 2 * 4 byte last log counts for log metrics (optional) // Intentionally not using MEMFAULT_MAX()/MEMFAULT_CEIL_DIV() here, to avoid // bringing in extra includes #define MEMFAULT_METRICS_MAX(a, b) ((a) > (b) ? (a) : (b)) #define MEMFAULT_METRICS_CEIL_DIV(a, b) (((a) + (b) - 1) / (b)) #define MEMFAULT_METRICS_CONTEXT_METRIC_SIZE MEMFAULT_METRICS_MAX(sizeof(uint32_t), sizeof(void *)) #if (INTPTR_MAX == 0x7fffffff) #define MEMFAULT_METRICS_CONTEXT_PAD_SIZE 0 #elif (INTPTR_MAX == 0x7fffffffffffffff) #define MEMFAULT_METRICS_CONTEXT_PAD_SIZE 4 #endif // clang-format off #define MEMFAULT_METRICS_CONTEXT_SIZE_BYTES \ /* context pointer */ \ sizeof(void *) + \ /* reliability context */ \ sizeof(uint32_t) * 2 + sizeof(bool) + /* padding */ 4 - sizeof(bool) + MEMFAULT_METRICS_CONTEXT_PAD_SIZE + \ /* session start and end callbacks */ \ kMfltMetricsSessionKey_COUNT * sizeof(MemfaultMetricsSessionStartCb) + \ kMfltMetricsSessionKey_COUNT * sizeof(MemfaultMetricsSessionEndCb) + \ /* all the heartbeat metric values */ \ kMfltMetricsIndexV2_COUNT * MEMFAULT_METRICS_CONTEXT_METRIC_SIZE + \ /* the "is set" bitmask */ \ MEMFAULT_METRICS_CEIL_DIV( \ kMfltMetricsIndexV2_COUNT, MEMFAULT_IS_SET_FLAGS_PER_BYTE) + \ /* padding of "is set" for 4-byte alignment */ \ (sizeof(uint32_t) - (MEMFAULT_METRICS_CEIL_DIV( \ kMfltMetricsIndexV2_COUNT, MEMFAULT_IS_SET_FLAGS_PER_BYTE) % sizeof(uint32_t))) % sizeof(uint32_t) + \ /* timer metrics metadata. include session count -1, since heartbeat is built-in */ \ (sizeof(g_memfault_metrics_timer_count_unused) + kMfltMetricsSessionKey_COUNT - 1) * \ sizeof(uint32_t) + \ /* string metrics storage */ \ sizeof(g_memfault_metrics_string_storage) + \ /* padding for alignment */ \ (sizeof(uint32_t) - ((sizeof(g_memfault_metrics_string_storage)) % sizeof(uint32_t))) % sizeof(uint32_t) + \ /* log metrics */ \ MEMFAULT_METRICS_CONTEXT_LOG_SIZE_BYTES // clang-format on //! Stub define to detect accidental usage outside of the heartbeat config files #define MEMFAULT_METRICS_KEY_DEFINE_TRAP_() \ MEMFAULT_STATIC_ASSERT(false, "MEMFAULT_METRICS_KEY_DEFINE should only be used " \ "in " MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE); //! NOTE: Access to a key should _always_ be made via the MEMFAULT_METRICS_KEY() macro to ensure //! source code compatibility with future APIs updates //! //! The struct wrapper does not have any function, except for preventing one from passing a C //! string to the API: typedef struct { int _impl; } MemfaultMetricId; #define MEMFAULT_METRICS_SESSION_KEY(key_name) kMfltMetricsSessionKey_##key_name #define _MEMFAULT_METRICS_ID_CREATE(id, session_name) { kMfltMetricsIndex_##session_name##__##id } #define _MEMFAULT_METRICS_ID(id, session_name) \ ((MemfaultMetricId){ kMfltMetricsIndex_##session_name##__##id }) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/metrics.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! The Memfault metric events API //! //! This APIs allows one to collect periodic events known as heartbeats for visualization in the //! Memfault web UI. Heartbeats are a great way to inspect the overall health of devices in your //! fleet. //! //! Typically, two types of information are collected: //! 1) values taken at the end of the interval (i.e battery life, heap high water mark, stack high //! water mark) //! 2) changes over the hour (i.e the percent battery drop, the number of bytes sent out over //! bluetooth, the time the mcu was running or in stop mode) //! //! From the Memfault web UI, you can view all of these metrics plotted for an individual device & //! configure alerts to fire when values are not within an expected range. //! //! For a step-by-step walk-through about how to integrate the Metrics component into your system, //! check out https://mflt.io/2D8TRLX //! //! For more details of the overall design and how serialization compression works check out: //! https://mflt.io/fw-event-serialization #include #include #include "memfault/config.h" #include "memfault/core/event_storage.h" #include "memfault/metrics/ids_impl.h" #ifdef __cplusplus extern "C" { #endif #include "memfault/core/compiler.h" //! Type of a metric value typedef enum MemfaultMetricValueType { //! unsigned integer (max. 32-bits) kMemfaultMetricType_Unsigned = 0, //! signed integer (max. 32-bits) kMemfaultMetricType_Signed, //! Tracks durations (i.e the time a certain task is running, or the time a MCU is in sleep mode) kMemfaultMetricType_Timer, //! Set a string value for a metric kMemfaultMetricType_String, //! Number of valid types. Must _always_ be last kMemfaultMetricType_NumTypes, } eMemfaultMetricType; //! Defines a key/value pair used for generating Memfault events. //! //! This define should _only_ be used for defining events in 'memfault_metric_heartbeat_config.def' //! i.e, the *.def file for a heartbeat which tracks battery level & temperature would look //! something like: //! //! // memfault_metrics_heartbeat_config.def //! MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(battery_level, kMemfaultMetricType_Unsigned, 0, 100) //! MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(ambient_temperature_celsius, kMemfaultMetricType_Signed, //! -40, 40) //! //! @param key_name The name of the key, without quotes. This gets surfaced in the Memfault UI, so //! it's useful to make these names human readable. C variable naming rules apply. //! @param value_type The eMemfaultMetricType type of the key //! @param min_value A hint as to what the expected minimum value of the metric will be //! @param max_value A hint as to what the expected maximum value of the metric will be //! //! @note min_value & max_value are used to define an expected range for a given metric. //! This information is used in the Memfault cloud to normalize the data to a range of your //! choosing. Metrics will still be ingested _even_ if they are outside the range defined. //! //! @note The definitions here are used to catch accidental usage outside of the //! '*_heartbeat_config.def' files; 'MEMFAULT_METRICS_KEY_DEFINE_TRAP_' is a placeholder used to //! detect //! this. //! //! @note key_names must be unique #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) \ MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Same as 'MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE' just with no range hints specified #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Declare a string metric for use in a heartbeat. 'max_length' is the maximum //! length of the string recorded to the metric (excluding null terminator). //! This declaration also reserves space to hold the string value when the //! metric is written. #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Declare a string metric for use in a session. See`MEMFAULT_METRICS_STRING_KEY_DEFINE` for //! details. #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Declare a metric which is tracked in a metric session. #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Same as 'MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION', with range hints specified #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Same as 'MEMFAULT_METRICS_KEY_DEFINE`, with scale value specified //! //! A scale value is used to scale down integer metric types. When a scale value is defined for a //! metric key, On ingestion, Memfault will apply this transformation to the metric value: //! transformed_metric_value = metric_value / scale_value //! Use this value to scale integer values down, like converting a permyriad value into percent #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Same as 'MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE', with session specified #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Define a metric session. //! //! A metric session is analogous to a heartbeat, but you can use it to track metrics //! across an arbitrary event. For example, if you wanted to track battery life over the course of //! an LTE session, you could start a session when the LTE modem is powered on and stop the session //! when the modem is powered off. #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) MEMFAULT_METRICS_KEY_DEFINE_TRAP_() //! Use a Heartbeat metric key. Before you can use a key, it should defined using //! MEMFAULT_METRICS_KEY_DEFINE in memfault_metrics_heartbeat_config.def. //! @param key_name The name of the key, without quotes, as defined using //! MEMFAULT_METRICS_KEY_DEFINE. #define MEMFAULT_METRICS_KEY(key_name) _MEMFAULT_METRICS_ID(key_name, heartbeat) //! Use a Session metric key. Before you can use a key, it should defined using //! MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION etc in memfault_metrics_heartbeat_config.def. //! @param key_name The name of the key, without quotes, as defined using //! MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION etc //! @param session_key The name of the session, without quotes, as defined using //! MEMFAULT_METRICS_SESSION_KEY_DEFINE #define MEMFAULT_METRICS_KEY_WITH_SESSION(key_name, session_key) \ _MEMFAULT_METRICS_ID(key_name, session_key) typedef struct MemfaultMetricsBootInfo { //! The number of times the system has rebooted unexpectedly since reporting the last heartbeat //! //! If you do not already have a system in place to track this, consider using the memfault //! reboot_tracking module (https://mflt.io/2QlOlgH). This info can be collected by //! passing the value returned from memfault_reboot_tracking_get_crash_count(). //! When using this API we recommend clearing the crash when the first heartbeat since boot //! is serialized by implementing memfault_metrics_heartbeat_collect_data() and calling //! memfault_reboot_tracking_reset_crash_count(). //! //! If any reboot is unexpected, initialization can also reduce to: //! sMemfaultMetricBootInfo info = { .unexpected_reboot_count = 1 }; uint32_t unexpected_reboot_count; } sMemfaultMetricBootInfo; //! Initializes the metric events API. //! All heartbeat values will be initialized to their reset values. //! Integer types will be reset to unset/null. //! Timer metrics will be reset to 0. //! String metrics will be reset to empty strings. //! @param storage_impl The storage location to serialize metrics out to //! @param boot_info Info added to metrics to facilitate computing aggregate statistics in //! the Memfault cloud //! @note Memfault will start collecting metrics once this function returns. //! @return 0 on success, else error code int memfault_metrics_boot(const sMemfaultEventStorageImpl *storage_impl, const sMemfaultMetricBootInfo *boot_info); //! Set the value of a signed integer metric. //! //! Integer metrics that are unset during a heartbeat interval //! are sent as null and dropped when received. //! @param key The key of the metric. @see MEMFAULT_METRICS_KEY //! @param value The new value to set for the metric //! @return 0 on success, else error code //! @note The metric must be of type kMemfaultMetricType_Signed int memfault_metrics_heartbeat_set_signed(MemfaultMetricId key, int32_t signed_value); //! Same as @memfault_metrics_heartbeat_set_signed except for a unsigned integer metric int memfault_metrics_heartbeat_set_unsigned(MemfaultMetricId key, uint32_t unsigned_value); //! Set the value of a string metric. //! @param key The key of the metric. @see MEMFAULT_METRICS_KEY //! @param value The new value to set for the metric //! @return 0 on success, else error code //! @note The metric must be of type kMemfaultMetricType_String int memfault_metrics_heartbeat_set_string(MemfaultMetricId key, const char *value); //! Used to start a "timer" metric //! //! Timer metrics can be useful for tracking durations of events which take place while the //! system is running. Some examples: //! - time a task was running //! - time spent in different power modes (i.e run, sleep, idle) //! - amount of time certain peripherals were running (i.e accel, bluetooth, wifi) //! //! @return 0 if starting the metric was successful, else error code. int memfault_metrics_heartbeat_timer_start(MemfaultMetricId key); //! Same as @memfault_metrics_heartbeat_start but *stops* the timer metric //! //! @return 0 if stopping the timer was successful, else error code int memfault_metrics_heartbeat_timer_stop(MemfaultMetricId key); //! Add the value to the current value of a metric. //! @param key The key of the metric. @see MEMFAULT_METRICS_KEY //! @param inc The amount to increment the metric by //! @return 0 on success, else error code //! @note The metric must be of type kMemfaultMetricType_Unsigned or kMemfaultMetricType_Signed int memfault_metrics_heartbeat_add(MemfaultMetricId key, int32_t amount); //! Alternate API that includes the 'MEMFAULT_METRICS_KEY()' expansion #define MEMFAULT_METRIC_SET_SIGNED(key_name, signed_value) \ memfault_metrics_heartbeat_set_signed(MEMFAULT_METRICS_KEY(key_name), (signed_value)) #define MEMFAULT_METRIC_SET_UNSIGNED(key_name, unsigned_value) \ memfault_metrics_heartbeat_set_unsigned(MEMFAULT_METRICS_KEY(key_name), (unsigned_value)) #define MEMFAULT_METRIC_SET_STRING(key_name, value) \ memfault_metrics_heartbeat_set_string(MEMFAULT_METRICS_KEY(key_name), (value)) #define MEMFAULT_METRIC_TIMER_START(key_name) \ memfault_metrics_heartbeat_timer_start(MEMFAULT_METRICS_KEY(key_name)) #define MEMFAULT_METRIC_TIMER_STOP(key_name) \ memfault_metrics_heartbeat_timer_stop(MEMFAULT_METRICS_KEY(key_name)) #define MEMFAULT_METRIC_ADD(key_name, amount) \ memfault_metrics_heartbeat_add(MEMFAULT_METRICS_KEY(key_name), (amount)) //! Alternate API for Session metrics #define MEMFAULT_METRIC_SESSION_SET_SIGNED(key_name, session_key, signed_value) \ memfault_metrics_heartbeat_set_signed(MEMFAULT_METRICS_KEY_WITH_SESSION(key_name, session_key), \ (signed_value)) #define MEMFAULT_METRIC_SESSION_SET_UNSIGNED(key_name, session_key, unsigned_value) \ memfault_metrics_heartbeat_set_unsigned( \ MEMFAULT_METRICS_KEY_WITH_SESSION(key_name, session_key), (unsigned_value)) #define MEMFAULT_METRIC_SESSION_SET_STRING(key_name, session_key, value) \ memfault_metrics_heartbeat_set_string(MEMFAULT_METRICS_KEY_WITH_SESSION(key_name, session_key), \ (value)) #define MEMFAULT_METRIC_SESSION_TIMER_START(key_name, session_key) \ memfault_metrics_heartbeat_timer_start(MEMFAULT_METRICS_KEY_WITH_SESSION(key_name, session_key)) #define MEMFAULT_METRIC_SESSION_TIMER_STOP(key_name, session_key) \ memfault_metrics_heartbeat_timer_stop(MEMFAULT_METRICS_KEY_WITH_SESSION(key_name, session_key)) #define MEMFAULT_METRIC_SESSION_ADD(key_name, session_key, amount) \ memfault_metrics_heartbeat_add(MEMFAULT_METRICS_KEY_WITH_SESSION(key_name, session_key), (amount)) //! For debugging purposes: prints the current heartbeat values using //! MEMFAULT_LOG_DEBUG(). Before printing, any active timer values are computed. //! Other metrics will print the current values. This can be called from the //! user-supplied memfault_metrics_heartbeat_collect_data() function to print //! values set there (use memfault_metrics_heartbeat_debug_trigger() to trigger //! a metrics collection). void memfault_metrics_heartbeat_debug_print(void); //! For debugging purposes: prints the current session values using //! MEMFAULT_LOG_DEBUG(). Before printing, any active timer values are computed. //! Other metrics will print the current values. This can be called from the //! user-supplied session end callback to print values at the end of a session //! use memfault_metrics_session_register_end_cb() to register a callback. //! @param session_key The key of the session void memfault_metrics_session_debug_print(eMfltMetricsSessionIndex session_key); //! For debugging purposes: prints the current values for all sessions (i.e. all metrics //! that are not heartbeat metrics) using MEMFAULT_LOG_DEBUG(). Before printing, any //! active timer values are computed. Other metrics will print the current values. //! This function is primarily used for printing session metrics from the Memfault demo CLI. void memfault_metrics_all_sessions_debug_print(void); //! For debugging purposes: triggers the heartbeat data collection handler, as if the heartbeat //! timer had fired. We recommend also testing that the heartbeat timer fires by itself. To get the //! periodic data collection triggering rapidly for testing and debugging, consider using a small //! value for MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS. void memfault_metrics_heartbeat_debug_trigger(void); //! Trigger heartbeat collection, including //! memfault_metrics_heartbeat_collect_data(). This is normally automatically //! called when the heartbeat timer fires, but can be manually called in //! scenarios where non-timer metrics should be tallied (for example, prior to //! entering deep sleep, when system state is lost, and metrics should be added //! up before saving metrics state). void memfault_metrics_heartbeat_collect(void); //! For debugging and unit test purposes, allows for the extraction of different values int memfault_metrics_heartbeat_read_unsigned(MemfaultMetricId key, uint32_t *read_val); int memfault_metrics_heartbeat_read_signed(MemfaultMetricId key, int32_t *read_val); int memfault_metrics_heartbeat_timer_read(MemfaultMetricId key, uint32_t *read_val); int memfault_metrics_heartbeat_read_string(MemfaultMetricId key, char *read_val, size_t read_val_len); //! Callback used to collect custom metrics at the start of a session. //! //! This CB is called when a session is ended. It can be used to set any state that is needed //! for metric collection during a session. typedef void (*MemfaultMetricsSessionStartCb)(void); //! Callback used to collect custom metrics at the end of a session. //! //! This CB is called when a session is ended. It can be used to collect custom metrics //! whose value at the end of a session are all that matters. For example, if you wanted to //! track the battery life over the course of an LTE session. typedef void (*MemfaultMetricsSessionEndCb)(void); //! Register a callback to be called when a session is started. //! //! @param session_key The key of the session to register the callback for //! @param session_start_cb The callback to be called when the session has started void memfault_metrics_session_register_start_cb(eMfltMetricsSessionIndex session_key, MemfaultMetricsSessionStartCb session_start_cb); //! Register a callback to be called when a session is ended. //! //! @param session_key The key of the session to register the callback for //! @param session_end_cb The callback to be called when the session has ended void memfault_metrics_session_register_end_cb(eMfltMetricsSessionIndex session_key, MemfaultMetricsSessionEndCb session_end_cb); //! Used to start a metric "session". //! //! A session is a special type of metric which tracks the time a certain event is active. //! For example, if you wanted to track battery life over the course of an LTE session, you could //! start a session when the LTE modem is powered on and stop the session when the modem is //! powered off. int memfault_metrics_session_start(eMfltMetricsSessionIndex session_key); //! Used to end a metric "session"- the session timer is stopped, the end //! callback is executed, and metric data is serialized. Session data is cleared //! after serialization. //! //! Same as @memfault_metrics_session_start except for stopping a session. int memfault_metrics_session_end(eMfltMetricsSessionIndex session_key); //! Reset a session. Used to deactivate a session instead of ending it. Session //! timer is stopped and session metric data is cleared. //! //! Safe to call on an inactive session. void memfault_metrics_session_reset(eMfltMetricsSessionIndex session_key); //! Alternate API that includes the 'MEMFAULT_METRICS_SESSION_KEY()' expansion #define MEMFAULT_METRICS_SESSION_START(key) \ memfault_metrics_session_start(MEMFAULT_METRICS_SESSION_KEY(key)) #define MEMFAULT_METRICS_SESSION_END(key) \ memfault_metrics_session_end(MEMFAULT_METRICS_SESSION_KEY(key)) #define MEMFAULT_METRICS_SESSION_RESET(key) \ memfault_metrics_session_reset(MEMFAULT_METRICS_SESSION_KEY(key)) //! Collect built-in metrics as part of default ports in memfault-firmware-sdk. //! //! It can (optionally) be overridden by a port to collect a set of built-in metrics //! as configured by the port/application. //! //! @note By default, a weak version of this function is implemented which is empty //! @note This API is for internal use only and should never be called by an end user void memfault_metrics_heartbeat_collect_sdk_data(void); //! Return a pointer to the metrics state. This is used to back up the metrics //! state prior to loss of memory (i.e. when entering a low power sleep mode). //! Note that the state can change at any time if a metrics API is called. This //! function is intended to be called just before the system enters deep sleep. //! //! @returns a pointer to the metrics state. The size of the state is //! MEMFAULT_METRICS_CONTEXT_SIZE_BYTES bytes. const void *memfault_metrics_get_state(void); //! Restore the metrics state. This is used to restore the metrics state after //! the system restarts from deep sleep. //! //! @param state The pointer to the metrics state backup. This should be the //! data previously returned by memfault_metrics_get_state(). Size of the data //! must be MEMFAULT_METRICS_CONTEXT_SIZE_BYTES bytes. //! @return true if the state was restored successfully, false otherwise extern bool memfault_metrics_restore_state(void *state); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/platform/battery.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Required dependency functions for the Memfault Battery Metrics component. #include #include #ifdef __cplusplus extern "C" { #endif typedef struct MfltPlatformBatterySoc { // Battery state of charge, scaled by MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE (default: 1). // Valid range: 0 to (100 * scale value). // // Example mappings: // +------------+-------------+------------+-------------------------------+ // | Battery % | Scale Value | .soc Value | battery_soc_pct in Memfault | // +------------+-------------+------------+-------------------------------+ // | 85% | 1 | 85 | 85 | // | 85.3% | 10 | 853 | 85.3 | // | 85.37% | 100 | 8537 | 85.37 | // +------------+-------------+------------+-------------------------------+ uint32_t soc; // Whether the battery is currently discharging. bool discharging; } sMfltPlatformBatterySoc; //! The platform must implement this function. It provides the current battery //! state of charge, and the discharging state. //! //! Certain device conditions may prevent reading a valid state of charge. In //! that case, the function should return a non-zero value, and the state of //! charge will be ignored. The discharging state will still be used and must //! always be valid. //! //! @param soc A pointer to a sMfltPlatformBatterySoc struct to be populated //! //! @return 0 if SoC is successfully resolved, non-zero otherwise. int memfault_platform_get_stateofcharge(sMfltPlatformBatterySoc *soc); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/platform/connectivity.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Optional platform-specific boot function for initializing connectivity metrics. #include #include #ifdef __cplusplus extern "C" { #endif //! The platform must implement this function. It is called during boot to initialize //! platform-specific connectivity metrics. //! //! For example, this function may register handlers for connectivity events, which when //! received, the handlers will mark the begin and end of connection periods. void memfault_platform_metrics_connectivity_boot(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/platform/overrides.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Functions that can be optionally overridden by the user of the SDK. //! //! Default "weak function" stub definitions are provided for each of these functions //! in the SDK itself #ifdef __cplusplus extern "C" { #endif //! Function invoked every time a heartbeat interval has passed //! //! It can (optionally) be overridden by the application to collect and update //! the data for the heartbeat before it is serialized and stored. void memfault_metrics_heartbeat_collect_data(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/platform/timer.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @brief //! //! A timer API which needs to be implemented to start collecting memfault heartbeat metrics #include #include #ifdef __cplusplus extern "C" { #endif //! A timer callback used by the Memfault library typedef void(MemfaultPlatformTimerCallback)(void); //! Start a repeating timer that invokes 'callback' every period_s //! //! @param period_sec The interval (in seconds) to invoke the callback at //! @param callback to invoke //! //! @return true if the timer was successfully created and started, false otherwise bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback *callback); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/reliability.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @brief APIs for tracking reliability metrics #ifdef __cplusplus extern "C" { #endif #include #include typedef struct { // Time since boot at the start of the last heartbeat interval uint32_t last_heartbeat_ms; // Aggregated operational milliseconds since the last heartbeat serialization uint32_t operational_ms; bool counted_unexpected_reboot; } sMemfaultMetricsReliabilityCtx; //! Load reliability metrics state on boot. Called internally by the Memfault //! SDK. void memfault_metrics_reliability_boot(sMemfaultMetricsReliabilityCtx *ctx); //! Returns the current reliability metrics context. Used to store it when //! saving state on deep sleep or reboot. sMemfaultMetricsReliabilityCtx *memfault_metrics_reliability_get_ctx(void); //! Collects reliability metrics. This is normally called automatically during //! the end of heartbeat timer callback, and should not be called by the user. void memfault_metrics_reliability_collect(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/serializer.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Heartbeat metrics are collected at a periodic interval //! (MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS). Each time they are collected, the data is //! serialized out into a compressed format to be sent up to the Memfault cloud via the "Data //! Packetizer" (see data_packetizer.h). The utilities in this module deal with this serialization //! process #include #include #include "memfault/core/event_storage.h" #include "memfault/metrics/ids_impl.h" #ifdef __cplusplus extern "C" { #endif //! Compute the worst case number of bytes required to serialize Memfault metrics for a heartbeat. //! //! @return the worst case amount of space needed to serialize an event size_t memfault_metrics_heartbeat_compute_worst_case_storage_size(void); //! Compute the worst case number of bytes required to serialize Memfault metrics for a given //! session size_t memfault_metrics_session_compute_worst_case_storage_size(eMfltMetricsSessionIndex session); //! Serialize out the current set of heartbeat metrics //! //! @return True if the data was successfully serialized, else false if there was not enough space //! to serialize the data bool memfault_metrics_heartbeat_serialize(const sMemfaultEventStorageImpl *storage_impl); bool memfault_metrics_session_serialize(const sMemfaultEventStorageImpl *storage_impl, eMfltMetricsSessionIndex session); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/metrics/utils.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Utilities to assist with querying Memfault Metric info //! //! @note A user of the Memfault SDK should _never_ call any //! of these routines directly #include #include "memfault/metrics/metrics.h" #ifdef __cplusplus extern "C" { #endif union MemfaultMetricValue { uint32_t u32; int32_t i32; void *ptr; }; typedef struct { MemfaultMetricId key; eMemfaultMetricType type; bool is_set; union MemfaultMetricValue val; eMfltMetricsSessionIndex session_key; } sMemfaultMetricInfo; //! The callback invoked when "memfault_metrics_heartbeat_iterate" is called //! //! @param ctx The context provided to "memfault_metrics_heartbeat_iterate" //! @param metric_info Info for the particular metric being returned in the callback //! See struct for more details //! //! @return bool to continue iterating, else false typedef bool (*MemfaultMetricIteratorCallback)(void *ctx, const sMemfaultMetricInfo *metric_info); void memfault_metrics_heartbeat_iterate(MemfaultMetricIteratorCallback cb, void *ctx); //! @return the number of metrics being required for a heartbeat size_t memfault_metrics_heartbeat_get_num_metrics(void); //! @return the number of metrics being required for a given session size_t memfault_metrics_session_get_num_metrics(eMfltMetricsSessionIndex session_key); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/arch/arm/aarch64.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! aarch64 specific aspects of panic handling. #include #include "memfault/core/compiler.h" #ifdef __cplusplus extern "C" { #endif //! Note: With aarch64, it is the programmer's responsibility to store an exception frame //! so there is no standard layout. MEMFAULT_PACKED_STRUCT MfltRegState { uint64_t sp; // sp prior to exception entry uint64_t pc; // pc prior to exception entry // Note: The CPSR is a 32 bit register but storing as a 64 bit register to keep packing simple uint64_t cpsr; uint64_t x[31]; // x0 - x30 }; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/arch/arm/cortex_m.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Cortex-M specific aspects of panic handling. #include #include "memfault/core/compiler.h" #ifdef __cplusplus extern "C" { #endif // Registers auto-stacked as part of Cortex-M exception entry typedef MEMFAULT_PACKED_STRUCT MfltExceptionFrame { uint32_t r0; uint32_t r1; uint32_t r2; uint32_t r3; uint32_t r12; uint32_t lr; uint32_t pc; uint32_t xpsr; } sMfltExceptionFrame; // Register State collected for Cortex-M at exception entry MEMFAULT_PACKED_STRUCT MfltRegState { // Exception-entry value of the stack pointer that was active when the fault occurred (i.e msp or // psp). This is where the hardware will automatically stack caller-saved register state // (https://mflt.io/2rejx7A) as part of the exception entry flow. This address is guaranteed to // match the layout of the "sMfltExceptionFrame". sMfltExceptionFrame *exception_frame; // callee saved registers 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; // on exception entry, this value is in the LR }; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/arch/arm/v7_a_r.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! ARMv7-R (Cortex-R) specific aspects of panic handling #include #include "memfault/core/compiler.h" #include "memfault/core/reboot_reason_types.h" #ifdef __cplusplus extern "C" { #endif //! Register State collected for ARMv7-A/R when a fault occurs. Non-arch-specific //! name for the struct type- this type is used when defining the coredump //! header layout. MEMFAULT_PACKED_STRUCT MfltRegState { uint32_t r0; uint32_t r1; uint32_t r2; uint32_t r3; 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 r12; uint32_t sp; // R13 uint32_t lr; // R14 uint32_t pc; // R15 uint32_t cpsr; }; //! Called by platform assertion handlers to save info before triggering fault //! handling //! //! @param pc Pointer to address of assertion location //! @param lr Pointer to return address of assertion location //! @param reason Reboot reason for the assertion void memfault_arch_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/arch/posix/posix.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Posix (Intel 80386) specific aspects of panic handling #include #include "memfault/core/compiler.h" #include "memfault/core/reboot_reason_types.h" #ifdef __cplusplus extern "C" { #endif //! Register State collected for Intel 80386 when a fault occurs MEMFAULT_PACKED_STRUCT MfltRegState { uint32_t eip; //!< Instruction pointer uint32_t cs; //!< Code segment uint32_t eflags; //!< Flags register uint32_t esp; //!< Stack pointer uint32_t ss; //!< Stack segment uint32_t eax; //!< Accumulator register uint32_t ebx; //!< Base register uint32_t ecx; //!< Counter register uint32_t edx; //!< Data register uint32_t edi; //!< Destination index register uint32_t esi; //!< Source index register }; //! Called by platform assertion handlers to save info before triggering fault handling //! //! @param pc Pointer to address of assertion location //! @param lr Pointer to return address of assertion location //! @param reason Reboot reason for the assertion void memfault_arch_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/arch/riscv/riscv.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! RISC-V specific aspects of panic handling #include #include "memfault/core/compiler.h" #include "memfault/core/reboot_reason_types.h" #ifdef __cplusplus extern "C" { #endif //! Register State collected for RISC-V when a fault occurs MEMFAULT_PACKED_STRUCT MfltRegState { uint32_t mepc; uint32_t ra; uint32_t sp; uint32_t gp; uint32_t tp; uint32_t t[7]; uint32_t s[12]; uint32_t a[8]; uint32_t mstatus; uint32_t mtvec; uint32_t mcause; uint32_t mtval; uint32_t mhartid; }; //! Called by platform assertion handlers to save info before triggering fault handling //! //! @param pc Pointer to address of assertion location //! @param lr Pointer to return address of assertion location //! @param reason Reboot reason for the assertion void memfault_arch_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/arch/xtensa/xtensa.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! ESP32 specific aspects of panic handling #include #include "memfault/core/compiler.h" #include "memfault/core/reboot_reason_types.h" #ifdef __cplusplus extern "C" { #endif typedef enum { // A complete dump of all the registers kMemfaultEsp32RegCollectionType_Full = 0, // A collection of only the active register window kMemfaultEsp32RegCollectionType_ActiveWindow = 1, // ESP8266 (Tensilica LX106 Core) register collection variant. Supported in // earlier versions of the SDK and kept as a reserved type. kMemfaultEsp32RegCollectionType_Lx106 = 2, } eMemfaultEsp32RegCollectionType; //! Register State collected for ESP32 when a fault occurs MEMFAULT_PACKED_STRUCT MfltRegState { uint32_t collection_type; // eMemfaultEsp32RegCollectionType // NOTE: This matches the layout expected for kMemfaultEsp32RegCollectionType_ActiveWindow uint32_t pc; uint32_t ps; // NB: The ESP32 has 64 "Address Registers" (ARs) across 4 register windows. Upon exception // entry all inactive register windows are force spilled to the stack by software. Therefore, we // only need to save the active windows registers at exception entry (referred to as a0-a15). uint32_t a[16]; uint32_t sar; uint32_t lbeg; uint32_t lend; uint32_t lcount; uint32_t exccause; uint32_t excvaddr; }; //! Called by platform assertion handlers to save info before triggering fault handling //! //! @param pc Pointer to address of assertion location //! @param lr Pointer to return address of assertion location //! @param reason Reboot reason for the assertion void memfault_arch_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/assert.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Hooks for linking system assert infra with Memfault error collection #include "memfault/core/compiler.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/panics/fault_handling.h" #ifdef __cplusplus extern "C" { #endif //! The 'MEMFAULT_ASSERT*' hooks that should be called as part of the normal ASSERT flow in //! the system. This will save the reboot info to persist across boots. //! //! - MEMFAULT_ASSERT : normal assert //! - MEMFAULT_ASSERT_EXTRA : assert with a single arbitrary uint32_t context value included //! - MEMFAULT_ASSERT_EXTRA_AND_REASON : assert with arbitrary uint32_t value and reason code //! - MEMFAULT_ASSERT_WITH_REASON : assert with reason code //! - MEMFAULT_SOFTWARE_WATCHDOG : assert with kMfltRebootReason_SoftwareWatchdog reason set //! //! NB: We may also want to think about whether we should save off something like __LINE__ (or use a //! compiler flag) that blocks the compiler from coalescing asserts (since that could be confusing //! to an end customer trying to figure out the exact assert hit) #define MEMFAULT_ASSERT_EXTRA_AND_REASON(_extra, _reason) \ do { \ void *pc; \ MEMFAULT_GET_PC(pc); \ void *lr; \ MEMFAULT_GET_LR(lr); \ sMemfaultAssertInfo _assert_info = { \ .extra = (uint32_t)_extra, \ .assert_reason = _reason, \ }; \ memfault_fault_handling_assert_extra(pc, lr, &_assert_info); \ } while (0) #define MEMFAULT_ASSERT_RECORD(_extra) \ MEMFAULT_ASSERT_EXTRA_AND_REASON(_extra, kMfltRebootReason_Assert) #define MEMFAULT_ASSERT_EXTRA(exp, _extra) \ do { \ if (!(exp)) { \ MEMFAULT_ASSERT_RECORD(_extra); \ } \ } while (0) #define MEMFAULT_ASSERT(exp) \ do { \ if (!(exp)) { \ void *pc; \ MEMFAULT_GET_PC(pc); \ void *lr; \ MEMFAULT_GET_LR(lr); \ memfault_fault_handling_assert(pc, lr); \ } \ } while (0) #define MEMFAULT_ASSERT_WITH_REASON(exp, _reason) \ do { \ if (!(exp)) { \ MEMFAULT_ASSERT_EXTRA_AND_REASON(0, _reason); \ } \ } while (0) //! Assert subclass to be used when a software watchdog trips. #define MEMFAULT_SOFTWARE_WATCHDOG() \ MEMFAULT_ASSERT_WITH_REASON(0, kMfltRebootReason_SoftwareWatchdog) //! Assert subclass to be used when a task watchdog trips. #define MEMFAULT_TASK_WATCHDOG() MEMFAULT_ASSERT_WITH_REASON(0, kMfltRebootReason_TaskWatchdog) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/coredump.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Infra for collecting backtraces which can be parsed by memfault! #include #include #include #include "memfault/config.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/panics/platform/coredump.h" #ifdef __cplusplus extern "C" { #endif typedef struct MemfaultCoredumpSaveInfo { const void *regs; size_t regs_size; eMemfaultRebootReason trace_reason; const sMfltCoredumpRegion *regions; size_t num_regions; } sMemfaultCoredumpSaveInfo; //! Invoked by assert handler to capture coredump. //! //! @note A user of the SDK shouldn't need to invoke this directly //! //! @param sMemfaultCoredumpSaveInfo Architecture specific information to save with the coredump //! @return true if the coredump was saved and false if the save failed bool memfault_coredump_save(const sMemfaultCoredumpSaveInfo *save_info); //! Handler to be invoked from fault handlers //! //! By default, the Memfault SDK will automatically call this function as part of //! exception handling for the target architecture. //! //! @param regs The register state at the time of the fault occurred //! @param reason The reason the fault occurred void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason); //! First function called in "memfault_fault_handler". //! //! This routine exists so an end user can optionally extend the fault handler logic //! or print metadata about the fault which occurred //! //! @note By default this is a weak function which behaves as a no-op. extern void memfault_platform_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason); //! Checks that a coredump can fit in the platform storage allocated //! //! @return true if the check passes & false otherwise. On failure, an error message //! is logged with information about how much more storage is needed. bool memfault_coredump_storage_check_size(void); //! Computes the size required to save a coredump on the system //! //! @note A user of the SDK can call this on boot to assert that //! coredump storage is large enough to capture the regions specified //! //! @return The space required to save the coredump or 0 on error //! (i.e no coredump regions defined, coredump storage of 0 size) size_t memfault_coredump_storage_compute_size_required(void); //! Returns the computed size of a coredump and the available storage capacity //! //! @param[out] total_size The size of the coredump in bytes //! @param[out] capacity The total capacity of the coredump storage in bytes void memfault_coredump_size_and_storage_capacity(size_t *total_size, size_t *capacity); //! Queries whether a valid coredump is present in the coredump storage. //! //! @param total_size_out Upon returning from the function, the size of the coredump //! in bytes has been written to the variable. //! @return true when a valid coredump is present in the storage. bool memfault_coredump_has_valid_coredump(size_t *total_size_out); // // Integration utilities // // We recommend using these to verify the integration of coredump storage. They are not intended // to be included in release builds. // //! Runs tests on platform's coredump storage implementation to verify functionality //! //! @note Since coredumps are saved from an interrupt context, we recommend calling //! this test routine from an ISR or with interrupts disabled. //! @note This routine records errors which will be dumped in //! memfault_coredump_storage_debug_test_finish() but does not do any logging itself since it //! runs from an ISR. //! //! @return true if checks passed, false otherwise. bool memfault_coredump_storage_debug_test_begin(void); //! Finishes platform coredump storage test and dumps info about any errors that occurred //! //! @note This function tests memfault_platform_coredump_storage_clear() which gets called //! while the system is running. //! //! @return if the entire storage test was successful. On error, information is dumped //! to the CLI for further debug. bool memfault_coredump_storage_debug_test_finish(void); #if MEMFAULT_CACHE_FAULT_REGS //! Defined in memfault_coredump_regions_armv7.c but needed for platform ports, //! like Zephyr, this function will allow the port to capture the ARM fault //! registers into a RAM buffer cache. The coredump region getter will //! use the cached copy if MEMFAULT_CACHE_FAULT_REGS is defined. //! //! @note the intent is to call this function before the port starts consuming //! the register values by wrapping the port's fault handler and then chaining //! to it after saving the registers. void memfault_coredump_cache_fault_regs(void); #endif #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/coredump_impl.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Internals used by the "coredump" subsystem in the "panics" component //! An end user should _never_ call any of these APIs directly. #include #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #ifdef __cplusplus extern "C" { #endif //! Coredump block types typedef enum MfltCoredumpBlockType { kMfltCoredumpBlockType_CurrentRegisters = 0, kMfltCoredumpBlockType_MemoryRegion = 1, kMfltCoredumpRegionType_DeviceSerial = 2, // Deprecated: kMfltCoredumpRegionType_FirmwareVersion = 3, kMfltCoredumpRegionType_HardwareVersion = 4, kMfltCoredumpRegionType_TraceReason = 5, kMfltCoredumpRegionType_PaddingRegion = 6, kMfltCoredumpRegionType_MachineType = 7, kMfltCoredumpRegionType_VendorCoredumpEspIdfV2ToV3_1 = 8, kMfltCoredumpRegionType_ArmV6orV7Mpu = 9, kMfltCoredumpRegionType_SoftwareVersion = 10, kMfltCoredumpRegionType_SoftwareType = 11, kMfltCoredumpRegionType_BuildId = 12, } eMfltCoredumpBlockType; // All elements are in word-sized units for alignment-friendliness. typedef struct MfltCachedBlock { uint32_t valid_cache; uint32_t cached_address; uint32_t blk_size; uint32_t blk[]; } sMfltCachedBlock; // We'll point to a properly sized memory block of type MfltCachedBlock. #define MEMFAULT_CACHE_BLOCK_SIZE_WORDS(blk_size) \ ((sizeof(sMfltCachedBlock) + blk_size) / sizeof(uint32_t)) //! Computes the amount of space that will be required to save a coredump //! //! @param save_info The platform specific information to save as part of the coredump //! @return The space required to save the coredump or 0 on error size_t memfault_coredump_get_save_size(const sMemfaultCoredumpSaveInfo *save_info); //! @param num_regions The number of regions in the list returned //! @return regions to collect based on the active architecture or NULL if there are no extra //! regions to collect const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions); //! Memory regions that are part of the SDK to include in a coredump //! //! @param num_regions The number of regions in the list returned //! @return memory regions with the SDK to collect or NULL if there are no extra //! regions to collect const sMfltCoredumpRegion *memfault_coredump_get_sdk_regions(size_t *num_regions); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/fault_handling.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Handlers for faults & exceptions that are included in the Memfault SDK. #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/reboot_reason_types.h" #ifdef __cplusplus extern "C" { #endif //! Additional information supplied to the Memfault extended assert handler typedef struct MemfaultAssertInfo { uint32_t extra; eMemfaultRebootReason assert_reason; } sMemfaultAssertInfo; #if MEMFAULT_COMPILER_ARM_CORTEX_M //! Function prototypes for fault handlers //! Non-Maskable Interrupt handler for ARM processors. The handler will capture fault //! information and PC/LR addresses, trigger a coredump to be captured and finally reboot. //! //! The NMI is the highest priority interrupt that can run on ARM devices and as the name implies //! cannot be disabled. Any fault from the NMI handler will trigger the ARM "lockup condition" //! which results in a reset. //! //! Usually a system will reboot or enter an infinite loop if an NMI interrupt is pended so we //! recommend overriding the default implementation with the Memfault Handler so you can discover //! if that ever happens. //! //! However, a few RTOSs and vendor SDKs make use of the handler. If you encounter a duplicate //! symbol name conflict due to this the memfault implementation can be disabled as follows: //! CFLAGS += -DMEMFAULT_EXC_HANDLER_NMI=MemfaultNmi_Handler_Disabled MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_NMI(void); //! Hard Fault handler for ARM processors. The handler will capture fault information //! and PC/LR addresses, trigger a coredump to be captured and finally reboot. MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_HARD_FAULT(void); //! Memory Management handler for ARM processors. The handler will capture fault information //! and PC/LR addresses, trigger a coredump to be captured and finally reboot. MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT(void); //! Bus Fault handler for ARM processors. The handler will capture fault information //! and PC/LR addresses, trigger a coredump to be captured and finally reboot. MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_BUS_FAULT(void); //! Usage Fault handler for ARM processors. The handler will capture fault information //! and PC/LR addresses, trigger a coredump to be captured and finally reboot. MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_USAGE_FAULT(void); //! (Optional) interrupt handler which can be installed for a watchdog //! //! If a Watchdog Peripheral supports an early wakeup interrupt or a timer peripheral //! has been configured as a "software" watchdog, this function should be used as //! the interrupt handler. //! //! For more ideas about configuring watchdogs in general check out: //! https://mflt.io/root-cause-watchdogs MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_WATCHDOG(void); #endif //! Runs the Memfault assert handler. //! //! This should be the last function called as part of an a assert. Upon completion //! it will reboot the system. Normally, this function is used via the //! MEMFAULT_ASSERT_RECORD and MEMFAULT_ASSERT macros that automatically passes the program //! counter and return address. //! //! @param pc The program counter //! @param lr The return address //! @see MEMFAULT_ASSERT_RECORD //! @see MEMFAULT_ASSERT #if defined(__CC_ARM) || defined(__ICCARM__) || defined(__clang__) //! ARMCC, IAR, and clang (include tiarmclang) will optimize away link register stores from //! callsites which makes it impossible for a reliable backtrace to be resolved so we don't use the //! NORETURN attribute #else MEMFAULT_NORETURN #endif void memfault_fault_handling_assert(void *pc, void *lr); //! Memfault assert handler with additional information. //! //! @param pc The program counter //! @param lr The return address //! @param extra_info Additional information used by the assert handler //! @see MEMFAULT_ASSERT_RECORD //! @see MEMFAULT_ASSERT #if defined(__CC_ARM) || defined(__ICCARM__) || defined(__clang__) //! ARMCC, IAR, and clang (include tiarmclang) will optimize away link register stores from //! callsites which makes it impossible for a reliable backtrace to be resolved so we don't use the //! NORETURN attribute #else MEMFAULT_NORETURN #endif void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info); //! Handler called in all system-specific fault_handlers, to perform generic //! bookkeeping or other operations shared by all fault handlers. void memfault_fault_handling_common(void); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/panics/platform/coredump.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Dependency functions required in order to use the Memfault coredump API //! //! @note The expectation is that all these functions are safe to call from ISRs and with //! interrupts disabled. //! @note It's also expected the caller has implemented memfault_platform_get_device_info(). That //! way //! we can save off information that allows for the coredump to be further decoded server side #include #include #include #include "memfault/core/reboot_reason_types.h" #ifdef __cplusplus extern "C" { #endif //! Architecture specific register state typedef struct MfltRegState sMfltRegState; typedef enum MfltCoredumpRegionType { kMfltCoredumpRegionType_Memory, kMfltCoredumpRegionType_MemoryWordAccessOnly, kMfltCoredumpRegionType_ImageIdentifier, kMfltCoredumpRegionType_ArmV6orV7MpuUnrolled, kMfltCoredumpRegionType_CachedMemory, } eMfltCoredumpRegionType; //! Convenience macro to define a sMfltCoredumpRegion of type kMfltCoredumpRegionType_Memory. #define MEMFAULT_COREDUMP_MEMORY_REGION_INIT(_start, _size) \ (sMfltCoredumpRegion) { \ .type = kMfltCoredumpRegionType_Memory, .region_start = _start, .region_size = _size, \ } typedef struct MfltCoredumpRegion { eMfltCoredumpRegionType type; const void *region_start; uint32_t region_size; } sMfltCoredumpRegion; typedef struct CoredumpCrashInfo { //! The address of the stack at the time of the error. This makes it easy to generate //! special regions such as "just the top of the stack" void *stack_address; //! The reason the reset is taking place. Sometimes you may want to collect different memory //! regions based on the reset type (i.e assert vs HardFault) eMemfaultRebootReason trace_reason; //! Arch specific exception state or NULL when the device is not in an exception state (i.e //! memfault_platform_coredump_get_regions() invoked from //! memfault_coredump_storage_compute_size_required()) //! //! Definitions for "struct MfltRegstate" are architecture dependent and //! can be found at: //! memfault/panics/arch/arm/cortex_m.h //! memfault/panics/arch/arm/cortex_r.h //! memfault/panics/arch/riscv/riscv.h //! memfault/panics/arch/xtensa/xtensa.h //! etc const sMfltRegState *exception_reg_state; } sCoredumpCrashInfo; //! Returns an array of the regions to capture when the system crashes //! @param crash_info Information pertaining to the crash. The user of the SDK can decide //! whether or not to use this info when generating coredump regions to collect. Some //! example ideas can be found in the comments for the struct above //! @param num_regions The number of regions in the list returned const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions); //! Given a pointer and size returns the actual size which should be collected. //! //! @note This is used to make sure the memory being collected is within the bounds //! of the MCUs memory map. //! @note A user _must_ implement this function if they are using a number of the default //! ports/ provided in the SDK //! //! @note This can be useful for truncating memory blocks that have a changing address based on //! where a crash takes place (i.e the stack pointer): //! //! const size_t stack_size = memfault_platform_sanitize_address_range( //! crash_info->stack_addr, 512 /* max amount of stack to collect */); //! s_coredump_regions[idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( //! crash_info->stack_address, stack_size); //! //! @param[in] start_addr The address of the start of the memory range to collect //! @param[in] desired_size The size trying to be collected for the region //! //! @return The actual size to collect or 0 if the address is invalid. size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size); typedef struct MfltCoredumpStorageInfo { //! The size of the coredump storage region (must be greater than the space needed to capture all //! the regions returned from @ref memfault_platform_coredump_get_regions) size_t size; //! Sector size for storage medium used for coredump storage size_t sector_size; } sMfltCoredumpStorageInfo; //! Return info pertaining to the region a coredump will be stored in void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info); //! Issue write to the platforms coredump storage region //! //! @param offset the offset within the platform coredump storage region to write to //! @param data opaque data to write //! @param data_len length of data to write in bytes bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len); //! Read from platforms coredump storage region //! //! @param offset the offset within the platform coredump storage region to read from //! @param data the buffer to read the data into //! @param read_len length of data to read in bytes bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len); //! Erase a region of the platforms coredump storage //! //! @param offset to start erasing within the coredump storage region (must be sector_size //! aligned) //! @param erase_size The amount of bytes to erase (must be in sector_size units) bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size); //! Invalidate any saved coredumps within the platform storage coredump region //! //! Coredumps are persisted until this function is called so this is useful to call after a //! coredump has been read so a new coredump will be captured upon next crash //! //! @note a coredump region can be invalidated by zero'ing out or erasing the first sector //! being used for storage void memfault_platform_coredump_storage_clear(void); //! Used to read coredumps out of storage when the system is not in a _crashed_ state //! //! @note A weak version of this API is defined in memfault_coredump.c and it will just use the //! implementation defined in memfault_platform_coredump_storage_read(). If the storage read //! implementation differs between when a coredump is saved and when it is read (i.e needs mutex //! locking), you can override the the function by defining it in your port file. //! //! @param offset The offset to start reading at //! @param buf The buffer to copy data to //! @param buf_len The number of bytes to read //! //! @return true if the read was successful, false otherwise extern bool memfault_coredump_read(uint32_t offset, void *buf, size_t buf_len); //! Called prior to invoking any platform_storage_[read/write/erase] calls upon crash //! //! @note a weak no-op version of this API is defined in memfault_coredump.c because many platforms //! will not need to implement this at all. Some potential use cases: //! - re-configured platform storage driver to be poll instead of interrupt based //! - give watchdog one last feed prior to storage code getting called it //! @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 ================================================ FILE: components/include/memfault/util/align.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #ifdef __cplusplus extern "C" { #endif #include #include #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/core/sdk_assert.h" // C99 does not have a max_align_t type, so we use a union // of types with the largest alignments to determine max align // Taken from // https://github.com/zephyrproject-rtos/zephyr/blob/bfa0a87277c31d9e1b36104a29b17f3fc1a47444/include/zephyr/types.h#L22-L30 typedef union { long long thelonglong; long double thelongdouble; uintmax_t theuintmax_t; size_t thesize_t; uintptr_t theuintptr_t; void *thepvoid; void (*thepfunc)(void); } uMemfaultMaxAlignType; #if !defined(MEMFAULT_ALIGNOF) #error \ "Compiler has no __alignof__ or equivalent extension. Please disable MEMFAULT_MBEDTLS_METRICS or contact support" #endif // !defined(MEMFAULT_ALIGN) // Allow override for tests #if !defined(MEMFAULT_MAX_ALIGN_SIZE) #define MEMFAULT_MAX_ALIGN_SIZE (MEMFAULT_ALIGNOF(uMemfaultMaxAlignType)) #endif // !defined(MEMFAULT_MAX_ALIGN_SIZE) #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/util/banner.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Memfault splash screen banner. #ifdef __cplusplus extern "C" { #endif // clang-format off #define MEMFAULT_BANNER_(MEMFAULT_COLOR_START, MEMFAULT_COLOR_END) \ "▙▗▌ ▗▀▖ ▜▐ " MEMFAULT_COLOR_START " ▄▄▀▀▄▄ " MEMFAULT_COLOR_END "\n" \ "▌▘▌▞▀▖▛▚▀▖▐ ▝▀▖▌ ▌▐▜▀ " MEMFAULT_COLOR_START " █▄ ▄█" MEMFAULT_COLOR_END "\n" \ "▌ ▌▛▀ ▌▐ ▌▜▀ ▞▀▌▌ ▌▐▐ ▖ " MEMFAULT_COLOR_START " ▄▀▀▄▄▀▀▄" MEMFAULT_COLOR_END "\n" \ "▘ ▘▝▀▘▘▝ ▘▐ ▝▀▘▝▀▘ ▘▀ " MEMFAULT_COLOR_START " ▀▀▄▄▀▀ " MEMFAULT_COLOR_END "\n" // clang-format on //! Memfault banner with colorized logo #define MEMFAULT_BANNER_COLORIZED MEMFAULT_BANNER_("\e[36m", "\e[0m") //! Memfault banner with monochrome logo #define MEMFAULT_BANNER_MONOCHROME MEMFAULT_BANNER_("", "") #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/util/base64.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Utilities for base64 encoding binary data #include #include #ifdef __cplusplus extern "C" { #endif //! Computes how many bytes will be needed to encode a binary blob of bin_len //! using base64 #define MEMFAULT_BASE64_ENCODE_LEN(bin_len) (4 * (((bin_len) + 2) / 3)) //! Computes the maximum size a base64 binary blob will wind up being when decoded back into binary //! (It is possible the actual size is up to 2 bytes less in length if padding bytes were added) #define MEMFAULT_BASE64_MAX_DECODE_LEN(base64_len) ((3 * (base64_len)) / 4) //! Base64 encode a given binary buffer //! //! @note Uses the standard base64 alphabet from https://tools.ietf.org/html/rfc4648#section-4 //! //! @param[in] bin Pointer to the binary buffer to base64 encode //! @param[in] bin_len Length of the binary buffer //! @param[out] pointer to buffer to write base64 encoded data into. The length of the buffer must //! be >= MEMFAULT_BASE64_ENCODE_LEN(bin_len) bytes void memfault_base64_encode(const void *buf, size_t buf_len, void *base64_out); //! Base64 encode a given binary buffer in place //! //! @note Uses the standard base64 alphabet from https://tools.ietf.org/html/rfc4648#section-4 //! //! @param[in, out] buf Pointer to the binary buffer to base64 encode. The total length of the //! buffer must be >= MEMFAULT_BASE64_ENCODE_LEN(bin_len) bytes since we will be encoding in //! place //! @param[in] bin_len Length of the binary data starting at buf[0] to be base64 encoded. void memfault_base64_encode_inplace(void *buf, size_t bin_len); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/util/cbor.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A utility that implements a small subset of the CBOR RFC: //! https://tools.ietf.org/html/rfc7049 //! //! //! CONTEXT: The Memfault metric events API serializes data out to CBOR. Since the actual CBOR //! serialization feature set needed by the SDK is a tiny subset of the CBOR RFC, a minimal //! implementation is implemented here. #include #include #include #ifdef __cplusplus extern "C" { #endif //! The context used to track an active cbor encoding operation //! A consumer of this API should never have to access the structure directly typedef struct MemfaultCborEncoder sMemfaultCborEncoder; //! The backing storage to write the encoded data to //! //! @param ctx The context provided as part of "memfault_cbor_encoder_init" //! @param offset The offset within the storage to write to. These offsets are guaranteed to be //! sequential. (i.e If the last write wrote 3 bytes at offset 0, the next write_cb will begin at //! offset 3) The offset is returned as a convenience. For example if the backing storage is a RAM //! buffer with no state-tracking of it's own //! @param buf The payload to write to the storage //! @param buf_len The size of the payload to write typedef void(MemfaultCborWriteCallback)(void *ctx, uint32_t offset, const void *buf, size_t buf_len); //! Initializes the 'encoder' structure. Must be called at the start of any new encoding //! //! @param encoder Context structure initialized by this routine for tracking active encoding state //! @param write_cb The callback to be invoked when a write needs to be performed //! @param context The context to be provided along with the write_cb //! @param buf_len The space free in the backing storage being encoded to. The encoder API //! will _never_ attempt to write more bytes than this void memfault_cbor_encoder_init(sMemfaultCborEncoder *encoder, MemfaultCborWriteCallback *cb, void *context, size_t buf_len); //! Same as "memfault_cbor_encoder_init" but instead of encoding to a buffer will //! only set the encoder up to compute the total size of the encode //! //! When encoding is done and "memfault_cbort_encoder_deinit" is called the total //! encoding size will be returned void memfault_cbor_encoder_size_only_init(sMemfaultCborEncoder *encoder); //! Resets the state of the encoder context //! //! @return the number of bytes successfully encoded size_t memfault_cbor_encoder_deinit(sMemfaultCborEncoder *encoder); //! Called to begin the encoding of a dictionary (also known as a map, object, hashes) //! //! @param encoder The encoder context to use //! @param num_elements The number of pairs of data items that will be in the dictionary //! //! @return true on success, false otherwise bool memfault_cbor_encode_dictionary_begin(sMemfaultCborEncoder *encoder, size_t num_elements); //! Called to begin the encoding of an array (also referred to as a list, sequence, or tuple) //! //! @param encoder The encoder context to use //! @param num_elements The number of data items that will be in the array //! //! @return true on success, false otherwise bool memfault_cbor_encode_array_begin(sMemfaultCborEncoder *encoder, size_t num_elements); //! Called to encode an unsigned 32-bit integer data item //! //! @param encoder The encoder context to use //! @param value The value to store //! //! @return true on success, false otherwise bool memfault_cbor_encode_unsigned_integer(sMemfaultCborEncoder *encoder, uint32_t value); //! Same as "memfault_cbor_encode_unsigned_integer" but store an unsigned integer instead bool memfault_cbor_encode_signed_integer(sMemfaultCborEncoder *encoder, int32_t value); //! Adds pre-encoded cbor data to the current encoder //! //! @param encoder The encoder context to use //! @param cbor_data The pre-encoded data to add to the current context //! @param cbor_data_len The length of the pre-encoded data //! //! @note Care must be taken by the end user to ensure the data being joined into the current //! encoding creates a valid cbor entry when combined. This utility can helpful, for example, when //! adding a value to a cbor dictionary/map which is a cbor record itself. bool memfault_cbor_join(sMemfaultCborEncoder *encoder, const void *cbor_data, size_t cbor_data_len); //! Called to encode an arbitrary binary payload //! //! @param encoder The encoder context to use //! @param buf The buffer to store //! @param buf_len The length of the buffer to store //! //! @return true on success, false otherwise bool memfault_cbor_encode_byte_string(sMemfaultCborEncoder *encoder, const void *buf, size_t buf_len); //! Called to encode a NUL terminated C string //! //! @param encoder The encoder context to use //! @param str The string to store //! //! @return true on success, false otherwise bool memfault_cbor_encode_string(sMemfaultCborEncoder *encoder, const char *str); //! Called to start the encoding of a C string //! //! @param encoder The encoder context to use //! @param str_len The length of the string to store in bytes, excluding NULL terminator. //! //! @return true on success, false otherwise //! //! @note Use one or more calls to memfault_cbor_join() to write the contents //! of the string. bool memfault_cbor_encode_string_begin(sMemfaultCborEncoder *encoder, size_t str_len); //! Called to start the encoding of an arbitrary binary payload //! //! @param encoder The encoder context to use //! @param buf_len The length of the binary payload to store in bytes, excluding NULL terminator. //! //! @return true on success, false otherwise //! //! @note Use one or more calls to memfault_cbor_join() to write the contents //! of the string. bool memfault_cbor_encode_byte_string_begin(sMemfaultCborEncoder *encoder, size_t bin_len); //! Encodes a IEEE 754 double-precision float that is packed in a uint64_t //! //! @param encoder The encoder context to use //! @param val The value of the float to encode //! //! @return true on success, false otherwise bool memfault_cbor_encode_uint64_as_double(sMemfaultCborEncoder *encoder, uint64_t value); //! Called to encode a signed 64 bit data item //! //! @param encode The encoder context to use //! @param value The value to store //! //! @return true on success, false otherwise bool memfault_cbor_encode_long_signed_integer(sMemfaultCborEncoder *encoder, int64_t value); //! Encode a CBOR null value //! //! @param encoder The encoder context to use //! @return true on success, false otherwise bool memfault_cbor_encode_null(sMemfaultCborEncoder *encoder); //! Helper utility to copy a given buffer to the output buffer at the given offset using memcpy. //! //! @param ctx Output buffer //! @param offset Offset data is copied to in output buffer //! @param buf Input buffer to be copied //! @param buf_len Length of input buffer void memfault_cbor_encoder_memcpy_write(void *ctx, uint32_t offset, const void *buf, size_t buf_len); //! Get the current status of the encoder //! @param encoder The encoder context to query //! @return 0 if no errors have occurred, non-zero error code compatible with errno otherwise #define MEMFAULT_CBOR_ENCODER_STATUS_ENOMEM 12 int memfault_cbor_encoder_get_status(sMemfaultCborEncoder *encoder); //! NOTE: For internal use only, included in the header so it's easy for a caller to statically //! allocate the structure struct MemfaultCborEncoder { bool compute_size_only; MemfaultCborWriteCallback *write_cb; void *write_cb_ctx; size_t buf_len; size_t encoded_size; int status; }; #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/util/chunk_transport.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Some IOT topologies have an MTU sizes that are less than the size of a Memfault Event or //! Coredump. In these situations, the Memfault SDK will chunk up the data to be shipped out over //! the transport and reassembled in the Memfault backend. That is what this API implements. //! //! NOTE: Consumers of the Memfault library should never be including this header directly or //! relying on the details of the serialization format in their own code. #include #include #include #ifdef __cplusplus extern "C" { #endif //! The minimum buffer size required to generate a chunk. #define MEMFAULT_MIN_CHUNK_BUF_LEN 9 //! Callback invoked by the chunking transport to read a piece of a message //! //! By using a callback, we avoid requiring that the entire message ever need to be allocated in //! memory at a given time. For example, a coredump saved in flash can be read out piecemeal. //! //! @param offset The offset within the message to read //! @param buf The buffer to copy the read data into //! @param buf_len The amount of data to be copied. //! @note The chunk transport will _never_ invoke this callback and request data that is past the //! total_size of the message being operated on typedef void(MfltChunkTransportMsgReaderCb)(uint32_t offset, void *buf, size_t buf_len); //! Context used to hold the state of the current message being chunked typedef struct { // Input Arguments //! The total size of the message to be sent uint32_t total_size; //! A callback for reading portions of the message to be sent MfltChunkTransportMsgReaderCb *read_msg; //! Instead of having a "chunk" span one call, allow for a chunk to span across multiple calls to //! this API. This is an optimization that allows us to send messages across "one" chunk if the //! transport does not have any size restrictions bool enable_multi_call_chunk; // Output Arguments //! The total chunk size of the message being operated on when sent as a single chunk uint32_t single_chunk_message_length; //! The offset the chunker has read to within the message to send uint32_t read_offset; //! A CRC computed over the data (up to read_offset). The CRC for the entire message is written //! at the end of the last chunk that makes up a message. uint16_t crc16_incremental; } sMfltChunkTransportCtx; //! Takes a message and chunks it up into smaller messages //! //! @param ctx The context tracking this chunking operation //! @param buf The buffer to copy the chunked message into //! @param[in,out] buf_len The size of the buffer to copy data into. On return, populated //! with the amount of data, in bytes, that was copied into the buffer. The length must be //! at least MEMFAULT_MIN_CHUNK_BUF_LEN //! //! @return true if there is more data to send in the message, false otherwise bool memfault_chunk_transport_get_next_chunk(sMfltChunkTransportCtx *ctx, void *buf, size_t *buf_len); //! Computes info about the current chunk being operated on and populates the output arguments of //! sMfltChunkTransportCtx with the info void memfault_chunk_transport_get_chunk_info(sMfltChunkTransportCtx *ctx); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/util/circular_buffer.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A simple circular buffer implementation. //! //! Note: the implementation does not have any locking. If the user is accessing the buffer //! from multiple contexts, it is their responsibility to lock things #include #include #include #ifdef __cplusplus extern "C" { #endif //! Structure tracking circular buffer state. In header for convenient static allocation but it //! should never be accessed directly! typedef struct { size_t read_offset; size_t read_size; size_t total_space; uint8_t *storage; } sMfltCircularBuffer; //! Called to initialize circular buffer context //! //! @param circular_buffer Allocated context for circular buffer tracking //! @param storage_buf storage area that will be used by circular buffer //! @param storage_len Size of storage area //! //! @return true if successfully configured, else false bool memfault_circular_buffer_init(sMfltCircularBuffer *circular_buf, void *storage_buf, size_t storage_len); //! Read the requested number of bytes //! //! @param circular_buffer The buffer to read from //! @param offset The offset within the buffer to start reading at (must be less than @ref //! memfault_circular_buffer_get_read_size()) //! @param data The buffer to copy read data into //! @param data_len The amount of data to read and the size of space available in data //! //! @return true if the requested data_len was read, false otherwise (i.e trying to read more bytes //! than are available or past the end of available bytes) bool memfault_circular_buffer_read(sMfltCircularBuffer *circular_buf, size_t offset, void *data, size_t data_len); //! Populates the read_ptr with a set of contiguous bytes which can be read. //! //! This can be useful to avoid the need of doing double buffering or implementing something like a //! circular buffer array //! //! @param circular_buf The buffer to read from //! @param offset The offset to start reading from //! @param data Populated with the pointer to read from //! @param data_len The length which can be read //! //! @return true if a read pointer was successfully populated bool memfault_circular_buffer_get_read_pointer(sMfltCircularBuffer *circular_buf, size_t offset, uint8_t **read_ptr, size_t *read_ptr_len); //! Callback invoked when "memfault_circular_buffer_read_with_callback" is called. //! //! @param ctx User defined context as passed into "memfault_circular_buffer_read_with_callback". //! @param offset The offset within the storage to write to. These offsets are guaranteed to be //! sequential. (i.e If the last write wrote 3 bytes at offset 0, the next write_cb will begin at //! offset 3) The offset is returned as a convenience. //! @param buf The payload to write to the storage //! @param buf_len The size of the payload to write //! @return false if writing was not successful. typedef bool (*MemfaultCircularBufferReadCallback)(void *ctx, size_t offset, const void *buf, size_t buf_len); //! Convenience wrapper around "memfault_circular_buffer_get_read_pointer", to directly access //! all the data in the circular buffer from a callback, without needing to copy the data to a //! temporary buffer first. //! //! @param offset The offset within the buffer to start reading at (must be less than //! memfault_circular_buffer_get_read_size()) //! @param data_len The amount of data to read and the size of space available in data //! @param ctx User defined context. //! @param callback If there is no data in the buffer, the callback will not get invoked. If there //! is data in the buffer and it is stored in a contiguous block of memory, the callback argument //! gets invoked once. If the data is stored in non-contiguous blocks of memory, the callback will //! be called for each block. bool memfault_circular_buffer_read_with_callback(sMfltCircularBuffer *circular_buf, size_t offset, size_t data_len, void *ctx, MemfaultCircularBufferReadCallback callback); //! Flush the requested number of bytes from the circular buffer //! //! @param circular_buffer The buffer to clear bytes from //! @param data_len The number of bytes to consume (must be less then @ref //! memfault_circular_buffer_get_read_size) //! //! @return true if the bytes were consumed, false otherwise (i.e trying to consume more bytes than //! exist) bool memfault_circular_buffer_consume(sMfltCircularBuffer *circular_buf, size_t consume_len); //! Same as "memfault_circular_buffer_consume" but flush the requested number of bytes from //! the _end_ of the circular buffer //! //! This can be useful for lazily aborting a write that spans multiple write calls //! where the entire size of the write is not known upfront bool memfault_circular_buffer_consume_from_end(sMfltCircularBuffer *circular_buf, size_t consume_len); //! Copy data into the circular buffer //! //! @param circular_buffer The buffer to clear bytes from //! @param data The buffer to copy //! @param data_len Length of buffer to copy //! //! @return true if there was enough space and the _entire_ buffer was copied, false otherwise. //! Note if there is not enough space, _no_ data will be written. bool memfault_circular_buffer_write(sMfltCircularBuffer *circular_buf, const void *data, size_t data_len); //! Copy data into the circular buffer starting at the provided offset from the end //! //! @param circular_buffer The buffer to clear bytes from //! @param offset_from_end Where to begin the write. For example if 10 bytes were written to the //! buffer //! and offset_from_end is 1, a write will begin at offset 9 within the buffer. offset_from_end //! must //! be less than or equal to the current amount of bytes currently stored in the buffer //! @param data The buffer to copy //! @param data_len Length of buffer to copy //! //! @return true if there was enough space and the _entire_ buffer was copied, false otherwise bool memfault_circular_buffer_write_at_offset(sMfltCircularBuffer *circular_buf, size_t offset_from_end, const void *data, size_t data_len); //! @return Amount of bytes available to read size_t memfault_circular_buffer_get_read_size(const sMfltCircularBuffer *circular_buf); //! @return Amount of bytes available for writing size_t memfault_circular_buffer_get_write_size(const sMfltCircularBuffer *circular_buf); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/util/crc16.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! C Implementation of the CRC-16/XMODEM polynomial: //! x^16 + x^12 + x^5 + 1 (0x1021) //! More details: //! http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.crc-16-xmodem #include #include #define MEMFAULT_CRC16_INITIAL_VALUE 0x0 #ifdef __cplusplus extern "C" { #endif //! Computes the CRC-16/XMODEM value for the given sequence. //! //! @param crc_initial_value Use MEMFAULT_CRC16_INITIAL_VALUE when computing a new checksum //! over //! the entire data set passed in. Use the current CRC16-CCITT value if computing over a data set //! incrementally //! @param data The data to compute the crc over //! @param data_len_bytes the length of the data to compute the crc over, in bytes uint16_t memfault_crc16_compute(uint16_t crc_initial_value, const void *data, size_t data_len_bytes); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/util/rle.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A utility for run length encoding a given stream of data //! https://en.wikipedia.org/wiki/Run-length_encoding //! //! In short, run-length-encoding is a very simple lossless compression scheme //! which shortens runs of the same data pattern. This can be very favorable for //! embedded applications where runs of 0 (i.e bss) and stack guards are very common //! //! The format used is ZigZag Varint | Payload where negative integers indicate the run is a //! sequence of non-repeating bytes and positive integers indicate that the same value which //! follows is repeated that number of times #include #include #include #ifdef __cplusplus extern "C" { #endif typedef enum { kMemfaultRleState_Init = 0, kMemfaultRleState_RepeatSeq, kMemfaultRleState_NonRepeatSeq, } eMemfaultRleState; typedef struct { //! true if the data is valid bool available; //! header that should prefix the write sequence uint8_t header[5]; //! length of the header to write (will always be less than sizeof(header)) size_t header_len; //! The offset within the original data fed into the encoder //! that the sequence begins at uint32_t write_start_offset; //! The number of bytes to write size_t write_len; } sMemfaultRleWriteInfo; typedef struct { // // Outputs // //! The total length of the encoded RLE sequence uint32_t total_rle_size; //! Populated when a new sequence has been detected. //! Reset on every invocation of 'memfault_rle_encode' sMemfaultRleWriteInfo write_info; // // Internals // //! The value of the last byte which the encoder inspected uint8_t last_byte; //! The start offset within the backing stream where the current //! sequence being inspected began. //! For example, for an input pattern of { *4, 4, *5, 6, 7, *8, *9, 9 } //! sequences start at the * markings (offset 0, 2, 5 & 6, respectively) uint32_t seq_start_offset; //! The current state the encoder is in. See eMemfaultRleState for more details eMemfaultRleState state; //! The number of bytes that make up the sequence size_t seq_count; //! The number of times the same value has been seen in a row. For example, after the pattern { //! *4, 5, 5 } the count would be 1 since 5 has been repeated once size_t num_repeats; //! The current number of bytes which have been streamed into the encoder and processed uint32_t curr_offset; } sMemfaultRleCtx; //! A utility for RLE a stream of data //! //! @param ctx The context tracking the state for the encoding //! @param buf Buffer filled with the data to process //! @param buf_size The size of the input buffer //! @return the number of bytes processed (may be less than buf_size) //! Upon return ctx->write_info.available can be checked to check if a new //! write sequence was detected size_t memfault_rle_encode(sMemfaultRleCtx *ctx, const void *buf, size_t buf_size); //! Should be called after an entire buffer has been encoded by memfault_rle_encode //! This will flush the final write needed to encode the sequence to ctx->write_info void memfault_rle_encode_finalize(sMemfaultRleCtx *ctx); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/util/varint.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Simple utilities for interacting with Base 128 Varints. //! //! More details about the encoding scheme can be found at: //! https://developers.google.com/protocol-buffers/docs/encoding#varints #include #include #ifdef __cplusplus extern "C" { #endif //! The maximum length of a VARINT encoding for a uint32_t in bytes #define MEMFAULT_UINT32_MAX_VARINT_LENGTH 5 //! Given a uint32_t, encodes it as a Varint //! //! @param[in] value The value to encode //! @param[out] buf The buffer to write the Varint encoding to. Needs to be appropriately sized to //! hold the encoding. The maximum encoding length is MEMFAULT_UINT32_MAX_VARINT_LENGTH //! @return The number of bytes written into the buffer size_t memfault_encode_varint_u32(uint32_t value, void *buf); //! Given an int32_t, encodes it as a ZigZag varint //! //! @note a ZigZag varint can encode the range -2147483648 to 2147483647 and is an optimization //! technique that ensures the smallest absolute values take up the smallest amount of //! space. More details can be found at the following link: //! https://developers.google.com/protocol-buffers/docs/encoding#signed-integers //! @param[in] value The value to encode //! @param[out] buf The buffer to write the Varint encoding to. Needs to be appropriately sized to //! hold the encoding. The maximum encoding length is MEMFAULT_UINT32_MAX_VARINT_LENGTH size_t memfault_encode_varint_si32(int32_t value, void *buf); #ifdef __cplusplus } #endif ================================================ FILE: components/include/memfault/version.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Contains Memfault SDK version information. #ifdef __cplusplus extern "C" { #endif #include typedef struct { uint8_t major; uint8_t minor; uint8_t patch; } sMfltSdkVersion; #define MEMFAULT_SDK_VERSION \ { .major = 1, .minor = 39, .patch = 0 } #define MEMFAULT_SDK_VERSION_STR "1.39.0" #ifdef __cplusplus } #endif ================================================ FILE: components/metrics/README.md ================================================ # metrics This API can easily be used to monitor device health over time (i.e. connectivity, battery life, MCU resource utilization, hardware degradation, etc.) and configure Alerts with the Memfault backend when things go astray. To get started, see this [document](https://mflt.io/2D8TRLX). ### Core features - Allows users to create entries that can be uploaded to the Memfault cloud. - Coredump info can include interrupt state, SDK info like Memfault log buffers, and reset reasons. ### Storage allocations - `memfault_metrics_boot()`: connects the metrics into the event storage region, sets up the metrics timer, starts the timer, and adds an "unexpected reboot" counter metric. ================================================ FILE: components/metrics/src/memfault_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Memfault metrics API implementation. See header for more details. #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/event_storage_implementation.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/overrides.h" #include "memfault/core/reboot_tracking.h" #include "memfault/core/serializer_helper.h" #include "memfault/metrics/battery.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/connectivity.h" #include "memfault/metrics/platform/overrides.h" #include "memfault/metrics/platform/timer.h" #include "memfault/metrics/reliability.h" #include "memfault/metrics/serializer.h" #include "memfault/metrics/utils.h" //! Disable this warning; it trips when there's no custom macros defined of a //! given type MEMFAULT_DISABLE_WARNING("-Wunused-macros") #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE //! Return codes used in this file #define MEMFAULT_METRICS_KEY_NOT_FOUND (-1) #define MEMFAULT_METRICS_TYPE_INCOMPATIBLE (-2) #define MEMFAULT_METRICS_TYPE_BAD_PARAM (-3) #define MEMFAULT_METRICS_TYPE_NO_CHANGE (-4) #define MEMFAULT_METRICS_STORAGE_TOO_SMALL (-5) #define MEMFAULT_METRICS_TIMER_BOOT_FAILED (-6) #define MEMFAULT_METRICS_VALUE_NOT_SET (-7) // Macros to check for scale values vs metric type #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, _min, _max) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, kMemfaultMetricType_String) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) \ MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(session_name) \ MEMFAULT_METRICS_KEY_DEFINE(session_name##__##MemfaultSdkMetric_IntervalMs, \ kMemfaultMetricType_Timer) \ MEMFAULT_METRICS_KEY_DEFINE(session_name##__##operational_crashes, kMemfaultMetricType_Unsigned) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, 0, 0, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE_(key_name, value_type, scale_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE_(key_name, value_type, scale_value) #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) \ MEMFAULT_METRICS_KEY_DEFINE_(key_name, value_type, 1) #define MEMFAULT_METRICS_KEY_DEFINE_(key_name, value_type, scale_value) \ MEMFAULT_STATIC_ASSERT((scale_value == 1) || (value_type != kMemfaultMetricType_Timer), \ "Scale values are only valid for signed and unsigned integer metrics"); // Generate static asserts to check for non-integer metric types with scale values #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE // END: Macros to check for scale values vs metric type #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, _min, _max) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) \ MEMFAULT_METRICS_STRING_KEY_DEFINE(session_key##__##key_name, max_length) //! Sessions have the following built-in keys: //! - "__MemfaultSdkMetric_IntervalMs" //! - "__operational_crashes" #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) \ MEMFAULT_METRICS_KEY_DEFINE(key_name##__##MemfaultSdkMetric_IntervalMs, \ kMemfaultMetricType_Timer) \ MEMFAULT_METRICS_KEY_DEFINE(key_name##__##operational_crashes, kMemfaultMetricType_Unsigned) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) \ MEMFAULT_METRICS_KEY_DEFINE(session_key##__##key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, 0, 0, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE(session_key##__##key_name, value_type) //! Pre-declare the sMemfaultMetricValues type, so we can take the size of it //! in the top-level struct for the heartbeat_value_is_set_flags[] member #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) +1 #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) +1 typedef struct { union MemfaultMetricValue values[0 #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE ]; } sMemfaultMetricValues; #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE #define MEMFAULT_METRICS_TIMER_VAL_MAX 0x80000000 typedef struct MemfaultMetricValueMetadata { bool is_running:1; // We'll use 32 bits since the rollover time is ~25 days which is much much greater than a // reasonable heartbeat interval. This let's us track whether or not the timer is running in the // top bit uint32_t start_time_ms:31; } sMemfaultMetricValueMetadata; // Value Set flag data structures and definitions typedef struct MemfaultMetricValueInfo { union MemfaultMetricValue *valuep; sMemfaultMetricValueMetadata *meta_datap; bool is_set; } sMemfaultMetricValueInfo; //! This structure contains all in-memory context for the metrics system static struct sMemfaultMetricsContext { const sMemfaultEventStorageImpl *storage_impl; #if MEMFAULT_METRICS_RESTORE_STATE sMemfaultMetricsReliabilityCtx reliability_ctx; #endif #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) +1 #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) MemfaultMetricsSessionStartCb session_start_cbs[0 #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE + 1 // dummy entry to prevent empty array ]; MemfaultMetricsSessionEndCb session_end_cbs[0 #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE + 1 // dummy entry to prevent empty array ]; #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE // From this point forward in this struct definition, higher level macros (i.e. // MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE) are not undefined. #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, _min, _max) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) \ MEMFAULT_METRICS_STRING_KEY_DEFINE(session_key##__##key_name, max_length) //! Sessions have the following built-in keys: //! - "__MemfaultSdkMetric_IntervalMs" //! - "__operational_crashes" #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) \ MEMFAULT_METRICS_KEY_DEFINE(key_name##__##MemfaultSdkMetric_IntervalMs, \ kMemfaultMetricType_Timer) \ MEMFAULT_METRICS_KEY_DEFINE(key_name##__##operational_crashes, kMemfaultMetricType_Unsigned) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) \ MEMFAULT_METRICS_KEY_DEFINE(session_key##__##key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, 0, 0, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE(session_key##__##key_name, value_type) sMemfaultMetricValues heartbeat_values; // Create a byte array to contain an is-set flag for each entry in heartbeat_values uint8_t heartbeat_value_is_set_flags[MEMFAULT_CEIL_DIV( MEMFAULT_ARRAY_SIZE(((sMemfaultMetricValues *)0)->values), MEMFAULT_IS_SET_FLAGS_PER_BYTE)]; // Timer metadata table #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Unsigned(_name) #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Signed(_name) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Timer(_name) +1 #define MEMFAULT_METRICS_KEY_DEFINE(_name, _type) MEMFAULT_METRICS_STATE_HELPER_##_type(_name) sMemfaultMetricValueMetadata heartbeat_timer_values_metadata[0 #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE ]; // Work-around for unused-macros error in case not all types are used in the .def file: MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Unsigned(_) MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Signed(_) // Allocate storage for string values- additional byte for null terminator. // Packed to ensure no padding bytes are inserted, which would make it // difficult to compute the size of this structure. MEMFAULT_PACKED_STRUCT sMemfaultStringMetricStorage { #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) \ MEMFAULT_METRICS_STRING_KEY_DEFINE_(key_name, max_length) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_(key_name, max_length) \ char g_memfault_metrics_string_##key_name[max_length + 1 /* for NUL */]; #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_ char placeholder; // dummy entry to avoid empty struct warnings } string_metrics_storage; #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE #if MEMFAULT_METRICS_LOGS_ENABLE uint32_t last_log_dropped_count; uint32_t last_log_recorded_count; #endif } s_memfault_metrics_ctx = { .session_start_cbs = { #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) NULL, #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE NULL, // dummy entry to prevent empty array }, .session_end_cbs = { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE NULL, // dummy entry to prevent empty array }, #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE }; #if MEMFAULT_METRICS_RESTORE_STATE // clang-format off MEMFAULT_STATIC_ASSERT(sizeof(s_memfault_metrics_ctx) == MEMFAULT_METRICS_CONTEXT_SIZE_BYTES, "s_memfault_metrics_ctx size mismatch"); // Uncomment below to see the size of MEMFAULT_METRICS_CONTEXT_SIZE_BYTES at compile time // #define BOOM(size_) MEMFAULT_PACKED_STRUCT kaboom { char dummy[size_]; }; char (*__kaboom)[sizeof(struct kaboom)] = 1; // BOOM(MEMFAULT_METRICS_CONTEXT_SIZE_BYTES) // BOOM(sizeof(s_memfault_metrics_ctx)) // clang-format on #endif // MEMFAULT_METRICS_RESTORE_STATE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE // Generate session key to timer name mapping #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) \ _MEMFAULT_METRICS_ID_CREATE(MemfaultSdkMetric_IntervalMs, key_name), #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) static const MemfaultMetricId s_memfault_metrics_session_timer_keys[] = { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE { 0 } // dummy entry to prevent empty array }; #if MEMFAULT_METRICS_SESSIONS_ENABLED // Generate session key to operational_crashes metric key mapping #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(session_name) \ _MEMFAULT_METRICS_ID_CREATE(operational_crashes, session_name), #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) static const MemfaultMetricId s_memfault_metrics_operational_crashes_keys[] = { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE_ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE { 0 } // dummy entry to prevent empty array }; // Active sessions are tracked in a single 32-bit word in the reboot tracking // data. This limits the maximum sessions to 32. MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_memfault_metrics_operational_crashes_keys) <= 32, "Too many sessions defined. Max allowed defined sessions is 32."); #endif typedef struct MemfaultMetricKVPair { MemfaultMetricId key; eMemfaultMetricType type; // Notes: // - We treat 'min' as a _signed_ integer when the 'type' == kMemfaultMetricType_Signed // - We parse this range information in the Memfault cloud to better normalize data presented in // the UI. uint32_t min; uint32_t range; eMfltMetricsSessionIndex session_key; // Scale down integer metric values by `scale_value` at ingestion by Memfault uint32_t scale_value; } sMemfaultMetricKVPair; #define MEMFAULT_KV_PAIR_ENTRY_(key_name, value_type, min_value, max_value, key_id, session, \ scale_val) \ { \ .key = key_id, \ .type = value_type, \ .min = (uint32_t)min_value, \ .range = ((int64_t)max_value - (int64_t)min_value), \ .session_key = session, \ .scale_value = scale_val, \ }, #define MEMFAULT_KV_PAIR_ENTRY(key_name, value_type, min_value, max_value, key_id, session, \ scale_value) \ MEMFAULT_KV_PAIR_ENTRY_(key_name, value_type, min_value, max_value, key_id, session, scale_value) // Generate heartbeat keys table (ROM): #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session) \ MEMFAULT_KV_PAIR_ENTRY(key_name, value_type, min_value, max_value, \ _MEMFAULT_METRICS_ID_CREATE(key_name, session), \ MEMFAULT_METRICS_SESSION_KEY(session), 1) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, min_value, max_value) \ MEMFAULT_KV_PAIR_ENTRY(key_name, value_type, min_value, max_value, \ _MEMFAULT_METRICS_ID_CREATE(key_name, heartbeat), \ MEMFAULT_METRICS_SESSION_KEY(heartbeat), 1) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, 0, 0, session_key) #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, 0, 0) // Store the string max length in the range field (excluding the null terminator) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, kMemfaultMetricType_String, 0, max_length) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, kMemfaultMetricType_String, 0, \ max_length, session_key) #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(session_name) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(MemfaultSdkMetric_IntervalMs, \ kMemfaultMetricType_Timer, session_name) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(operational_crashes, kMemfaultMetricType_Unsigned, \ session_name) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) \ MEMFAULT_KV_PAIR_ENTRY(key_name, value_type, 0, 0, \ _MEMFAULT_METRICS_ID_CREATE(key_name, heartbeat), \ MEMFAULT_METRICS_SESSION_KEY(heartbeat), scale_value) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) \ MEMFAULT_KV_PAIR_ENTRY(key_name, value_type, 0, 0, \ _MEMFAULT_METRICS_ID_CREATE(key_name, session_key), \ MEMFAULT_METRICS_SESSION_KEY(session_key), scale_value) static const sMemfaultMetricKVPair s_memfault_heartbeat_keys[] = { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION #undef MEMFAULT_METRICS_KEY_DEFINE_ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE }; // From this point forward in the file, higher level macros (i.e. // MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE) are not undefined. This means if you need to redefine a // higher-level macro OR need to redefine the interface of the low-level macros // (MEMFAULT_METRICS_KEY_DEFINE, MEMFAULT_METRICS_STRING_KEY_DEFINE, MEMFAULT_METRICS_KEY_DEFINE_) // you must do this prior to this comment block #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(key_name, value_type, _min, _max) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(key_name, max_length, session_key) \ MEMFAULT_METRICS_STRING_KEY_DEFINE(session_key##__##key_name, max_length) //! Sessions have the following built-in keys: //! - "__MemfaultSdkMetric_IntervalMs" //! - "__operational_crashes" #define MEMFAULT_METRICS_SESSION_KEY_DEFINE(key_name) \ MEMFAULT_METRICS_KEY_DEFINE(key_name##__##MemfaultSdkMetric_IntervalMs, \ kMemfaultMetricType_Timer) \ MEMFAULT_METRICS_KEY_DEFINE(key_name##__##operational_crashes, kMemfaultMetricType_Unsigned) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, min_value, \ max_value, session_key) \ MEMFAULT_METRICS_KEY_DEFINE(session_key##__##key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(key_name, value_type, session_key) \ MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, value_type, 0, 0, session_key) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(key_name, value_type, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(key_name, value_type, \ session_key, scale_value) \ MEMFAULT_METRICS_KEY_DEFINE(session_key##__##key_name, value_type) MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys) != 0, "At least one \"MEMFAULT_METRICS_KEY_DEFINE\" must be defined"); // Generate a mapping of key index to key value position in s_memfault_heartbeat_values. // First produce a sparse enum for the key values that are stored in s_memfault_heartbeat_values. typedef enum MfltMetricKeyToValueIndex { #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) \ MEMFAULT_METRICS_KEY_DEFINE_(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_(key_name, value_type) kMfltMetricKeyToValueIndex_##key_name, #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_ kMfltMetricKeyToValueIndex_Count } eMfltMetricKeyToValueIndex; // Now generate a table mapping the canonical key ID to the index in s_memfault_heartbeat_values static const eMfltMetricKeyToValueIndex s_memfault_heartbeat_key_to_valueindex[] = { #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) \ MEMFAULT_METRICS_KEY_DEFINE_(key_name, value_type) #define MEMFAULT_METRICS_KEY_DEFINE_(key_name, value_type) kMfltMetricKeyToValueIndex_##key_name, #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) \ (eMfltMetricKeyToValueIndex)0, // 0 for the placeholder so it's safe to index with #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_KEY_DEFINE_ #undef MEMFAULT_METRICS_STRING_KEY_DEFINE }; MEMFAULT_STATIC_ASSERT( MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys) == MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_key_to_valueindex), "Mismatch between s_memfault_heartbeat_keys and s_memfault_heartbeat_key_to_valueindex"); // And a similar approach for the strings typedef enum MfltMetricStringKeyToIndex { #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) \ MEMFAULT_METRICS_STRING_KEY_DEFINE_(key_name, max_length) #define MEMFAULT_METRICS_STRING_KEY_DEFINE_(key_name, max_length) \ kMfltMetricStringKeyToIndex_##key_name, #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_ kMfltMetricStringKeyToIndex_Count } eMfltMetricStringKeyToIndex; // Now generate a table mapping the canonical key ID to the index in s_memfault_heartbeat_values static const eMfltMetricStringKeyToIndex s_memfault_heartbeat_string_key_to_index[] = { #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) \ (eMfltMetricStringKeyToIndex)0, // 0 for the placeholder so it's safe to index with #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) \ kMfltMetricStringKeyToIndex_##key_name, #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE }; MEMFAULT_STATIC_ASSERT( MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys) == MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_string_key_to_index), "Mismatch between s_memfault_heartbeat_keys and s_memfault_heartbeat_string_key_to_index"); // String value lookup table. Const- the pointers do not change at runtime, so // this table can be stored in ROM and save a little RAM. #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) \ { .ptr = s_memfault_metrics_ctx.string_metrics_storage.g_memfault_metrics_string_##key_name }, static const union MemfaultMetricValue s_memfault_heartbeat_string_values[] = { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE // include a stub entry to prevent compilation errors when no strings are defined { .ptr = NULL }, }; // We need a key-index table of pointers to timer metadata for fast lookups. // The enum eMfltMetricsTimerIndex will create a subset of indexes for use // in the s_memfault_metrics_ctx.heartbeat_timer_values_metadata[] table. The // s_metric_timer_metadata_mapping[] table provides the mapping from the // exhaustive list of keys to valid timer indexes or -1 if not a timer. #define MEMFAULT_METRICS_KEY_DEFINE(_name, _type) MEMFAULT_METRICS_STATE_HELPER_##_type(_name) // String metrics are not present in eMfltMetricsTimerIndex #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) #undef MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Timer #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Timer(key_name) \ kMfltMetricsTimerIndex_##key_name, typedef enum MfltTimerIndex { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE } eMfltMetricsTimerIndex; #undef MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Unsigned #undef MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Signed #undef MEMFAULT_METRICS_STRING_KEY_DEFINE #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Unsigned(_name) -1, #define MEMFAULT_METRICS_STATE_HELPER_kMemfaultMetricType_Signed(_name) -1, #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) -1, static const int s_metric_timer_metadata_mapping[] = { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE }; // Helper macros to convert between the various metrics indices #define MEMFAULT_METRICS_ID_TO_KEY(id) ((size_t)(id)._impl) #define MEMFAULT_METRICS_KEY_TO_KV_INDEX(key) (s_memfault_heartbeat_key_to_valueindex[(key)]) #define MEMFAULT_METRICS_ID_TO_KV_INDEX(id) \ (MEMFAULT_METRICS_KEY_TO_KV_INDEX(MEMFAULT_METRICS_ID_TO_KEY(id))) // // Routines which can be overridden by customers // MEMFAULT_WEAK void memfault_metrics_heartbeat_collect_data(void) { } MEMFAULT_WEAK void memfault_metrics_heartbeat_collect_sdk_data(void) { } #if MEMFAULT_METRICS_LOGS_ENABLE static void prv_memfault_collect_log_metrics(void) { const uint32_t log_dropped_count = memfault_log_get_dropped_count(); const uint32_t log_recorded_count = memfault_log_get_recorded_count(); // Note: this will wrap when the counts overflow, but as long as there aren't // > UINT32_MAX dropped/recorded lines in a single heartbeat, the arithmetic // will be valid. const uint32_t delta_log_dropped_count = log_dropped_count - s_memfault_metrics_ctx.last_log_dropped_count; s_memfault_metrics_ctx.last_log_dropped_count = log_dropped_count; MEMFAULT_METRIC_ADD(MemfaultSDKMetric_log_dropped_lines, (int32_t)delta_log_dropped_count); const uint32_t delta_log_recorded_count = log_recorded_count - s_memfault_metrics_ctx.last_log_recorded_count; s_memfault_metrics_ctx.last_log_recorded_count = log_recorded_count; MEMFAULT_METRIC_ADD(MemfaultSDKMetric_log_recorded_lines, (int32_t)delta_log_recorded_count); } #endif // This function calls built in metrics collection functions. static void prv_collect_builtin_data(void) { memfault_metrics_reliability_collect(); #if MEMFAULT_METRICS_BATTERY_ENABLE memfault_metrics_battery_collect_data(); #endif #if MEMFAULT_METRICS_LOGS_ENABLE prv_memfault_collect_log_metrics(); #endif #if MEMFAULT_METRICS_UPTIME_ENABLE // uptime_s is a 32-bit value measuring seconds since boot. Convert the uptime // in milliseconds to seconds using the following approach, to avoid 64-bit // division: // // precomputed_scale_factor = (1 << 32) / 1000 = 0x418937 // uptime_seconds = (uptime_ms * 0x418937) >> 32 // // The maximum millisecond value that can be converted to seconds without // overflowing using this algorithm is: // // (0xFFFF_FFFF_FFFF_FFFF / ((1 << 32) / 1000)) = 4294967592000 milliseconds // // 4294967592000 / 1000 / 60 / 60 / 24 / 365 ≈ 136 years // // 4294967592000 / 1000 = 4294967592 seconds, 0x1_0000_0128 in hex, which // exceeds the possible value for a 32-bit integer, so we don't lose any range // with this algorithm. const uint32_t uptime_seconds = (uint32_t)((memfault_platform_get_time_since_boot_ms() * 0x418937ull) >> 32); MEMFAULT_METRIC_SET_UNSIGNED(uptime_s, uptime_seconds); #endif } // Returns NULL if not a timer type or out of bounds index. static sMemfaultMetricValueMetadata *prv_find_timer_metadatap(eMfltMetricsIndex metric_index) { if (metric_index >= MEMFAULT_ARRAY_SIZE(s_metric_timer_metadata_mapping)) { MEMFAULT_LOG_ERROR("Metric index %u exceeds expected array bounds %d\n", metric_index, (int)MEMFAULT_ARRAY_SIZE(s_metric_timer_metadata_mapping)); return NULL; } const int timer_index = s_metric_timer_metadata_mapping[metric_index]; if (timer_index == -1) { return NULL; } return &s_memfault_metrics_ctx.heartbeat_timer_values_metadata[timer_index]; } //! Helper function to read/write is_set bits for the provided metric //! //! @param id Metric ID to select corresponding is_set field //! @param write Boolean to control whether to write 1 to is_set //! @return Returns the value of metric's is_set field. The updated value is returned if write = //! true static bool prv_read_write_is_value_set(MemfaultMetricId id, bool write) { // Shift the kv index by MEMFAULT_IS_SET_FLAGS_DIVIDER to select byte within // s_memfault_metrics_ctx.heartbeat_value_is_set_flags size_t byte_index = MEMFAULT_METRICS_ID_TO_KV_INDEX(id) >> MEMFAULT_IS_SET_FLAGS_DIVIDER; // Modulo the kv index by MEMFAULT_IS_SET_FLAGS_PER_BYTE to get bit of the selected byte size_t bit_index = MEMFAULT_METRICS_ID_TO_KV_INDEX(id) % MEMFAULT_IS_SET_FLAGS_PER_BYTE; if (write) { s_memfault_metrics_ctx.heartbeat_value_is_set_flags[byte_index] |= (1 << bit_index); } return (s_memfault_metrics_ctx.heartbeat_value_is_set_flags[byte_index] >> bit_index) & 0x01; } static void prv_clear_is_value_set(eMfltMetricKeyToValueIndex key) { // Shift the kv index by MEMFAULT_IS_SET_FLAGS_DIVIDER to select byte within // s_memfault_metrics_ctx.heartbeat_value_is_set_flags size_t byte_index = key >> MEMFAULT_IS_SET_FLAGS_DIVIDER; // Modulo the kv index by MEMFAULT_IS_SET_FLAGS_PER_BYTE to get bit of the selected byte size_t bit_index = key % MEMFAULT_IS_SET_FLAGS_PER_BYTE; s_memfault_metrics_ctx.heartbeat_value_is_set_flags[byte_index] &= ~(1 << bit_index); } static eMemfaultMetricType prv_find_value_for_key(MemfaultMetricId id, sMemfaultMetricValueInfo *value_info_out) { const size_t idx = MEMFAULT_METRICS_ID_TO_KEY(id); if (idx >= MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys)) { *value_info_out = (sMemfaultMetricValueInfo){ 0 }; return kMemfaultMetricType_NumTypes; } // get the index for the value matching this key. eMfltMetricKeyToValueIndex key_index = MEMFAULT_METRICS_KEY_TO_KV_INDEX(idx); // for scalar types, this will be the returned value pointer. non-scalars // will be handled in the switch below union MemfaultMetricValue *value_ptr = &s_memfault_metrics_ctx.heartbeat_values.values[key_index]; eMemfaultMetricType key_type = s_memfault_heartbeat_keys[idx].type; switch (key_type) { case kMemfaultMetricType_String: { // get the string value associated with this key eMfltMetricStringKeyToIndex string_key_index = s_memfault_heartbeat_string_key_to_index[idx]; // cast to uintptr_t then the final pointer type we want to drop the // 'const' and prevent tripping -Wcast-qual. this is safe, because we // never modify *value_ptr, only value_ptr->ptr, for non-scalar types. value_ptr = (union MemfaultMetricValue *)(uintptr_t)&s_memfault_heartbeat_string_values[string_key_index]; } break; case kMemfaultMetricType_Timer: case kMemfaultMetricType_Signed: case kMemfaultMetricType_Unsigned: case kMemfaultMetricType_NumTypes: // To silence -Wswitch-enum default: break; } *value_info_out = (sMemfaultMetricValueInfo){ .valuep = value_ptr, .meta_datap = prv_find_timer_metadatap((eMfltMetricsIndex)idx), }; if (key_type == kMemfaultMetricType_Unsigned || key_type == kMemfaultMetricType_Signed) { value_info_out->is_set = prv_read_write_is_value_set(id, false); } return key_type; } typedef bool (*MemfaultMetricKvIteratorCb)(void *ctx, const sMemfaultMetricKVPair *kv_pair, const sMemfaultMetricValueInfo *value_info); static void prv_metric_iterator(void *ctx, MemfaultMetricKvIteratorCb cb) { for (uint32_t idx = 0; idx < MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys); ++idx) { const sMemfaultMetricKVPair *const kv_pair = &s_memfault_heartbeat_keys[idx]; sMemfaultMetricValueInfo value_info = { 0 }; (void)prv_find_value_for_key(kv_pair->key, &value_info); bool do_continue = cb(ctx, kv_pair, &value_info); if (!do_continue) { break; } } } static const sMemfaultMetricKVPair *prv_find_kvpair_for_key(MemfaultMetricId key) { const size_t idx = (size_t)key._impl; if (idx >= MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys)) { return NULL; } return &s_memfault_heartbeat_keys[idx]; } static int prv_find_value_info_for_type(MemfaultMetricId key, eMemfaultMetricType expected_type, sMemfaultMetricValueInfo *value_info) { const eMemfaultMetricType type = prv_find_value_for_key(key, value_info); if (value_info->valuep == NULL) { return MEMFAULT_METRICS_KEY_NOT_FOUND; } if (type != expected_type) { // To easily get name of metric in gdb, p/s (eMfltMetricsIndex)0 MEMFAULT_LOG_ERROR("Invalid type (%u vs %u) for key: %d", expected_type, type, key._impl); return MEMFAULT_METRICS_TYPE_INCOMPATIBLE; } return 0; } static void prv_set_value_for_key(MemfaultMetricId key, union MemfaultMetricValue *new_value, sMemfaultMetricValueInfo *value_info) { *value_info->valuep = *new_value; prv_read_write_is_value_set(key, true); } static int prv_find_and_set_value_for_key(MemfaultMetricId key, eMemfaultMetricType expected_type, union MemfaultMetricValue *new_value) { sMemfaultMetricValueInfo value_info = { 0 }; int rv = prv_find_value_info_for_type(key, expected_type, &value_info); if (rv != 0) { return rv; } prv_set_value_for_key(key, new_value, &value_info); return 0; } int memfault_metrics_heartbeat_set_signed(MemfaultMetricId key, int32_t signed_value) { int rv; memfault_lock(); { rv = prv_find_and_set_value_for_key(key, kMemfaultMetricType_Signed, &(union MemfaultMetricValue){ .i32 = signed_value }); } memfault_unlock(); return rv; } int memfault_metrics_heartbeat_set_unsigned(MemfaultMetricId key, uint32_t unsigned_value) { int rv; memfault_lock(); { rv = prv_find_and_set_value_for_key(key, kMemfaultMetricType_Unsigned, &(union MemfaultMetricValue){ .u32 = unsigned_value }); } memfault_unlock(); return rv; } int memfault_metrics_heartbeat_set_string(MemfaultMetricId key, const char *value) { int rv; memfault_lock(); { sMemfaultMetricValueInfo value_info = { 0 }; rv = prv_find_value_info_for_type(key, kMemfaultMetricType_String, &value_info); const sMemfaultMetricKVPair *kv = prv_find_kvpair_for_key(key); // error if either the key is bad, or we can't find the kvpair for the key // (both checks should have the same result though) rv = (rv != 0 || kv == NULL) ? MEMFAULT_METRICS_KEY_NOT_FOUND : 0; if (rv == 0) { const size_t len = MEMFAULT_MIN(strlen(value), kv->range); memcpy(value_info.valuep->ptr, value, len); // null terminate ((char *)value_info.valuep->ptr)[len] = '\0'; } } memfault_unlock(); return rv; } typedef enum { kMemfaultTimerOp_Start, kMemfaultTimerOp_Stop, kMemfaultTimerOp_ForceValueUpdate, } eMemfaultTimerOp; static bool prv_update_timer_metric(const sMemfaultMetricValueInfo *value_info, eMemfaultTimerOp op) { sMemfaultMetricValueMetadata *meta_datap = value_info->meta_datap; #if defined(MEMFAULT_UNITTEST) // It's a programming error if we reach this point and meta_datap is NULL. It // should always be valid for a timer metric class. Use c stdlib assert to // prevent the static analyzer from triggering below. #include assert(meta_datap != NULL); #endif const bool timer_running = meta_datap->is_running; // The timer is not running _and_ we received a Start request if (!timer_running && op == kMemfaultTimerOp_Start) { meta_datap->start_time_ms = memfault_platform_get_time_since_boot_ms(); meta_datap->is_running = true; return true; } // the timer is running and we received a Stop or ForceValueUpdate request if (timer_running && op != kMemfaultTimerOp_Start) { const uint32_t stop_time_ms = memfault_platform_get_time_since_boot_ms() & ~MEMFAULT_METRICS_TIMER_VAL_MAX; const uint32_t start_time_ms = meta_datap->start_time_ms; uint32_t delta; if (stop_time_ms >= start_time_ms) { delta = stop_time_ms - start_time_ms; } else { // account for rollover delta = MEMFAULT_METRICS_TIMER_VAL_MAX - start_time_ms + stop_time_ms; } value_info->valuep->u32 += delta; if (op == kMemfaultTimerOp_Stop) { meta_datap->start_time_ms = 0; meta_datap->is_running = false; } else { meta_datap->start_time_ms = stop_time_ms; } return true; } // We were already in the state requested and no update took place return false; } static int prv_find_timer_metric_and_update(MemfaultMetricId key, eMemfaultTimerOp op) { sMemfaultMetricValueInfo value_info = { 0 }; int rv = prv_find_value_info_for_type(key, kMemfaultMetricType_Timer, &value_info); if (rv != 0) { return rv; } // If the value did not change because the timer was already in the state requested return an // error code. This will make it easier for users of the external API to catch if their calls // were unbalanced. const bool did_update = prv_update_timer_metric(&value_info, op); return did_update ? 0 : MEMFAULT_METRICS_TYPE_NO_CHANGE; } int memfault_metrics_heartbeat_timer_start(MemfaultMetricId key) { int rv; memfault_lock(); { rv = prv_find_timer_metric_and_update(key, kMemfaultTimerOp_Start); } memfault_unlock(); return rv; } int memfault_metrics_heartbeat_timer_stop(MemfaultMetricId key) { int rv; memfault_lock(); { rv = prv_find_timer_metric_and_update(key, kMemfaultTimerOp_Stop); } memfault_unlock(); return rv; } static bool prv_tally_and_update_timer_cb(MEMFAULT_UNUSED void *ctx, const sMemfaultMetricKVPair *key, const sMemfaultMetricValueInfo *value) { if (key->type != kMemfaultMetricType_Timer) { return true; } prv_update_timer_metric(value, kMemfaultTimerOp_ForceValueUpdate); return true; } static void prv_reset_metrics(bool full_reset, eMfltMetricsSessionIndex session_key) { if (full_reset) { // if a full reset is indicated zero out all metrics regardless of session. memset(s_memfault_metrics_ctx.heartbeat_values.values, 0, sizeof(s_memfault_metrics_ctx.heartbeat_values.values)); memset(s_memfault_metrics_ctx.heartbeat_value_is_set_flags, 0, sizeof(s_memfault_metrics_ctx.heartbeat_value_is_set_flags)); memset(s_memfault_metrics_ctx.heartbeat_timer_values_metadata, 0, sizeof(s_memfault_metrics_ctx.heartbeat_timer_values_metadata)); // reset all string metric values. -1 to skip the last, stub entry in the // table for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_string_values); i++) { // set null terminator if (s_memfault_heartbeat_string_values[i].ptr) { ((char *)s_memfault_heartbeat_string_values[i].ptr)[0] = 0; } } } else { // otherwise only clear metrics from the specified session for (uint32_t idx = 0; idx < MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys); ++idx) { const sMemfaultMetricKVPair *const kv_pair = &s_memfault_heartbeat_keys[idx]; // Skip all metrics from a different session if (kv_pair->session_key != session_key) { continue; } eMfltMetricStringKeyToIndex string_idx = s_memfault_heartbeat_string_key_to_index[idx]; eMfltMetricKeyToValueIndex key_index = MEMFAULT_METRICS_KEY_TO_KV_INDEX(idx); switch (kv_pair->type) { case kMemfaultMetricType_Timer: case kMemfaultMetricType_Signed: case kMemfaultMetricType_Unsigned: { s_memfault_metrics_ctx.heartbeat_values.values[key_index] = (union MemfaultMetricValue){ 0 }; prv_clear_is_value_set(key_index); break; } case kMemfaultMetricType_String: if (s_memfault_heartbeat_string_values[string_idx].ptr) { ((char *)s_memfault_heartbeat_string_values[string_idx].ptr)[0] = 0; } break; case kMemfaultMetricType_NumTypes: // To silence -Wswitch-enum default: break; } } } } static void prv_heartbeat_timer_update(void) { // force an update of the timer value for any actively running timers prv_metric_iterator(NULL, prv_tally_and_update_timer_cb); } //! Triggers a heartbeat update only, no timer update static void prv_heartbeat_update(void) { memfault_metrics_heartbeat_collect_sdk_data(); prv_collect_builtin_data(); memfault_metrics_heartbeat_collect_data(); } //! Trigger an update of heartbeat timers + metrics, serialize out to storage, and reset. static void prv_heartbeat_timer(void) { prv_heartbeat_timer_update(); prv_heartbeat_update(); memfault_metrics_heartbeat_serialize(s_memfault_metrics_ctx.storage_impl); prv_reset_metrics(false, MEMFAULT_METRICS_SESSION_KEY(heartbeat)); } static int prv_find_key_and_add(MemfaultMetricId key, int32_t amount) { sMemfaultMetricValueInfo value_info = { 0 }; const eMemfaultMetricType type = prv_find_value_for_key(key, &value_info); if (value_info.valuep == NULL) { return MEMFAULT_METRICS_KEY_NOT_FOUND; } union MemfaultMetricValue *value = value_info.valuep; switch ((int)type) { case kMemfaultMetricType_Signed: { // Clip in case of overflow: int64_t new_value = (int64_t)value->i32 + (int64_t)amount; if (new_value > INT32_MAX) { value->i32 = INT32_MAX; } else if (new_value < INT32_MIN) { value->i32 = INT32_MIN; } else { value->i32 = new_value; } break; } case kMemfaultMetricType_Unsigned: { uint32_t new_value = value->u32 + (uint32_t)amount; const bool amount_is_positive = amount > 0; const bool did_increase = new_value > value->u32; // Clip in case of overflow: if ((uint32_t)amount_is_positive ^ (uint32_t)did_increase) { new_value = amount_is_positive ? UINT32_MAX : 0; } value->u32 = new_value; break; } case kMemfaultMetricType_Timer: case kMemfaultMetricType_String: case kMemfaultMetricType_NumTypes: // To silence -Wswitch-enum default: // To easily get name of metric in gdb, p/s (eMfltMetricsIndex)0 MEMFAULT_LOG_ERROR("Can only add to number types (key: %d)", key._impl); return MEMFAULT_METRICS_TYPE_INCOMPATIBLE; } return 0; } int memfault_metrics_heartbeat_add(MemfaultMetricId key, int32_t amount) { int rv; memfault_lock(); { rv = prv_find_key_and_add(key, amount); if (rv == 0) { prv_read_write_is_value_set(key, true); } } memfault_unlock(); return rv; } static int prv_find_key_of_type(MemfaultMetricId key, eMemfaultMetricType expected_type, union MemfaultMetricValue **value_out) { sMemfaultMetricValueInfo value_info = { 0 }; const eMemfaultMetricType type = prv_find_value_for_key(key, &value_info); if (value_info.valuep == NULL) { return MEMFAULT_METRICS_KEY_NOT_FOUND; } if (type != expected_type) { return MEMFAULT_METRICS_TYPE_INCOMPATIBLE; } if ((type == kMemfaultMetricType_Signed || type == kMemfaultMetricType_Unsigned) && !(value_info.is_set)) { return MEMFAULT_METRICS_VALUE_NOT_SET; } *value_out = value_info.valuep; return 0; } int memfault_metrics_heartbeat_read_unsigned(MemfaultMetricId key, uint32_t *read_val) { if (read_val == NULL) { return MEMFAULT_METRICS_TYPE_BAD_PARAM; } int rv; memfault_lock(); { union MemfaultMetricValue *value; rv = prv_find_key_of_type(key, kMemfaultMetricType_Unsigned, &value); if (rv == 0) { *read_val = value->u32; } } memfault_unlock(); return rv; } int memfault_metrics_heartbeat_read_signed(MemfaultMetricId key, int32_t *read_val) { if (read_val == NULL) { return MEMFAULT_METRICS_TYPE_BAD_PARAM; } int rv; memfault_lock(); { union MemfaultMetricValue *value; rv = prv_find_key_of_type(key, kMemfaultMetricType_Signed, &value); if (rv == 0) { *read_val = value->i32; } } memfault_unlock(); return rv; } int memfault_metrics_heartbeat_timer_read(MemfaultMetricId key, uint32_t *read_val) { if (read_val == NULL) { return MEMFAULT_METRICS_TYPE_BAD_PARAM; } int rv; memfault_lock(); { union MemfaultMetricValue *value; prv_find_timer_metric_and_update(key, kMemfaultTimerOp_ForceValueUpdate); rv = prv_find_key_of_type(key, kMemfaultMetricType_Timer, &value); if (rv == 0) { *read_val = value->u32; } } memfault_unlock(); return rv; } int memfault_metrics_heartbeat_read_string(MemfaultMetricId key, char *read_val, size_t read_val_len) { if ((read_val == NULL) || (read_val_len == 0)) { return MEMFAULT_METRICS_TYPE_BAD_PARAM; } int rv; memfault_lock(); { union MemfaultMetricValue *value; rv = prv_find_key_of_type(key, kMemfaultMetricType_String, &value); const sMemfaultMetricKVPair *kv = prv_find_kvpair_for_key(key); rv = (rv != 0 || kv == NULL) ? MEMFAULT_METRICS_KEY_NOT_FOUND : 0; if (rv == 0) { // copy up to the min of the length of the string and the length of the // provided buffer size_t len = strlen((const char *)value->ptr) + 1; memcpy(read_val, value->ptr, MEMFAULT_MIN(len, read_val_len)); // always null terminate read_val[read_val_len - 1] = '\0'; } } memfault_unlock(); return rv; } static int prv_metrics_session_start(eMfltMetricsSessionIndex session_key, bool start_timer) { int rv = 0; memfault_lock(); { // Reset all metrics for the session. Any changes that happened before the // session was started don't matter and can be discarded. prv_reset_metrics(false, session_key); if (start_timer) { MemfaultMetricId key = s_memfault_metrics_session_timer_keys[session_key]; rv = prv_find_timer_metric_and_update(key, kMemfaultTimerOp_Start); } // Mark the session as active for tracking operational_crashes memfault_reboot_tracking_metrics_session(true, session_key); } memfault_unlock(); MemfaultMetricsSessionStartCb session_start_cb = s_memfault_metrics_ctx.session_start_cbs[session_key]; if (session_start_cb != NULL) { session_start_cb(); } return rv; } int memfault_metrics_session_start(eMfltMetricsSessionIndex session_key) { return prv_metrics_session_start(session_key, true); } static int prv_metrics_session_end(eMfltMetricsSessionIndex session_key, bool stop_timer) { MemfaultMetricsSessionEndCb session_end_cb = s_memfault_metrics_ctx.session_end_cbs[session_key]; if (session_end_cb != NULL) { session_end_cb(); } int rv = 0; memfault_lock(); { if (stop_timer) { MemfaultMetricId key = s_memfault_metrics_session_timer_keys[session_key]; rv = prv_find_timer_metric_and_update(key, kMemfaultTimerOp_Stop); } if (rv == 0) { bool serialize_result = memfault_metrics_session_serialize(s_memfault_metrics_ctx.storage_impl, session_key); if (serialize_result == false) { rv = MEMFAULT_METRICS_STORAGE_TOO_SMALL; } } // Mark the session as inactive for tracking operational_crashes memfault_reboot_tracking_metrics_session(false, session_key); } memfault_unlock(); return rv; } int memfault_metrics_session_end(eMfltMetricsSessionIndex session_key) { return prv_metrics_session_end(session_key, true); } void memfault_metrics_session_reset(eMfltMetricsSessionIndex session_key) { memfault_lock(); { // Reset all metrics for the session, including the session timer MemfaultMetricId key = s_memfault_metrics_session_timer_keys[session_key]; (void)prv_find_timer_metric_and_update(key, kMemfaultTimerOp_Stop); prv_reset_metrics(false, session_key); memfault_reboot_tracking_metrics_session(false, session_key); } memfault_unlock(); } void memfault_metrics_session_register_start_cb(eMfltMetricsSessionIndex session_key, MemfaultMetricsSessionStartCb session_start_cb) { memfault_lock(); { s_memfault_metrics_ctx.session_start_cbs[session_key] = session_start_cb; } memfault_unlock(); } void memfault_metrics_session_register_end_cb(eMfltMetricsSessionIndex session_key, MemfaultMetricsSessionEndCb session_end_cb) { memfault_lock(); { s_memfault_metrics_ctx.session_end_cbs[session_key] = session_end_cb; } memfault_unlock(); } typedef struct { MemfaultMetricIteratorCallback user_cb; void *user_ctx; } sMetricHeartbeatIterateCtx; static bool prv_metrics_heartbeat_iterate_cb(void *ctx, const sMemfaultMetricKVPair *key_info, const sMemfaultMetricValueInfo *value_info) { sMetricHeartbeatIterateCtx *ctx_info = (sMetricHeartbeatIterateCtx *)ctx; if (value_info->valuep == NULL) { return false; } sMemfaultMetricInfo info = { .key = key_info->key, .type = key_info->type, .val = *value_info->valuep, .is_set = value_info->is_set, .session_key = key_info->session_key, }; return ctx_info->user_cb(ctx_info->user_ctx, &info); } void memfault_metrics_heartbeat_iterate(MemfaultMetricIteratorCallback cb, void *ctx) { memfault_lock(); { sMetricHeartbeatIterateCtx user_ctx = { .user_cb = cb, .user_ctx = ctx, }; prv_metric_iterator(&user_ctx, prv_metrics_heartbeat_iterate_cb); } memfault_unlock(); } typedef struct { size_t num_metrics; eMfltMetricsSessionIndex session_key; } sGetNumMetricsCtx; static bool prv_get_num_metrics_iter_cb(void *ctx, const sMemfaultMetricInfo *metric_info) { sGetNumMetricsCtx *num_metrics_ctx = (sGetNumMetricsCtx *)ctx; if (metric_info->session_key == num_metrics_ctx->session_key) { num_metrics_ctx->num_metrics++; } return true; } static size_t prv_get_num_metrics(eMfltMetricsSessionIndex session_key) { sGetNumMetricsCtx ctx = { .session_key = session_key }; memfault_metrics_heartbeat_iterate(prv_get_num_metrics_iter_cb, (void *)&ctx); return ctx.num_metrics; } size_t memfault_metrics_heartbeat_get_num_metrics(void) { return prv_get_num_metrics(MEMFAULT_METRICS_SESSION_KEY(heartbeat)); } size_t memfault_metrics_session_get_num_metrics(eMfltMetricsSessionIndex session_key) { return prv_get_num_metrics(session_key); } #define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type) MEMFAULT_QUOTE(key_name), #define MEMFAULT_METRICS_STRING_KEY_DEFINE(key_name, max_length) MEMFAULT_QUOTE(key_name), static const char *s_idx_to_metric_name[] = { #include "memfault/metrics/heartbeat_config.def" #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE #undef MEMFAULT_METRICS_KEY_DEFINE #undef MEMFAULT_METRICS_STRING_KEY_DEFINE }; typedef bool(MemfaultMetricDebugPrintFilterCb)(eMfltMetricsSessionIndex ctx_key, eMfltMetricsSessionIndex current_key); typedef struct { MemfaultMetricDebugPrintFilterCb *print_filter; eMfltMetricsSessionIndex session_key; } sMetricDebugPrintCtx; static bool prv_metrics_debug_print(void *ctx, const sMemfaultMetricInfo *metric_info) { sMetricDebugPrintCtx *cb_ctx = (sMetricDebugPrintCtx *)ctx; if (!cb_ctx->print_filter(cb_ctx->session_key, metric_info->session_key)) { return true; } const MemfaultMetricId *key = &metric_info->key; const union MemfaultMetricValue *value = &metric_info->val; const char *key_name = s_idx_to_metric_name[key->_impl]; (void)value, (void)key_name; // to silence unused variable warnings if logging is disabled switch (metric_info->type) { case kMemfaultMetricType_Timer: MEMFAULT_LOG_INFO(" %s: %" PRIu32, key_name, value->u32); break; case kMemfaultMetricType_Unsigned: if (metric_info->is_set) { MEMFAULT_LOG_INFO(" %s: %" PRIu32, key_name, value->u32); } else { MEMFAULT_LOG_INFO(" %s: null", key_name); } break; case kMemfaultMetricType_Signed: if (metric_info->is_set) { MEMFAULT_LOG_INFO(" %s: %" PRIi32, key_name, value->i32); } else { MEMFAULT_LOG_INFO(" %s: null", key_name); } break; case kMemfaultMetricType_String: MEMFAULT_LOG_INFO(" %s: \"%s\"", key_name, (const char *)value->ptr); break; case kMemfaultMetricType_NumTypes: // To silence -Wswitch-enum default: MEMFAULT_LOG_INFO(" %s: ", key_name); break; } return true; // continue iterating } //! Print all session metrics, skip heartbeat metrics static bool prv_all_sessions_debug_print_filter(MEMFAULT_UNUSED eMfltMetricsSessionIndex ctx_key, eMfltMetricsSessionIndex current_key) { return (current_key != MEMFAULT_METRICS_SESSION_KEY(heartbeat)); } //! Print all metrics from one session only static bool prv_session_debug_print_filter(eMfltMetricsSessionIndex ctx_key, eMfltMetricsSessionIndex current_key) { return (current_key == ctx_key); } void memfault_metrics_session_debug_print(eMfltMetricsSessionIndex session_key) { prv_heartbeat_timer_update(); MEMFAULT_LOG_INFO("Metrics keys/values:"); sMetricDebugPrintCtx ctx = { .session_key = session_key, .print_filter = &prv_session_debug_print_filter, }; memfault_metrics_heartbeat_iterate(prv_metrics_debug_print, (void *)&ctx); } void memfault_metrics_all_sessions_debug_print(void) { prv_heartbeat_timer_update(); MEMFAULT_LOG_INFO("Metrics keys/values:"); sMetricDebugPrintCtx ctx = { .print_filter = &prv_all_sessions_debug_print_filter, }; memfault_metrics_heartbeat_iterate(prv_metrics_debug_print, (void *)&ctx); } void memfault_metrics_heartbeat_debug_print(void) { memfault_metrics_session_debug_print(MEMFAULT_METRICS_SESSION_KEY(heartbeat)); } void memfault_metrics_heartbeat_debug_trigger(void) { prv_heartbeat_timer(); } void memfault_metrics_heartbeat_collect(void) { prv_heartbeat_update(); } #if MEMFAULT_METRICS_SESSIONS_ENABLED //! Called on boot, this function checks if the reboot was unexpected. If so, //! any session that was active at time of reboot is triggered to record an //! `operational_crash=1` metric, and serialized to storage. static void prv_session_check_for_unexpected_reboot(void) { int rv = -1; bool unexpected_reboot; for (eMfltMetricsSessionIndex session_key = (eMfltMetricsSessionIndex)0; session_key < (MEMFAULT_ARRAY_SIZE(s_memfault_metrics_operational_crashes_keys)); session_key++) { // This table always ends with a blank entry, so we can stop one iteration // early (or if no sessions were defined). Some compilers warn on empty // arrays. if (session_key == MEMFAULT_ARRAY_SIZE(s_memfault_metrics_operational_crashes_keys) - 1) { break; } // Only run the check for unexpected reboot once, if it hasn't been already // run in this function. Ideally this check would be outside the for loop, // but since we can't conditionally compile this whole block based on if // any sessions are defined, we have to do it this way. if (rv != 0) { rv = memfault_reboot_tracking_get_unexpected_reboot_occurred(&unexpected_reboot); if ((rv != 0) || !unexpected_reboot) { break; } } // If the session was active at time of reboot, serialize a session with the // duration_ms=0 and operational_crashes=1. if (memfault_reboot_tracking_metrics_session_was_active(session_key)) { // Do not start the session timer, to keep the session duration = 0ms prv_metrics_session_start(session_key, false); memfault_metrics_heartbeat_add(s_memfault_metrics_operational_crashes_keys[session_key], 1); // Note: the below function clears the reboot tracking bit for this session, // since the session is now inactive. A second crash after this won't be // recorded as an operational_crash for the session (until the session is // activated). prv_metrics_session_end(session_key, false); } } // Unconditionally clear the reboot tracking data for "active sessions". memfault_reboot_tracking_clear_metrics_sessions(); } #endif #if MEMFAULT_METRICS_RESTORE_STATE const void *memfault_metrics_get_state(void) { // Refresh the saved reliability context sMemfaultMetricsReliabilityCtx *ctx = memfault_metrics_reliability_get_ctx(); s_memfault_metrics_ctx.reliability_ctx = *ctx; return (const void *)&s_memfault_metrics_ctx; } #endif int memfault_metrics_boot(const sMemfaultEventStorageImpl *storage_impl, const sMemfaultMetricBootInfo *info) { if (storage_impl == NULL || info == NULL) { return MEMFAULT_METRICS_TYPE_BAD_PARAM; } if (!memfault_serializer_helper_check_storage_size( storage_impl, memfault_metrics_heartbeat_compute_worst_case_storage_size, "metrics")) { return MEMFAULT_METRICS_STORAGE_TOO_SMALL; } #if MEMFAULT_METRICS_RESTORE_STATE const bool restored = memfault_metrics_restore_state(&s_memfault_metrics_ctx); if (restored) { // If we restored the state, attach the storage impl to the restored context s_memfault_metrics_ctx.storage_impl = storage_impl; memfault_metrics_reliability_boot(&s_memfault_metrics_ctx.reliability_ctx); } else // Otherwise, continue with normal metrics boot sequence #endif // MEMFAULT_METRICS_RESTORE_STATE { s_memfault_metrics_ctx.storage_impl = storage_impl; // Reset reliability state memfault_metrics_reliability_boot(NULL); prv_reset_metrics(true, MEMFAULT_METRICS_SESSION_KEY(heartbeat)); int rv = MEMFAULT_METRIC_TIMER_START(MemfaultSdkMetric_IntervalMs); if (rv != 0) { return rv; } rv = MEMFAULT_METRIC_SET_UNSIGNED(MemfaultSdkMetric_UnexpectedRebootCount, info->unexpected_reboot_count); if (rv != 0) { return rv; } #if MEMFAULT_METRICS_SESSIONS_ENABLED prv_session_check_for_unexpected_reboot(); #endif } // Finally, start the heartbeat timer. const bool success = memfault_platform_metrics_timer_boot( MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS, prv_heartbeat_timer); if (!success) { return MEMFAULT_METRICS_TIMER_BOOT_FAILED; } #if MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT memfault_platform_metrics_connectivity_boot(); #endif return 0; } ================================================ FILE: components/metrics/src/memfault_metrics_battery.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Memfault Battery Metrics implementation. See header file for details. #include "memfault/config.h" #if MEMFAULT_METRICS_BATTERY_ENABLE #include "memfault/core/debug_log.h" #include "memfault/core/platform/core.h" #include "memfault/metrics/battery.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/battery.h" static struct { //! stores the SOC at the time of the previous data collection. -1 means it //! was invalid int32_t last_state_of_charge; //! start time of the current interval uint64_t start_time_ms; //! since the last data collection, did discharging stop? bool stopped_discharging_during_interval; } s_memfault_battery_ctx; static void prv_accumulate_discharge_session_drop(bool session_valid, uint32_t current_stateofcharge) { const int32_t drop = (int32_t)s_memfault_battery_ctx.last_state_of_charge - (int32_t)current_stateofcharge; if (drop < 0) { // Format drop as percentage with max 3 decimal places const int32_t scale = MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE; const int32_t integer_part = -drop / scale; const uint32_t fractional_part = (((uint32_t)(-drop) % (uint32_t)scale) * 1000) / (uint32_t)scale; (void)integer_part; (void)fractional_part; // to silence unused variable warning if debug logging is disabled MEMFAULT_LOG_DEBUG("Battery drop was negative (-%" PRIi32 ".%03" PRIu32 "%%), skipping", integer_part, fractional_part); } // compute elapsed time since last data collection const uint64_t current_time = memfault_platform_get_time_since_boot_ms(); const uint32_t session_length_ms = current_time - s_memfault_battery_ctx.start_time_ms; s_memfault_battery_ctx.start_time_ms = current_time; if (session_valid && (drop >= 0)) { // explicitly write a 0 drop if there was a 0% SOC change (">=0") during the // discharge, instead of leaving it unset, which would result in CBOR null MEMFAULT_METRIC_SET_UNSIGNED(battery_soc_pct_drop, (uint32_t)drop); // store the session length MEMFAULT_METRIC_SET_UNSIGNED(battery_discharge_duration_ms, session_length_ms); } } void memfault_metrics_battery_stopped_discharging(void) { s_memfault_battery_ctx.stopped_discharging_during_interval = true; } static bool prv_soc_is_valid(int32_t soc) { return (soc >= 0); } //! Called as part of the end-of-heartbeat-interval collection functions void memfault_metrics_battery_collect_data(void) { // cache the stopped_discharging value const bool stopped_discharging_during_interval = s_memfault_battery_ctx.stopped_discharging_during_interval; // now get the current discharging state, which is used to reset the // stopped_discharging context sMfltPlatformBatterySoc soc; int rv; bool is_currently_discharging; { rv = memfault_platform_get_stateofcharge(&soc); is_currently_discharging = soc.discharging; // Reset the stopped-discharging event tracking. Note that there's a race // condition here, if the memfault_metrics_battery_stopped_discharging() // function is called after the above memfault_platform_get_stateofcharge() // call but before the below STR instruction. // // This can result in invalid SOC drop data if the charger is disconnected // before the end of session, because the device was not actually // discharging throughout the whole session. // // Ideally this block would be enclosed in a critical section (or atomic // get-and-set), but that's challenging to support across all our platforms, // so just accept that in extremely rare occasions, a bad session may be // recorded. s_memfault_battery_ctx.stopped_discharging_during_interval = !is_currently_discharging; } // get the current SOC. set it as -1 if it's invalid. const int32_t current_stateofcharge = (rv == 0) ? ((int32_t)soc.soc) : (-1); const bool state_of_charge_valid = prv_soc_is_valid(current_stateofcharge) && prv_soc_is_valid(s_memfault_battery_ctx.last_state_of_charge); if (!state_of_charge_valid) { MEMFAULT_LOG_WARN("Current (%d) or prev (%d) SOC invalid, dropping data", (int)current_stateofcharge, (int)s_memfault_battery_ctx.last_state_of_charge); } // this means we didn't receive the stopped-discharging event as expected if (!is_currently_discharging && !stopped_discharging_during_interval) { MEMFAULT_LOG_WARN("Stopped-discharging event missed, dropping data"); } // the session should be dropped if at any point during the session (including // right now) we weren't discharging const bool session_valid = is_currently_discharging && !stopped_discharging_during_interval && state_of_charge_valid; // accumulate SOC drop prv_accumulate_discharge_session_drop(session_valid, (uint32_t)current_stateofcharge); // save the current SOC s_memfault_battery_ctx.last_state_of_charge = current_stateofcharge; // record instantaneous SOC for single-device history if (prv_soc_is_valid(current_stateofcharge)) { MEMFAULT_METRIC_SET_UNSIGNED(battery_soc_pct, (uint32_t)current_stateofcharge); } } void memfault_metrics_battery_boot(void) { sMfltPlatformBatterySoc soc; int rv = memfault_platform_get_stateofcharge(&soc); // save starting SOC s_memfault_battery_ctx.last_state_of_charge = (rv == 0) ? ((int32_t)soc.soc) : (-1); // if the device is not discharging when it boots, save that condition s_memfault_battery_ctx.stopped_discharging_during_interval = !soc.discharging; s_memfault_battery_ctx.start_time_ms = memfault_platform_get_time_since_boot_ms(); } #endif // MEMFAULT_METRICS_BATTERY_ENABLE ================================================ FILE: components/metrics/src/memfault_metrics_connectivity.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief Connectivity metrics implementation. #include "memfault/metrics/connectivity.h" // non-module includes below #include "memfault/core/debug_log.h" #include "memfault/metrics/metrics.h" // Sync success/failure metrics. The metrics add return code is ignored, this // should not fail, but there's no action to take if it does. #if MEMFAULT_METRICS_SYNC_SUCCESS void memfault_metrics_connectivity_record_sync_success(void) { (void)MEMFAULT_METRIC_ADD(sync_successful, 1); } void memfault_metrics_connectivity_record_sync_failure(void) { (void)MEMFAULT_METRIC_ADD(sync_failure, 1); } #endif // MEMFAULT_METRICS_SYNC_SUCCESS #if MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS void memfault_metrics_connectivity_record_memfault_sync_success(void) { (void)MEMFAULT_METRIC_ADD(sync_memfault_successful, 1); } void memfault_metrics_connectivity_record_memfault_sync_failure(void) { (void)MEMFAULT_METRIC_ADD(sync_memfault_failure, 1); } #endif // MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS #if MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME void memfault_metrics_connectivity_connected_state_change(eMemfaultMetricsConnectivityState state) { switch (state) { case kMemfaultMetricsConnectivityState_Stopped: (void)MEMFAULT_METRIC_TIMER_STOP(connectivity_connected_time_ms); (void)MEMFAULT_METRIC_TIMER_STOP(connectivity_expected_time_ms); break; case kMemfaultMetricsConnectivityState_Started: (void)MEMFAULT_METRIC_TIMER_START(connectivity_expected_time_ms); break; case kMemfaultMetricsConnectivityState_Connected: // In case the "Started" state was skipped, start the expected time timer // here as well (void)MEMFAULT_METRIC_TIMER_START(connectivity_expected_time_ms); (void)MEMFAULT_METRIC_TIMER_START(connectivity_connected_time_ms); break; case kMemfaultMetricsConnectivityState_ConnectionLost: // In case the "Started" state was skipped, start the expected time timer // here as well. "ConnectionLost" means the connection SHOULD be up, but // isn't (void)MEMFAULT_METRIC_TIMER_START(connectivity_expected_time_ms); (void)MEMFAULT_METRIC_TIMER_STOP(connectivity_connected_time_ms); break; case kMemfaultMetricsConnectivityState_NumStates: default: MEMFAULT_LOG_ERROR("Unexpected connection state: %u", state); break; } } #endif // MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME ================================================ FILE: components/metrics/src/memfault_metrics_reliability.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @brief APIs for tracking reliability metrics #include "memfault/metrics/reliability.h" // non-module includes below #include "memfault/components.h" static sMemfaultMetricsReliabilityCtx s_memfault_metrics_reliability_ctx; //! Load optional system state on boot. void memfault_metrics_reliability_boot(sMemfaultMetricsReliabilityCtx *ctx) { if (ctx != NULL) { s_memfault_metrics_reliability_ctx = *ctx; } else { // Set the last heartbeat time to the current time since boot. This supports // a system with a time-since-boot timer that counts through intentional // resets (for example OTA). s_memfault_metrics_reliability_ctx.last_heartbeat_ms = memfault_platform_get_time_since_boot_ms(); } } sMemfaultMetricsReliabilityCtx *memfault_metrics_reliability_get_ctx(void) { return &s_memfault_metrics_reliability_ctx; } // Accumulate operational hours. void memfault_metrics_reliability_collect(void) { // Accumulate ms since last heartbeat const uint32_t time_since_boot_ms = memfault_platform_get_time_since_boot_ms(); const uint32_t this_interval_ms = time_since_boot_ms - s_memfault_metrics_reliability_ctx.last_heartbeat_ms; s_memfault_metrics_reliability_ctx.operational_ms += this_interval_ms; // Count any accumulated hours const uint32_t uptime_hours = s_memfault_metrics_reliability_ctx.operational_ms / 1000 / 3600; // Deduct the counted hours from the accumulated ms s_memfault_metrics_reliability_ctx.operational_ms -= uptime_hours * 3600 * 1000; // Reset the accumulated ms for the next heartbeat s_memfault_metrics_reliability_ctx.last_heartbeat_ms = time_since_boot_ms; // Only report uptime if it's non-zero if (!uptime_hours) { return; } MEMFAULT_METRIC_ADD(operational_hours, (int32_t)uptime_hours); bool unexpected_reboot = false; if (!s_memfault_metrics_reliability_ctx.counted_unexpected_reboot) { s_memfault_metrics_reliability_ctx.counted_unexpected_reboot = true; (void)memfault_reboot_tracking_get_unexpected_reboot_occurred(&unexpected_reboot); } // Deduct an hour from the crashfree hours count if there was an unexpected // reboot. This only applies to the first hour of uptime, which is where the // crash is counted. MEMFAULT_METRIC_ADD(operational_crashfree_hours, (int32_t)(uptime_hours - (uint32_t)unexpected_reboot)); } ================================================ FILE: components/metrics/src/memfault_metrics_serializer.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Utility responsible for serializing out a Memfault Heartbeat. Today, the serialization format //! used is cbor but the actual format used is opaque to an end user and no assumptions should be //! made in user code based on it. #include "memfault/metrics/serializer.h" // non-module headers below #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/event_storage.h" #include "memfault/core/event_storage_implementation.h" #include "memfault/core/platform/device_info.h" #include "memfault/core/serializer_helper.h" #include "memfault/core/serializer_key_ids.h" #include "memfault/metrics/utils.h" #include "memfault/util/cbor.h" typedef struct { sMemfaultCborEncoder encoder; bool compute_worst_case_size; bool encode_success; eMfltMetricsSessionIndex session; } sMemfaultSerializerState; static bool prv_metric_heartbeat_write_integer(sMemfaultSerializerState *state, sMemfaultCborEncoder *encoder, const sMemfaultMetricInfo *metric_info) { if (!metric_info->is_set && !state->compute_worst_case_size) { return memfault_cbor_encode_null(encoder); } if (metric_info->type == kMemfaultMetricType_Unsigned) { const uint32_t value = state->compute_worst_case_size ? UINT32_MAX : metric_info->val.u32; return memfault_cbor_encode_unsigned_integer(encoder, value); } if (metric_info->type == kMemfaultMetricType_Signed) { const int32_t value = state->compute_worst_case_size ? INT32_MIN : metric_info->val.i32; return memfault_cbor_encode_signed_integer(encoder, value); } // Should be unreachable return false; } static bool prv_metric_heartbeat_writer(void *ctx, const sMemfaultMetricInfo *metric_info) { sMemfaultSerializerState *state = (sMemfaultSerializerState *)ctx; sMemfaultCborEncoder *encoder = &state->encoder; // only encode metrics for the session we are interested in if (metric_info->session_key != state->session) { state->encode_success = true; return state->encode_success; } // encode the value switch (metric_info->type) { case kMemfaultMetricType_Timer: { const uint32_t value = state->compute_worst_case_size ? UINT32_MAX : metric_info->val.u32; state->encode_success = memfault_cbor_encode_unsigned_integer(encoder, value); break; } case kMemfaultMetricType_Unsigned: case kMemfaultMetricType_Signed: { state->encode_success = prv_metric_heartbeat_write_integer(state, encoder, metric_info); break; } case kMemfaultMetricType_String: { const char *value = (const char *)metric_info->val.ptr; state->encode_success = memfault_cbor_encode_string(encoder, value); break; } case kMemfaultMetricType_NumTypes: // silence error with -Wswitch-enum default: break; } // only continue iterating if the encode was successful return state->encode_success; } static bool prv_serialize_latest_heartbeat_and_deinit(sMemfaultSerializerState *state) { bool success = false; sMemfaultCborEncoder *encoder = &state->encoder; if (!memfault_serializer_helper_encode_metadata(encoder, kMemfaultEventType_Heartbeat)) { goto cleanup; } // Encode up to "metrics:" section if (!memfault_cbor_encode_unsigned_integer(encoder, kMemfaultEventKey_EventInfo) || !memfault_cbor_encode_dictionary_begin(encoder, 2) || !memfault_serializer_helper_encode_uint32_kv_pair(encoder, kMemfaultHeartbeatInfoKey_Session, state->session) || !memfault_cbor_encode_unsigned_integer(encoder, kMemfaultHeartbeatInfoKey_Metrics) || !memfault_cbor_encode_array_begin(encoder, memfault_metrics_session_get_num_metrics(state->session))) { goto cleanup; } memfault_metrics_heartbeat_iterate(prv_metric_heartbeat_writer, state); success = state->encode_success; cleanup: return success; } static bool prv_encode_cb(MEMFAULT_UNUSED sMemfaultCborEncoder *encoder, void *ctx) { sMemfaultSerializerState *state = (sMemfaultSerializerState *)ctx; return prv_serialize_latest_heartbeat_and_deinit(state); } static size_t prv_compute_worst_case_size(eMfltMetricsSessionIndex session) { sMemfaultSerializerState state = { .compute_worst_case_size = true, .session = session }; return memfault_serializer_helper_compute_size(&state.encoder, prv_encode_cb, &state); } size_t memfault_metrics_heartbeat_compute_worst_case_storage_size(void) { return prv_compute_worst_case_size(MEMFAULT_METRICS_SESSION_KEY(heartbeat)); } size_t memfault_metrics_session_compute_worst_case_storage_size(eMfltMetricsSessionIndex session) { return prv_compute_worst_case_size(session); } bool memfault_metrics_heartbeat_serialize(const sMemfaultEventStorageImpl *storage_impl) { return memfault_metrics_session_serialize(storage_impl, MEMFAULT_METRICS_SESSION_KEY(heartbeat)); } bool memfault_metrics_session_serialize(const sMemfaultEventStorageImpl *storage_impl, eMfltMetricsSessionIndex session) { // Build a heartbeat event, which looks like this: // { // "type": "heartbeat", // "device_serial": "DAABBCCDD", // "software_type": "main", // "software_version": "1.2.3", // "hardware_version": "evt_24", // "event_info": { // "session_key": Heartbeat // "metrics": { // ... heartbeat metrics ... // } // } // NOTE: "sdk_version" is not included, but derived from the CborSchemaVersion // NOTE: We'll always attempt to serialize the heartbeat and rollback if we are out of space // avoiding the need to serialize the data twice sMemfaultSerializerState state = { 0 }; state.session = session; const bool success = memfault_serializer_helper_encode_to_storage(&state.encoder, storage_impl, prv_encode_cb, &state); return success; } ================================================ FILE: components/panics/README.md ================================================ # panics Fault handling, coredump, reboot tracking and reboot loop detection API. See documentation in header files for details on the API itself. ## Application/platform-specific customizations ### Coredump memory regions & storage The file `include/memfault/platform/coredump.h` contains two functions that likely need to be customized to your application: - `memfault_platform_coredump_get_regions()`: the platform reference implementation usually capture all of RAM (if possible on the reference platform). Capturing all of RAM provides the best debugging experience. But this may not feasible, for example if not enough (flash) storage space is available. Or it may not be desirable, for example if RAM contains sensitive information that is not allowed to leave the device. - `memfault_platform_coredump_storage_get_info()` - the platform reference implementation usually allocates a large size for coredump storage. Depending on how you change `memfault_platform_coredump_get_regions()`, you may want to shrink the storage size as well. - `memfault_platform_coredump_storage...()` - the platform reference implementation uses whatever storage the reference platform has handy for storing coredump data. This may or may not be the best choice for your application. Please change the reference implementations in ways that makes sense for your project. ### Exception Handlers This component includes exception handlers that automatically capture a coredump and reboot tracking information. The handlers are strongly defined and use symbol names following common naming conventions for the architecture. For example, for ARM, the ARM CMSIS naming conventions are followed and the handlers are called `HardFault_Handler`, `BusFault_Handler`, etc. Often, RTOSes and BSPs come with weakly defined fault handlers. In those cases, Memfault's handlers will just override the weak ones seamlessly. Occasionally, you may need to rename the handlers. You can do so by defining the appropriate `MEMFAULT_EXC_HANDLER_...` preprocessor defines. See `memfault_fault_handling_*.c` for more details. ================================================ FILE: components/panics/src/memfault_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Logic for saving a coredump to backing storage and reading it out //! //! See https://mflt.io/mcu-coredump-format for a tabular view of the //! Memfault coredump format #include #include #include "memfault/core/build_info.h" #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/math.h" #include "memfault/core/platform/device_info.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" #include "memfault/panics/platform/coredump.h" #define MEMFAULT_COREDUMP_MAGIC 0x45524f43 //! Version 2 //! - If there is not enough storage space for memory regions, //! coredumps will now be truncated instead of failing completely //! - Added sMfltCoredumpFooter to end of coredump. In this region //! we track whether or not a coredump was truncated. #define MEMFAULT_COREDUMP_VERSION 2 typedef MEMFAULT_PACKED_STRUCT MfltCoredumpHeader { uint32_t magic; uint32_t version; uint32_t total_size; uint8_t data[]; } sMfltCoredumpHeader; #define MEMFAULT_COREDUMP_FOOTER_MAGIC 0x504d5544 typedef enum MfltCoredumpFooterFlags { kMfltCoredumpBlockType_SaveTruncated = 0, } eMfltCoredumpFooterFlags; typedef MEMFAULT_PACKED_STRUCT MfltCoredumpFooter { uint32_t magic; uint32_t flags; // reserving for future footer additions such as a CRC over the contents saved uint32_t rsvd[2]; } sMfltCoredumpFooter; typedef MEMFAULT_PACKED_STRUCT MfltCoredumpBlock { eMfltCoredumpBlockType block_type:8; uint8_t rsvd[3]; uint32_t address; uint32_t length; } sMfltCoredumpBlock; typedef MEMFAULT_PACKED_STRUCT MfltTraceReasonBlock { uint32_t reason; } sMfltTraceReasonBlock; // Using ELF machine enum values which is a half word: // https://refspecs.linuxfoundation.org/elf/gabi4%2B/ch4.eheader.html // // NB: We use the upper 16 bits of the MachineType TLV pair in the coredump to // encode additional metadata about the architecture being targeted #define MEMFAULT_MACHINE_TYPE_ARM 40 #define MEMFAULT_MACHINE_TYPE_SUBTYPE_OFFSET 16 //! ESP32 #define MEMFAULT_MACHINE_TYPE_XTENSA 94 //! ESP8266 is kept as a reserved type, it was supported in earlier versions of the SDK #define MEMFAULT_MACHINE_TYPE_XTENSA_LX106 \ ((1 << MEMFAULT_MACHINE_TYPE_SUBTYPE_OFFSET) | MEMFAULT_MACHINE_TYPE_XTENSA) //! ESP32-S2 #define MEMFAULT_MACHINE_TYPE_XTENSA_LX7 \ ((2 << MEMFAULT_MACHINE_TYPE_SUBTYPE_OFFSET) | MEMFAULT_MACHINE_TYPE_XTENSA) //! ESP32-S3 #define MEMFAULT_MACHINE_TYPE_XTENSA_LX7_DUAL \ ((3 << MEMFAULT_MACHINE_TYPE_SUBTYPE_OFFSET) | MEMFAULT_MACHINE_TYPE_XTENSA) //! Cortex-R #define MEMFAULT_MACHINE_TYPE_CORTEX_R \ ((1 << MEMFAULT_MACHINE_TYPE_SUBTYPE_OFFSET) | MEMFAULT_MACHINE_TYPE_ARM) typedef enum MfltCoredumpMachineType { kMfltCoredumpMachineType_None = 0, kMfltCoredumpMachineType_Intel80386 = 3, kMfltCoredumpMachineType_ARM = MEMFAULT_MACHINE_TYPE_ARM, kMfltCoredumpMachineType_Aarch64 = 183, kMfltCoredumpMachineType_Xtensa = MEMFAULT_MACHINE_TYPE_XTENSA, kMfltCoredumpMachineType_XtensaLx106 = MEMFAULT_MACHINE_TYPE_XTENSA_LX106, kMfltCoredumpMachineType_XtensaLx7 = MEMFAULT_MACHINE_TYPE_XTENSA_LX7, kMfltCoredumpMachineType_XtensaLx7Dual = MEMFAULT_MACHINE_TYPE_XTENSA_LX7_DUAL, kMfltCoredumpMachineType_RiscV = 243, kMfltCoredumpMachineType_ARM_Cortex_R = MEMFAULT_MACHINE_TYPE_CORTEX_R, } eMfltCoredumpMachineType; typedef MEMFAULT_PACKED_STRUCT MfltMachineTypeBlock { uint32_t machine_type; } sMfltMachineTypeBlock; typedef struct { // the space available for saving a coredump uint32_t storage_size; // the offset within storage currently being written to uint32_t offset; // set to true when no writes should be performed and only the total size of the write should be // computed bool compute_size_only; // set to true if writing a block was truncated bool truncated; // set to true if a call to "memfault_platform_coredump_storage_write" failed bool write_error; } sMfltCoredumpWriteCtx; // Checks to see if the block is a cached region and applies // required fixups to allow the coredump to properly record // the original cached address and its associated data. Will // succeed if not a cached block or is a valid cached block. // Callers should ignore the region if failure is returned // because the block is not valid. static bool prv_fixup_if_cached_block(sMfltCoredumpRegion *region, uint32_t *cached_address) { if (region->type == kMfltCoredumpRegionType_CachedMemory) { const sMfltCachedBlock *cached_blk = (const sMfltCachedBlock *)region->region_start; if (!cached_blk->valid_cache) { // Ignore this block. return false; } // This is where we want Memfault to indicate the data came from. *cached_address = cached_blk->cached_address; // The cached block is just regular memory. region->type = kMfltCoredumpRegionType_Memory; // Remove our header from the size and region_start // is where we cached the 's data. region->region_size = cached_blk->blk_size; region->region_start = cached_blk->blk; // Must be last operation! } // Success, untouched or fixed up. return true; } static bool prv_platform_coredump_write(const void *data, size_t len, sMfltCoredumpWriteCtx *write_ctx) { // if we are just computing the size needed, don't write any data but keep // a count of how many bytes would be written. if (!write_ctx->compute_size_only && !memfault_platform_coredump_storage_write(write_ctx->offset, data, len)) { write_ctx->write_error = true; return false; } write_ctx->offset += len; return true; } static bool prv_write_block_with_address(eMfltCoredumpBlockType block_type, const void *block_payload, size_t block_payload_size, uint32_t address, sMfltCoredumpWriteCtx *write_ctx, bool word_aligned_reads_only) { // nothing to write, ignore the request if (block_payload_size == 0 || (block_payload == NULL)) { return true; } const size_t total_length = sizeof(sMfltCoredumpBlock) + block_payload_size; const size_t storage_bytes_free = write_ctx->storage_size > write_ctx->offset ? write_ctx->storage_size - write_ctx->offset : 0; if (!write_ctx->compute_size_only && storage_bytes_free < total_length) { // We are trying to write a new block in the coredump and there is not enough // space. Let's see if we can truncate the block to fit in the space that is left write_ctx->truncated = true; if (storage_bytes_free < sizeof(sMfltCoredumpBlock)) { return false; } block_payload_size = MEMFAULT_FLOOR(storage_bytes_free - sizeof(sMfltCoredumpBlock), 4); if (block_payload_size == 0) { return false; } } const sMfltCoredumpBlock blk = { .block_type = block_type, .address = address, .length = block_payload_size, }; if (!prv_platform_coredump_write(&blk, sizeof(blk), write_ctx)) { return false; } if (!word_aligned_reads_only || ((block_payload_size % 4) != 0)) { // no requirements on how the 'address' is read so whatever the user implementation does is fine return prv_platform_coredump_write(block_payload, block_payload_size, write_ctx); } // We have a region that needs to be read 32 bits at a time. // // Typically these are very small regions such as a memory mapped register address const uint32_t *word_data = (const uint32_t *)block_payload; for (uint32_t i = 0; i < block_payload_size / 4; i++) { const uint32_t data = word_data[i]; if (!prv_platform_coredump_write(&data, sizeof(data), write_ctx)) { return false; } } return !write_ctx->truncated; } static bool prv_write_non_memory_block(eMfltCoredumpBlockType block_type, const void *block_payload, size_t block_payload_size, sMfltCoredumpWriteCtx *ctx) { const bool word_aligned_reads_only = false; return prv_write_block_with_address(block_type, block_payload, block_payload_size, 0, ctx, word_aligned_reads_only); } static eMfltCoredumpBlockType prv_region_type_to_storage_type(eMfltCoredumpRegionType type) { switch (type) { case kMfltCoredumpRegionType_ArmV6orV7MpuUnrolled: return kMfltCoredumpRegionType_ArmV6orV7Mpu; case kMfltCoredumpRegionType_ImageIdentifier: case kMfltCoredumpRegionType_Memory: case kMfltCoredumpRegionType_MemoryWordAccessOnly: case kMfltCoredumpRegionType_CachedMemory: default: return kMfltCoredumpBlockType_MemoryRegion; } } static eMfltCoredumpMachineType prv_get_machine_type(void) { return #if defined(MEMFAULT_UNITTEST) kMfltCoredumpMachineType_None #elif MEMFAULT_COMPILER_ARM_CORTEX_M kMfltCoredumpMachineType_ARM #elif MEMFAULT_COMPILER_ARM_V7_A_R kMfltCoredumpMachineType_ARM_Cortex_R #elif defined(__aarch64__) kMfltCoredumpMachineType_Aarch64 #elif defined(__XTENSA__) && defined(__XTENSA_WINDOWED_ABI__) && defined(__XTENSA_SOFT_FLOAT__) // ESP32-S2 has this unique compiler-defined symbol kMfltCoredumpMachineType_XtensaLx7 #elif defined(__XTENSA__) && defined(__XTENSA_WINDOWED_ABI__) && defined(CONFIG_IDF_TARGET_ESP32S3) // rely on Kconfig provided flag for ESP32-S3; compiler defined symbols are identical to ESP32. // ❯ diff -duw <(xtensa-esp32-elf-gcc -dM -E - < /dev/null) <(xtensa-esp32s3-elf-gcc -dM -E - < // /dev/null) kMfltCoredumpMachineType_XtensaLx7Dual #elif defined(__XTENSA__) && defined(__XTENSA_WINDOWED_ABI__) // default xtensa windowed target is vanilla ESP32 kMfltCoredumpMachineType_Xtensa #elif defined(__XTENSA__) // ESP8266 is kept as a reserved type, it was supported in earlier versions of the SDK kMfltCoredumpMachineType_XtensaLx106 #elif defined(__riscv) kMfltCoredumpMachineType_RiscV #elif defined(__i386__) kMfltCoredumpMachineType_Intel80386 #else #error "Coredumps are not supported for target architecture" #endif ; } static bool prv_write_device_info_blocks(sMfltCoredumpWriteCtx *ctx) { struct MemfaultDeviceInfo info; memfault_platform_get_device_info(&info); #if MEMFAULT_COREDUMP_INCLUDE_BUILD_ID sMemfaultBuildInfo build_info; if (memfault_build_info_read(&build_info)) { if (!prv_write_non_memory_block(kMfltCoredumpRegionType_BuildId, build_info.build_id, sizeof(build_info.build_id), ctx)) { return false; } } #endif #if MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL if (info.device_serial) { if (!prv_write_non_memory_block(kMfltCoredumpRegionType_DeviceSerial, info.device_serial, strlen(info.device_serial), ctx)) { return false; } } #endif if (info.software_version) { if (!prv_write_non_memory_block(kMfltCoredumpRegionType_SoftwareVersion, info.software_version, strlen(info.software_version), ctx)) { return false; } } if (info.software_type) { if (!prv_write_non_memory_block(kMfltCoredumpRegionType_SoftwareType, info.software_type, strlen(info.software_type), ctx)) { return false; } } if (info.hardware_version) { if (!prv_write_non_memory_block(kMfltCoredumpRegionType_HardwareVersion, info.hardware_version, strlen(info.hardware_version), ctx)) { return false; } } eMfltCoredumpMachineType machine_type = prv_get_machine_type(); const sMfltMachineTypeBlock machine_block = { .machine_type = (uint32_t)machine_type, }; return prv_write_non_memory_block(kMfltCoredumpRegionType_MachineType, &machine_block, sizeof(machine_block), ctx); } static bool prv_write_coredump_header(size_t total_coredump_size, sMfltCoredumpWriteCtx *ctx) { sMfltCoredumpHeader hdr = (sMfltCoredumpHeader){ .magic = MEMFAULT_COREDUMP_MAGIC, .version = MEMFAULT_COREDUMP_VERSION, .total_size = total_coredump_size, }; return prv_platform_coredump_write(&hdr, sizeof(hdr), ctx); } static bool prv_write_trace_reason(sMfltCoredumpWriteCtx *ctx, uint32_t trace_reason) { sMfltTraceReasonBlock trace_info = { .reason = trace_reason, }; return prv_write_non_memory_block(kMfltCoredumpRegionType_TraceReason, &trace_info, sizeof(trace_info), ctx); } // When copying out some regions (for example, memory or register banks) // we want to make sure we can do word-aligned accesses. static void prv_insert_padding_if_necessary(sMfltCoredumpWriteCtx *write_ctx) { #define MEMFAULT_WORD_SIZE 4 const size_t remainder = write_ctx->offset % MEMFAULT_WORD_SIZE; if (remainder == 0) { return; } #define MEMFAULT_MAX_PADDING_BYTES_NEEDED (MEMFAULT_WORD_SIZE - 1) uint8_t pad_bytes[MEMFAULT_MAX_PADDING_BYTES_NEEDED]; size_t padding_needed = MEMFAULT_WORD_SIZE - remainder; memset(pad_bytes, 0x0, padding_needed); prv_write_non_memory_block(kMfltCoredumpRegionType_PaddingRegion, &pad_bytes, padding_needed, write_ctx); } //! Callback that will be called to write coredump data. typedef bool (*MfltCoredumpReadCb)(uint32_t offset, void *data, size_t read_len); static bool prv_get_info_and_header(sMfltCoredumpHeader *hdr_out, sMfltCoredumpStorageInfo *info_out, MfltCoredumpReadCb coredump_read_cb) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); if (info.size == 0) { return false; // no space for core files! } if (!coredump_read_cb(0, hdr_out, sizeof(*hdr_out))) { // NB: This path is sometimes _expected_. For situations where // memfault_platform_coredump_storage_clear() is an asynchronous operation a caller may return // false for from memfault_coredump_read() to prevent any access to the coredump storage area. return false; } if (info_out) { *info_out = info; } return true; } static bool prv_coredump_get_header(sMfltCoredumpHeader *hdr_out, MfltCoredumpReadCb coredump_read_cb) { return prv_get_info_and_header(hdr_out, NULL, coredump_read_cb); } static bool prv_coredump_header_is_valid(const sMfltCoredumpHeader *hdr) { return (hdr && hdr->magic == MEMFAULT_COREDUMP_MAGIC); } static bool prv_write_regions(sMfltCoredumpWriteCtx *write_ctx, const sMfltCoredumpRegion *regions, size_t num_regions) { for (size_t i = 0; i < num_regions; i++) { prv_insert_padding_if_necessary(write_ctx); // Just in case *regions is some how in r/o memory make a non-const copy // and work with that from here on. sMfltCoredumpRegion region_copy = regions[i]; uint32_t address = (uint32_t)(uintptr_t)region_copy.region_start; if (!prv_fixup_if_cached_block(®ion_copy, &address)) { // We must skip invalid cached blocks. continue; } const bool word_aligned_reads_only = (region_copy.type == kMfltCoredumpRegionType_MemoryWordAccessOnly); if (!prv_write_block_with_address(prv_region_type_to_storage_type(region_copy.type), region_copy.region_start, region_copy.region_size, address, write_ctx, word_aligned_reads_only)) { return false; } } return true; } static bool prv_write_coredump_sections(const sMemfaultCoredumpSaveInfo *save_info, bool compute_size_only, size_t *total_size) { sMfltCoredumpStorageInfo info = { 0 }; sMfltCoredumpHeader hdr = { 0 }; // are there some regions for us to save? size_t num_regions = save_info->num_regions; const sMfltCoredumpRegion *regions = save_info->regions; if ((regions == NULL) || (num_regions == 0)) { // sanity check that we got something valid from the caller return false; } if (!compute_size_only) { if (!memfault_port_coredump_save_begin() || !memfault_platform_coredump_save_begin()) { return false; } // If we are saving a new coredump but one is already stored, don't overwrite it. This way // the first issue which started the crash loop can be determined MfltCoredumpReadCb coredump_read_cb = memfault_platform_coredump_storage_read; if (!prv_get_info_and_header(&hdr, &info, coredump_read_cb)) { return false; } if (prv_coredump_header_is_valid(&hdr)) { return false; // don't overwrite what we got! } } // erase storage provided we aren't just computing the size if (!compute_size_only && !memfault_platform_coredump_storage_erase(0, info.size)) { return false; } sMfltCoredumpWriteCtx write_ctx = { // We will write the header last as a way to mark validity // so advance the offset past it to start .offset = sizeof(hdr), .compute_size_only = compute_size_only, .storage_size = info.size, }; if (write_ctx.storage_size > sizeof(sMfltCoredumpFooter)) { // always leave space for footer write_ctx.storage_size -= sizeof(sMfltCoredumpFooter); } const void *regs = save_info->regs; const size_t regs_size = save_info->regs_size; if (regs != NULL) { #if MEMFAULT_COREDUMP_CPU_COUNT == 1 if (!prv_write_non_memory_block(kMfltCoredumpBlockType_CurrentRegisters, regs, regs_size, &write_ctx)) { return false; } #else // If we have multiple CPUs, we need to save the registers for each CPU. // save_info->regs is an array of CPU0, CPU1, etc. registers. for (size_t i = 0; i < MEMFAULT_COREDUMP_CPU_COUNT; i++) { const size_t cpu_regs_size = regs_size / MEMFAULT_COREDUMP_CPU_COUNT; const uint32_t *cpu_regs = (const uint32_t *)((const uintptr_t)regs + (i * cpu_regs_size)); if (!prv_write_non_memory_block(kMfltCoredumpBlockType_CurrentRegisters, cpu_regs, cpu_regs_size, &write_ctx)) { return false; } } #endif } if (!prv_write_device_info_blocks(&write_ctx)) { return false; } const uint32_t trace_reason = save_info->trace_reason; if (!prv_write_trace_reason(&write_ctx, trace_reason)) { return false; } // write out any architecture specific regions size_t num_arch_regions = 0; const sMfltCoredumpRegion *arch_regions = memfault_coredump_get_arch_regions(&num_arch_regions); size_t num_sdk_regions = 0; const sMfltCoredumpRegion *sdk_regions = memfault_coredump_get_sdk_regions(&num_sdk_regions); const bool write_completed = prv_write_regions(&write_ctx, arch_regions, num_arch_regions) && prv_write_regions(&write_ctx, sdk_regions, num_sdk_regions) && prv_write_regions(&write_ctx, regions, num_regions); if (!write_completed && write_ctx.write_error) { return false; } const sMfltCoredumpFooter footer = (sMfltCoredumpFooter){ .magic = MEMFAULT_COREDUMP_FOOTER_MAGIC, .flags = write_ctx.truncated ? (1 << kMfltCoredumpBlockType_SaveTruncated) : 0, }; write_ctx.storage_size = info.size; if (!prv_platform_coredump_write(&footer, sizeof(footer), &write_ctx)) { return false; } const size_t end_offset = write_ctx.offset; write_ctx.offset = 0; // we are writing the header so reset our write offset const bool success = prv_write_coredump_header(end_offset, &write_ctx); if (success) { *total_size = end_offset; } return success; } 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; prv_write_coredump_sections(save_info, compute_size_only, &total_size); return total_size; } bool memfault_coredump_save(const sMemfaultCoredumpSaveInfo *save_info) { const bool compute_size_only = false; size_t total_size = 0; return prv_write_coredump_sections(save_info, compute_size_only, &total_size); } bool memfault_coredump_has_valid_coredump(size_t *total_size_out) { sMfltCoredumpHeader hdr = { 0 }; // This routine is only called while the system is running so _always_ use the // memfault_coredump_read, which is safe to call while the system is running MfltCoredumpReadCb coredump_read_cb = memfault_coredump_read; if (!prv_coredump_get_header(&hdr, coredump_read_cb)) { return false; } if (!prv_coredump_header_is_valid(&hdr)) { return false; } if (total_size_out) { *total_size_out = hdr.total_size; } return true; } MEMFAULT_WEAK bool memfault_coredump_read(uint32_t offset, void *buf, size_t buf_len) { return memfault_platform_coredump_storage_read(offset, buf, buf_len); } //! Expose a data source for use by the Memfault Packetizer const sMemfaultDataSourceImpl g_memfault_coredump_data_source = { .has_more_msgs_cb = memfault_coredump_has_valid_coredump, .read_msg_cb = memfault_coredump_read, .mark_msg_read_cb = memfault_platform_coredump_storage_clear, }; ================================================ FILE: components/panics/src/memfault_coredump_regions_armv7.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Architecture-specific registers collected collected by Memfault SDK Extra decoding and analysis //! of these registers is provided from the Memfault cloud #include #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #if MEMFAULT_COMPILER_ARM_CORTEX_M #include "memfault/core/math.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" #include "memfault/panics/platform/coredump.h" //! Round up to the nearest multiple of 32. Interrupt state is collected in //! groups of 32 due to register layout. #define MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP \ ((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT + 31) / 32 * 32) MEMFAULT_STATIC_ASSERT(((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP) % 32 == 0) || ((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP) == 496), "Must be a multiple of 32 or 496"); MEMFAULT_STATIC_ASSERT((MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP) <= 512, "Exceeded max possible size"); MEMFAULT_STATIC_ASSERT((MEMFAULT_MPU_REGIONS_TO_COLLECT) <= 16, "Exceeded max possible size"); #if MEMFAULT_COLLECT_FAULT_REGS // Subset of ARMv7-M "System Control and ID blocks" related to fault status // On some ports some of these registers may need to be pre-cached before // the OS consumes them. For simplicity if any need pre-caching then we // grab them all. typedef MEMFAULT_PACKED_STRUCT { uint32_t SHCSR; uint32_t CFSR; uint32_t HFSR; uint32_t DFSR; uint32_t MMFAR; uint32_t BFAR; uint32_t AFSR; } sMfltFaultRegs; #endif /* MEMFAULT_COLLECT_FAULT_REGS */ typedef MEMFAULT_PACKED_STRUCT { uint32_t ICTR; uint32_t ACTLR; uint32_t rsvd; uint32_t SYST_CSR; } sMfltControlRegs; typedef MEMFAULT_PACKED_STRUCT { uint32_t ICSR; uint32_t VTOR; } sMfltIntControlRegs; typedef MEMFAULT_PACKED_STRUCT { uint32_t SHPR1; uint32_t SHPR2; uint32_t SHPR3; } sMfltSysHandlerPriorityRegs; typedef MEMFAULT_PACKED_STRUCT { uint32_t DEMCR; } sMfltDebugExcMonCtrlReg; typedef MEMFAULT_PACKED_STRUCT { // representation for NVIC ISER, ISPR, and IABR ... // A single bit encodes whether or not the interrupt is enabled, pending, active, respectively. uint32_t IxxR[MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP / 32]; } sMfltNvicIserIsprIabr; typedef MEMFAULT_PACKED_STRUCT { // 8 bits are used to encode the priority so 4 interrupts are covered by each register // NB: unimplemented priority levels read back as 0 uint32_t IPR[MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT_ROUNDED_UP / 4]; } sMfltNvicIpr; #if MEMFAULT_COLLECT_MPU_STATE // Number of regions (RNR) is implementation defined so the caller // can configure this precisely based on what their HW provides and // how their software employs the MPU. The array of RBAR/RASR pair // structs attempts to flatten out the HW's paged registers that are // exposed via RNR. RNR (@0xE000ED98) is not included in the dump // as it adds no diagnostic value. typedef MEMFAULT_PACKED_STRUCT { uint32_t TYPE; // 0xE000ED90 uint32_t CTRL; // 0xE000ED94 struct { uint32_t RBAR; // 0xE000ED9C, Indexed by RNR uint32_t RASR; // 0xE000EDA0, Indexed by RNR } pair[MEMFAULT_MPU_REGIONS_TO_COLLECT]; } sMfltMpuRegs; #endif /* MEMFAULT_COLLECT_MPU_STATE */ #if !MEMFAULT_COLLECT_FAULT_REGS #define FAULT_REG_REGION_TYPE kMfltCoredumpRegionType_CachedMemory #define FAULT_REG_REGION_START (0) #define FAULT_REG_REGION_SIZE (0) #elif MEMFAULT_CACHE_FAULT_REGS #define FAULT_REG_REGION_TYPE kMfltCoredumpRegionType_CachedMemory #define FAULT_REG_REGION_START ((void *)&s_cached_fault_regs) #define FAULT_REG_REGION_SIZE (sizeof(s_cached_fault_regs)) // Raw buffer for a sMfltCachedBlock. static uint32_t s_cached_fault_regs[MEMFAULT_CACHE_BLOCK_SIZE_WORDS(sizeof(sMfltFaultRegs))]; // This fault register harvester function allows us to get unadulterated copies // of the ARM fault registers before they are modified (cleared) by an OS. We send // this information in place of whatever may be in the HW register by the // time we get to them. void memfault_coredump_cache_fault_regs(void) { sMfltCachedBlock *fault_regs = (sMfltCachedBlock *)&s_cached_fault_regs[0]; fault_regs->cached_address = 0xE000ED24; // SCB->SHCSR const volatile uint32_t *const hwreg = (uint32_t *)fault_regs->cached_address; sMfltFaultRegs *const scb = (sMfltFaultRegs *)fault_regs->blk; scb->SHCSR = hwreg[offsetof(sMfltFaultRegs, SHCSR) / sizeof(*hwreg)]; scb->CFSR = hwreg[offsetof(sMfltFaultRegs, CFSR) / sizeof(*hwreg)]; scb->HFSR = hwreg[offsetof(sMfltFaultRegs, HFSR) / sizeof(*hwreg)]; scb->DFSR = hwreg[offsetof(sMfltFaultRegs, DFSR) / sizeof(*hwreg)]; scb->MMFAR = hwreg[offsetof(sMfltFaultRegs, MMFAR) / sizeof(*hwreg)]; scb->BFAR = hwreg[offsetof(sMfltFaultRegs, BFAR) / sizeof(*hwreg)]; scb->AFSR = hwreg[offsetof(sMfltFaultRegs, AFSR) / sizeof(*hwreg)]; fault_regs->blk_size = sizeof(sMfltFaultRegs); fault_regs->valid_cache = true; } #else #define FAULT_REG_REGION_TYPE kMfltCoredumpRegionType_MemoryWordAccessOnly #define FAULT_REG_REGION_START 0xE000ED24 // Start at SHCSR #define FAULT_REG_REGION_SIZE (sizeof(sMfltFaultRegs)) #endif const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) { #if MEMFAULT_COLLECT_MPU_STATE static sMfltMpuRegs s_mflt_mpu_regs; // We need to unroll the MPU registers from hardware into the representation // that is parsed by the Memfault cloud. Be sure this algorithm matches the // Python version in memfault_gdb.py. s_mflt_mpu_regs.TYPE = *(uint32_t volatile *)0xE000ED90; if (s_mflt_mpu_regs.TYPE) { // MPU is implemented, get the region count but don't // exceed the caller's limit if smaller than the HW supports. size_t num_mpu_regions = (s_mflt_mpu_regs.TYPE >> 8) & 0xFF; num_mpu_regions = MEMFAULT_MIN(num_mpu_regions, MEMFAULT_ARRAY_SIZE(s_mflt_mpu_regs.pair)); // Save CTRL but skip RNR as it has no debug value. s_mflt_mpu_regs.CTRL = *(uint32_t volatile *)0xE000ED94; // Unroll the paged register pairs into our array representation by select-and-read. for (size_t region = 0; region < num_mpu_regions; ++region) { *(uint32_t volatile *)0xE000ED98 = region; s_mflt_mpu_regs.pair[region].RBAR = *(uint32_t volatile *)0xE000ED9C; s_mflt_mpu_regs.pair[region].RASR = *(uint32_t volatile *)0xE000EDA0; } } #endif /* MEMFAULT_COLLECT_MPU_STATE */ #if MEMFAULT_CACHE_FAULT_REGS // Cache the faults registers if it hasn't happened already sMfltCachedBlock *fault_regs = (sMfltCachedBlock *)&s_cached_fault_regs[0]; if (!fault_regs->valid_cache) { memfault_coredump_cache_fault_regs(); } #endif static const sMfltCoredumpRegion s_coredump_regions[] = { { .type = FAULT_REG_REGION_TYPE, .region_start = (void *)FAULT_REG_REGION_START, .region_size = FAULT_REG_REGION_SIZE }, #if MEMFAULT_COLLECT_INTERRUPT_STATE { .type = kMfltCoredumpRegionType_MemoryWordAccessOnly, .region_start = (void *)0xE000ED18, .region_size = sizeof(sMfltSysHandlerPriorityRegs) }, { .type = kMfltCoredumpRegionType_MemoryWordAccessOnly, .region_start = (void *)0xE000E004, .region_size = sizeof(sMfltControlRegs) }, { .type = kMfltCoredumpRegionType_MemoryWordAccessOnly, .region_start = (void *)0xE000ED04, .region_size = sizeof(sMfltIntControlRegs) }, { .type = kMfltCoredumpRegionType_MemoryWordAccessOnly, .region_start = (void *)0xE000EDFC, .region_size = sizeof(sMfltDebugExcMonCtrlReg) }, { .type = kMfltCoredumpRegionType_MemoryWordAccessOnly, .region_start = (void *)0xE000E100, .region_size = sizeof(sMfltNvicIserIsprIabr) }, { .type = kMfltCoredumpRegionType_MemoryWordAccessOnly, .region_start = (void *)0xE000E200, .region_size = sizeof(sMfltNvicIserIsprIabr) }, { .type = kMfltCoredumpRegionType_MemoryWordAccessOnly, .region_start = (void *)0xE000E300, .region_size = sizeof(sMfltNvicIserIsprIabr) }, { .type = kMfltCoredumpRegionType_MemoryWordAccessOnly, .region_start = (void *)0xE000E400, .region_size = sizeof(sMfltNvicIpr) }, #endif /* MEMFAULT_COLLECT_INTERRUPT_STATE */ #if MEMFAULT_COLLECT_MPU_STATE { .type = kMfltCoredumpRegionType_ArmV6orV7MpuUnrolled, .region_start = (void *)&s_mflt_mpu_regs, // Cannot point at the hardware, needs processing .region_size = sizeof(s_mflt_mpu_regs) }, #endif /* MEMFAULT_COLLECT_MPU_STATE */ }; *num_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions); return &s_coredump_regions[0]; } #endif /* MEMFAULT_COMPILER_ARM_CORTEX_M */ ================================================ FILE: components/panics/src/memfault_coredump_sdk_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Enables collection of memory regions used by the Memfault SDK based on compile time defines. //! //! This is intended for users that are collecting select RAM regions in a coredump //! (instead of all of RAM). //! //! Regions to collect are controlled by the following compile time defines: //! //! MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS=1 //! - Collects the regions used by the Memfault logging APIs. When enabled, //! any logs collected will be included in the coredump. More details //! can be found at https://mflt.io/logging //! MEMFAULT_COREDUMP_COLLECT_HEAP_STATS=1 //! - Collects the regions used by the Memfault heap stat APIs. When enabled, //! statistics around heap allocations will be included in the coredump to facilitate //! debug #include "memfault/config.h" #include "memfault/core/heap_stats_impl.h" #include "memfault/core/log_impl.h" #include "memfault/core/math.h" #include "memfault/core/task_watchdog_impl.h" #include "memfault/panics/coredump_impl.h" // The number of regions to be collected varies per configuration // clang-format off #define CONFIGURED_NUM_REGIONS_TO_COLLECT (0 \ + (MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS ? MEMFAULT_LOG_NUM_RAM_REGIONS : 0) \ + (MEMFAULT_COREDUMP_COLLECT_HEAP_STATS ? MEMFAULT_HEAP_STATS_NUM_RAM_REGIONS : 0) \ + (MEMFAULT_COREDUMP_COLLECT_TASK_WATCHDOG_REGION ? 1 : 0)\ ) // clang-format on // if no special sdk regions are enabled, default array size to 1 to silence // compiler warnings #define NUM_REGIONS_TO_COLLECT MEMFAULT_MAX(CONFIGURED_NUM_REGIONS_TO_COLLECT, 1) const sMfltCoredumpRegion *memfault_coredump_get_sdk_regions(size_t *num_regions) { static sMfltCoredumpRegion s_sdk_coredump_regions[NUM_REGIONS_TO_COLLECT]; size_t total_regions = 0; #if MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS sMemfaultLogRegions regions = { 0 }; if (memfault_log_get_regions(®ions)) { for (size_t i = 0; i < MEMFAULT_LOG_NUM_RAM_REGIONS; i++) { sMemfaultLogMemoryRegion *log_region = ®ions.region[i]; s_sdk_coredump_regions[total_regions] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(log_region->region_start, log_region->region_size); total_regions++; } } #endif #if MEMFAULT_COREDUMP_COLLECT_HEAP_STATS if (!memfault_heap_stats_empty()) { s_sdk_coredump_regions[total_regions] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&g_memfault_heap_stats, sizeof(g_memfault_heap_stats)); total_regions++; s_sdk_coredump_regions[total_regions] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( &g_memfault_heap_stats_pool, sizeof(g_memfault_heap_stats_pool)); total_regions++; } #endif #if MEMFAULT_COREDUMP_COLLECT_TASK_WATCHDOG_REGION s_sdk_coredump_regions[total_regions] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( &g_memfault_task_channel_info, sizeof(g_memfault_task_channel_info)); total_regions++; #endif *num_regions = total_regions; return total_regions != 0 ? &s_sdk_coredump_regions[0] : NULL; } ================================================ FILE: components/panics/src/memfault_coredump_storage_debug.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A collection of utilities that can be used to validate the platform port of //! the memfault coredump storage API is working as expected. //! //! Example Usage: //! //! void validate_coredump_storage_implementation(void) { //! // exercise storage routines used during a fault //! __disable_irq(); //! memfault_coredump_storage_debug_test_begin() //! __enabled_irq(); //! //! // analyze results from test and print results to console //! memfault_coredump_storage_debug_test_finish(); //! } #include #include #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" typedef enum { kMemfaultCoredumpStorageTestOp_Prepare = 0, kMemfaultCoredumpStorageTestOp_Erase, kMemfaultCoredumpStorageTestOp_Write, kMemfaultCoredumpStorageTestOp_Clear, kMemfaultCoredumpStorageTestOp_GetInfo, } eMemfaultCoredumpStorageTestOp; typedef enum { kMemfaultCoredumpStorageResult_Success = 0, kMemfaultCoredumpStorageResult_PlatformApiFail, kMemfaultCoredumpStorageResult_ReadFailed, kMemfaultCoredumpStorageResult_CompareFailed, } eMemfaultCoredumpStorageResult; typedef struct { eMemfaultCoredumpStorageResult result; eMemfaultCoredumpStorageTestOp op; uint32_t offset; const uint8_t *expected_buf; uint32_t size; } sMemfaultCoredumpStorageTestResult; static sMemfaultCoredumpStorageTestResult s_test_result; static uint8_t s_read_buf[16]; static void prv_record_failure(eMemfaultCoredumpStorageTestOp op, eMemfaultCoredumpStorageResult result, uint32_t offset, uint32_t size) { s_test_result = (sMemfaultCoredumpStorageTestResult){ .op = op, .result = result, .offset = offset, .size = size, }; } //! Helper to scrub the read buffer with a pattern not used by the test //! This way even if a call to memfault_platform_coredump_storage_read() //! returns true but does not populate the buffer we will not have a match static void prv_scrub_read_buf(void) { const uint8_t unused_pattern = 0xef; memset(s_read_buf, unused_pattern, sizeof(s_read_buf)); } static bool prv_verify_erased(uint8_t byte) { // NB: Depending on storage topology, pattern can differ // 0x00 if coredump storage is in RAM // 0xFF if coredump storage is some type of flash (i.e NOR, EMMC, etc) return ((byte == 0x00) || (byte == 0xff)); } bool memfault_coredump_storage_debug_test_begin(void) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); if (info.size == 0) { prv_record_failure(kMemfaultCoredumpStorageTestOp_GetInfo, kMemfaultCoredumpStorageResult_PlatformApiFail, 0, info.size); return false; } // 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_port_coredump_save_begin() || !memfault_platform_coredump_save_begin()) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Prepare, kMemfaultCoredumpStorageResult_PlatformApiFail, 0, info.size); return false; } // // Confirm we can erase the storage region // if (!memfault_platform_coredump_storage_erase(0, info.size)) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Erase, kMemfaultCoredumpStorageResult_PlatformApiFail, 0, info.size); return false; } for (size_t i = 0; i < info.size; i += sizeof(s_read_buf)) { prv_scrub_read_buf(); const size_t bytes_to_read = MEMFAULT_MIN(sizeof(s_read_buf), info.size - i); if (!memfault_platform_coredump_storage_read(i, s_read_buf, bytes_to_read)) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Erase, kMemfaultCoredumpStorageResult_ReadFailed, i, bytes_to_read); return false; } for (size_t j = 0; j < bytes_to_read; j++) { if (!prv_verify_erased(s_read_buf[j])) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Erase, kMemfaultCoredumpStorageResult_CompareFailed, j + i, 1); return false; } } } // // Confirm we can write to storage by alternating writes of a 12 byte & 7 byte pattern. // This way we can verify that writes starting at different offsets are working // // The data for memfault coredumps is always written sequentially with the exception of // the 12 byte header which is written last. We will simulate that behavior here. // static const uint8_t pattern1[] = { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab }; MEMFAULT_STATIC_ASSERT(sizeof(pattern1) < sizeof(s_read_buf), "pattern1 is too long"); static const uint8_t pattern2[] = { 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59 }; MEMFAULT_STATIC_ASSERT(sizeof(pattern2) < sizeof(s_read_buf), "pattern2 is too long"); struct { const uint8_t *pattern; size_t len; } patterns[] = { { .pattern = pattern1, .len = sizeof(pattern1) }, { .pattern = pattern2, .len = sizeof(pattern2) }, }; #define MEMFAULT_COREDUMP_STORAGE_HEADER_LEN 12 MEMFAULT_STATIC_ASSERT(sizeof(pattern1) == MEMFAULT_COREDUMP_STORAGE_HEADER_LEN, "pattern1 length must match coredump header size"); // Skip the "header" and begin writing alternating patterns to various offsets size_t offset = sizeof(pattern1); size_t num_writes = 1; while (offset < info.size) { const size_t pattern_idx = num_writes % MEMFAULT_ARRAY_SIZE(patterns); num_writes++; const uint8_t *pattern = patterns[pattern_idx].pattern; size_t pattern_len = patterns[pattern_idx].len; pattern_len = MEMFAULT_MIN(pattern_len, info.size - offset); if (!memfault_platform_coredump_storage_write(offset, pattern, pattern_len)) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Write, kMemfaultCoredumpStorageResult_PlatformApiFail, offset, pattern_len); return false; } offset += pattern_len; } // now simulate writing a coredump header if (!memfault_platform_coredump_storage_write(0, pattern1, sizeof(pattern1))) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Write, kMemfaultCoredumpStorageResult_PlatformApiFail, offset, sizeof(pattern1)); return false; } // now read back what we wrote and confirm it matches the expected pattern sequence offset = 0; num_writes = 0; while (offset < info.size) { const size_t pattern_idx = num_writes % MEMFAULT_ARRAY_SIZE(patterns); num_writes++; const uint8_t *pattern = patterns[pattern_idx].pattern; size_t pattern_len = patterns[pattern_idx].len; pattern_len = MEMFAULT_MIN(pattern_len, info.size - offset); prv_scrub_read_buf(); if (!memfault_platform_coredump_storage_read(offset, s_read_buf, pattern_len)) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Write, kMemfaultCoredumpStorageResult_ReadFailed, offset, pattern_len); return false; } if (memcmp(s_read_buf, pattern, pattern_len) != 0) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Write, kMemfaultCoredumpStorageResult_CompareFailed, offset, pattern_len); s_test_result.expected_buf = pattern; return false; } offset += pattern_len; } s_test_result = (sMemfaultCoredumpStorageTestResult){ .result = kMemfaultCoredumpStorageResult_Success, }; return true; } static bool prv_verify_coredump_clear_operation(void) { memfault_platform_coredump_storage_clear(); const size_t min_clear_size = 1; prv_scrub_read_buf(); // NB: memfault_coredump_read() instead of memfault_platform_coredump_read() here because that's // the routine we use when the system is running (in case that mode needs locking) if (!memfault_coredump_read(0, s_read_buf, min_clear_size)) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Clear, kMemfaultCoredumpStorageResult_ReadFailed, 0, min_clear_size); return false; } if (!prv_verify_erased(s_read_buf[0])) { prv_record_failure(kMemfaultCoredumpStorageTestOp_Clear, kMemfaultCoredumpStorageResult_CompareFailed, 0, 1); return false; } return true; } static void prv_log_error_hexdump(const char *prefix, const uint8_t *buf, size_t buf_len) { // s_read_buf is the largest buffer passed to this function, use this to determine max char buffer // size for hex representation #define MAX_BUF_LEN (sizeof(s_read_buf) * 2 + 1) char hex_buffer[MAX_BUF_LEN]; for (uint32_t j = 0; j < buf_len; ++j) { // Convert byte into hex chars, write up to 3 chars (2 hex digits, 1 null) // MAX_BUF_LEN guarantees enough space to safely store output chars in hex_buffer snprintf(&hex_buffer[j * 2], 3, "%02x", buf[j]); } // make sure buffer is NUL terminated even if buf_len = 0 hex_buffer[buf_len * 2] = '\0'; (void)prefix; MEMFAULT_LOG_ERROR("%s: %s", prefix, hex_buffer); } bool memfault_coredump_storage_debug_test_finish(void) { if ((s_test_result.result == kMemfaultCoredumpStorageResult_Success) && prv_verify_coredump_clear_operation()) { MEMFAULT_LOG_INFO("Coredump Storage Verification Passed"); return true; } MEMFAULT_LOG_ERROR("Coredump Storage Verification Failed"); const char *op_suffix; switch (s_test_result.op) { case kMemfaultCoredumpStorageTestOp_Prepare: op_suffix = "prepare"; break; case kMemfaultCoredumpStorageTestOp_Erase: op_suffix = "erase"; break; case kMemfaultCoredumpStorageTestOp_Write: op_suffix = "write"; break; case kMemfaultCoredumpStorageTestOp_Clear: op_suffix = "clear"; break; case kMemfaultCoredumpStorageTestOp_GetInfo: op_suffix = "get info"; break; default: op_suffix = "unknown"; break; } const char *reason_str = ""; switch (s_test_result.result) { case kMemfaultCoredumpStorageResult_Success: break; case kMemfaultCoredumpStorageResult_PlatformApiFail: reason_str = "Api call failed during"; break; case kMemfaultCoredumpStorageResult_ReadFailed: reason_str = "Call to memfault_platform_coredump_storage_read() failed during"; break; case kMemfaultCoredumpStorageResult_CompareFailed: reason_str = "Read pattern mismatch during"; break; default: reason_str = "Unknown"; break; } (void)reason_str, (void)op_suffix; MEMFAULT_LOG_ERROR("%s memfault_platform_coredump_storage_%s() test", reason_str, op_suffix); MEMFAULT_LOG_ERROR("Storage offset: 0x%08" PRIx32 ", %s size: %d", s_test_result.offset, op_suffix, (int)s_test_result.size); if (s_test_result.result == kMemfaultCoredumpStorageResult_CompareFailed) { if (s_test_result.expected_buf != NULL) { prv_log_error_hexdump("Expected", s_test_result.expected_buf, s_test_result.size); } else if (s_test_result.op != kMemfaultCoredumpStorageTestOp_Write) { MEMFAULT_LOG_ERROR("expected erase pattern is 0xff or 0x00"); } prv_log_error_hexdump("Actual ", s_read_buf, s_test_result.size); } return false; } ================================================ FILE: components/panics/src/memfault_coredump_utils.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Convenience utilities that can be used with coredumps #include "memfault/core/debug_log.h" #include "memfault/panics/coredump.h" bool memfault_coredump_storage_check_size(void) { sMfltCoredumpStorageInfo storage_info = { 0 }; memfault_platform_coredump_storage_get_info(&storage_info); const size_t size_needed = memfault_coredump_storage_compute_size_required(); if (size_needed <= storage_info.size) { return true; } MEMFAULT_LOG_WARN("Coredump storage is %dB but need %dB", (int)storage_info.size, (int)size_needed); return false; } void memfault_coredump_size_and_storage_capacity(size_t *total_size, size_t *capacity) { sMfltCoredumpStorageInfo storage_info = { 0 }; memfault_platform_coredump_storage_get_info(&storage_info); *capacity = storage_info.size; *total_size = memfault_coredump_storage_compute_size_required(); } ================================================ FILE: components/panics/src/memfault_fault_handling_aarch64.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fault handling for AARCH64 based devices #if defined(__aarch64__) #include "memfault/core/platform/core.h" #include "memfault/core/reboot_tracking.h" #include "memfault/panics/arch/arm/aarch64.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" #include "memfault/panics/fault_handling.h" //! Provide stub implementations here to support building only void memfault_platform_halt_if_debugging(void) { } bool memfault_arch_is_inside_isr(void) { return false; } MEMFAULT_WEAK void memfault_platform_fault_handler(MEMFAULT_UNUSED const sMfltRegState *regs, MEMFAULT_UNUSED eMemfaultRebootReason reason) { } const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) { *num_regions = 0; return NULL; } static eMemfaultRebootReason s_crash_reason = kMfltRebootReason_Unknown; MEMFAULT_NORETURN void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { memfault_platform_fault_handler(regs, reason); if (s_crash_reason == kMfltRebootReason_Unknown) { sMfltRebootTrackingRegInfo info = { .pc = regs->pc, .lr = regs->x[30], }; memfault_reboot_tracking_mark_reset_imminent(reason, &info); s_crash_reason = reason; } sMemfaultCoredumpSaveInfo save_info = { .regs = regs, .regs_size = sizeof(*regs), .trace_reason = s_crash_reason, }; sCoredumpCrashInfo info = { .stack_address = (void *)regs->sp, .trace_reason = save_info.trace_reason, .exception_reg_state = regs, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); const bool coredump_saved = memfault_coredump_save(&save_info); if (coredump_saved) { memfault_reboot_tracking_mark_coredump_saved(); } memfault_platform_reboot(); MEMFAULT_UNREACHABLE; } MEMFAULT_NORETURN static void prv_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) { // Only set the crash reason if it's unset, in case we are in a nested assert if (s_crash_reason == kMfltRebootReason_Unknown) { sMfltRebootTrackingRegInfo info = { .pc = (uint32_t)(uintptr_t)pc, .lr = (uint32_t)(uintptr_t)lr, }; s_crash_reason = reason; memfault_reboot_tracking_mark_reset_imminent(s_crash_reason, &info); } // For assert path, we will trap into fault handler __builtin_trap(); } void memfault_fault_handling_assert(void *pc, void *lr) { prv_fault_handling_assert(pc, lr, kMfltRebootReason_Assert); } void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { prv_fault_handling_assert(pc, lr, extra_info->assert_reason); } size_t memfault_coredump_storage_compute_size_required(void) { // actual values don't matter since we are just computing the size sMfltRegState core_regs = { 0 }; sMemfaultCoredumpSaveInfo save_info = { .regs = &core_regs, .regs_size = sizeof(core_regs), .trace_reason = kMfltRebootReason_UnknownError, }; sCoredumpCrashInfo info = { // we'll just pass the current stack pointer, value shouldn't matter .stack_address = (void *)&core_regs, .trace_reason = save_info.trace_reason, .exception_reg_state = NULL, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); return memfault_coredump_get_save_size(&save_info); } #endif /* __aarch64__ */ ================================================ FILE: components/panics/src/memfault_fault_handling_arm.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fault handling for Cortex M based devices #include "memfault/core/compiler.h" #if MEMFAULT_COMPILER_ARM_CORTEX_M #include "memfault/core/platform/core.h" #include "memfault/core/reboot_tracking.h" #include "memfault/panics/arch/arm/cortex_m.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" #include "memfault/panics/fault_handling.h" static eMemfaultRebootReason s_crash_reason = kMfltRebootReason_Unknown; typedef MEMFAULT_PACKED_STRUCT MfltCortexMRegs { uint32_t r0; uint32_t r1; uint32_t r2; uint32_t r3; 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 r12; uint32_t sp; uint32_t lr; uint32_t pc; uint32_t psr; uint32_t msp; uint32_t psp; } sMfltCortexMRegs; size_t memfault_coredump_storage_compute_size_required(void) { // actual values don't matter since we are just computing the size sMfltCortexMRegs core_regs = { 0 }; sMemfaultCoredumpSaveInfo save_info = { .regs = &core_regs, .regs_size = sizeof(core_regs), .trace_reason = kMfltRebootReason_UnknownError, }; const sMfltRegState exception_regs = { // spoof the EXC_RETURN value as "PSP active", to avoid checking PSP // register when computing size. it's unused in bare metal applications. .exc_return = 1 << 2, }; sCoredumpCrashInfo info = { // we'll just pass the current stack pointer, value shouldn't matter .stack_address = (void *)&core_regs, .trace_reason = save_info.trace_reason, .exception_reg_state = &exception_regs, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); return memfault_coredump_get_save_size(&save_info); } #if defined(__CC_ARM) static uint32_t prv_read_psp_reg(void) { register uint32_t reg_val __asm("psp"); return reg_val; } static uint32_t prv_read_msp_reg(void) { register uint32_t reg_val __asm("msp"); return reg_val; } #elif defined(__TI_ARM__) static uint32_t prv_read_psp_reg(void) { return __get_PSP(); } static uint32_t prv_read_msp_reg(void) { return __get_MSP(); } #elif defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__) static uint32_t prv_read_psp_reg(void) { uint32_t reg_val; __asm volatile("mrs %0, psp" : "=r"(reg_val)); return reg_val; } static uint32_t prv_read_msp_reg(void) { uint32_t reg_val; __asm volatile("mrs %0, msp" : "=r"(reg_val)); return reg_val; } #else #error "New compiler to add support for!" #endif #if !MEMFAULT_PLATFORM_FAULT_HANDLER_CUSTOM MEMFAULT_WEAK void memfault_platform_fault_handler(MEMFAULT_UNUSED const sMfltRegState *regs, MEMFAULT_UNUSED eMemfaultRebootReason reason) { } #endif /* MEMFAULT_PLATFORM_FAULT_HANDLER_CUSTOM */ MEMFAULT_USED void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { memfault_platform_fault_handler(regs, reason); if (s_crash_reason == kMfltRebootReason_Unknown) { sMfltRebootTrackingRegInfo info = { .pc = regs->exception_frame->pc, .lr = regs->exception_frame->lr, }; memfault_reboot_tracking_mark_reset_imminent(reason, &info); s_crash_reason = reason; } const bool fpu_stack_space_rsvd = ((regs->exc_return & (1 << 4)) == 0); const bool stack_alignment_forced = ((regs->exception_frame->xpsr & (1 << 9)) != 0); uint32_t sp_prior_to_exception = (uint32_t)regs->exception_frame + (fpu_stack_space_rsvd ? 0x68 : 0x20); if (stack_alignment_forced) { sp_prior_to_exception += 0x4; } // Read the "SPSEL" bit where // 0 = Main Stack Pointer in use prior to exception // 1 = Process Stack Pointer in use prior to exception const bool msp_was_active = (regs->exc_return & (1 << 2)) == 0; sMfltCortexMRegs core_regs = { .r0 = regs->exception_frame->r0, .r1 = regs->exception_frame->r1, .r2 = regs->exception_frame->r2, .r3 = regs->exception_frame->r3, .r4 = regs->r4, .r5 = regs->r5, .r6 = regs->r6, .r7 = regs->r7, .r8 = regs->r8, .r9 = regs->r9, .r10 = regs->r10, .r11 = regs->r11, .r12 = regs->exception_frame->r12, .sp = sp_prior_to_exception, .lr = regs->exception_frame->lr, .pc = regs->exception_frame->pc, .psr = regs->exception_frame->xpsr, .msp = msp_was_active ? sp_prior_to_exception : prv_read_msp_reg(), .psp = !msp_was_active ? sp_prior_to_exception : prv_read_psp_reg(), }; sMemfaultCoredumpSaveInfo save_info = { .regs = &core_regs, .regs_size = sizeof(core_regs), .trace_reason = s_crash_reason, }; sCoredumpCrashInfo info = { .stack_address = (void *)sp_prior_to_exception, .trace_reason = save_info.trace_reason, .exception_reg_state = regs, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); const bool coredump_saved = memfault_coredump_save(&save_info); if (coredump_saved) { memfault_reboot_tracking_mark_coredump_saved(); } #if MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN if (reason == kMfltRebootReason_SoftwareWatchdog) { // In this configuration, just return from the fault handler return; } #endif #if !MEMFAULT_FAULT_HANDLER_RETURN memfault_platform_reboot(); MEMFAULT_UNREACHABLE; #endif } // The fault handling shims below figure out what stack was being used leading up to the exception, // build the sMfltRegState argument and pass that as well as the reboot reason to // memfault_fault_handler #if defined(__CC_ARM) // armcc emits a define for the CPU target. // // Use that information to decide whether or not to pick up the ARMV6M port by default // // Cortex M0 (--cpu=cortex-m0) // __TARGET_CPU_CORTEX_M0 // Cortex M0+ (--cpu=cortex-m0plus or --cpu=cortex-m0+) // __TARGET_CPU_CORTEX_M0PLUS // __TARGET_CPU_CORTEX_M0_ #if defined(__TARGET_CPU_CORTEX_M0) || defined(__TARGET_CPU_CORTEX_M0_) || \ defined(__TARGET_CPU_CORTEX_M0PLUS) #define MEMFAULT_USE_ARMV6M_FAULT_HANDLER 1 #endif #if !defined(MEMFAULT_USE_ARMV6M_FAULT_HANDLER) // Disable formatting; clang-format puts the ALIGN directive on the previous line // clang-format off __asm __forceinline void memfault_fault_handling_shim(int reason) { extern memfault_fault_handler; tst lr, #4 ite eq mrseq r3, msp mrsne r3, psp push {r3-r11, lr} mov r1, r0 mov r0, sp b memfault_fault_handler ALIGN } #else __asm __forceinline void memfault_fault_handling_shim(int reason) { extern memfault_fault_handler; PRESERVE8 mov r1, lr movs r2, #4 tst r1,r2 mrs r12, msp beq msp_active_at_crash mrs r12, psp msp_active_at_crash mov r3, r11 mov r2, r10 mov r1, r9 mov r9, r0 mov r0, r8 push {r0-r3, lr} mov r3, r12 push {r3-r7} mov r0, sp mov r1, r9 ldr r2, =memfault_fault_handler bx r2 ALIGN } #endif MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_HARD_FAULT(void) { ldr r0, =0x9400 // kMfltRebootReason_HardFault ldr r1, =memfault_fault_handling_shim bx r1 ALIGN } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT(void) { ldr r0, =0x9200 // kMfltRebootReason_MemFault ldr r1, =memfault_fault_handling_shim bx r1 ALIGN } //! MemoryManagement_Handler() is the previous default name, supported for //! backwards compatibility #if !defined(MEMFAULT_DISABLE_OLD_MEMMANAGE_HANDLER) MEMFAULT_NAKED_FUNC void MemoryManagement_Handler(void) { ldr r0, =0x9200 // kMfltRebootReason_MemFault ldr r1, =memfault_fault_handling_shim bx r1 ALIGN } #endif MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_BUS_FAULT(void) { ldr r0, =0x9100 // kMfltRebootReason_BusFault ldr r1, =memfault_fault_handling_shim bx r1 ALIGN } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_USAGE_FAULT(void) { ldr r0, =0x9300 // kMfltRebootReason_UsageFault ldr r1, =memfault_fault_handling_shim bx r1 ALIGN } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_NMI(void) { ldr r0, =0x8004 // kMfltRebootReason_Nmi ldr r1, =memfault_fault_handling_shim bx r1 ALIGN } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_WATCHDOG(void) { ldr r0, =0x8006 // kMfltRebootReason_SoftwareWatchdog ldr r1, =memfault_fault_handling_shim bx r1 #if MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN #error \ "Please contact mflt.io/contact-support for assistance enabling watchdog return support for ARM Compiler" #endif ALIGN } // clang-format on #elif defined(__TI_ARM__) // Note: 'reason' is passed as arg0. However we mark the function // as void so the TI compiler does not emit any function prologue // pushing args on the stack MEMFAULT_NAKED_FUNC void memfault_fault_handling_shim(void /* int reason */) { __asm(" tst lr, #4 \n" " ite eq \n" " mrseq r3, msp \n" " mrsne r3, psp \n" " push {r3-r11, lr} \n" " mov r1, r0 \n" " mov r0, sp \n" " b memfault_fault_handler"); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_HARD_FAULT(void) { __asm(" mov r0, #0x9400 \n" // kMfltRebootReason_HardFault " b memfault_fault_handling_shim \n"); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT(void) { __asm(" mov r0, #0x9200 \n" // kMfltRebootReason_MemFault " b memfault_fault_handling_shim \n"); } #if !defined(MEMFAULT_DISABLE_OLD_MEMMANAGE_HANDLER) MEMFAULT_NAKED_FUNC void MemoryManagement_Handler(void) { __asm(" mov r0, #0x9200 \n" // kMfltRebootReason_MemFault " b memfault_fault_handling_shim \n"); } #endif MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_BUS_FAULT(void) { __asm(" mov r0, #0x9100 \n" // kMfltRebootReason_BusFault " b memfault_fault_handling_shim \n"); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_USAGE_FAULT(void) { __asm(" mov r0, #0x9300 \n" // kMfltRebootReason_UsageFault " b memfault_fault_handling_shim \n"); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_NMI(void) { __asm(" mov r0, #0x8004 \n" // kMfltRebootReason_Nmi " b memfault_fault_handling_shim \n"); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_WATCHDOG(void) { #if MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN #error \ "Please contact mflt.io/contact-support for assistance enabling watchdog return support for TI ARM Compiler" #else __asm(" mov r0, #0x8006 \n" // kMfltRebootReason_SoftwareWatchdog " b memfault_fault_handling_shim \n"); #endif } #elif defined(__GNUC__) || defined(__clang__) #if defined(__ARM_ARCH) && (__ARM_ARCH == 6) #define MEMFAULT_USE_ARMV6M_FAULT_HANDLER 1 #endif // Note: ARMV8-M has a subprofile referred to as the "Baseline" implementation // with an instruction set similar to ARMV6-M. See https://mflt.io/armv8m-subprofiles // for more details. #if defined(__ARM_ARCH_8M_BASE__) && (__ARM_ARCH_8M_BASE__ == 1) #define MEMFAULT_USE_ARMV8M_BASE_FAULT_HANDLER 1 #endif #if (!defined(MEMFAULT_USE_ARMV6M_FAULT_HANDLER) && \ !defined(MEMFAULT_USE_ARMV8M_BASE_FAULT_HANDLER)) #define MEMFAULT_HARDFAULT_HANDLING_ASM(_x) \ __asm volatile("tst lr, #4 \n" \ "ite eq \n" \ "mrseq r3, msp \n" \ "mrsne r3, psp \n" \ "push {r3-r11, lr} \n" \ "mov r0, sp \n" \ "ldr r1, =%c0 \n" \ "bl memfault_fault_handler \n" \ : \ : "i"((uint32_t)_x)) #else #define MEMFAULT_HARDFAULT_HANDLING_ASM(_x) \ __asm volatile("mov r0, lr \n" \ "movs r1, #4 \n" \ "tst r0,r1 \n" \ "mrs r12, msp \n" \ "beq msp_active_at_crash_%= \n" \ "mrs r12, psp \n" \ "msp_active_at_crash_%=: \n" \ "mov r0, r8 \n" \ "mov r1, r9 \n" \ "mov r2, r10 \n" \ "mov r3, r11 \n" \ "push {r0-r3, lr} \n" \ "mov r3, r12 \n" \ "push {r3-r7} \n" \ "mov r0, sp \n" \ "ldr r1, =%c0 \n" \ "bl memfault_fault_handler \n" \ : \ : "i"((uint32_t)_x)) #endif MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_HARD_FAULT(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_HardFault); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_MemFault); } #if !defined(MEMFAULT_DISABLE_OLD_MEMMANAGE_HANDLER) MEMFAULT_NAKED_FUNC void MemoryManagement_Handler(void); MEMFAULT_NAKED_FUNC void MemoryManagement_Handler(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_MemFault); } #endif MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_BUS_FAULT(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_BusFault); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_USAGE_FAULT(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_UsageFault); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_NMI(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_Nmi); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_WATCHDOG(void) { #if MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN // For watchdog-return builds, reuse the hardfault handling macro to collect // register state and call memfault_fault_handler, then unwind the stack // frame it created and perform a normal exception return via the original LR. MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_SoftwareWatchdog); #if (!defined(MEMFAULT_USE_ARMV6M_FAULT_HANDLER) && \ !defined(MEMFAULT_USE_ARMV8M_BASE_FAULT_HANDLER)) __asm volatile(" pop {r3-r11, lr} \n" " bx lr "); #else __asm volatile(" pop {r3-r7} \n" " mov r12, r3 \n" " pop {r0-r3, lr} \n" " mov r8, r0 \n" " mov r9, r1 \n" " mov r10, r2 \n" " mov r11, r3 \n" " bx lr "); #endif #else MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_SoftwareWatchdog); #endif } #elif defined(__ICCARM__) #if __ARM_ARCH == 6 #define MEMFAULT_USE_ARMV6M_FAULT_HANDLER 1 #endif #if !defined(MEMFAULT_USE_ARMV6M_FAULT_HANDLER) #define MEMFAULT_HARDFAULT_HANDLING_ASM(_x) \ __asm volatile("tst lr, #4 \n" \ "ite eq \n" \ "mrseq r3, msp \n" \ "mrsne r3, psp \n" \ "push {r3-r11, lr} \n" \ "mov r0, sp \n" \ "mov r1, %0 \n" \ "b memfault_fault_handler \n" \ : \ : "i"(_x)) #else // Note: Below IAR will build the enum value // as part of the prologue to the asm statement and // place the value in r0 #define MEMFAULT_HARDFAULT_HANDLING_ASM(_x) \ __asm volatile("mov r1, lr \n" \ "movs r2, #4 \n" \ "tst r1,r2 \n" \ "mrs r12, msp \n" \ "beq msp_active_at_crash \n" \ "mrs r12, psp \n" \ "msp_active_at_crash: \n" \ "mov r3, r11 \n" \ "mov r2, r10 \n" \ "mov r1, r9 \n" \ "mov r9, r0 \n" \ "mov r0, r8 \n" \ "push {r0-r3, lr} \n" \ "mov r3, r12 \n" \ "push {r3-r7} \n" \ "mov r0, sp \n" \ "mov r1, r9 \n" \ "ldr r2, =memfault_fault_handler \n" \ "bx r2 \n" \ : \ : "r"(_x)) #endif MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_HARD_FAULT(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_HardFault); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_MemFault); } #if !defined(MEMFAULT_DISABLE_OLD_MEMMANAGE_HANDLER) MEMFAULT_NAKED_FUNC void MemoryManagement_Handler(void); MEMFAULT_NAKED_FUNC void MemoryManagement_Handler(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_MemFault); } #endif MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_BUS_FAULT(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_BusFault); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_USAGE_FAULT(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_UsageFault); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_NMI(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_Nmi); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_WATCHDOG(void) { MEMFAULT_HARDFAULT_HANDLING_ASM(kMfltRebootReason_SoftwareWatchdog); #if MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN #error \ "Please contact mflt.io/contact-support for assistance enabling watchdog return support for IAR ARM Compiler" #endif } #else #error "New compiler to add support for!" #endif // The ARM architecture has a reserved instruction that is "Permanently Undefined" and always // generates an Undefined Instruction exception causing an ARM fault handler to be invoked. // // We use this instruction to "trap" into the fault handler logic. We use 'M' (77) as the // immediate value for easy disambiguation from any other udf invocations in a system. // // Disable formatting; clang-format puts the ALIGN directive on the previous line // clang-format off #if defined(__CC_ARM) __asm __forceinline void MEMFAULT_ASSERT_TRAP(void) { PRESERVE8 UND #77 ALIGN } // clang-format on #elif defined(__TI_ARM__) // The TI Compiler doesn't support the udf asm instruction // so we encode the instruction & a nop as a word literal #pragma diag_push #pragma diag_suppress 1119 void MEMFAULT_ASSERT_TRAP(void) { __asm(" .word 3204505165"); // 0xbf00de4d } #pragma diag_pop #else #define MEMFAULT_ASSERT_TRAP() __asm volatile("udf #77") #endif #if defined(__clang__) // When using clang with LTO, the compiler can optimize storing the LR + FP from this function, // disable optimizations to fix MEMFAULT_NO_OPT #endif static void prv_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) { // Only set the crash reason if it's unset, in case we are in a nested assert if (s_crash_reason == kMfltRebootReason_Unknown) { sMfltRebootTrackingRegInfo info = { .pc = (uint32_t)pc, .lr = (uint32_t)lr, }; s_crash_reason = reason; memfault_reboot_tracking_mark_reset_imminent(s_crash_reason, &info); } #if MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED memfault_platform_halt_if_debugging(); #endif MEMFAULT_ASSERT_TRAP(); // We just trap'd into the fault handler logic so it should never be possible to get here but if // we do the best thing that can be done is rebooting the system to recover it. memfault_platform_reboot(); } // ARM compiler 5 requires even more explicit no optimization directives to prevent our assert // functions from optimizing away the callee saved registers. #if defined(__CC_ARM) #pragma push #pragma optimize O0 #endif // Note: These functions are annotated as "noreturn" which can be useful for static analysis. // However, this can also lead to compiler optimizations that make recovering local variables // difficult (such as ignoring ABI requirements to preserve callee-saved registers) MEMFAULT_NO_OPT void memfault_fault_handling_assert(void *pc, void *lr) { prv_fault_handling_assert(pc, lr, kMfltRebootReason_Assert); #if (defined(__clang__) && defined(__ti__)) || defined(__CC_ARM) //! tiarmclang does not respect the no optimization request and will //! strip the pushing callee saved registers making it impossible to recover //! an accurate backtrace so we skip over providing the unreachable hint. #else MEMFAULT_UNREACHABLE; #endif } MEMFAULT_NO_OPT void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { prv_fault_handling_assert(pc, lr, extra_info->assert_reason); #if (defined(__clang__) && defined(__ti__)) || defined(__CC_ARM) //! See comment in memfault_fault_handling_assert for more context #else MEMFAULT_UNREACHABLE; #endif } #if defined(__CC_ARM) #pragma pop #endif #endif /* MEMFAULT_COMPILER_ARM_CORTEX_M */ ================================================ FILE: components/panics/src/memfault_fault_handling_armv7_a_r.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fault handling for ARMv7-A/R based devices #include "memfault/core/compiler.h" #if MEMFAULT_COMPILER_ARM_V7_A_R #include "memfault/components.h" #include "memfault/panics/arch/arm/v7_a_r.h" #include "memfault/panics/coredump_impl.h" //! These are the default exception handler function names, which need to be //! registered in the interrupt vector table for Memfault fault handling to //! work. #if !defined(MEMFAULT_EXC_HANDLER_UNDEFINED_INSTRUCTION) #define MEMFAULT_EXC_HANDLER_UNDEFINED_INSTRUCTION UndefinedInstruction_Handler #endif #if !defined(MEMFAULT_EXC_HANDLER_DATA_ABORT) #define MEMFAULT_EXC_HANDLER_DATA_ABORT DataAbort_Handler #endif #if !defined(MEMFAULT_EXC_HANDLER_PREFETCH_ABORT) #define MEMFAULT_EXC_HANDLER_PREFETCH_ABORT PrefetchAbort_Handler #endif extern void MEMFAULT_EXC_HANDLER_UNDEFINED_INSTRUCTION(void); extern void MEMFAULT_EXC_HANDLER_DATA_ABORT(void); extern void MEMFAULT_EXC_HANDLER_PREFETCH_ABORT(void); static eMemfaultRebootReason s_crash_reason = kMfltRebootReason_Unknown; const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) { *num_regions = 0; return NULL; } bool memfault_arch_is_inside_isr(void) { // read the CPSR register and check if the CPU is in User or System mode. All // other modes are exception/interrupt modes. See the ARMv7-A/R Architecture // Reference Manual section "B1.3.1 ARM processor modes" for these values. #define CPSR_MODE_msk 0x1f #define CPSR_USER_msk 0x10 #define CPSR_SUPERVISOR_msk 0x13 #define CPSR_SYSTEM_msk 0x1f uint32_t cpsr; __asm volatile("mrs %[cpsr], cpsr" : [cpsr] "=r"(cpsr)); const uint32_t mode = cpsr & CPSR_MODE_msk; const bool in_user_mode = (mode == CPSR_USER_msk); const bool in_system_mode = (mode == CPSR_SYSTEM_msk); const bool in_supervisor_mode = (mode == CPSR_SUPERVISOR_msk); return !(in_user_mode || in_system_mode || in_supervisor_mode); } #if defined(__GNUC__) // Coprocessor access macro. This mnemonic is based on: // https://github.com/ARM-software/CMSIS_5/blob/5.9.0/CMSIS/Core_A/Include/cmsis_gcc.h#L838 #define __get_CP(cp, op1, Rt, CRn, CRm, op2) \ __asm volatile("mrc p" #cp ", " #op1 ", %0, c" #CRn ", c" #CRm ", " #op2 \ : "=r"(Rt) \ : \ : "memory") static uint32_t __get_DBGDSCR(void) { uint32_t result; __get_CP(14, 0, result, 0, 1, 0); return result; } #else #error "Unsupported compiler" #endif void memfault_platform_halt_if_debugging(void) { // Check for the "Halting Debug Enable" bit in the DBGDSCR register. If set, // we are in a debugger and should break. uint32_t dbgdscr = __get_DBGDSCR(); #define DBGDSCR_HDBGen_msk (1 << 14) if (dbgdscr & DBGDSCR_HDBGen_msk) { // NB: A breakpoint with value 'M' (77) for easy disambiguation from other breakpoints that may // be used by the system. __asm("BKPT 77"); } } size_t memfault_coredump_storage_compute_size_required(void) { // actual values don't matter since we are just computing the size sMfltRegState core_regs = { 0 }; sMemfaultCoredumpSaveInfo save_info = { .regs = &core_regs, .regs_size = sizeof(core_regs), .trace_reason = kMfltRebootReason_UnknownError, }; sCoredumpCrashInfo info = { // we'll just pass the current stack pointer, value shouldn't matter .stack_address = (void *)&core_regs, .trace_reason = save_info.trace_reason, .exception_reg_state = NULL, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); return memfault_coredump_get_save_size(&save_info); } #if !MEMFAULT_PLATFORM_FAULT_HANDLER_CUSTOM MEMFAULT_WEAK void memfault_platform_fault_handler(MEMFAULT_UNUSED const sMfltRegState *regs, MEMFAULT_UNUSED eMemfaultRebootReason reason) { } #endif // MEMFAULT_PLATFORM_FAULT_HANDLER_CUSTOM MEMFAULT_USED void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { // From "ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition", // section 'B2.4.10 Data Abort exception', we could decode these, need to make // sure we capture the right registers. // // IFSR, Instruction Fault Status Register // // // Data Abort types. // enumeration DAbort{ // DAbort_AccessFlag, // DAbort_Alignment, // DAbort_Background, // DAbort_Domain, // DAbort_Permission, // DAbort_Translation, // DAbort_SyncExternal, // DAbort_SyncExternalonWalk, // DAbort_SyncParity, // DAbort_SyncParityonWalk, // DAbort_AsyncParity, // DAbort_AsyncExternal, // DAbort_SyncWatchpoint, // DAbort_AsyncWatchpoint, // DAbort_TLBConflict, // DAbort_Lockdown, // DAbort_Coproc, // DAbort_ICacheMaint, // }; sMfltRegState core_regs = { .r0 = regs->r0, .r1 = regs->r1, .r2 = regs->r2, .r3 = regs->r3, .r4 = regs->r4, .r5 = regs->r5, .r6 = regs->r6, .r7 = regs->r7, .r8 = regs->r8, .r9 = regs->r9, .r10 = regs->r10, .r11 = regs->r11, .r12 = regs->r12, .sp = regs->sp, .lr = regs->lr, .pc = regs->pc, .cpsr = regs->cpsr, }; memfault_platform_fault_handler(regs, reason); if (s_crash_reason == kMfltRebootReason_Unknown) { sMfltRebootTrackingRegInfo info = { .pc = core_regs.pc, .lr = core_regs.lr, }; memfault_reboot_tracking_mark_reset_imminent(reason, &info); s_crash_reason = reason; } sMemfaultCoredumpSaveInfo save_info = { .regs = &core_regs, .regs_size = sizeof(core_regs), .trace_reason = s_crash_reason, }; sCoredumpCrashInfo info = { .stack_address = (void *)core_regs.sp, .trace_reason = save_info.trace_reason, .exception_reg_state = regs, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); const bool coredump_saved = memfault_coredump_save(&save_info); if (coredump_saved) { memfault_reboot_tracking_mark_coredump_saved(); } memfault_platform_reboot(); MEMFAULT_UNREACHABLE; } // The fault handling shims below figure out what stack was being used leading // up to the exception, build the sMfltRegState argument and pass that as well // as the reboot reason to memfault_fault_handler #if defined(__GNUC__) // Processor mode values, used when saving LR and SPSR to the appropriate stack. // From https://developer.arm.com/documentation/dui0801/a/CHDEDCCD // Defined as strings for macro concatenation below #define CPU_MODE_FIQ_STR "0x11" #define CPU_MODE_IRQ_STR "0x12" #define CPU_MODE_SUPERVISOR_STR "0x13" #define CPU_MODE_ABORT_STR "0x17" #define CPU_MODE_UNDEFINED_STR "0x1b" #define CPU_MODE_SYSTEM_STR "0x1f" // We push callee r0-r12, lr, pc, and cpsr onto the stack, then pass the stack // pointer to the C memfault_fault_handler. // // Note that r0-r7 are shared across all execution modes, and r8-r12 are // shared in all modes except for FIQ. // // We need to ensure that we enter SYS MODE rather than USER MODE, so we // perform a check and mask if the fault occurred in USER mode. // // In order to preserve the layout of `MfltRegState` and ensure that we don't // clobber any registers, pushing to the stack to the stack is done out of // order, moving the stack pointer around manually to ensure ordering is // consistent. // // Here's a worked example of the position the registers are saved on the stack: // // sp = 128 <- starting example stack pointer value // // // First push callee pc (exception handler adjusted lr) + spsr (callee cpsr). // // using 'srsdb' // cpsr @ 124 // pc @ 120 // // // Now, create a hole for the user mode registers. // sp -> 92 // // // Now push r0-r7 to the stack // r7 @ 88 // r6 @ 84 // r5 @ 80 // r4 @ 76 // r3 @ 72 // r2 @ 68 // r1 @ 64 // r0 @ 60 <- this is the struct address we pass to memfault_fault_handler // // // Now enter user mode, with r2 address @ 120 (the hole we made earlier) // // Now push user mode r8-r12, sp, lr, using r2 as the stack pointer // lr @ 116 // sp @ 112 // r12 @ 108 // r11 @ 104 // r10 @ 100 // r9 @ 96 // r8 @ 92 // // // Now switch back to exception mode, set up args in r0 & r1, and call the // // fault handler. #define MEMFAULT_FAULT_HANDLING_ASM(_mode_string, _reason) \ __asm volatile(".arm \n" /* assemble in ARM mode so we can copy sp with a stm instruction */ \ \ "srsdb sp!, #" _mode_string " \n" /* Push PC + CPSR of previous mode */ \ "sub sp, sp, #28 \n" /* Want r0-r7 after r8 - r14, make space */ \ "stmfd sp!, {r0-r7} \n" /* Push r0-r7, shared amongst all modes */ \ "add r2, sp, #60 \n" /* r2 for previous mode to push r8-r14 */ \ \ "mrs r3, cpsr \n" /* r3: CPSR to return to exception mode */ \ "mrs r4, spsr \n" /* Get the previous mode CPSR */ \ "tst r4, #0xf \n" /* Set Z Flag if in USR mode */ \ "orreq r4, r4, #0xf \n" /* r4: Previous mode changed to SYS if was USR */ \ \ "msr CPSR_c, r4 \n" /* Enter the previous mode */ \ "stmfd r2!, {r8-r12, sp, lr} \n" /* Push all banked registers */ \ \ "msr CPSR_cxsf, r3 \n" /* Back to our exception mode */ \ \ "mov r0, sp \n" /* arg0 = sp */ \ "ldr r1, =%c0 \n" /* arg1 = reason */ \ "b memfault_fault_handler \n" \ : \ : "i"((uint16_t)_reason)) // From the "ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition", // Table B1-7, "Offsets applied to Link value for exceptions taken to PL1 modes" // | |Offset, for | // | |processor state of:| // |Exception |ARM |Thumb|Jazelle | // |Undefined Instruction |+4 |+2 |-b | // |Supervisor Call |None|None |-c | // |Secure Monitor Call |None|None |-c | // |Prefetch Abort |+4 |+4 |+4 | // |Data Abort |+8 |+8 |+8 | // |Virtual Abort |+8 |+8 |+8 | // |IRQ or FIQ |+4 |+4 |+4 | // |Virtual IRQ or Virtual FIQ|+4 |+4 |+4 | MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_UNDEFINED_INSTRUCTION(void) { __asm volatile( // for undefined instruction fault, the PC is offset by 2 if in Thumb // mode, and 4 if in ARM mode. assemble in ARM mode to avoid ITE block. ".arm \n" "push {r0} \n" "mrs r0, spsr \n" "tst r0, #0x20 \n" // thumb execution state bit "subeq lr, #4 \n" // ARM mode (!thumb_bit) "subne lr, #2 \n" // Thumb mode "pop {r0} \n"); MEMFAULT_FAULT_HANDLING_ASM(CPU_MODE_UNDEFINED_STR, kMfltRebootReason_UsageFault); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_DATA_ABORT(void) { // data abort applies a 8 byte offset to PC regardless of instruction mode __asm volatile("sub lr, #8"); MEMFAULT_FAULT_HANDLING_ASM(CPU_MODE_ABORT_STR, kMfltRebootReason_MemFault); } MEMFAULT_NAKED_FUNC void MEMFAULT_EXC_HANDLER_PREFETCH_ABORT(void) { // prefetch abort applies a 4 byte offset to PC regardless of instruction mode __asm volatile("sub lr, #4"); MEMFAULT_FAULT_HANDLING_ASM(CPU_MODE_ABORT_STR, kMfltRebootReason_BusFault); } // TODO, watchdog interrupt wrapper // MEMFAULT_NAKED_FUNC // void MEMFAULT_EXC_HANDLER_WATCHDOG(void) { // MEMFAULT_FAULT_HANDLING_ASM(kMfltRebootReason_SoftwareWatchdog); // } #elif defined(__TI_ARM__) // Currently only support GCC. See 72bca31346146ad0ff51b02d777d19efb28d6bbe for // partial TI-ARM compiler support, if needed in the future. #error "TI ARM compiler is not supported" #else #error "New compiler to add support for!" #endif // The ARM architecture has a reserved instruction that is "Permanently // Undefined" and always generates an Undefined Instruction exception causing an // ARM fault handler to be invoked. // // We use this instruction to "trap" into the fault handler logic. We use 'M' // (77) as the immediate value for easy disambiguation from any other udf // invocations in a system. #if defined(__GNUC__) #define MEMFAULT_ASSERT_TRAP() __asm volatile("udf #77") #else #error "Unsupported compiler" #endif static void prv_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) { // Only set the crash reason if it's unset, in case we are in a nested assert if (s_crash_reason == kMfltRebootReason_Unknown) { sMfltRebootTrackingRegInfo info = { .pc = (uint32_t)pc, .lr = (uint32_t)lr, }; s_crash_reason = reason; memfault_reboot_tracking_mark_reset_imminent(s_crash_reason, &info); } #if MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED memfault_platform_halt_if_debugging(); #endif MEMFAULT_ASSERT_TRAP(); // We just trap'd into the fault handler logic so it should never be possible // to get here but if we do the best thing that can be done is rebooting the // system to recover it. memfault_platform_reboot(); } // Note: These functions are annotated as "noreturn" which can be useful for // static analysis. However, this can also lead to compiler optimizations that // make recovering local variables difficult (such as ignoring ABI requirements // to preserve callee-saved registers) MEMFAULT_NO_OPT void memfault_fault_handling_assert(void *pc, void *lr) { prv_fault_handling_assert(pc, lr, kMfltRebootReason_Assert); #if (defined(__clang__) && defined(__ti__)) //! tiarmclang does not respect the no optimization request and will //! strip the pushing callee saved registers making it impossible to recover //! an accurate backtrace so we skip over providing the unreachable hint. #else MEMFAULT_UNREACHABLE; #endif } MEMFAULT_NO_OPT void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { prv_fault_handling_assert(pc, lr, extra_info->assert_reason); #if (defined(__clang__) && defined(__ti__)) //! See comment in memfault_fault_handling_assert for more context #else MEMFAULT_UNREACHABLE; #endif } #endif // MEMFAULT_COMPILER_ARM_V7_A_R ================================================ FILE: components/panics/src/memfault_fault_handling_posix.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fault handling for Posix (i386) #if defined(__i386__) #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_tracking.h" #include "memfault/panics/arch/posix/posix.h" #include "memfault/panics/assert.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) { *num_regions = 0; return NULL; } static eMemfaultRebootReason s_crash_reason = kMfltRebootReason_Unknown; static void prv_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) { if (s_crash_reason != kMfltRebootReason_Unknown) { // we've already been called once, ignore the second call return; } sMfltRebootTrackingRegInfo info = { .pc = (uint32_t)pc, .lr = (uint32_t)lr, }; s_crash_reason = reason; memfault_reboot_tracking_mark_reset_imminent(s_crash_reason, &info); } void memfault_platform_halt_if_debugging(void) { // unimplemented } bool memfault_arch_is_inside_isr(void) { return false; // unimplemented } MEMFAULT_NO_OPT void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { prv_fault_handling_assert(pc, lr, extra_info->assert_reason); memfault_platform_reboot(); MEMFAULT_UNREACHABLE; } MEMFAULT_NO_OPT void memfault_fault_handling_assert(void *pc, void *lr) { prv_fault_handling_assert(pc, lr, kMfltRebootReason_Assert); memfault_platform_reboot(); MEMFAULT_UNREACHABLE; } void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { if (s_crash_reason == kMfltRebootReason_Unknown) { // skip LR saving here. prv_fault_handling_assert((void *)regs->eip, (void *)0, reason); } sMemfaultCoredumpSaveInfo save_info = { .regs = regs, .regs_size = sizeof(*regs), .trace_reason = s_crash_reason, }; sCoredumpCrashInfo info = { .stack_address = (void *)regs->esp, .trace_reason = save_info.trace_reason, .exception_reg_state = regs, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); const bool coredump_saved = memfault_coredump_save(&save_info); if (coredump_saved) { memfault_reboot_tracking_mark_coredump_saved(); } } size_t memfault_coredump_storage_compute_size_required(void) { // actual values don't matter since we are just computing the size sMfltRegState core_regs[MEMFAULT_COREDUMP_CPU_COUNT] = { 0 }; sMemfaultCoredumpSaveInfo save_info = { .regs = &core_regs, .regs_size = sizeof(core_regs), .trace_reason = kMfltRebootReason_UnknownError, }; sCoredumpCrashInfo info = { // we'll just pass the current stack pointer, value shouldn't matter .stack_address = (void *)&core_regs, .trace_reason = save_info.trace_reason, .exception_reg_state = NULL, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); return memfault_coredump_get_save_size(&save_info); } #endif /* __i386__ */ ================================================ FILE: components/panics/src/memfault_fault_handling_riscv.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fault handling for RISC-V based architectures #if defined(__riscv) #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_tracking.h" #include "memfault/panics/arch/riscv/riscv.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" #include "memfault/panics/fault_handling.h" const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) { *num_regions = 0; return NULL; } static eMemfaultRebootReason s_crash_reason = kMfltRebootReason_Unknown; static void prv_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) { if (s_crash_reason != kMfltRebootReason_Unknown) { // we've already been called once, ignore the second call return; } sMfltRebootTrackingRegInfo info = { .pc = (uint32_t)pc, .lr = (uint32_t)lr, }; s_crash_reason = reason; memfault_reboot_tracking_mark_reset_imminent(s_crash_reason, &info); } void memfault_arch_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) { prv_fault_handling_assert(pc, lr, reason); } // For non-esp-idf riscv implementations, provide a full assert handler and // other utilities. #if defined(__ZEPHYR__) #include // Zephyr RISC-V targets have different ways to check if a debugger is // attached #if defined(CONFIG_MEMFAULT_SOC_FAMILY_ESP32) #include void memfault_platform_halt_if_debugging(void) { // Zephyr 3.7.0 deprecated cpu_ll_is_debugger_attached() in favor of // esp_cpu_dbgr_is_attached(). Support both Kconfigs. #if MEMFAULT_ZEPHYR_VERSION_GT(3, 6) if (esp_cpu_dbgr_is_attached()) { MEMFAULT_BREAKPOINT(); } #else if (cpu_ll_is_debugger_attached()) { MEMFAULT_BREAKPOINT(); } #endif // MEMFAULT_ZEPHYR_VERSION_GT(3, 6) } #elif defined(CONFIG_RISCV_CORE_NORDIC_VPR) #include void memfault_platform_halt_if_debugging(void) { #if defined(CONFIG_SOC_NRF54H20_CPUPPR) || defined(CONFIG_SOC_NRF9280_CPUPPR) #define MEMFAULT_VPR_NODELABEL cpuppr_vpr #else #define MEMFAULT_VPR_NODELABEL cpuflpr_vpr #endif NRF_VPR_Type const volatile *vpr = (NRF_VPR_Type const volatile *)DT_REG_ADDR(DT_NODELABEL(MEMFAULT_VPR_NODELABEL)); bool dbg_enabled = nrfy_vpr_debugif_dmcontrol_get((NRF_VPR_Type const *)vpr, NRF_VPR_DMCONTROL_DMACTIVE); if (dbg_enabled) { MEMFAULT_BREAKPOINT(); } } #elif !defined(ESP_PLATFORM) // Current target is not one of the supported configurations: // 1. Zephyr on ESP32 (__ZEPHYR__ && CONFIG_MEMFAULT_SOC_FAMILY_ESP32) // 2. Zephyr on Nordic VPR RISC-V (__ZEPHYR__ && // CONFIG_RISCV_CORE_NORDIC_VPR) // 3. ESP-IDF RISC-V (!defined(__ZEPHYR__) && defined(ESP_PLATFORM)); // memfault_platform_halt_if_debugging() is implemented in a generic // esp_idf port file. // // Error out- it's not a hard limitation but we want to double check when // a user hits this case. #error "Unsupported RISC-V platform. Please visit https://mflt.io/contact-support" #endif bool memfault_arch_is_inside_isr(void) { // Use the Zephyr-specific implementation. // // It's not clear if there's a RISC-V standard way to check if the CPU is in // an exception mode. The mcause register comes close but it won't tell us if // a trap was taken due to a non-interrupt cause: // https://five-embeddev.com/riscv-isa-manual/latest/machine.html#sec:mcause return k_is_in_isr(); } static void prv_fault_handling_assert_native(void *pc, void *lr, eMemfaultRebootReason reason) { prv_fault_handling_assert(pc, lr, reason); #if MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED memfault_platform_halt_if_debugging(); #endif // dereference a null pointer to trigger fault *(uint32_t *)0 = 0x77; // We just trap'd into the fault handler logic so it should never be possible to get here but if // we do the best thing that can be done is rebooting the system to recover it. memfault_platform_reboot(); } MEMFAULT_NO_OPT void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { prv_fault_handling_assert_native(pc, lr, extra_info->assert_reason); MEMFAULT_UNREACHABLE; } MEMFAULT_NO_OPT void memfault_fault_handling_assert(void *pc, void *lr) { prv_fault_handling_assert_native(pc, lr, kMfltRebootReason_Assert); MEMFAULT_UNREACHABLE; } #endif // !defined(ESP_PLATFORM) && defined(__ZEPHYR__) void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { if (s_crash_reason == kMfltRebootReason_Unknown) { // TODO confirm this works correctly- we should have the correct // pre-exception reg set here prv_fault_handling_assert((void *)regs->mepc, (void *)regs->ra, reason); } sMemfaultCoredumpSaveInfo save_info = { .regs = regs, .regs_size = sizeof(*regs), .trace_reason = s_crash_reason, }; sCoredumpCrashInfo info = { // Zephyr fault shim saves the stack pointer in s[0] .stack_address = (void *)regs->s[0], .trace_reason = save_info.trace_reason, .exception_reg_state = regs, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); const bool coredump_saved = memfault_coredump_save(&save_info); if (coredump_saved) { memfault_reboot_tracking_mark_coredump_saved(); } #if !MEMFAULT_FAULT_HANDLER_RETURN memfault_platform_reboot(); MEMFAULT_UNREACHABLE; #endif } size_t memfault_coredump_storage_compute_size_required(void) { // actual values don't matter since we are just computing the size sMfltRegState core_regs = { 0 }; sMemfaultCoredumpSaveInfo save_info = { .regs = &core_regs, .regs_size = sizeof(core_regs), .trace_reason = kMfltRebootReason_UnknownError, }; sCoredumpCrashInfo info = { // we'll just pass the current stack pointer, value shouldn't matter .stack_address = (void *)&core_regs, .trace_reason = save_info.trace_reason, .exception_reg_state = NULL, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); return memfault_coredump_get_save_size(&save_info); } #endif // __riscv ================================================ FILE: components/panics/src/memfault_fault_handling_xtensa.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fault handling for Xtensa based architectures #if defined(__XTENSA__) #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_tracking.h" #include "memfault/panics/arch/xtensa/xtensa.h" #include "memfault/panics/assert.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) { *num_regions = 0; return NULL; } static eMemfaultRebootReason s_crash_reason = kMfltRebootReason_Unknown; static void prv_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) { if (s_crash_reason != kMfltRebootReason_Unknown) { // we've already been called once, ignore the second call return; } sMfltRebootTrackingRegInfo info = { .pc = (uint32_t)pc, .lr = (uint32_t)lr, }; s_crash_reason = reason; memfault_reboot_tracking_mark_reset_imminent(s_crash_reason, &info); } void memfault_arch_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) { prv_fault_handling_assert(pc, lr, reason); } // For Zephyr Xtensa, provide an assert handler and other utilities. // The Kconfig for the ESP32 family changed in v3.7.0. Support both Kconfigs #if defined(__ZEPHYR__) && (defined(CONFIG_MEMFAULT_SOC_FAMILY_ESP32)) #include #include #include "memfault/ports/zephyr/version.h" void memfault_platform_halt_if_debugging(void) { // cpu_ll.h was deprecated in v3.7.0 #if MEMFAULT_ZEPHYR_VERSION_GT(3, 6) if (esp_cpu_dbgr_is_attached()) { MEMFAULT_BREAKPOINT(); } #else if (cpu_ll_is_debugger_attached()) { MEMFAULT_BREAKPOINT(); } #endif } bool memfault_arch_is_inside_isr(void) { // Use the Zephyr-specific implementation. return k_is_in_isr(); } static void prv_fault_handling_assert_native(void *pc, void *lr, eMemfaultRebootReason reason) { prv_fault_handling_assert(pc, lr, reason); #if MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED memfault_platform_halt_if_debugging(); #endif // dereference a null pointer to trigger fault. we might want to use abort() // instead here *(uint32_t *)0 = 0x77; // We just trap'd into the fault handler logic so it should never be possible to get here but if // we do the best thing that can be done is rebooting the system to recover it. memfault_platform_reboot(); } MEMFAULT_NO_OPT void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { prv_fault_handling_assert_native(pc, lr, extra_info->assert_reason); MEMFAULT_UNREACHABLE; } MEMFAULT_NO_OPT void memfault_fault_handling_assert(void *pc, void *lr) { prv_fault_handling_assert_native(pc, lr, kMfltRebootReason_Assert); MEMFAULT_UNREACHABLE; } #elif !defined(ESP_PLATFORM) #error "Unsupported Xtensa platform. Please visit https://mflt.io/contact-support" #endif // !defined(ESP_PLATFORM) && defined(__ZEPHYR__) #if MEMFAULT_COREDUMP_CPU_COUNT > 1 #if defined(__ZEPHYR__) #error "Dual-core support not yet implemented for Zephyr Xtensa" #else #include "esp_cpu.h" #endif static int prv_get_current_cpu_id(void) { return esp_cpu_get_core_id(); } #endif // MEMFAULT_COREDUMP_CPU_COUNT == 1 void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { #if MEMFAULT_COREDUMP_CPU_COUNT == 1 const sMfltRegState *current_cpu_regs = regs; #else const int cpu_id = prv_get_current_cpu_id(); const sMfltRegState *current_cpu_regs = ®s[cpu_id]; #endif if (s_crash_reason == kMfltRebootReason_Unknown) { // skip LR saving here. prv_fault_handling_assert((void *)current_cpu_regs->pc, (void *)0, reason); } sMemfaultCoredumpSaveInfo save_info = { .regs = regs, .regs_size = sizeof(*regs) * MEMFAULT_COREDUMP_CPU_COUNT, .trace_reason = s_crash_reason, }; // Check out "Windowed Procedure-Call Protocol" in the Xtensa ISA Reference Manual: Some data is // stored "below the stack pointer (since they are infrequently referenced), leaving the limited // range of the ISA’s load/store offsets available for more frequently referenced locals." // // Processor saves callers a0..a3 in the 16 bytes below the "sp" // The next 48 bytes beneath that are from a _WindowOverflow12 on exception // capturing callers a4 - a15 // // For the windowed ABI, a1 always holds the current "sp": // https://github.com/espressif/esp-idf/blob/v4.0/components/freertos/readme_xtensa.txt#L421-L428 const uint32_t windowed_abi_spill_size = 64; const uint32_t sp_prior_to_exception = current_cpu_regs->a[1] - windowed_abi_spill_size; sCoredumpCrashInfo info = { .stack_address = (void *)sp_prior_to_exception, .trace_reason = save_info.trace_reason, .exception_reg_state = regs, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); const bool coredump_saved = memfault_coredump_save(&save_info); if (coredump_saved) { memfault_reboot_tracking_mark_coredump_saved(); } } size_t memfault_coredump_storage_compute_size_required(void) { // actual values don't matter since we are just computing the size sMfltRegState core_regs[MEMFAULT_COREDUMP_CPU_COUNT] = { 0 }; sMemfaultCoredumpSaveInfo save_info = { .regs = &core_regs, .regs_size = sizeof(core_regs), .trace_reason = kMfltRebootReason_UnknownError, }; sCoredumpCrashInfo info = { // we'll just pass the current stack pointer, value shouldn't matter .stack_address = (void *)&core_regs, .trace_reason = save_info.trace_reason, .exception_reg_state = NULL, }; save_info.regions = memfault_platform_coredump_get_regions(&info, &save_info.num_regions); return memfault_coredump_get_save_size(&save_info); } #endif /* __XTENSA__ */ ================================================ FILE: components/panics/src/memfault_stdlib_assert.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Implements a hook into C stdlib assert() to capture coredumps #include #include #include "memfault/panics/assert.h" // TI ARM compiler's stdlib assert is not supported right now. #if MEMFAULT_ASSERT_CSTDLIB_HOOK_ENABLED && !defined(__TI_ARM__) //! The symbol name for the low-level assert handler is different between Newlib //! and ARM/IAR #if (defined(__CC_ARM)) || (defined(__ARMCC_VERSION)) || (defined(__ICCARM__)) void __aeabi_assert(const char *failedExpr, const char *file, int line) { (void)failedExpr, (void)file, (void)line; #elif (defined(__GNUC__)) // Disable -Wreserved-identifier for clang #pragma GCC diagnostic push #if defined(__clang__) #pragma GCC diagnostic ignored "-Wreserved-identifier" #endif extern void __assert_func(const char *file, int line, const char *func, const char *failedexpr); void __assert_func(const char *file, int line, const char *func, const char *failedexpr) { #pragma GCC diagnostic pop (void)file, (void)line, (void)func, (void)failedexpr; #endif MEMFAULT_ASSERT(0); #if defined(__clang__) MEMFAULT_UNREACHABLE; #endif } #endif // MEMFAULT_ASSERT_CSTDLIB_HOOK_ENABLED && !defined(__TI_ARM__) ================================================ FILE: components/util/README.md ================================================ # util Various utilities that support the Memfault services. - A base-64 encoder - A CCITT CRC16 calculator - An RLE encoder - A circular buffer implementation - A minimal CBOR implementation - Base-128 Varint utilities - The Memfault chunk transport utilities for ensuring chunks map to device MTU sizes ================================================ FILE: components/util/src/memfault_base64.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include "memfault/util/base64.h" static char prv_get_char_from_word(uint32_t word, int offset) { const char *base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const uint8_t base64_mask = 0x3f; // one char per 6 bits return base64_table[(word >> (offset * 6)) & base64_mask]; } void memfault_base64_encode(const void *buf, size_t buf_len, void *base64_out) { const uint8_t *bin_inp = (const uint8_t *)buf; char *out_bufp = (char *)base64_out; int curr_idx = 0; for (size_t bin_idx = 0; bin_idx < buf_len; bin_idx += 3) { const uint32_t byte0 = bin_inp[bin_idx]; const uint32_t byte1 = ((bin_idx + 1) < buf_len) ? bin_inp[bin_idx + 1] : 0; const uint32_t byte2 = ((bin_idx + 2) < buf_len) ? bin_inp[bin_idx + 2] : 0; const uint32_t triple = (byte0 << 16) + (byte1 << 8) + byte2; out_bufp[curr_idx++] = prv_get_char_from_word(triple, 3); out_bufp[curr_idx++] = prv_get_char_from_word(triple, 2); out_bufp[curr_idx++] = ((bin_idx + 1) < buf_len) ? prv_get_char_from_word(triple, 1) : '='; out_bufp[curr_idx++] = ((bin_idx + 2) < buf_len) ? prv_get_char_from_word(triple, 0) : '='; } } void memfault_base64_encode_inplace(void *buf, size_t bin_len) { // When encoding with base64, every 3 bytes is represented as 4 characters. If the input binary // blob is not a multiple of 3, we will need to use the "=" padding character. Here we determine // what index to start encoding at that will end on a multiple of 3 boundary const size_t remainder = bin_len % 3; const size_t start_idx = (remainder == 0) ? bin_len - 3 : bin_len - remainder; // The index to write the base64 character conversion into starting with the last location const size_t encoded_len = MEMFAULT_BASE64_ENCODE_LEN(bin_len); int curr_idx = (int)encoded_len - 1; const uint8_t *bin_inp = (const uint8_t *)buf; char *out_bufp = (char *)buf; // NB: By encoding from last set of 3 bytes to first set, we can edit the buffer inplace // without clobbering the input data we need to determine the encoding for (int bin_idx = (int)start_idx; bin_idx >= 0; bin_idx -= 3) { const uint32_t byte0 = bin_inp[bin_idx]; const uint32_t byte1 = ((bin_idx + 1) < (int)bin_len) ? bin_inp[bin_idx + 1] : 0; const uint32_t byte2 = ((bin_idx + 2) < (int)bin_len) ? bin_inp[bin_idx + 2] : 0; const uint32_t triple = (byte0 << 16) + (byte1 << 8) + byte2; out_bufp[curr_idx--] = ((bin_idx + 2) < (int)bin_len) ? prv_get_char_from_word(triple, 0) : '='; out_bufp[curr_idx--] = ((bin_idx + 1) < (int)bin_len) ? prv_get_char_from_word(triple, 1) : '='; out_bufp[curr_idx--] = prv_get_char_from_word(triple, 2); out_bufp[curr_idx--] = prv_get_char_from_word(triple, 3); } } ================================================ FILE: components/util/src/memfault_chunk_transport.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! See header for more details around the motivation //! //! Chunk Message Types //! //! INIT Message: //! HEADER_BYTE || (HEADER_BYTE.MD ? varint(TOTAL_LENGTH) : b"") || CHUNK_DATA || //! (HEADER_BYTE.MD ? 0b"" : CRC_16_CCITT) //! //! NOTE: If the entire message can fit in a single MTU, no TOTAL_LENGTH is encoded //! NOTE: We expect message integrity from the underlying transport. The CRC16 is only present //! to eventually detect if this expectation is broken by the consumer stack. //! //! CONTINUATION Message: //! HEADER_BYTE || varint(OFFSET) || CHUNK_DATA || (HEADER_BYTE.MD ? 0b"" : CRC_16_CCITT) #include #include #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/util/chunk_transport.h" #include "memfault/util/crc16.h" #include "memfault/util/varint.h" typedef struct { bool md; bool continuation; } sMemfaultHeaderSettings; static uint8_t prv_build_hdr(const sMemfaultHeaderSettings *settings) { // bits 0-2: channel id (0 - 7) Always 0 at the moment but reserved for future where we want // prioritization // bit 3-5: CFG - Protocol configuration settings // For INIT Packet // 0b000 indicates crc16 is written in the init chunk // 0b001 indicates crc16 is written at the end of the last chunk which makes up the msg // Remaining Values: Reserved for future use // For CONTINUATION: // All zeros. Reserved for future use (i.e. to make TOTAL_LENGTH and CRC16_CCITT // optional) // bit 6: MD: 1 if a CONTINUATION will follow (more data) or 0 if this is the last chunk of // this // message. Right now I'm only using it to conditionally include the TOTAL_LENGTH. But // I think it could also be useful as a trigger for the consumer to run a // "basic_recover" to get all messages in the queue when the final one has arrived and // process them all at once. This only makes sense if we're using more specific queues // (i.e. per-device). // bit 7: CONT: 0 for INIT, 1 for CONTINUATION // The first chunk in a sequence of chunks must use INIT and following chunks must // use CONTINUATION. uint8_t hdr = ((uint8_t)(settings->continuation << 7) | (uint8_t)(settings->md << 6)); if (!settings->continuation) { hdr |= 1 << 3; } return hdr; } MEMFAULT_STATIC_ASSERT( MEMFAULT_MIN_CHUNK_BUF_LEN == (1 /* hdr */ + MEMFAULT_UINT32_MAX_VARINT_LENGTH + 2 /* crc16 */ + 1 /* at least one data byte */), "Not enough space to chunk up at least one byte in a maximally sized message"); static size_t prv_compute_single_message_chunk_size(sMfltChunkTransportCtx *ctx) { return 1 /* hdr */ + 2 /* crc16 */ + ctx->total_size; } bool memfault_chunk_transport_get_next_chunk(sMfltChunkTransportCtx *ctx, void *out_buf, size_t *out_buf_len) { // There's not enough space to encode anything. Consumers of this API should be // passing a buffer of at least MEMFAULT_MIN_CHUNK_BUF_LEN in length if (*out_buf_len < MEMFAULT_MIN_CHUNK_BUF_LEN) { *out_buf_len = 0; return true; } uint8_t *chunk_msg = (uint8_t *)out_buf; size_t varint_len = 0; size_t chunk_msg_start_offset = 0; size_t bytes_to_read = 0; const bool init_pkt_type = ctx->read_offset == 0; bool more_data; const size_t crc16_len = 2; if (init_pkt_type) { varint_len = memfault_encode_varint_u32(ctx->total_size, &chunk_msg[1]); const size_t single_msg_size = prv_compute_single_message_chunk_size(ctx); more_data = single_msg_size > *out_buf_len; const sMemfaultHeaderSettings init_settings = { .md = more_data && !ctx->enable_multi_call_chunk, .continuation = false }; ctx->single_chunk_message_length = single_msg_size; chunk_msg[0] = prv_build_hdr(&init_settings); chunk_msg_start_offset = 1; if (init_settings.md) { bytes_to_read = *out_buf_len - 1 /* hdr */ - varint_len; chunk_msg_start_offset += varint_len; } else if (ctx->enable_multi_call_chunk) { bytes_to_read = MEMFAULT_MIN(*out_buf_len - 1 /* hdr */, ctx->total_size); } else { bytes_to_read = ctx->total_size; } } else if (ctx->enable_multi_call_chunk) { const size_t bytes_remaining = ctx->total_size - ctx->read_offset; bytes_to_read = MEMFAULT_MIN(*out_buf_len, bytes_remaining); const size_t out_buf_space_rem = *out_buf_len - bytes_to_read; more_data = (out_buf_space_rem < crc16_len); } else { varint_len = memfault_encode_varint_u32(ctx->read_offset, &chunk_msg[1]); const size_t bytes_remaining = ctx->total_size - ctx->read_offset; size_t out_buf_space_rem = *out_buf_len - 1 /* hdr */ - varint_len; bytes_to_read = MEMFAULT_MIN(out_buf_space_rem, bytes_remaining); out_buf_space_rem -= bytes_to_read; more_data = (out_buf_space_rem < crc16_len); const sMemfaultHeaderSettings cont_settings = { .md = more_data, .continuation = true, }; chunk_msg[0] = prv_build_hdr(&cont_settings); chunk_msg_start_offset = 1 /* hdr */ + varint_len; } if (bytes_to_read != 0) { uint8_t *msg_bufp = &chunk_msg[chunk_msg_start_offset]; ctx->read_msg(ctx->read_offset, msg_bufp, bytes_to_read); ctx->crc16_incremental = memfault_crc16_compute(ctx->crc16_incremental, msg_bufp, bytes_to_read); chunk_msg_start_offset += bytes_to_read; } if (!more_data) { // The entire message CRC has been computed, add it to the end of the message // for sanity checking message integrity const uint16_t crc16 = ctx->crc16_incremental; chunk_msg[chunk_msg_start_offset] = crc16 & 0xff; chunk_msg[chunk_msg_start_offset + 1] = (crc16 >> 8) & 0xff; chunk_msg_start_offset += crc16_len; } ctx->read_offset += bytes_to_read; const size_t bytes_written = chunk_msg_start_offset; const size_t buf_space_rem = *out_buf_len - bytes_written; if (buf_space_rem != 0) { // The encoded chunk consumes less space than the buffer provided. This can // happen when we reach the end of the underlying message being encoded // before exhausting the provided buffer. // // Note: ONLY for cases where the message spans multiple chunks, the final // chunk can be submitted to Memfault including the spare buffer space bytes // (i.e. using a fixed payload size to Memfault), and the Memfault backend // will discard the spare bytes. This is ONLY supported for multi-chunk // messages; single-chunk messages must be submitted with the exact payload // size as returned in *out_buf_len. // // Scrub the remaining part of the buffer for this situation with a known // pattern for debug purposes. memset(&chunk_msg[bytes_written], 0xBA, buf_space_rem); } *out_buf_len = bytes_written; return more_data; } void memfault_chunk_transport_get_chunk_info(sMfltChunkTransportCtx *ctx) { if (ctx->read_offset != 0) { // info has already been populated return; } ctx->single_chunk_message_length = prv_compute_single_message_chunk_size(ctx); } ================================================ FILE: components/util/src/memfault_circular_buffer.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Convenience circular buffer utility #include #include #include #include #include "memfault/core/math.h" #include "memfault/util/circular_buffer.h" bool memfault_circular_buffer_init(sMfltCircularBuffer *circular_buf, void *storage_buf, size_t storage_len) { if ((circular_buf == NULL) || (storage_buf == NULL) || (storage_len == 0)) { return false; } // doesn't really matter but put buffer in a clean state for easier debug memset(storage_buf, 0x0, storage_len); *circular_buf = (sMfltCircularBuffer){ .read_offset = 0, .read_size = 0, .total_space = storage_len, .storage = (uint8_t *)storage_buf }; return true; } bool memfault_circular_buffer_read(sMfltCircularBuffer *circular_buf, size_t offset, void *data, size_t data_len) { if ((circular_buf == NULL) || (data == NULL)) { return false; } if (circular_buf->read_size < (offset + data_len)) { return false; } size_t read_idx = (circular_buf->read_offset + offset) % circular_buf->total_space; size_t contiguous_space_available = circular_buf->total_space - read_idx; size_t bytes_to_read = (contiguous_space_available > data_len) ? data_len : contiguous_space_available; uint8_t *buf = (uint8_t *)data; memcpy(buf, &circular_buf->storage[read_idx], bytes_to_read); buf += bytes_to_read; size_t bytes_rem = data_len - bytes_to_read; if (bytes_rem != 0) { memcpy(buf, &circular_buf->storage[0], bytes_rem); } return true; } bool memfault_circular_buffer_get_read_pointer(sMfltCircularBuffer *circular_buf, size_t offset, uint8_t **read_ptr, size_t *read_ptr_len) { if ((circular_buf == NULL) || (read_ptr == NULL) || (read_ptr_len == NULL)) { return false; } if (circular_buf->read_size < offset) { return false; } const size_t read_idx = (circular_buf->read_offset + offset) % circular_buf->total_space; const size_t max_bytes_to_read = circular_buf->read_size - offset; const size_t contiguous_space_available = circular_buf->total_space - read_idx; *read_ptr = &circular_buf->storage[read_idx]; *read_ptr_len = MEMFAULT_MIN(contiguous_space_available, max_bytes_to_read); return true; } bool memfault_circular_buffer_read_with_callback(sMfltCircularBuffer *circular_buf, size_t offset, size_t data_len, void *ctx, MemfaultCircularBufferReadCallback callback) { if (circular_buf == NULL) { return false; } if (callback == NULL) { return false; } if (circular_buf->read_size < (offset + data_len)) { return false; } size_t bytes_left = data_len; uint8_t *read_ptr = NULL; size_t read_ptr_len = 0; while (bytes_left) { const size_t dst_offset = data_len - bytes_left; if (!memfault_circular_buffer_get_read_pointer(circular_buf, offset + dst_offset, &read_ptr, &read_ptr_len)) { // Note: At this point, the memfault_circular_buffer_get_read_pointer() calls should never // fail. A failure is indicative of memory corruption (e.g calls taking place from multiple // tasks without having implemented memfault_lock() / memfault_unlock()) return false; } const size_t bytes_to_read = MEMFAULT_MIN(bytes_left, read_ptr_len); if (!callback(ctx, dst_offset, read_ptr, bytes_to_read)) { return false; } bytes_left -= bytes_to_read; } return true; } bool memfault_circular_buffer_consume(sMfltCircularBuffer *circular_buf, size_t consume_len) { if (circular_buf == NULL) { return false; } if (circular_buf->read_size < consume_len) { return false; } circular_buf->read_offset = (circular_buf->read_offset + consume_len) % circular_buf->total_space; circular_buf->read_size -= consume_len; return true; } bool memfault_circular_buffer_consume_from_end(sMfltCircularBuffer *circular_buf, size_t consume_len) { if (circular_buf == NULL) { return false; } if (circular_buf->read_size < consume_len) { return false; } circular_buf->read_size -= consume_len; return true; } static size_t prv_get_space_available(const sMfltCircularBuffer *circular_buf) { return circular_buf->total_space - circular_buf->read_size; } size_t memfault_circular_buffer_get_write_size(const sMfltCircularBuffer *circular_buf) { if (circular_buf == NULL) { return 0; } return prv_get_space_available(circular_buf); } static bool prv_write_at_offset_from_end(sMfltCircularBuffer *circular_buf, size_t offset_from_end, const void *data, size_t data_len) { if ((circular_buf == NULL) || (data == NULL)) { return false; } if (circular_buf->read_size < offset_from_end) { // we can't write to an offset that doesn't exist return false; } const size_t new_bytes_to_write = data_len > offset_from_end ? data_len - offset_from_end : 0; if (prv_get_space_available(circular_buf) < new_bytes_to_write) { return false; } size_t write_idx = (circular_buf->read_offset + circular_buf->read_size - offset_from_end) % circular_buf->total_space; size_t contiguous_space_available = circular_buf->total_space - write_idx; size_t bytes_to_write = (contiguous_space_available > data_len) ? data_len : contiguous_space_available; const uint8_t *buf = (const uint8_t *)data; memcpy(&circular_buf->storage[write_idx], buf, bytes_to_write); buf += bytes_to_write; size_t bytes_rem = data_len - bytes_to_write; if (bytes_rem != 0) { memcpy(&circular_buf->storage[0], buf, bytes_rem); } circular_buf->read_size += new_bytes_to_write; return true; } bool memfault_circular_buffer_write(sMfltCircularBuffer *circular_buf, const void *data, size_t data_len) { return prv_write_at_offset_from_end(circular_buf, 0, data, data_len); } bool memfault_circular_buffer_write_at_offset(sMfltCircularBuffer *circular_buf, size_t offset_from_end, const void *data, size_t data_len) { return prv_write_at_offset_from_end(circular_buf, offset_from_end, data, data_len); } size_t memfault_circular_buffer_get_read_size(const sMfltCircularBuffer *circular_buf) { if (circular_buf == NULL) { return 0; } return circular_buf->read_size; } ================================================ FILE: components/util/src/memfault_crc16_ccitt.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Note that the CRC implementation is CRC-16/XMODEM, not CRC-16/CCITT //! (canonically named "CRC-16/KERMIT"). //! //! It implements the following polynomial: //! x^16 + x^12 + x^5 + 1 (0x1021) //! More details: //! http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.crc-16-xmodem #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/util/crc16.h" //! Users can provide their own implementation of memfault_crc16_compute by //! setting MEMFAULT_CRC16_BUILTIN to 0 in their platform configuration file. #if MEMFAULT_CRC16_BUILTIN #if MEMFAULT_CRC16_LOOKUP_TABLE_ENABLE static const uint16_t s_crc16_table[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, }; MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_crc16_table) == 256, "Unexpected CRC lookup table size"); uint16_t memfault_crc16_compute(uint16_t crc_initial_value, const void *data, size_t data_len_bytes) { uint16_t crc = crc_initial_value; const uint8_t *curr_ptr = (const uint8_t *)data; for (size_t i = 0; i < data_len_bytes; i++) { uint16_t table_idx = ((crc >> 8) ^ *curr_ptr++) & 0xff; crc = ((uint16_t)(crc << 8)) ^ s_crc16_table[table_idx]; } return crc; } #else // MEMFAULT_CRC16_LOOKUP_TABLE_ENABLE uint16_t memfault_crc16_compute(uint16_t crc_initial_value, const void *data, size_t data_len_bytes) { const uint32_t polynomial = 0x1021; uint32_t crc = crc_initial_value; const uint8_t *curr_ptr = (const uint8_t *)data; for (size_t i = 0; i < data_len_bytes; i++) { crc = (crc ^ (uint32_t)(*curr_ptr++ << 8)); for (int bit = 0; bit < 8; bit++) { crc = (crc << 1); if (crc & 0x10000) { crc = ((crc ^ polynomial) & 0xFFFF); } } } return (uint16_t)crc; } #endif // MEMFAULT_CRC16_LOOKUP_TABLE_ENABLE #endif // MEMFAULT_CRC16_BUILTIN ================================================ FILE: components/util/src/memfault_minimal_cbor.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A minimal implementation of a CBOR encoder. See header for more details #include #include #include "memfault/util/cbor.h" // https://tools.ietf.org/html/rfc7049#section-2.1 typedef enum CborMajorType { kCborMajorType_UnsignedInteger = 0, kCborMajorType_NegativeInteger = 1, kCborMajorType_ByteString = 2, kCborMajorType_TextString = 3, kCborMajorType_Array = 4, kCborMajorType_Map = 5, kCborMajorType_Tag = 6, kCborMajorType_SimpleType = 7, } eCborMajorType; // Definitions for Additional Info for Simple Values can be found here: // https://www.rfc-editor.org/rfc/rfc7049#section-2.3 typedef enum CborAddInfoSimpleVals { kCborAddInfoSimpleVals_False = 20, kCborAddInfoSimpleVals_True = 21, kCborAddInfoSimpleVals_Null = 22, kCborAddInfoSimpleVals_Undefined = 23, } eCborAddInfoSimpleVals; // A CBOR payload is composed of a stream of "data items" // The main type of each "data item" is known as the CBOR Major Type // and populates the upper 3 bits of the first byte of each "data item" #define CBOR_SERIALIZE_MAJOR_TYPE(mt) ((uint8_t)((mt & 0x7) << 5)) // CBOR NULL is formed by using a major type of SimpleValue and the assigned value of null #define CBOR_NULL \ (CBOR_SERIALIZE_MAJOR_TYPE(kCborMajorType_SimpleType) | kCborAddInfoSimpleVals_Null) void memfault_cbor_encoder_init(sMemfaultCborEncoder *encoder, MemfaultCborWriteCallback write_cb, void *write_cb_ctx, size_t buf_len) { const bool compute_size_only = (write_cb == NULL); *encoder = (sMemfaultCborEncoder){ .compute_size_only = compute_size_only, .write_cb = write_cb, .write_cb_ctx = write_cb_ctx, .buf_len = buf_len, }; } void memfault_cbor_encoder_size_only_init(sMemfaultCborEncoder *encoder) { memfault_cbor_encoder_init(encoder, NULL, NULL, 0); } size_t memfault_cbor_encoder_deinit(sMemfaultCborEncoder *encoder) { const size_t bytes_encoded = encoder->encoded_size; *encoder = (sMemfaultCborEncoder){ 0 }; return bytes_encoded; } static bool prv_add_to_result_buffer(sMemfaultCborEncoder *encoder, const void *data, size_t data_len) { if (data_len == 0) { // no work to do return true; } if (encoder->compute_size_only) { // no need to perform size checks because we are only computing the total encoder size encoder->encoded_size += data_len; return true; } if ((encoder->encoded_size + data_len) > encoder->buf_len) { // not enough space encoder->status = MEMFAULT_CBOR_ENCODER_STATUS_ENOMEM; return false; } encoder->write_cb(encoder->write_cb_ctx, encoder->encoded_size, data, data_len); encoder->encoded_size += data_len; return true; } static bool prv_encode_unsigned_integer(sMemfaultCborEncoder *encoder, uint8_t major_type, uint32_t val) { uint8_t mt = CBOR_SERIALIZE_MAJOR_TYPE(major_type); uint8_t tmp_buf[5]; uint8_t *p = &tmp_buf[0]; if (val < 24) { *p++ = mt + (uint8_t)val; } else { if (val <= UINT8_MAX) { *p++ = mt + 24; *p++ = val & 0xff; } else if (val <= UINT16_MAX) { *p++ = mt + 25; *p++ = (val >> 8) & 0xff; *p++ = val & 0xff; } else { *p++ = mt + 26; *p++ = (val >> 24) & 0xff; *p++ = (val >> 16) & 0xff; *p++ = (val >> 8) & 0xff; *p++ = val & 0xff; } } const size_t tmp_buf_len = (uint32_t)(p - tmp_buf); return prv_add_to_result_buffer(encoder, tmp_buf, tmp_buf_len); } static void prv_encode_uint64(uint8_t buf[8], uint64_t val) { uint8_t *p = &buf[0]; for (int shift = 56; shift >= 0; shift -= 8) { *p++ = (val >> shift) & 0xff; } } #define MEMFAULT_CBOR_UINT64_MAX_ITEM_SIZE_BYTES 9 bool memfault_cbor_encode_long_signed_integer(sMemfaultCborEncoder *encoder, int64_t value) { // Logic derived from "Appendix C Pseudocode" of RFC 7049. Suppress // CodeChecker violation, this has predictable output on supported compilers // codechecker_suppress [cppcheck-shiftTooManyBitsSigned] int64_t ui = (value >> 63); // Figure out if we are encoding a Negative or Unsigned Integer by reading the sign extension bit const uint8_t cbor_major_type = ui & 0x1; ui ^= value; if (ui <= UINT32_MAX) { return prv_encode_unsigned_integer(encoder, cbor_major_type, (uint32_t)ui); } uint8_t tmp_buf[MEMFAULT_CBOR_UINT64_MAX_ITEM_SIZE_BYTES]; const uint8_t uint64_type_value = 27; tmp_buf[0] = CBOR_SERIALIZE_MAJOR_TYPE(cbor_major_type) | uint64_type_value; prv_encode_uint64(&tmp_buf[1], (uint64_t)ui); return prv_add_to_result_buffer(encoder, tmp_buf, sizeof(tmp_buf)); } bool memfault_cbor_encode_uint64_as_double(sMemfaultCborEncoder *encoder, uint64_t val) { uint8_t tmp_buf[MEMFAULT_CBOR_UINT64_MAX_ITEM_SIZE_BYTES]; const uint8_t ieee_754_double_precision_float_type = 27; tmp_buf[0] = CBOR_SERIALIZE_MAJOR_TYPE(kCborMajorType_SimpleType) | ieee_754_double_precision_float_type; prv_encode_uint64(&tmp_buf[1], val); return prv_add_to_result_buffer(encoder, tmp_buf, sizeof(tmp_buf)); } bool memfault_cbor_encode_unsigned_integer(sMemfaultCborEncoder *encoder, uint32_t value) { return prv_encode_unsigned_integer(encoder, kCborMajorType_UnsignedInteger, value); } bool memfault_cbor_join(sMemfaultCborEncoder *encoder, const void *cbor_data, size_t cbor_data_len) { return prv_add_to_result_buffer(encoder, cbor_data, cbor_data_len); } bool memfault_cbor_encode_signed_integer(sMemfaultCborEncoder *encoder, int32_t value) { // Logic derived from "Appendix C Pseudocode" of RFC 7049. Suppress // CodeChecker violation, this has predictable output on supported compilers // codechecker_suppress [cppcheck-shiftTooManyBitsSigned] int32_t ui = (value >> 31); // Figure out if we are encoding a Negative or Unsigned Integer by reading a sign extension bit const uint8_t cbor_major_type = ui & 0x1; ui ^= value; return prv_encode_unsigned_integer(encoder, cbor_major_type, (uint32_t)ui); } bool memfault_cbor_encode_byte_string(sMemfaultCborEncoder *encoder, const void *buf, size_t buf_len) { return (prv_encode_unsigned_integer(encoder, kCborMajorType_ByteString, buf_len) && prv_add_to_result_buffer(encoder, buf, buf_len)); } bool memfault_cbor_encode_byte_string_begin(sMemfaultCborEncoder *encoder, size_t buf_len) { return prv_encode_unsigned_integer(encoder, kCborMajorType_ByteString, buf_len); } bool memfault_cbor_encode_string(sMemfaultCborEncoder *encoder, const char *str) { const size_t str_len = strlen(str); return (prv_encode_unsigned_integer(encoder, kCborMajorType_TextString, str_len) && prv_add_to_result_buffer(encoder, str, str_len)); } bool memfault_cbor_encode_string_begin(sMemfaultCborEncoder *encoder, size_t str_len) { return prv_encode_unsigned_integer(encoder, kCborMajorType_TextString, str_len); } bool memfault_cbor_encode_dictionary_begin(sMemfaultCborEncoder *encoder, size_t num_elements) { return prv_encode_unsigned_integer(encoder, kCborMajorType_Map, num_elements); } bool memfault_cbor_encode_array_begin(sMemfaultCborEncoder *encoder, size_t num_elements) { return prv_encode_unsigned_integer(encoder, kCborMajorType_Array, num_elements); } bool memfault_cbor_encode_null(sMemfaultCborEncoder *encoder) { uint8_t tmp_buf[] = { CBOR_NULL }; return prv_add_to_result_buffer(encoder, tmp_buf, sizeof(tmp_buf)); } void memfault_cbor_encoder_memcpy_write(void *ctx, uint32_t offset, const void *buf, size_t buf_len) { uint8_t *out_buf = (uint8_t *)ctx; memcpy(&out_buf[offset], buf, buf_len); } int memfault_cbor_encoder_get_status(sMemfaultCborEncoder *encoder) { return encoder->status; } ================================================ FILE: components/util/src/memfault_rle.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief #include #include #include "memfault/core/compiler.h" #include "memfault/util/rle.h" #include "memfault/util/varint.h" MEMFAULT_STATIC_ASSERT(sizeof(((sMemfaultRleWriteInfo *)0x0)->header) == MEMFAULT_UINT32_MAX_VARINT_LENGTH, "header buffer not appropriately sized"); static void prv_handle_rle_change(sMemfaultRleCtx *ctx) { // Are we currently encoding a repeat sequence? const bool repeated_pattern = ctx->state == kMemfaultRleState_RepeatSeq; ctx->write_info = (sMemfaultRleWriteInfo){ .available = true, .write_start_offset = ctx->seq_start_offset, .write_len = repeated_pattern ? 1 : ctx->seq_count, }; int32_t rle_size = (int)(repeated_pattern ? ctx->seq_count : -ctx->seq_count); ctx->write_info.header_len = memfault_encode_varint_si32(rle_size, &ctx->write_info.header[0]); ctx->total_rle_size += ctx->write_info.header_len + ctx->write_info.write_len; if (repeated_pattern) { ctx->seq_start_offset = ctx->curr_offset; ctx->seq_count = 0; } else { // We've found a minimal length repeat sequence to encode ctx->seq_start_offset = ctx->curr_offset - ctx->num_repeats; ctx->seq_count = ctx->num_repeats; } } void memfault_rle_encode_finalize(sMemfaultRleCtx *ctx) { prv_handle_rle_change(ctx); } size_t memfault_rle_encode(sMemfaultRleCtx *ctx, const void *buf, size_t buf_size) { if (buf == NULL || buf_size == 0) { return 0; } // NB: The caller should check this between calls to find out if a new sequence to // write has been detected so we reset it upon every invocation ctx->write_info = (sMemfaultRleWriteInfo){ 0 }; const uint32_t start_offset = ctx->curr_offset; const uint8_t *byte_buf = (const uint8_t *)buf; for (uint32_t i = 0; i < buf_size; i++) { const uint8_t byte = byte_buf[i]; // NB: We flag the first encoded byte as a repeat sequence until proven otherwise const bool is_repeat_seq = (ctx->curr_offset != 0) && ctx->last_byte == byte; if ((ctx->curr_offset != 0) && (ctx->last_byte == byte)) { ctx->num_repeats++; } else { ctx->num_repeats = 0; } switch (ctx->state) { case kMemfaultRleState_RepeatSeq: if (!is_repeat_seq) { prv_handle_rle_change(ctx); } // Starting a new sequence, flag the current byte as non-repeating until proven otherwise ctx->state = kMemfaultRleState_NonRepeatSeq; break; case kMemfaultRleState_NonRepeatSeq: // NB: If we only have two repeating bytes and leading non-repeating bytes we // want to encode this as one sequence to save space: // // 1, 2, 2, 3 encoded as a non repeat + repeats is 6 bytes: (-1), 1, (2), 2, (-1), 3 // whereas // 1, 2, 2, 3 encoded as one sequence is 5 bytes: (-4), 1, 2, 2, 3 if (is_repeat_seq && ctx->num_repeats >= 2) { ctx->seq_count -= ctx->num_repeats; prv_handle_rle_change(ctx); } break; case kMemfaultRleState_Init: ctx->state = kMemfaultRleState_NonRepeatSeq; break; default: break; } if (ctx->num_repeats >= 1 && ctx->seq_count == ctx->num_repeats) { // The sequence currently being encoded is comprised of at least // two repeating bytes so let's mark the state as repeating ctx->state = kMemfaultRleState_RepeatSeq; } ctx->last_byte = byte; ctx->seq_count++; ctx->curr_offset++; if (ctx->write_info.available) { break; } } return ctx->curr_offset - start_offset; } ================================================ FILE: components/util/src/memfault_varint.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/util/varint.h" size_t memfault_encode_varint_u32(uint32_t value, void *buf) { uint8_t *res = (uint8_t *)buf; // any value under 128 just looks the same as if it was packed in a uint8_t while (value >= 128) { *res++ = 0x80 | (value & 0x7f); value >>= 7; } *res++ = value & 0xff; return (size_t)(res - (uint8_t *)buf); } size_t memfault_encode_varint_si32(int32_t value, void *buf) { // A representation that maps negative numbers onto odd positive numbers and // positive numbers onto positive even numbers. Some example conversions follow: // 0 -> 0 // -1 -> 1 // 1 -> 2 // -2 -> 3 // Disable codechecker violation for unsigned shift by 31 bits. This has // predictable output on the compilers we support. // codechecker_suppress [cppcheck-shiftTooManyBitsSigned] uint32_t u32_repr = ((uint32_t)value << 1) ^ (uint32_t)(value >> 31); return memfault_encode_varint_u32(u32_repr, buf); } ================================================ FILE: examples/README.md ================================================ # Example Platform Ports with Demo Applications ## Demo Applications We have example integrations available for a number of development boards which can be used as a reference while working on your integration or to explore the Memfault SDK: - Arm Mbed OS 5 / STMicroelectronics STM32F4 series (STM32F429I-DISC1) - nRF5 SDK / Nordic nRF52840 (PCA10056) - Quantum Leap Quantum Platform in C / STMicroelectronics STM32F4 series (STM32F407G-DISC1) - WICED SDK / Cypress BCM943364WCD1 - Zephyr / STMicroelectronics STM32L4 series (B-L475E-IOT01A Discovery kit) - Amazon FreeRTOS / Cypress PSoC 64 MCU (CY8CKIT-064S0S2-4343W) - Dialog DA145xx and DA1469x - Espressif ESP-IDF / ESP32 (ESP-WROVER-KIT) - nRF Connect SDK / Nordic nRF9160 (PCA10090) ## Using the Demo Application The demo application is a typical debug console style application that showcases the various SDK features through console commands. If you have one of the supported boards and want to jump right in, look at the "Getting Started" section in `examples/**/README.md`. The file contains detailed information on how to build and run the demo application for the specific platform. ## Directory Structure The structure of each platform folder varies from platform to platform, following the idiosyncrasies of each. Each platform folder contains at least: - `README.md` - instructions on how to build and run the demo app, as well as integration notes specific for the given platform. - `**/platform_reference_impl` - implementations of platform functionality that the Memfault SDK depends on for the given platform. - `**/memfault_demo_app` - demo application for the given platform (see Demo applications below). - Build system files (specific to the platform) to compile the components as well as the demo applications for the platform. ## Common Tasks The SDK uses [pyinvoke] to offer a convenient way to run common tasks such as building, flashing and debugging demo applications. ### Installing Pyinvoke To install `pyinvoke`, make sure you have a recent version of Python 3.x installed. Then run `pip3 install -r requirements.txt` to install it. > Tip: use a [virtualenv] to avoid conflicts with dependencies of other projects > that use Python [pyinvoke]: https://www.pyinvoke.org [virtualenv]: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments ### Tasks To get a list of available "tasks", run `invoke --list` from anywhere inside the SDK. It will print the list of available tasks, for example: ```bash $ invoke --list Available tasks: nrf.build Build a demo application that runs on the nrf52 nrf.clean Clean demo application that runs on the nrf52 nrf.console Start a RTT console session ... etc ... ``` ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/Makefile ================================================ ################################################################################ # \file Makefile # \version 1.0 # # \brief # Top-level application make file. # ################################################################################ # \copyright # Copyright 2018-2019 Cypress Semiconductor Corporation # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ################################################################################ # Choose between "aws_demos" and "aws_tests" CY_AFR_BUILD=aws_demos # Root location of AFR directory CY_AFR_ROOT=../../../../../.. # Build artifact location CY_BUILD_RELATIVE_LOCATION=$(CY_AFR_ROOT)/build/cy CY_BUILD_LOCATION=$(abspath $(CY_BUILD_RELATIVE_LOCATION)) # Memfault MEMFAULT_SDK_ROOT := ../../.. MEMFAULT_PORT_ROOT := $(MEMFAULT_SDK_ROOT)/examples/cypress/CY8CKIT-064S0S2-4343W/src MEMFAULT_COMPONENTS := core util panics metrics include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk ################################################################################ # Basic Configuration ################################################################################ # Target board/hardware TARGET=CY8CKIT_064S0S2_4343W # Name of application (used to derive name of final linked file). APPNAME=$(CY_AFR_BUILD) # Name of toolchain to use. Options include: # # GCC_ARM -- GCC 7.2.1, provided with ModusToolbox IDE # ARM -- ARM Compiler (must be installed separately) # IAR -- IAR Compiler (must be installed separately) # # See also: CY_COMPILER_PATH below TOOLCHAIN=GCC_ARM # Default build configuration. Options include: # # Debug -- build with minimal optimizations, focus on debugging. # Release -- build with full optimizations CONFIG=Debug # If set to "true" or "1", display full command-lines when building. VERBOSE= ################################################################################ # Feature Configuration ################################################################################ # Enable or disable BLE module BLE_SUPPORTED=1 # Set to 1 to add OTA defines, sources, and libraries (must be used with MCUBoot) # NOTE: Extra code must be called from your app to initialize AFR OTA Agent OTA_SUPPORT=0 # This platform always uses EXTERNAL_FLASH OTA_USE_EXTERNAL_FLASH:=1 # Define CY_TEST_APP_VERSION_IN_TAR here to test application version # in TAR archive at start of OTA image download. # NOTE: This requires that the version numbers here and in the header file match. # NOTE: This will create compile warnings such as # 'warning: "APP_VERSION_MAJOR" redefined' # # CY_TEST_APP_VERSION_IN_TAR=1 # # APP_VERSION_MAJOR:=1 # APP_VERSION_MINOR:=0 # APP_VERSION_BUILD:=0 # CY_TFM_PSA_SUPPORTED feature cannot be disabled on this platform. CY_TFM_PSA_SUPPORTED=1 # Using new Bootloader with SWAP / STATUS CY_MCUBOOT_SWAP_USING_STATUS=1 ################################################################################ # Advanced Configuration ################################################################################ # Enable optional code that is ordinarily disabled by default. # # Available components depend on the specific targeted hardware and firmware # in use. In general, if you have # # COMPONENTS=foo bar # # ... then code in directories named COMPONENT_foo and COMPONENT_bar will be # added to the build # COMPONENTS= # Like COMPONENTS, but disable optional code that was enabled by default. DISABLE_COMPONENTS= # By default the build system automatically looks in the Makefile's directory # tree for source code and builds it. The SOURCES variable can be used to # manually add source code to the build process from a location not searched # by default, or otherwise not found by the build system. SOURCES = \ $(MEMFAULT_COMPONENTS_SRCS) \ $(MEMFAULT_SDK_ROOT)/ports/freertos/src/memfault_metrics_freertos.c \ $(MEMFAULT_SDK_ROOT)/ports/freertos/src/memfault_core_freertos.c \ $(MEMFAULT_SDK_ROOT)/ports/freertos/src/memfault_freertos_ram_regions.c # Like SOURCES, but for include directories. Value should be paths to # directories (without a leading -I). # TODO remove this once whd is update to new version INCLUDES = \ $(CY_AFR_ROOT)/vendors/cypress/MTB/libraries/wifi-host-driver/WiFi_Host_Driver/resources/nvram/TARGET_CY8CKIT_064B0S2_4343W \ $(MEMFAULT_COMPONENTS_INC_FOLDERS) \ $(MEMFAULT_SDK_ROOT)/ports/include \ $(MEMFAULT_PORT_ROOT) # Add additional defines to the build process (without a leading -D). DEFINES= # Select softfp or hardfp floating point. Default is softfp. VFP_SELECT= # Additional / custom C compiler flags. # # NOTE: Includes and defines should use the INCLUDES and DEFINES variable # above. CFLAGS= # Additional / custom C++ compiler flags. # # NOTE: Includes and defines should use the INCLUDES and DEFINES variable # above. CXXFLAGS= # Additional / custom assembler flags. # # NOTE: Includes and defines should use the INCLUDES and DEFINES variable # above. ASFLAGS= # Additional / custom linker flags. LDFLAGS=-Wl,--build-id # Additional / custom libraries to link in to the application. LDLIBS= # Path to the linker script to use (if empty, use the default linker script). export LINKER_SCRIPT=cyb06xxa_cm4_dual.ld # Custom pre-build commands to run. PREBUILD= # Custom post-build commands to run. POSTBUILD= ################################################################################ # Paths ################################################################################ # Relative path to the project directory (default is the Makefile's directory). # # This controls where automatic source code discovery looks for code. CY_APP_PATH= # Relative path to the "base" library. It provides the core makefile build # infrastructure. CY_BASELIB_PATH=$(CY_AFR_ROOT)/vendors/cypress/MTB/psoc6/psoc6make # Absolute path to the compiler's "bin" directory. # # The default depends on the selected TOOLCHAIN (GCC_ARM uses the ModusToolbox # IDE provided compiler by default). CY_COMPILER_PATH= # Include aFR configuration make file include $(CY_AFR_ROOT)/projects/cypress/make_support/afr.mk ################################################################################ # Tools path ################################################################################ # Locate ModusToolbox IDE helper tools folders in default installation # locations for Windows, Linux, and macOS. CY_WIN_HOME=$(subst \,/,$(USERPROFILE)) CY_TOOLS_PATHS ?= $(wildcard \ $(CY_WIN_HOME)/ModusToolbox/tools_* \ $(HOME)/ModusToolbox/tools_* \ /Applications/ModusToolbox/tools_*) # If you install ModusToolbox IDE in a custom location, add the path to its # "tools_X.Y" folder (where X and Y are the version number of the tools # folder). CY_TOOLS_PATHS+= # Default to the newest installed tools folder, or the users override (if it's # found). CY_TOOLS_DIR=$(lastword $(sort $(wildcard $(CY_TOOLS_PATHS)))) ifeq ($(CY_TOOLS_DIR),) $(error Unable to find any of the available CY_TOOLS_PATHS -- $(CY_TOOLS_PATHS)) endif $(info Tools Directory: $(CY_TOOLS_DIR)) include $(CY_TOOLS_DIR)/make/start.mk ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/README.md ================================================ # Memfault for Cypress CY8CKIT-064S0S2-4343W kit with Amazon-FreeRTOS This folder contains an integration example for the Cypress [CY8CKIT-064S0S2-4343W](https://www.cypress.com/documentation/development-kitsboards/psoc-64-standard-secure-aws-wi-fi-bt-pioneer-kit-cy8ckit) kit with Amazon-FreeRTOS & AWS IoT/Lambda. It is an app with a simple console interface that allows you to generate a fault, heartbeat or trace and once data is available a secure connection is estabilished with the AWS IoT MQTT broker, all available chunks are published, then passed from AWS IoT to Lambda using an IoT rule and eventually posted to Memfault. The demo app is tested on a [CY8CKIT-064S0S2-4343W](https://www.cypress.com/documentation/development-kitsboards/psoc-64-standard-secure-aws-wi-fi-bt-pioneer-kit-cy8ckit) evaluation board with ModusToolbox 2.3. ## Getting Started - [awscli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) is required to perform some of the steps in this document. - ModusToolbox setup instructions can be found [here](https://docs.aws.amazon.com/freertos/latest/userguide/getting_started_cypress_psoc64.html). - You'll need an AWS account with an IAM user. How to setup an IAM user can be found [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html). - The board, IAM user and AWS IoT should be configured according to this [document](https://docs.aws.amazon.com/freertos/latest/userguide/freertos-prereqs.html). ## Provisioning, certificates, keys & policies ### Provision the board In order to be able to do anything with your device it should be provisioned for secure boot as in the [document](https://community.cypress.com/docs/DOC-20043), with one difference - use `policy_multi_CM0_CM4_jitp.json`, as this is the one actually used by the project to generate a firmware image. If you'll specify the default tfm policy, the board won't be able to boot. Here's an example of the provisioning steps: ```bash # install the cysecuretools python tools ❯ pip install --update cysecuretools # change to the directory in the amazon-freertos tree where we run the provisioning ❯ cd vendors/cypress/MTB/psoc6/psoc64tfm/security # for our provisioning, we need to copy the device + root certs to specific locations ❯ mkdir -p certificates ❯ cp /-certificate.pem.crt certificates/device_cert.pem ❯ cp /AmazonRootCA1.pem certificates/rootCA.pem # init and provision the device (use 're-provision-device' on a previously provisioned board) ❯ cysecuretools --target CY8CKIT-064S0S2-4343W init ❯ cysecuretools --policy ./policy/policy_multi_CM0_CM4_jitp.json --target CY8CKIT-064S0S2-4343W provision-device ``` ### Configure the application In `demos/include/aws_clientcredential.h`, you'll have to type in: - `clientcredentialMQTT_BROKER_ENDPOINT` - MQTT broker endpoint retrieved from `awscli` in the [first steps in FreeRTOS document](https://docs.aws.amazon.com/freertos/latest/userguide/freertos-prereqs.html). - `clientcredentialIOT_THING_NAME` - AWS IoT Thing name. - `clientcredentialMEMFAULT_PROJECT_KEY` - Memfault project key, find it in the [Memfault dashboard](https://app.memfault.com) under "Settings" - `clientcredentialWIFI_SSID` - Wifi SSID string. - `clientcredentialWIFI_PASSWORD` - Wifi password. - `clientcredentialWIFI_SECURITY` - Wifi security. You'll also have to generate the header file with your AWS IoT Thing certificate & private key. To do that, you'll have to pass the files generated during Thing register process to `tools/certificate_configuration/CertificateConfigurator.html` and replace `demos/include/aws_clientcredential_keys.h` with the resulting file. ## Adding the Sources Clone the amazon-freertos repository, and add the sources + patch included in this example: ```bash # clone the amazon-freertos repo at a known-working tag git clone https://github.com/aws/amazon-freertos.git -b 202107.00 ❯ cd amazon-freertos # add the memfault sdk- you can clone it directly, as below; # or add it as a submodule to the freertos repository; # or symlink from another location, per your preference ❯ git clone https://github.com/memfault/memfault-firmware-sdk.git \ libraries/3rdparty/memfault-firmware-sdk/ # apply the patch to integrate this example with amazon-freertos ❯ git apply \ libraries/3rdparty/memfault-firmware-sdk/examples/cypress/CY8CKIT-064S0S2-4343W/amazon-freertos.patch ``` Once the below steps are done, to build, run `make build` in the example directory: ```bash ❯ make -C \ libraries/3rdparty/memfault-firmware-sdk/examples/cypress/CY8CKIT-064S0S2-4343W/ build ``` To program the board, use `make program` (it might be necessary to press the "MODE SELECT" (SW3) button to switch the board to programming mode). ## Setting up AWS IoT & Lambda In order to be able to use Lambda with IoT, the user has to [have permission to create roles and attach role policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-service.html). On top of that, we want two more actions to be available for the IAM user: `lambda:AddPermission` and `iam:CreatePolicy`. You can do it either using `awscli` or the IAM Management Console GUI - either way you'll have to attach the appropriate permissions policy to the IAM entity you're using. Next, we need to setup an [IoT trust role and policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-create-role.html). Once that's done, you can use the same configuration here (this example publishes to the same MQTT topic as the Pinnacle 100 example): > ## Memfault port Port stores coredumps, which include stack, data & bss sections, in flash memory between `__MfltCoredumpsStart` and `__MfltCoredumpsEnd` symbols. Demo implementation provides a 512-byte aligned 128kB region at the end of CM4 flash memory region, just behind the CM4 application signature. To uniquely identify your firmware type, version & device in your Memfault dashboard, please also make sure to edit `memfault_platform_port.c` and fill all the related fields in `memfault_platform_get_device_info`. ## Building, flashing & debugging Build the application using `Build aws_demos Application`, or do it directly from shell in the project directory by using `make build`. Analogously to flash the device either use `aws_demos Program (KitProg3)` from ModusToolbox, or `make program` from shell. In order for it to work, please make sure that the KitProg connection is in CMSIS-DAP mode. You can use the gdbserver/client integrated into ModusToolbox to debug the application by using `aws_demos Debug (KitProg3)` or `make debug` from shell. ## Application details Main function is in `vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/main.c`. MQTT communication for the application is implemented in `mqtt_memfault_project/demo/memfault/mqtt_demo_memfault.c`. Demo overview: - First the user is asked whether a fault, trace or heartbeat event should be generated or the application should continue. - TCP connection to AWS IoT is estabilished. - `CONNECT` to MQTT broker is sent. - All data chunks if available are retrieved and published over MQTT on the `prod/+/+/memfault/chunks` topic. - AWS IoT retrieves the chunks and passes them to the Lambda function. - Lambda function forwards the chunks to Memfault. ## Serial console via UART There should be two `/dev/ttyACM` devices available when the board is connected using the KitProg USB - one of them is the console. Connect to it using e.g. `screen`. Serial parameters are: ```text Baud rate: 115200 Data: 8 bit Parity: None Stop bits: 1 Flow control: None ``` ## Uploading symbol file The demo app's symbol file is located here: ```text build/cy/CY8CKIT-064S0S2-4343W/CY8CKIT_064S0S2_4343W/Debug/aws_demos.elf ``` Upload the symbol file either with the web UI or [using the cli](https://mflt.io/symbol-file-build-ids/). ## Troubleshooting ### TLS error If you get a TLS error when trying to establish a connection to the IoT MQTT Broker saying that your certificate/key is incorrect, you might need to use a different IoT endpoint - try replacing the default address with: `aws iot describe-endpoint --endpoint-type iot:Data-ATS`. ## Related documents - [PSoC® 64 Secure MCU Secure Boot SDK User Guide](https://www.cypress.com/documentation/software-and-drivers/psoc-64-secure-mcu-secure-boot-sdk-user-guide) - [Provisioning Guide for the Cypress CY8CKIT-064S0S2-4343W Kit](https://community.cypress.com/t5/Resource-Library/Provisioning-Guide-for-the-Cypress-CY8CKIT-064S0S2-4343W-Kit/ta-p/252469) - [PSoC 64 Standard Secure - AWS Wi-Fi BT Pioneer Kit Guide](https://www.cypress.com/file/509676/download) ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/amazon-freertos.patch ================================================ From 05af48609ae5887a55c1d8b396b8417132bd838d Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Fri, 3 Sep 2021 13:03:59 -0400 Subject: [PATCH] Integrate memfault with the aws_demos app Adds the necessary modifications to integrate the memfault example into the `aws_demos` app for the CY8CKIT_064S0S2_4343 board. --- demos/coreMQTT/CMakeLists.txt | 2 +- demos/demo_runner/iot_demo_freertos.c | 4 ++ demos/include/aws_clientcredential.h | 5 ++ demos/include/iot_demo_runner.h | 6 ++ .../TOOLCHAIN_GCC_ARM/cyb06xxa_cm4_dual.ld | 30 +++++++- .../aws_demos/application_code/main.c | 68 ++++++++++++++++++- .../aws_demos/config_files/FreeRTOSConfig.h | 1 + .../aws_demos/config_files/aws_demo_config.h | 7 +- 8 files changed, 115 insertions(+), 8 deletions(-) diff --git a/demos/coreMQTT/CMakeLists.txt b/demos/coreMQTT/CMakeLists.txt index 4ebe836af..9a0c949b4 100755 --- a/demos/coreMQTT/CMakeLists.txt +++ b/demos/coreMQTT/CMakeLists.txt @@ -14,7 +14,7 @@ afr_module_cmake_files(${AFR_CURRENT_MODULE} afr_module_sources( ${AFR_CURRENT_MODULE} INTERFACE - "${CMAKE_CURRENT_LIST_DIR}/mqtt_demo_mutual_auth.c" + "${CMAKE_CURRENT_LIST_DIR}/mqtt_demo_memfault.c" # Add the header file added to the target's metadata so that it # is available in code downloaded from the FreeRTOS console. ${AFR_DEMOS_DIR}/include/aws_iot_metrics.h diff --git a/demos/demo_runner/iot_demo_freertos.c b/demos/demo_runner/iot_demo_freertos.c index 1d717ee3e..58b0ceaf7 100644 --- a/demos/demo_runner/iot_demo_freertos.c +++ b/demos/demo_runner/iot_demo_freertos.c @@ -40,6 +40,8 @@ #include "aws_demo.h" #include "iot_init.h" +#include "memfault/panics/assert.h" + static IotNetworkManagerSubscription_t subscription = IOT_NETWORK_MANAGER_SUBSCRIPTION_INITIALIZER; /* Semaphore used to wait for a network to be available. */ @@ -437,6 +439,8 @@ void runDemoTask( void * pArgument ) ( void ) xTask; ( void ) pcTaskName; + MEMFAULT_ASSERT(0); + /* Loop forever */ for( ; ; ) { diff --git a/demos/include/aws_clientcredential.h b/demos/include/aws_clientcredential.h index c3bda361f..477c4335a 100644 --- a/demos/include/aws_clientcredential.h +++ b/demos/include/aws_clientcredential.h @@ -45,6 +45,11 @@ */ #define clientcredentialIOT_THING_NAME "" +/* + * @brief Memfault project key, used to forward chunk data to memfault.com + */ +#define clientcredentialMEMFAULT_PROJECT_KEY "" + /* * @brief Port number the MQTT broker is using. */ diff --git a/demos/include/iot_demo_runner.h b/demos/include/iot_demo_runner.h index b05338675..033331db7 100644 --- a/demos/include/iot_demo_runner.h +++ b/demos/include/iot_demo_runner.h @@ -48,6 +48,12 @@ #undef democonfigDEMO_PRIORITY #define democonfigDEMO_PRIORITY democonfigMQTT_ECHO_TASK_PRIORITY #endif +#elif defined( CONFIG_CORE_MQTT_MEMFAULT_DEMO_ENABLED ) + #define DEMO_entryFUNCTION RunCoreMqttMemfaultDemo + #undef democonfigDEMO_STACKSIZE + #define democonfigDEMO_STACKSIZE democonfigMQTT_MEMFAULT_TASK_STACK_SIZE + #undef democonfigDEMO_PRIORITY + #define democonfigDEMO_PRIORITY democonfigMQTT_MEMFAULT_TASK_PRIORITY #elif defined( CONFIG_CORE_MQTT_AGENT_DEMO_ENABLED ) #define DEMO_entryFUNCTION RunCoreMqttAgentDemo #if defined( democonfigMQTT_ECHO_TASK_STACK_SIZE ) diff --git a/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/cy_code/COMPONENT_CM4/TOOLCHAIN_GCC_ARM/cyb06xxa_cm4_dual.ld b/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/cy_code/COMPONENT_CM4/TOOLCHAIN_GCC_ARM/cyb06xxa_cm4_dual.ld index 2af75a607..232e3d98e 100644 --- a/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/cy_code/COMPONENT_CM4/TOOLCHAIN_GCC_ARM/cyb06xxa_cm4_dual.ld +++ b/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/cy_code/COMPONENT_CM4/TOOLCHAIN_GCC_ARM/cyb06xxa_cm4_dual.ld @@ -46,6 +46,13 @@ STACK_SIZE = 0x1000; /* The size of the MCU boot header area at the start of FLASH */ BOOT_HEADER_SIZE = 0x400; +/* The size of the coredumps storage at the end of FLASH, before signature */ +COREDUMPS_SIZE = 0x20000; + +/* The size of the application signature at the end of FLASH */ +CY_SIGNATURE_SIZE = 0x100; +CY_SIGNATURE_PADDING = 0x200 - CY_SIGNATURE_SIZE; + /* Force symbol to be entered in the output file as an undefined symbol. Doing * this may, for example, trigger linking of additional modules from standard * libraries. You may list several symbols for each EXTERN, and you may use @@ -211,6 +218,12 @@ SECTIONS __zero_table_end__ = .; } > flash + .note.gnu.build-id : + { + __start_gnu_build_id_start = .; + KEEP(*(.note.gnu.build-id)) + } > flash + __etext = . ; @@ -259,13 +272,12 @@ SECTIONS } > ram - /* Place variables in the section that should not be initialized during the * device startup. */ .noinit (NOLOAD) : ALIGN(8) { - KEEP(*(.noinit)) + KEEP(*(.noinit*)) } > ram @@ -321,11 +333,23 @@ SECTIONS /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + /* Coredump storage region. */ + .mflt_coredumps ORIGIN(flash) + LENGTH(flash) - (COREDUMPS_SIZE + CY_SIGNATURE_SIZE + CY_SIGNATURE_PADDING) : + { + . = ALIGN(512); /* Should be aligned, but make sure it is. */ + __MfltCoredumpsStart = .; + KEEP(*(.mflt_coredumps)) + . = ORIGIN(flash) + LENGTH(flash) - (CY_SIGNATURE_SIZE + CY_SIGNATURE_PADDING); + __MfltCoredumpsEnd = .; + } > flash + + ASSERT(__MfltCoredumpsStart >= __etext, "Text overflows memfault coredumps region") /* Used for the digital signature of the secure application and the Bootloader SDK application. * The size of the section depends on the required data size. */ - .cy_app_signature ORIGIN(flash) + LENGTH(flash) - 256 : + .cy_app_signature ORIGIN(flash) + LENGTH(flash) - CY_SIGNATURE_SIZE : { + __CyAppSignatureStart = .; KEEP(*(.cy_app_signature)) } > flash diff --git a/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/main.c b/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/main.c index eb7441aa3..f1ac25a94 100755 --- a/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/main.c +++ b/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/application_code/main.c @@ -78,8 +78,15 @@ #include "tfm_ns_mailbox.h" #endif -/* For Watchdog */ +#include "kvstore.h" + #include "cyhal_wdt.h" +#include "cyhal_flash.h" + +#include +#include +#include +#include /* Logging Task Defines. */ #define mainLOGGING_MESSAGE_QUEUE_LENGTH ( 90 ) @@ -93,7 +100,7 @@ #define mainTEST_RUNNER_TASK_STACK_SIZE ( configMINIMAL_STACK_SIZE * 16 ) /* The name of the devices for xApplicationDNSQueryHook. */ -#define mainDEVICE_NICK_NAME "cypress_demo" /* FIX ME.*/ +#define mainDEVICE_NICK_NAME "memfault-cypress-cy8ckit" /* Static arrays for FreeRTOS-Plus-TCP stack initialization for Ethernet network @@ -179,6 +186,31 @@ void vApplicationDaemonTaskStartupHook( void ); */ static WIFIReturnCode_t prvWifiConnect( void ); +// +// Test Platform Ports +// + +int test_logging(int argc, char *argv[]) { + MEMFAULT_LOG_DEBUG("Debug log!"); + MEMFAULT_LOG_INFO("Info log!"); + MEMFAULT_LOG_WARN("Warning log!"); + MEMFAULT_LOG_ERROR("Error log!"); + return 0; +} + +// Runs a sanity test to confirm coredump port is working as expected +int test_coredump_storage(int argc, char *argv[]) { + + // Note: Coredump saving runs from an ISR prior to reboot so should + // be safe to call with interrupts disabled, but disable just to be sure. + __disable_irq(); + memfault_coredump_storage_debug_test_begin(); + __enable_irq(); + + memfault_coredump_storage_debug_test_finish(); + return 0; +} + /** * @brief Initializes the board. */ @@ -241,6 +273,7 @@ cy_rslt_t prvWatchdogTaskStart(void) cy_rslt_t result; configPRINTF(("Create Watchdog Timer\n")); + result = cyhal_wdt_init(&prvWatchdogTimerObj, CY_WATCHDOG_TIMER_MILLISECONDS); if (result == CY_RSLT_WDT_ALREADY_INITIALIZED) { @@ -335,6 +368,9 @@ static void prvMiscInitialization( void ) CY_ASSERT(CY_RSLT_SUCCESS == result); } +extern cyhal_flash_t flash_obj; +extern cyhal_flash_info_t flash_obj_info; + /*-----------------------------------------------------------*/ void vApplicationDaemonTaskStartupHook( void ) { @@ -347,6 +383,28 @@ void vApplicationDaemonTaskStartupHook( void ) result = kvstore_init(); CY_ASSERT(CY_RSLT_SUCCESS == result); + cyhal_flash_init(&flash_obj); + cyhal_flash_get_info(&flash_obj, &flash_obj_info); + + printf("Press \"1\" to simulate a fault and reboot,\n" + "\"2\" to generate heartbeat and continue,\n" + "\"3\" to generate a trace and continue,\n" + "Anything else to just continue.\n" + "(Confirm with enter)\n"); + + char c = getchar(); + + if (c == 0x31) + memfault_test_fault(); + else if (c == 0x32) + memfault_test_heartbeat(0, NULL); + else if (c == 0x33) + memfault_test_trace(0, NULL); + + printf("Running the demo app.\n"); + + memfault_platform_boot(); + #ifdef CY_BOOT_USE_EXTERNAL_FLASH #ifdef PDL_CODE if (qspi_init_sfdp(1) < 0) @@ -382,7 +440,10 @@ void vApplicationDaemonTaskStartupHook( void ) { printf( "Start Watchdog Task Failed.\r\n"); } - + else + { + printf( "Start Watchdog Task Success.\r\n"); + } /* Connect to the Wi-Fi before running the tests. */ if (prvWifiConnect() == eWiFiSuccess) { @@ -649,6 +710,7 @@ void vAssertCalled(const char * pcFile, * this function to be exited. */ taskDISABLE_INTERRUPTS(); { + MEMFAULT_ASSERT(0); while (ulBlockVariable == 0UL) { vTaskDelay( pdMS_TO_TICKS( ulLongSleep ) ); diff --git a/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/config_files/FreeRTOSConfig.h b/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/config_files/FreeRTOSConfig.h index ebc221e0c..e883a4186 100644 --- a/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/config_files/FreeRTOSConfig.h +++ b/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/config_files/FreeRTOSConfig.h @@ -48,6 +48,7 @@ #include #include #include "cycfg_system.h" +#include "memfault/ports/freertos_trace.h" extern uint32_t SystemCoreClock; diff --git a/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/config_files/aws_demo_config.h b/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/config_files/aws_demo_config.h index 1879447af..f2c3ab4a1 100644 --- a/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/config_files/aws_demo_config.h +++ b/vendors/cypress/boards/CY8CKIT_064S0S2_4343W/aws_demos/config_files/aws_demo_config.h @@ -34,6 +34,7 @@ * CONFIG_CORE_HTTP_S3_DOWNLOAD_MULTITHREADED_DEMO_ENABLED * CONFIG_CORE_HTTP_S3_UPLOAD_DEMO_ENABLED * CONFIG_CORE_MQTT_MUTUAL_AUTH_DEMO_ENABLED + * CONFIG_CORE_MQTT_MEMFAULT_DEMO_ENABLED * CONFIG_DEVICE_SHADOW_DEMO_ENABLED * CONFIG_DEVICE_DEFENDER_DEMO_ENABLED * CONFIG_JOBS_DEMO_ENABLED @@ -47,7 +48,7 @@ * * These defines are used in iot_demo_runner.h for demo selection */ -#define CONFIG_CORE_MQTT_MUTUAL_AUTH_DEMO_ENABLED +#define CONFIG_CORE_MQTT_MEMFAULT_DEMO_ENABLED /* Default configuration for all demos. Individual demos can override these below */ #define democonfigDEMO_STACKSIZE ( configMINIMAL_STACK_SIZE * 8 ) @@ -63,6 +64,10 @@ #define democonfigMQTT_ECHO_TASK_STACK_SIZE ( configMINIMAL_STACK_SIZE * 8 ) #define democonfigMQTT_ECHO_TASK_PRIORITY ( tskIDLE_PRIORITY ) +#define democonfigMQTT_MEMFAULT_TLS_NEGOTIATION_TIMEOUT pdMS_TO_TICKS( 12000 ) +#define democonfigMQTT_MEMFAULT_TASK_STACK_SIZE ( configMINIMAL_STACK_SIZE * 8 ) +#define democonfigMQTT_MEMFAULT_TASK_PRIORITY ( tskIDLE_PRIORITY ) + /* Number of sub pub tasks that connect to a broker that is not using TLS. */ #define democonfigMQTT_SUB_PUB_NUM_UNSECURE_TASKS ( 1 ) /* Number of sub pub tasks that connect to a broker that is using TLS. */ -- 2.33.0 ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/README.md ================================================ # CY8CKIT-064S0S2-4343W port Port for the [CY8CKIT-064S0S2-4343W](https://www.cypress.com/documentation/development-kitsboards/psoc-64-standard-secure-aws-wi-fi-bt-pioneer-kit-cy8ckit) evaluation kit. # Integration steps Make sure that both `flash_obj` and `flash_obj_info` from `memfault_platform_storage.h/c` are initialized before the actual Memfault library. `__MfltCoredumpsStart` and `__MfltCoredumpsEnd` define the memory region for coredumps. It should be a 512-byte aligned flash memory region, at least 96kB size is recommended. The demo application has an example implementation you can use. To uniquely identify your firmware type, version & device in your Memfault dashboard, please also make sure to edit `memfault_platform_port.c` and fill all the related fields in `memfault_platform_get_device_info`. # Port details Coredumps include data, bss & stack regions. Port structure: - `memfault_platform_port.c` - Device info, reboot, memory range, reboot reason, logging & memfault/freertos boot setup. - `memfault_platform_storage.c/h` - Implements IO operations on coredump region in flash memory & device memory regions to dump. - `memfault_test.c/h` - Functions for testing Memfault functionality - on demand heartbeat, trace and fault generation. ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_metrics_heartbeat_config.def ================================================ //! Define custom system metrics to track. For example, // MEMFAULT_METRICS_KEY_DEFINE(main_task_stack_hwm, kMemfaultMetricType_Unsigned) // MEMFAULT_METRICS_KEY_DEFINE(ble_min_rssi, kMemfaultMetricType_Signed) // MEMFAULT_METRICS_KEY_DEFINE(mcu_sleep_time_ms, kMemfaultMetricType_Timer) ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_platform_config.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" #pragma once #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_USE_GNU_BUILD_ID 1 #ifdef __cplusplus } #endif ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_platform_log_config.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! // Logging depends on how your configuration does logging. See // https://mflt.io/logging-dependency #pragma once #include "memfault/core/platform/debug_log.h" #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_platform_port.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Glue layer between the Memfault SDK and the underlying platform //! #include #include #include #include #include #include #include "memfault/components.h" #include "memfault/core/compiler.h" #include "memfault/ports/freertos.h" #include "memfault/ports/reboot_reason.h" #include "memfault_platform_log_config.h" #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) typedef struct { uint32_t start_addr; size_t length; } sMemRegions; sMemRegions s_mcu_mem_regions[] = { { .start_addr = 0x08030000, .length = 0xB7000 }, }; void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { // IMPORTANT: All strings returned in info must be constant // or static as they will be used _after_ the function returns // See https://mflt.io/version-nomenclature for more context *info = (sMemfaultDeviceInfo){ // An ID that uniquely identifies the device in your fleet // (i.e serial number, mac addr, chip id, etc) // Regular expression defining valid device serials: ^[-a-zA-Z0-9_]+$ .device_serial = "DEMOSERIAL", // A name to represent the firmware running on the MCU. // (i.e "ble-fw", "main-fw", or a codename for your project) .software_type = "app-fw", // The version of the "software_type" currently running. // "software_type" + "software_version" must uniquely represent // a single binary .software_version = "1.0.0", // The revision of hardware for the device. This value must remain // the same for a unique device. // (i.e evt, dvt, pvt, or rev1, rev2, etc) // Regular expression defining valid hardware versions: ^[-a-zA-Z0-9_\.\+]+$ .hardware_version = "dvt1", }; } //! Last function called after a coredump is saved. Should perform //! any final cleanup and then reset the device void memfault_platform_reboot(void) { NVIC_SystemReset(); while (1) { } // unreachable } //! If device does not track time, return false, else return true if time is valid bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { *time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = 0 }, }; return false; } size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_mcu_mem_regions); i++) { const uint32_t lower_addr = s_mcu_mem_regions[i].start_addr; const uint32_t upper_addr = lower_addr + s_mcu_mem_regions[i].length; if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); } } return 0; } void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); const uint32_t reset_cause = Cy_SysLib_GetResetReason(); eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, GetResetReason=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); if (reset_cause & CY_SYSLIB_RESET_HWWDT) { MEMFAULT_PRINT_RESET_INFO(" Watchdog Timer Reset"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & CY_SYSLIB_RESET_ACT_FAULT) { MEMFAULT_PRINT_RESET_INFO(" Fault Logging System Active Reset Request"); reset_reason = kMfltRebootReason_UnknownError; } else if (reset_cause & CY_SYSLIB_RESET_DPSLP_FAULT) { MEMFAULT_PRINT_RESET_INFO(" Fault Logging System Deep-Sleep Reset Request"); reset_reason = kMfltRebootReason_DeepSleep; } else if (reset_cause & CY_SYSLIB_RESET_SOFT) { MEMFAULT_PRINT_RESET_INFO(" Software Reset"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & CY_SYSLIB_RESET_SWWDT0) { MEMFAULT_PRINT_RESET_INFO(" MCWDT0 Reset"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & CY_SYSLIB_RESET_SWWDT1) { MEMFAULT_PRINT_RESET_INFO(" MCWDT1 Reset"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & CY_SYSLIB_RESET_SWWDT2) { MEMFAULT_PRINT_RESET_INFO(" MCWDT2 Reset"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & CY_SYSLIB_RESET_SWWDT3) { MEMFAULT_PRINT_RESET_INFO(" MCWDT3 Reset"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & CY_SYSLIB_RESET_HIB_WAKEUP) { MEMFAULT_PRINT_RESET_INFO(" Hibernation Exit Reset"); reset_reason = kMfltRebootReason_DeepSleep; } else { MEMFAULT_PRINT_RESET_INFO(" Other Error"); reset_reason = kMfltRebootReason_Unknown; } Cy_SysLib_ClearResetReason(); *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { va_list args; va_start(args, fmt); char log_buf[128]; vsnprintf(log_buf, sizeof(log_buf), fmt, args); const char *lvl_str; switch (level) { case kMemfaultPlatformLogLevel_Debug: lvl_str = "D"; break; case kMemfaultPlatformLogLevel_Info: lvl_str = "I"; break; case kMemfaultPlatformLogLevel_Warning: lvl_str = "W"; break; case kMemfaultPlatformLogLevel_Error: lvl_str = "E"; break; default: lvl_str = "D"; break; } vsnprintf(log_buf, sizeof(log_buf), fmt, args); printf("[%s] MFLT: %s\n", lvl_str, log_buf); } MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_info") static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; void memfault_platform_reboot_tracking_boot(void) { sResetBootupInfo reset_info = { 0 }; memfault_reboot_reason_get(&reset_info); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); } int memfault_platform_boot(void) { memfault_freertos_port_boot(); memfault_build_info_dump(); memfault_device_info_dump(); memfault_platform_reboot_tracking_boot(); static uint8_t s_event_storage[1024]; const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(evt_storage); memfault_reboot_tracking_collect_reset_info(evt_storage); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); MEMFAULT_LOG_INFO("Memfault Initialized!"); return 0; } ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_platform_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Persistent storage using chip internal flash. //! #include "memfault_platform_storage.h" #include #include "memfault/components.h" /* Flash page size */ #define MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE 512 /* Coredump working buffer */ typedef struct { uint8_t data[MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE]; uint32_t address; uint32_t bytes_written; } sCoredumpWorkingBuffer; /* Flash block object, initialized in main.c. */ cyhal_flash_t flash_obj; cyhal_flash_info_t flash_obj_info; /* Section addresses from the linker script. */ extern uint8_t __bss_start__; extern uint8_t __bss_end__; extern uint8_t __data_start__; extern uint8_t __data_end__; extern uint8_t __StackLimit; extern uint8_t __StackTop; MEMFAULT_ALIGNED(8) static sCoredumpWorkingBuffer s_working_buffer_header; MEMFAULT_ALIGNED(8) static sCoredumpWorkingBuffer s_working_buffer; static sCoredumpWorkingBuffer *prv_get_working_buf(uint32_t offset) { return (offset == 0) ? &s_working_buffer_header : &s_working_buffer; } static bool prv_write_blk(sCoredumpWorkingBuffer *blk) { const uint32_t start_addr = (uint32_t)&__MfltCoredumpsStart; const uint32_t addr = start_addr + blk->address; cy_rslt_t result = cyhal_flash_write(&flash_obj, addr, (uint32_t *)blk->data); if (result != CY_RSLT_SUCCESS) { return false; } *blk = (sCoredumpWorkingBuffer){ 0 }; return true; } static bool prv_try_flush(void) { sCoredumpWorkingBuffer *hdr_block = &s_working_buffer_header; sCoredumpWorkingBuffer *data_block = &s_working_buffer; if (hdr_block->bytes_written == MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) { // this is the final write for the coredump (header) // it's possible the last data blob in the coredump isn't a multiple // of our write size so let's flush whatever is queued up. (Unused bytes // are just zero'd since the working buffer is memset to 0 before each use) if (data_block->bytes_written != 0) { if (!prv_write_blk(data_block)) { return false; } } // write the header if (!prv_write_blk(hdr_block)) { return false; } } if (data_block->bytes_written == MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) { if (!prv_write_blk(data_block)) { return false; } } return true; } const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { static sMfltCoredumpRegion s_coredump_regions[3]; uint32_t bss_size = &__bss_end__ - &__bss_start__; uint32_t data_size = &__data_end__ - &__data_start__; uint32_t stack_size = &__StackTop - &__StackLimit; s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&__data_start__, data_size); s_coredump_regions[1] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&__bss_start__, bss_size); s_coredump_regions[2] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&__StackLimit, stack_size); *num_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions); return s_coredump_regions; } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { /* Size of the coredumps region based on addresses from the linker script. */ uint32_t coredumps_sz = (&__MfltCoredumpsEnd - &__MfltCoredumpsStart); /* We're in the first block only, so get the page size from the first block. */ *info = (sMfltCoredumpStorageInfo){ .size = coredumps_sz, .sector_size = flash_obj_info.blocks[0].page_size, }; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { cy_rslt_t result; if (&__MfltCoredumpsStart + offset + read_len > &__MfltCoredumpsEnd) { return false; } result = cyhal_flash_read(&flash_obj, (uint32_t)(&__MfltCoredumpsStart) + offset, (uint8_t *)data, read_len); return result == CY_RSLT_SUCCESS; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (&__MfltCoredumpsStart + offset + erase_size > &__MfltCoredumpsEnd) { return false; } memfault_platform_coredump_storage_clear(); return true; } /* Based on buffered_coredump_storage.h */ bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if (&__MfltCoredumpsStart + offset + data_len > &__MfltCoredumpsEnd) { return false; } const uint8_t *datap = data; uint32_t start_addr = offset; uint32_t page_aligned_start_address = (start_addr / MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) * MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE; /* We have to copy data into a temporary buffer because we can only issue aligned writes. */ if (page_aligned_start_address != start_addr) { sCoredumpWorkingBuffer *working_buffer = prv_get_working_buf(page_aligned_start_address); uint32_t bytes_to_write = MEMFAULT_MIN( (page_aligned_start_address + MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) - start_addr, data_len); uint32_t write_offset = start_addr - page_aligned_start_address; memcpy(&working_buffer->data[write_offset], datap, bytes_to_write); working_buffer->bytes_written += bytes_to_write; working_buffer->address = page_aligned_start_address; if (!prv_try_flush()) { return false; } start_addr += bytes_to_write; data_len -= bytes_to_write; datap += bytes_to_write; } for (uint32_t i = 0; i < data_len; i += MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) { const uint32_t size = MEMFAULT_MIN(MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE, data_len - i); sCoredumpWorkingBuffer *working_buffer = prv_get_working_buf(start_addr + i); memcpy(&working_buffer->data, &datap[i], size); working_buffer->bytes_written += size; working_buffer->address = start_addr + i; if (!prv_try_flush()) { return false; } } return true; } void memfault_platform_coredump_storage_clear(void) { uint32_t i; uint32_t page_addr; uint32_t coredumps_sz = (&__MfltCoredumpsEnd - &__MfltCoredumpsStart); uint32_t page_sz = MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE; uint32_t page_count = coredumps_sz / page_sz; /* Erase the whole coredumps region. */ for (i = 0; i < page_count; i++) { page_addr = (uint32_t)(&__MfltCoredumpsStart) + (i * page_sz); cyhal_flash_erase(&flash_obj, page_addr); } } ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_platform_storage.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Persistent storage using chip internal flash. //! #pragma once #include #ifdef __cplusplus extern "C" { #endif /* Flash block object */ extern cyhal_flash_t flash_obj; extern cyhal_flash_info_t flash_obj_info; /* Coredumps section addresses from the linker script. */ extern uint8_t __MfltCoredumpsStart; extern uint8_t __MfltCoredumpsEnd; #ifdef __cplusplus } #endif ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_test.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Test functions for exercising memfault functionality. //! #include "memfault_test.h" #include "memfault/components.h" #include "memfault/ports/freertos.h" #define MEMFAULT_CHUNK_SIZE 1500 // Triggers an immediate heartbeat capture (instead of waiting for timer // to expire) int memfault_test_heartbeat(int argc, char *argv[]) { memfault_metrics_heartbeat_debug_trigger(); return 0; } int memfault_test_trace(int argc, char *argv[]) { MEMFAULT_TRACE_EVENT_WITH_LOG(critical_error, "A test error trace!"); return 0; } //! Trigger a user initiated reboot and confirm reason is persisted int memfault_test_reboot(int argc, char *argv[]) { MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_UserReset); memfault_platform_reboot(); } // // Test different crash types where a coredump should be captured // int memfault_test_assert(int argc, char *argv[]) { MEMFAULT_ASSERT(0); return -1; // should never get here } int memfault_test_fault(void) { void (*bad_func)(void) = (void *)0xEEEEDEAD; bad_func(); return -1; // should never get here } int memfault_test_hang(int argc, char *argv[]) { while (1) { } return -1; // should never get here } // Dump Memfault data collected to console int memfault_test_export(int argc, char *argv[]) { memfault_data_export_dump_chunks(); return 0; } ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_test.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Test functions for exercising memfault functionality. //! #pragma once #ifdef __cplusplus extern "C" { #endif // Trigger an immediate heartbeat capture (instead of waiting for timer // to expire) int memfault_test_heartbeat(int argc, char *argv[]); // Trigger a trace int memfault_test_trace(int argc, char *argv[]); // Trigger a user initiated reboot and confirm reason is persisted int memfault_test_reboot(int argc, char *argv[]); // Test different crash types where a coredump should be captured int memfault_test_assert(int argc, char *argv[]); int memfault_test_fault(void); int memfault_test_hang(int argc, char *argv[]); // Dump Memfault data collected to console int memfault_test_export(int argc, char *argv[]); #ifdef __cplusplus } #endif ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/memfault_trace_reason_user_config.def ================================================ //! Define custom error reasons that can be filtered & searched //! on in the Memfault UI. MEMFAULT_TRACE_REASON_DEFINE(critical_error) ================================================ FILE: examples/cypress/CY8CKIT-064S0S2-4343W/src/mqtt_demo_memfault.c ================================================ /* * FreeRTOS V202012.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://www.FreeRTOS.org * http://aws.amazon.com/freertos * */ /* * Demo for showing use of the MQTT API using a mutually authenticated * network connection. * * The Example shown below uses MQTT APIs to create MQTT messages and send them * over the mutually authenticated network connection established with the * MQTT broker. This example is single threaded and uses statically allocated * memory. It uses QoS1 for sending to and receiving messages from the broker. * * A mutually authenticated TLS connection is used to connect to the * MQTT message broker in this example. Define democonfigMQTT_BROKER_ENDPOINT * and democonfigROOT_CA_PEM, in mqtt_demo_mutual_auth_config.h, and the client * private key and certificate, in aws_clientcredential_keys.h, to establish a * mutually authenticated connection. * * This example is based on the * /demos/coreMQTT/mqtt_demo_mutual_auth.c , modified to * publish any available memfault chunks ('memfault_packetizer_get_chunk') to * the MQTT 'mqttexampleTOPIC'. */ /** * @file mqtt_demo_memfault.c * @brief Demonstrates usage of the MQTT library. */ /* Standard includes. */ #include #include #include /* Demo Specific configs. */ #include "mqtt_demo_mutual_auth_config.h" /* Include common demo header. */ #include "aws_demo.h" /* Kernel includes. */ #include "FreeRTOS.h" #include "task.h" /* MQTT library includes. */ #include "core_mqtt.h" /* Retry utilities include. */ #include "backoff_algorithm.h" /* Include PKCS11 helpers header. */ #include "pkcs11_helpers.h" /* Transport interface implementation include header for TLS. */ #include "transport_secure_sockets.h" /* Include header for connection configurations. */ #include "aws_clientcredential.h" /* Include header for client credentials. */ #include "aws_clientcredential_keys.h" /* Include header for root CA certificates. */ #include "iot_default_root_certificates.h" /* Include AWS IoT metrics macros header. */ #include "aws_iot_metrics.h" /* Include memfault headers */ #include #include #include /*------------- Demo configurations -------------------------*/ /** Note: The device client certificate and private key credentials are * obtained by the transport interface implementation (with Secure Sockets) * from the demos/include/aws_clientcredential_keys.h file. * * The following macros SHOULD be defined for this demo which uses both server * and client authentications for TLS session: * - keyCLIENT_CERTIFICATE_PEM for client certificate. * - keyCLIENT_PRIVATE_KEY_PEM for client private key. */ /** * @brief The MQTT broker endpoint used for this demo. */ #ifndef democonfigMQTT_BROKER_ENDPOINT #define democonfigMQTT_BROKER_ENDPOINT clientcredentialMQTT_BROKER_ENDPOINT #endif /** * @brief The root CA certificate belonging to the broker. */ #ifndef democonfigROOT_CA_PEM #define democonfigROOT_CA_PEM tlsATS1_ROOT_CERTIFICATE_PEM #endif #ifndef democonfigCLIENT_IDENTIFIER /** * @brief The MQTT client identifier used in this example. Each client identifier * must be unique so edit as required to ensure no two clients connecting to the * same broker use the same client identifier. */ #define democonfigCLIENT_IDENTIFIER clientcredentialIOT_THING_NAME #endif #ifndef democonfigMQTT_BROKER_PORT /** * @brief The port to use for the demo. */ #define democonfigMQTT_BROKER_PORT clientcredentialMQTT_BROKER_PORT #endif /** * @brief The maximum number of times to run the subscribe publish loop in this * demo. */ #ifndef democonfigMQTT_MAX_DEMO_COUNT #define democonfigMQTT_MAX_DEMO_COUNT (3) #endif /*-----------------------------------------------------------*/ /** * @brief The maximum number of retries for network operation with server. */ #define RETRY_MAX_ATTEMPTS (5U) /** * @brief The maximum back-off delay (in milliseconds) for retrying failed operation * with server. */ #define RETRY_MAX_BACKOFF_DELAY_MS (5000U) /** * @brief The base back-off delay (in milliseconds) to use for network operation retry * attempts. */ #define RETRY_BACKOFF_BASE_MS (500U) /** * @brief Timeout for receiving CONNACK packet in milliseconds. */ #define mqttexampleCONNACK_RECV_TIMEOUT_MS (1000U) /** * @brief The topic to subscribe and publish to in the example. * * Contains fingerprint and project key for forwarding to memfault: * prod///memfault//chunk */ #define mqttexampleTOPIC \ "prod/psoc64/" democonfigCLIENT_IDENTIFIER "/memfault" \ "/" clientcredentialMEMFAULT_PROJECT_KEY "/chunk" #define mqttforwardTOPIC "device/" democonfigCLIENT_IDENTIFIER "/forward" /** * @brief The number of topic filters to subscribe. */ #define mqttexampleTOPIC_COUNT (1) /** * @brief The MQTT message published in this example. */ #define mqttexampleMESSAGE "Hello World!" /** * @brief Time in ticks to wait between each cycle of the demo implemented * by RunCoreMqttMemfaultDemo(). */ #define mqttexampleDELAY_BETWEEN_DEMO_ITERATIONS_TICKS (pdMS_TO_TICKS(5000U)) /** * @brief Timeout for MQTT_ProcessLoop in milliseconds. */ #define mqttexamplePROCESS_LOOP_TIMEOUT_MS (700U) /** * @brief The maximum number of times to call MQTT_ProcessLoop() when polling * for a specific packet from the broker. */ #define MQTT_PROCESS_LOOP_PACKET_WAIT_COUNT_MAX (30U) /** * @brief Keep alive time reported to the broker while establishing * an MQTT connection. * * It is the responsibility of the Client to ensure that the interval between * Control Packets being sent does not exceed the this Keep Alive value. In the * absence of sending any other Control Packets, the Client MUST send a * PINGREQ Packet. */ #define mqttexampleKEEP_ALIVE_TIMEOUT_SECONDS (60U) /** * @brief Delay (in ticks) between consecutive cycles of MQTT publish operations in a * demo iteration. * * Note that the process loop also has a timeout, so the total time between * publishes is the sum of the two delays. */ #define mqttexampleDELAY_BETWEEN_PUBLISHES_TICKS (pdMS_TO_TICKS(5000U)) /** * @brief Transport timeout in milliseconds for transport send and receive. */ #define mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS (500U) /** * @brief Milliseconds per second. */ #define MILLISECONDS_PER_SECOND (1000U) /** * @brief Milliseconds per FreeRTOS tick. */ #define MILLISECONDS_PER_TICK (MILLISECONDS_PER_SECOND / configTICK_RATE_HZ) /** * @brief Memfault chunk size. */ #define MEMFAULT_CHUNK_SIZE (1500U) /** * @brief Chunk upload delay. */ #define mqttexampleDELAY_UPLOAD (pdMS_TO_TICKS(5000U)) /*-----------------------------------------------------------*/ /** * @brief Each compilation unit that consumes the NetworkContext must define it. * It should contain a single pointer to the type of your desired transport. * When using multiple transports in the same compilation unit, define this pointer as void *. * * @note Transport stacks are defined in * amazon-freertos/libraries/abstractions/transport/secure_sockets/transport_secure_sockets.h. */ struct NetworkContext { SecureSocketsTransportParams_t *pParams; }; /*-----------------------------------------------------------*/ /** * @brief Calculate and perform an exponential backoff with jitter delay for * the next retry attempt of a failed network operation with the server. * * The function generates a random number, calculates the next backoff period * with the generated random number, and performs the backoff delay operation if the * number of retries have not exhausted. * * @note The PKCS11 module is used to generate the random number as it allows access * to a True Random Number Generator (TRNG) if the vendor platform supports it. * It is recommended to seed the random number generator with a device-specific entropy * source so that probability of collisions from devices in connection retries is mitigated. * * @note The backoff period is calculated using the backoffAlgorithm library. * * @param[in, out] pxRetryAttempts The context to use for backoff period calculation * with the backoffAlgorithm library. * * @return pdPASS if calculating the backoff period was successful; otherwise pdFAIL * if there was failure in random number generation OR all retry attempts had exhausted. */ static BaseType_t prvBackoffForRetry(BackoffAlgorithmContext_t *pxRetryParams); /** * @brief Connect to MQTT broker with reconnection retries. * * If connection fails, retry is attempted after a timeout. * Timeout value will exponentially increase until maximum * timeout value is reached or the number of attempts are exhausted. * * @param[out] pxNetworkContext The output parameter to return the created network context. * * @return pdFAIL on failure; pdPASS on successful TLS+TCP network connection. */ static BaseType_t prvConnectToServerWithBackoffRetries(NetworkContext_t *pNetworkContext); /** * @brief Sends an MQTT Connect packet over the already connected TLS over TCP connection. * * @param[in, out] pxMQTTContext MQTT context pointer. * @param[in] xNetworkContext Network context. * * @return pdFAIL on failure; pdPASS on successful MQTT connection. */ static BaseType_t prvCreateMQTTConnectionWithBroker(MQTTContext_t *pxMQTTContext, NetworkContext_t *pxNetworkContext); /** * @brief Function to update variable #xTopicFilterContext with status * information from Subscribe ACK. Called by the event callback after processing * an incoming SUBACK packet. * * @param[in] pxPacketInfo Server response to the subscription request. */ static void prvUpdateSubAckStatus(MQTTPacketInfo_t *pxPacketInfo); /** * @brief Publishes a message mqttexampleMESSAGE on mqttexampleTOPIC topic. * * @param[in] pxMQTTContext MQTT context pointer. * * @return pdFAIL on failure; pdPASS on successful PUBLISH operation. */ static BaseType_t prvMQTTPublishToTopic(MQTTContext_t *pxMQTTContext, const void *payload, size_t len); /** * @brief The timer query function provided to the MQTT context. * * @return Time in milliseconds. */ static uint32_t prvGetTimeMs(void); /** * @brief Process a response or ack to an MQTT request (PING, PUBLISH, * SUBSCRIBE or UNSUBSCRIBE). This function processes PINGRESP, PUBACK, * SUBACK, and UNSUBACK. * * @param[in] pxIncomingPacket is a pointer to structure containing deserialized * MQTT response. * @param[in] usPacketId is the packet identifier from the ack received. */ static void prvMQTTProcessResponse(MQTTPacketInfo_t *pxIncomingPacket, uint16_t usPacketId); /** * @brief Process incoming Publish message. * * @param[in] pxPublishInfo is a pointer to structure containing deserialized * Publish message. */ static void prvMQTTProcessIncomingPublish(MQTTPublishInfo_t *pxPublishInfo); /** * @brief The application callback function for getting the incoming publishes, * incoming acks, and ping responses reported from the MQTT library. * * @param[in] pxMQTTContext MQTT context pointer. * @param[in] pxPacketInfo Packet Info pointer for the incoming packet. * @param[in] pxDeserializedInfo Deserialized information from the incoming packet. */ static void prvEventCallback(MQTTContext_t *pxMQTTContext, MQTTPacketInfo_t *pxPacketInfo, MQTTDeserializedInfo_t *pxDeserializedInfo); /*-----------------------------------------------------------*/ /** * @brief Static buffer used to hold MQTT messages being sent and received. */ static uint8_t ucSharedBuffer[democonfigNETWORK_BUFFER_SIZE]; /** * @brief Global entry time into the application to use as a reference timestamp * in the #prvGetTimeMs function. #prvGetTimeMs will always return the difference * between the current time and the global entry time. This will reduce the chances * of overflow for the 32 bit unsigned integer used for holding the timestamp. */ static uint32_t ulGlobalEntryTimeMs; /** * @brief Packet Identifier generated when Publish request was sent to the broker; * it is used to match received Publish ACK to the transmitted Publish packet. */ static uint16_t usPublishPacketIdentifier; /** * @brief Packet Identifier generated when Subscribe request was sent to the broker; * it is used to match received Subscribe ACK to the transmitted Subscribe packet. */ static uint16_t usSubscribePacketIdentifier; /** * @brief Packet Identifier generated when Unsubscribe request was sent to the broker; * it is used to match received Unsubscribe response to the transmitted Unsubscribe * request. */ static uint16_t usUnsubscribePacketIdentifier; /** * @brief MQTT packet type received from the MQTT broker. * * @note Only on receiving incoming PUBLISH, SUBACK, and UNSUBACK, this * variable is updated. For MQTT packets PUBACK and PINGRESP, the variable is * not updated since there is no need to specifically wait for it in this demo. * A single variable suffices as this demo uses single task and requests one operation * (of PUBLISH, SUBSCRIBE, UNSUBSCRIBE) at a time before expecting response from * the broker. Hence it is not possible to receive multiple packets of type PUBLISH, * SUBACK, and UNSUBACK in a single call of #prvWaitForPacket. * For a multi task application, consider a different method to wait for the packet, if needed. */ static uint16_t usPacketTypeReceived = 0U; /** * @brief A pair containing a topic filter and its SUBACK status. */ typedef struct topicFilterContext { const char *pcTopicFilter; MQTTSubAckStatus_t xSubAckStatus; } topicFilterContext_t; /** * @brief An array containing the context of a SUBACK; the SUBACK status * of a filter is updated when the event callback processes a SUBACK. */ static topicFilterContext_t xTopicFilterContext[mqttexampleTOPIC_COUNT] = { { mqttforwardTOPIC, MQTTSubAckFailure } }; /** @brief Static buffer used to hold MQTT messages being sent and received. */ static MQTTFixedBuffer_t xBuffer = { ucSharedBuffer, democonfigNETWORK_BUFFER_SIZE }; /*-----------------------------------------------------------*/ /* * @brief The example shown below uses MQTT APIs to create MQTT messages and * send them over the mutually authenticated network connection established with the * MQTT broker. This example is single threaded and uses statically allocated * memory. It uses QoS1 for sending to and receiving messages from the broker. * * This MQTT client subscribes to the topic as specified in mqttexampleTOPIC at the * top of this file by sending a subscribe packet and then waiting for a subscribe * acknowledgment (SUBACK).This client will then publish to the same topic it * subscribed to, so it will expect all the messages it sends to the broker to be * sent back to it from the broker. * * This example runs for democonfigMQTT_MAX_DEMO_COUNT, if the * connection to the broker goes down, the code tries to reconnect to the broker * with an exponential backoff mechanism. */ int RunCoreMqttMemfaultDemo(bool awsIotMqttMode, const char *pIdentifier, void *pNetworkServerInfo, void *pNetworkCredentialInfo, const IotNetworkInterface_t *pNetworkInterface) { uint32_t ulDemoRunCount = 0U; NetworkContext_t xNetworkContext = { 0 }; MQTTContext_t xMQTTContext = { 0 }; TransportSocketStatus_t xNetworkStatus; BaseType_t xIsConnectionEstablished = pdFALSE; SecureSocketsTransportParams_t secureSocketsTransportParams = { 0 }; uint8_t mfltBuf[MEMFAULT_CHUNK_SIZE]; size_t mfltLen = sizeof(mfltBuf); bool isDataAvailable = false; bool isDemoSuccessful = false; /* Upon return, pdPASS will indicate a successful demo execution. * pdFAIL will indicate some failures occurred during execution. The * user of this demo must check the logs for any failure codes. */ BaseType_t xDemoStatus = pdFAIL; /* Remove compiler warnings about unused parameters. */ (void)awsIotMqttMode; (void)pIdentifier; (void)pNetworkServerInfo; (void)pNetworkCredentialInfo; (void)pNetworkInterface; /* Set the entry time of the demo application. This entry time will be used * to calculate relative time elapsed in the execution of the demo application, * by the timer utility function that is provided to the MQTT library. */ ulGlobalEntryTimeMs = prvGetTimeMs(); xNetworkContext.pParams = &secureSocketsTransportParams; for (;;) { /* Attempt to establish TLS session with MQTT broker. If connection fails, * retry after a timeout. Timeout value will be exponentially increased until * the maximum number of attempts are reached or the maximum timeout value is reached. * The function returns a failure status if the TLS over TCP connection cannot be established * to the broker after the configured number of attempts. */ xDemoStatus = prvConnectToServerWithBackoffRetries(&xNetworkContext); if (xDemoStatus == pdPASS) { /* Set a flag indicating a TLS connection exists. This is done to * disconnect if the loop exits before disconnection happens. */ xIsConnectionEstablished = pdTRUE; /* Sends an MQTT Connect packet over the already established TLS connection, * and waits for connection acknowledgment (CONNACK) packet. */ LogInfo(("Creating an MQTT connection to %s.", democonfigMQTT_BROKER_ENDPOINT)); xDemoStatus = prvCreateMQTTConnectionWithBroker(&xMQTTContext, &xNetworkContext); } if (xDemoStatus == pdPASS) { LogInfo(("Retrieving data chunk.")); isDataAvailable = memfault_packetizer_get_chunk(mfltBuf, &mfltLen); while (isDataAvailable) { LogInfo(("Publishing to %s.", mqttexampleTOPIC)); xDemoStatus = prvMQTTPublishToTopic(&xMQTTContext, mfltBuf, mfltLen); vTaskDelay(mqttexampleDELAY_UPLOAD); if (xDemoStatus == pdPASS) { LogInfo(("Publish success.")); mfltLen = sizeof(mfltBuf); isDataAvailable = memfault_packetizer_get_chunk(mfltBuf, &mfltLen); } else { LogInfo(("Publish failure.")); isDataAvailable = false; } } } if (xDemoStatus == pdPASS) { /* Send an MQTT Disconnect packet over the already connected TLS over TCP connection. * There is no corresponding response for the disconnect packet. After sending * disconnect, client must close the network connection. */ LogInfo(("Disconnecting the MQTT connection with %s.", democonfigMQTT_BROKER_ENDPOINT)); MQTT_Disconnect(&xMQTTContext); } /* We will always close the network connection, even if an error may have occurred during * demo execution, to clean up the system resources that it may have consumed. */ if (xIsConnectionEstablished == pdTRUE) { /* Close the network connection. */ xNetworkStatus = SecureSocketsTransport_Disconnect(&xNetworkContext); if (xNetworkStatus != TRANSPORT_SOCKET_STATUS_SUCCESS) { xDemoStatus = pdFAIL; LogError(("SecureSocketsTransport_Disconnect() failed to close the network connection. " "StatusCode=%d.", (int)xNetworkStatus)); } } if (xDemoStatus == pdPASS) { isDemoSuccessful = true; LogInfo(("Demo completed successfully.")); break; } else { /* Demo loop will be repeated for up to democonfigMQTT_MAX_DEMO_COUNT * times if current loop resulted in a failure. */ isDemoSuccessful = false; if (ulDemoRunCount >= democonfigMQTT_MAX_DEMO_COUNT) { LogInfo(("Demo failed, exceeded restart limit.")); break; } else { LogInfo(("Demo failed, restarting after a delay.")); ulDemoRunCount++; vTaskDelay(mqttexampleDELAY_BETWEEN_DEMO_ITERATIONS_TICKS); } } } if (isDemoSuccessful) { xDemoStatus = pdPASS; LogInfo(("Demo run successful.")); for (;;); } else { xDemoStatus = pdFAIL; } return (xDemoStatus == pdPASS) ? EXIT_SUCCESS : EXIT_FAILURE; } /*-----------------------------------------------------------*/ static BaseType_t prvBackoffForRetry(BackoffAlgorithmContext_t *pxRetryParams) { BaseType_t xReturnStatus = pdFAIL; uint16_t usNextRetryBackOff = 0U; BackoffAlgorithmStatus_t xBackoffAlgStatus = BackoffAlgorithmSuccess; /** * To calculate the backoff period for the next retry attempt, we will * generate a random number to provide to the backoffAlgorithm library. * * Note: The PKCS11 module is used to generate the random number as it allows access * to a True Random Number Generator (TRNG) if the vendor platform supports it. * It is recommended to use a random number generator seeded with a device-specific * entropy source so that probability of collisions from devices in connection retries * is mitigated. */ uint32_t ulRandomNum = 0; if (xPkcs11GenerateRandomNumber((uint8_t *)&ulRandomNum, sizeof(ulRandomNum)) == pdPASS) { /* Get back-off value (in milliseconds) for the next retry attempt. */ xBackoffAlgStatus = BackoffAlgorithm_GetNextBackoff(pxRetryParams, ulRandomNum, &usNextRetryBackOff); if (xBackoffAlgStatus == BackoffAlgorithmRetriesExhausted) { LogError(("All retry attempts have exhausted. Operation will not be retried")); } else if (xBackoffAlgStatus == BackoffAlgorithmSuccess) { /* Perform the backoff delay. */ vTaskDelay(pdMS_TO_TICKS(usNextRetryBackOff)); xReturnStatus = pdPASS; LogInfo(("Retry attempt %lu out of maximum retry attempts %lu.", (pxRetryParams->attemptsDone + 1), pxRetryParams->maxRetryAttempts)); } } else { LogError(("Unable to retry operation with broker: Random number generation failed")); } return xReturnStatus; } /*-----------------------------------------------------------*/ static BaseType_t prvConnectToServerWithBackoffRetries(NetworkContext_t *pxNetworkContext) { ServerInfo_t xServerInfo = { 0 }; SocketsConfig_t xSocketsConfig = { 0 }; TransportSocketStatus_t xNetworkStatus = TRANSPORT_SOCKET_STATUS_SUCCESS; BackoffAlgorithmContext_t xReconnectParams; BaseType_t xBackoffStatus = pdFALSE; /* Set the credentials for establishing a TLS connection. */ /* Initializer server information. */ xServerInfo.pHostName = democonfigMQTT_BROKER_ENDPOINT; xServerInfo.hostNameLength = strlen(democonfigMQTT_BROKER_ENDPOINT); xServerInfo.port = democonfigMQTT_BROKER_PORT; /* Configure credentials for TLS mutual authenticated session. */ xSocketsConfig.enableTls = true; xSocketsConfig.pAlpnProtos = NULL; xSocketsConfig.maxFragmentLength = 0; xSocketsConfig.disableSni = false; xSocketsConfig.pRootCa = democonfigROOT_CA_PEM; xSocketsConfig.rootCaSize = sizeof(democonfigROOT_CA_PEM); xSocketsConfig.sendTimeoutMs = mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS; xSocketsConfig.recvTimeoutMs = mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS; /* Initialize reconnect attempts and interval. */ BackoffAlgorithm_InitializeParams(&xReconnectParams, RETRY_BACKOFF_BASE_MS, RETRY_MAX_BACKOFF_DELAY_MS, RETRY_MAX_ATTEMPTS); /* Attempt to connect to MQTT broker. If connection fails, retry after * a timeout. Timeout value will exponentially increase till maximum * attempts are reached. */ do { /* Establish a TLS session with the MQTT broker. This example connects to * the MQTT broker as specified in democonfigMQTT_BROKER_ENDPOINT and * democonfigMQTT_BROKER_PORT at the top of this file. */ LogInfo(("Creating a TLS connection to %s:%u.", democonfigMQTT_BROKER_ENDPOINT, democonfigMQTT_BROKER_PORT)); /* Attempt to create a mutually authenticated TLS connection. */ xNetworkStatus = SecureSocketsTransport_Connect(pxNetworkContext, &xServerInfo, &xSocketsConfig); if (xNetworkStatus != TRANSPORT_SOCKET_STATUS_SUCCESS) { LogWarn( ("Connection to the broker failed. Attempting connection retry after backoff delay.")); /* As the connection attempt failed, we will retry the connection after an * exponential backoff with jitter delay. */ /* Calculate the backoff period for the next retry attempt and perform the wait operation. */ xBackoffStatus = prvBackoffForRetry(&xReconnectParams); } else { LogInfo(("Connection attempt success.")); } } while ((xNetworkStatus != TRANSPORT_SOCKET_STATUS_SUCCESS) && (xBackoffStatus == pdPASS)); LogInfo(("Connection attempt finished.")); return (xNetworkStatus == TRANSPORT_SOCKET_STATUS_SUCCESS) ? pdPASS : pdFAIL; } /*-----------------------------------------------------------*/ static BaseType_t prvCreateMQTTConnectionWithBroker(MQTTContext_t *pxMQTTContext, NetworkContext_t *pxNetworkContext) { MQTTStatus_t xResult; MQTTConnectInfo_t xConnectInfo; bool xSessionPresent; TransportInterface_t xTransport; BaseType_t xStatus = pdFAIL; /* Fill in Transport Interface send and receive function pointers. */ xTransport.pNetworkContext = pxNetworkContext; xTransport.send = SecureSocketsTransport_Send; xTransport.recv = SecureSocketsTransport_Recv; /* Initialize MQTT library. */ xResult = MQTT_Init(pxMQTTContext, &xTransport, prvGetTimeMs, prvEventCallback, &xBuffer); configASSERT(xResult == MQTTSuccess); /* Some fields are not used in this demo so start with everything at 0. */ (void)memset((void *)&xConnectInfo, 0x00, sizeof(xConnectInfo)); /* Start with a clean session i.e. direct the MQTT broker to discard any * previous session data. Also, establishing a connection with clean session * will ensure that the broker does not store any data when this client * gets disconnected. */ xConnectInfo.cleanSession = true; /* The client identifier is used to uniquely identify this MQTT client to * the MQTT broker. In a production device the identifier can be something * unique, such as a device serial number. */ xConnectInfo.pClientIdentifier = democonfigCLIENT_IDENTIFIER; xConnectInfo.clientIdentifierLength = (uint16_t)strlen(democonfigCLIENT_IDENTIFIER); /* Use the metrics string as username to report the OS and MQTT client version * metrics to AWS IoT. */ xConnectInfo.pUserName = AWS_IOT_METRICS_STRING; xConnectInfo.userNameLength = AWS_IOT_METRICS_STRING_LENGTH; /* Set MQTT keep-alive period. If the application does not send packets at an interval less than * the keep-alive period, the MQTT library will send PINGREQ packets. */ xConnectInfo.keepAliveSeconds = mqttexampleKEEP_ALIVE_TIMEOUT_SECONDS; /* Send MQTT CONNECT packet to broker. LWT is not used in this demo, so it * is passed as NULL. */ xResult = MQTT_Connect(pxMQTTContext, &xConnectInfo, NULL, mqttexampleCONNACK_RECV_TIMEOUT_MS, &xSessionPresent); if (xResult != MQTTSuccess) { LogError(("Failed to establish MQTT connection: Server=%s, MQTTStatus=%s", democonfigMQTT_BROKER_ENDPOINT, MQTT_Status_strerror(xResult))); } else { /* Successfully established and MQTT connection with the broker. */ LogInfo(("An MQTT connection is established with %s.", democonfigMQTT_BROKER_ENDPOINT)); xStatus = pdPASS; } return xStatus; } /*-----------------------------------------------------------*/ static void prvUpdateSubAckStatus(MQTTPacketInfo_t *pxPacketInfo) { MQTTStatus_t xResult = MQTTSuccess; uint8_t *pucPayload = NULL; size_t ulSize = 0; uint32_t ulTopicCount = 0U; xResult = MQTT_GetSubAckStatusCodes(pxPacketInfo, &pucPayload, &ulSize); /* MQTT_GetSubAckStatusCodes always returns success if called with packet info * from the event callback and non-NULL parameters. */ configASSERT(xResult == MQTTSuccess); for (ulTopicCount = 0; ulTopicCount < ulSize; ulTopicCount++) { xTopicFilterContext[ulTopicCount].xSubAckStatus = pucPayload[ulTopicCount]; } } /*-----------------------------------------------------------*/ static BaseType_t prvMQTTPublishToTopic(MQTTContext_t *pxMQTTContext, const void *payload, size_t size) { MQTTStatus_t xResult; MQTTPublishInfo_t xMQTTPublishInfo; BaseType_t xStatus = pdPASS; /* Some fields are not used by this demo so start with everything at 0. */ (void)memset((void *)&xMQTTPublishInfo, 0x00, sizeof(xMQTTPublishInfo)); /* This demo uses QoS1. */ xMQTTPublishInfo.qos = MQTTQoS1; xMQTTPublishInfo.retain = false; xMQTTPublishInfo.pTopicName = mqttexampleTOPIC; xMQTTPublishInfo.topicNameLength = (uint16_t)strlen(mqttexampleTOPIC); xMQTTPublishInfo.pPayload = payload; xMQTTPublishInfo.payloadLength = size; /* Get a unique packet id. */ usPublishPacketIdentifier = MQTT_GetPacketId(pxMQTTContext); /* Send PUBLISH packet. Packet ID is not used for a QoS1 publish. */ xResult = MQTT_Publish(pxMQTTContext, &xMQTTPublishInfo, usPublishPacketIdentifier); if (xResult != MQTTSuccess) { xStatus = pdFAIL; LogError(("Failed to send PUBLISH message to broker: Topic=%s, Error=%s", mqttexampleTOPIC, MQTT_Status_strerror(xResult))); } return xStatus; } /*-----------------------------------------------------------*/ static void prvMQTTProcessResponse(MQTTPacketInfo_t *pxIncomingPacket, uint16_t usPacketId) { uint32_t ulTopicCount = 0U; switch (pxIncomingPacket->type) { case MQTT_PACKET_TYPE_PUBACK: LogInfo(("PUBACK received for packet Id %u.", usPacketId)); /* Make sure ACK packet identifier matches with Request packet identifier. */ configASSERT(usPublishPacketIdentifier == usPacketId); break; case MQTT_PACKET_TYPE_SUBACK: /* Update the packet type received to SUBACK. */ usPacketTypeReceived = MQTT_PACKET_TYPE_SUBACK; /* A SUBACK from the broker, containing the server response to our subscription request, has * been received. It contains the status code indicating server approval/rejection for the * subscription to the single topic requested. The SUBACK will be parsed to obtain the status * code, and this status code will be stored in global variable #xTopicFilterContext. */ prvUpdateSubAckStatus(pxIncomingPacket); for (ulTopicCount = 0; ulTopicCount < mqttexampleTOPIC_COUNT; ulTopicCount++) { if (xTopicFilterContext[ulTopicCount].xSubAckStatus != MQTTSubAckFailure) { LogInfo(("Subscribed to the topic %s with maximum QoS %u.", xTopicFilterContext[ulTopicCount].pcTopicFilter, xTopicFilterContext[ulTopicCount].xSubAckStatus)); } } /* Make sure ACK packet identifier matches with Request packet identifier. */ configASSERT(usSubscribePacketIdentifier == usPacketId); break; case MQTT_PACKET_TYPE_UNSUBACK: LogInfo(("Unsubscribed from the topic %s.", mqttforwardTOPIC)); /* Update the packet type received to UNSUBACK. */ usPacketTypeReceived = MQTT_PACKET_TYPE_UNSUBACK; /* Make sure ACK packet identifier matches with Request packet identifier. */ configASSERT(usUnsubscribePacketIdentifier == usPacketId); break; case MQTT_PACKET_TYPE_PINGRESP: LogInfo(("Ping Response successfully received.")); break; case MQTT_PACKET_TYPE_PUBLISH: LogInfo(("Publish successfully received from " mqttforwardTOPIC)); break; /* Any other packet type is invalid. */ default: LogWarn(("prvMQTTProcessResponse() called with unknown packet type:(%02X).", pxIncomingPacket->type)); } } /*-----------------------------------------------------------*/ static void prvMQTTProcessIncomingPublish(MQTTPublishInfo_t *pxPublishInfo) { configASSERT(pxPublishInfo != NULL); /* Set the global for indicating that an incoming publish is received. */ usPacketTypeReceived = MQTT_PACKET_TYPE_PUBLISH; /* Process incoming Publish. */ LogInfo(("Incoming QoS : %d\n", pxPublishInfo->qos)); /* Verify the received publish is for the we have subscribed to. */ if ((pxPublishInfo->topicNameLength == strlen(mqttforwardTOPIC)) && (0 == strncmp(mqttforwardTOPIC, pxPublishInfo->pTopicName, pxPublishInfo->topicNameLength))) { LogInfo(("Incoming Publish Topic Name: %.*s matches subscribed topic." "Incoming Publish Message : %.*s", pxPublishInfo->topicNameLength, pxPublishInfo->pTopicName, pxPublishInfo->payloadLength, pxPublishInfo->pPayload)); } else { LogInfo(("Incoming Publish Topic Name: %.*s does not match subscribed topic.", pxPublishInfo->topicNameLength, pxPublishInfo->pTopicName)); } } /*-----------------------------------------------------------*/ static void prvEventCallback(MQTTContext_t *pxMQTTContext, MQTTPacketInfo_t *pxPacketInfo, MQTTDeserializedInfo_t *pxDeserializedInfo) { /* The MQTT context is not used for this demo. */ (void)pxMQTTContext; if ((pxPacketInfo->type & 0xF0U) == MQTT_PACKET_TYPE_PUBLISH) { prvMQTTProcessIncomingPublish(pxDeserializedInfo->pPublishInfo); } else { prvMQTTProcessResponse(pxPacketInfo, pxDeserializedInfo->packetIdentifier); } } /*-----------------------------------------------------------*/ static uint32_t prvGetTimeMs(void) { TickType_t xTickCount = 0; uint32_t ulTimeMs = 0UL; /* Get the current tick count. */ xTickCount = xTaskGetTickCount(); /* Convert the ticks to milliseconds. */ ulTimeMs = (uint32_t)xTickCount * MILLISECONDS_PER_TICK; /* Reduce ulGlobalEntryTimeMs from obtained time so as to always return the * elapsed time in the application. */ ulTimeMs = (uint32_t)(ulTimeMs - ulGlobalEntryTimeMs); return ulTimeMs; } /*-----------------------------------------------------------*/ ================================================ FILE: examples/dialog/README.md ================================================ # Memfault for Dialog Semiconductor Devices The subdirectories in this folder contain example integrations of the Memfault coredump feature for Dialog Semiconductor Wireless MCUs. Within each subdirectory, more detailed descriptions can be found about how to integrate Memfault into the target Wireless MCU. ================================================ FILE: examples/dialog/da145xx/README.md ================================================ # Memfault for the DA145xx Family of Wireless Microcontrollers This example application shows an integration with Dialog Semiconductor's SDK6 for the DA145xx family of Wireless Microcontrollers. ## Getting Started We assume you have a working setup for firmware development on the Dialog DA145xx family of devices: - have Dialogs SmartSnippets Studio installed OR the Keil MDK - have installed the latest version of the DA145xx SDK (this example was tested with version 6.0.14.1114) ### Adding Memfault to the Dialog SDK Create a directory called `memfault` in the `third_party` folder of the Dialog SDK and then clone the `memfault-firmware-sdk` repository into this folder. The resulting directory structure should look like the following: ``` ├── README.md ├── binaries ├── config ├── doc ├── projects ├── sdk ├── third_party │ ├── README │ ├── crc32 │ ├── hash │ ├── irng │ ├── memfault │ │ └── memfault-firmware-sdk │ │ ├── cmake │ │ ├── components │ │ ├── examples │ │ ├── makefiles │ │ ├── ports │ │ ├── scripts │ │ ├── tasks │ │ └── tests │ ├── micro_ecc │ └── rand └── utilities ``` ## Using the demo app The demo app is compatible with the following DA145xx Development Kit boards: [DA14585 Development Kit PRO](https://www.dialog-semiconductor.com/products/bluetooth-low-energy/da14585-development-kit-pro) [DA14531 Development Kit PRO](https://www.dialog-semiconductor.com/products/bluetooth-low-energy/da14530-and-da14531-development-kit-pro) ### Hardware Configuration **DA14585 Development Kit PRO configuration** The jumpers on the DA14585 Development Kit PRO board must be configured as shown below when running this example (and the jumper wires shown must be added): ![DA14585 Development Kit PRO configuration](docs/da14585_pro_dk.png) **DA14531 Development Kit PRO configuration** The jumpers on the DA14531 Development Kit PRO board must be configured as shown below when running this example (and the jumper wires shown must be added): ![DA14531 Development Kit PRO configuration](docs/da14531_pro_dk.png) ### Building the demo app The demo application can be built using either the Eclipse/GCC (SmartSnippets Studio) toolchain or the Keil MDK: #### SmartSnippets Studio First, a few small changes must be made to the Dialog SDK to enable integration of Memfault support. To make these changes navigate to the root of the Dialog SDK and apply the following patches: `git apply --ignore-whitespace third_party/memfault/memfault-firmware-sdk/ports/dialog/da145xx/gnu-build-id.patch` `git apply --ignore-whitespace third_party/memfault/memfault-firmware-sdk/ports/dialog/da145xx/gcc-hardfault.patch` Next, open SmartSnipperts Studio and import the demo application project, which can be found in the following location: `/third_party/memfault/memfault-firmware-sdk/examples/dialog/da145xx/apps/memfault_demo_app/Eclipse` Build the application and then program into the flash memory contained on the Development Kit board. **NOTE:** A SmartSnippets Studio project is not available for the DA14531 based example as building the example with the GCC compiler exhausts the available memory. Use the Keil toolchain to build the example for the DA14531 (see instructions below). #### Keil MDK First, a number of changes must be made to the Dialog SDK to enable integration of Memfault support. To make these changes navigate to the root of the Dialog SDK and apply the following patch: `git apply --ignore-whitespace third_party/memfault/memfault-firmware-sdk/ports/dialog/da145xx/armcc-fault-handler.patch` Next, use the Keil IDE to open the demo application project, which can be found in the following location: `\third_party\memfault\memfault-firmware-sdk\examples\dialog\da145xx\apps\memfault_demo_app` Build the application and then program into the flash memory contained on the Development Kit board. ### Running the demo app The demo app is a simple console based app that has commands to cause a crash in several ways. Once a coredump is captured, it can be sent to Memfault's web services to get analyzed. Once the DA145xx Development Kit has been programmed, connect to it using a Terminal emulator (such as TeraTerm etc.) at 115200-8-N-1. **NOTE**: The DA145xx will enumerate two COM ports when connected to a PC, select the lowest numbered of the two ports when connecting via a terminal emulator. The following commands are supported by the demo app and can be initiated by sending the command listed: - crash: Generate different types of crashes with crash [0..3] - trace: Generate a trace event (not available on the DA14531) - get_core: Get coredump (not available on the DA14531) - clear_core: Clear coredump (not available on the DA14531) - test_storage: Test coredump storage (not available on the DA14531) - get_device_info: Display device information (not available on the DA14531) - reboot: Reboot & record event - export: Export chunk - help: show list of available commands ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/.gitignore ================================================ DA14531/ DA14585/ DA14586/ .settings/ *.uvguix.* jlink.log JLinkLog.txt JLinkSettings.ini EventRecorderStub.scvd ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/Eclipse/.cproject ================================================ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/Eclipse/.project ================================================ memfault_demo_app org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder full,incremental, org.eclipse.cdt.core.cnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature memfault_components 2 virtual:/virtual memfault_dialog 2 virtual:/virtual sdk_app 2 virtual:/virtual sdk_arch 2 virtual:/virtual sdk_ble 2 virtual:/virtual sdk_boot 2 virtual:/virtual sdk_driver 2 virtual:/virtual sdk_profiles 2 virtual:/virtual user_app 2 virtual:/virtual user_config 2 virtual:/virtual user_custom_profile 2 virtual:/virtual user_platform 2 virtual:/virtual memfault_components/arch_arm_cortex_m.c 1 PARENT-6-PROJECT_LOC/components/core/src/arch_arm_cortex_m.c memfault_components/memfault_base64.c 1 PARENT-6-PROJECT_LOC/components/util/src/memfault_base64.c memfault_components/memfault_batched_events.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_batched_events.c memfault_components/memfault_build_id.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_build_id.c memfault_components/memfault_chunk_transport.c 1 PARENT-6-PROJECT_LOC/components/util/src/memfault_chunk_transport.c memfault_components/memfault_circular_buffer.c 1 PARENT-6-PROJECT_LOC/components/util/src/memfault_circular_buffer.c memfault_components/memfault_core_utils.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_core_utils.c memfault_components/memfault_coredump.c 1 PARENT-6-PROJECT_LOC/components/panics/src/memfault_coredump.c memfault_components/memfault_coredump_regions_armv7.c 1 PARENT-6-PROJECT_LOC/components/panics/src/memfault_coredump_regions_armv7.c memfault_components/memfault_coredump_sdk_regions.c 1 PARENT-6-PROJECT_LOC/components/panics/src/memfault_coredump_sdk_regions.c memfault_components/memfault_coredump_storage_debug.c 1 PARENT-6-PROJECT_LOC/components/panics/src/memfault_coredump_storage_debug.c memfault_components/memfault_coredump_utils.c 1 PARENT-6-PROJECT_LOC/components/panics/src/memfault_coredump_utils.c memfault_components/memfault_crc16_ccitt.c 1 PARENT-6-PROJECT_LOC/components/util/src/memfault_crc16_ccitt.c memfault_components/memfault_data_export.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_data_export.c memfault_components/memfault_data_packetizer.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_data_packetizer.c memfault_components/memfault_data_source_rle.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_data_source_rle.c memfault_components/memfault_demo_cli_aux.c 1 PARENT-6-PROJECT_LOC/components/demo/src/panics/memfault_demo_cli_aux.c memfault_components/memfault_demo_cli_trace_event.c 1 PARENT-6-PROJECT_LOC/components/demo/src/memfault_demo_cli_trace_event.c memfault_components/memfault_demo_core.c 1 PARENT-6-PROJECT_LOC/components/demo/src/memfault_demo_core.c memfault_components/memfault_demo_panics.c 1 PARENT-6-PROJECT_LOC/components/demo/src/panics/memfault_demo_panics.c memfault_components/memfault_demo_shell.c 1 PARENT-6-PROJECT_LOC/components/demo/src/memfault_demo_shell.c memfault_components/memfault_demo_shell_commands.c 1 PARENT-6-PROJECT_LOC/components/demo/src/memfault_demo_shell_commands.c memfault_components/memfault_event_storage.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_event_storage.c memfault_components/memfault_fault_handling_arm.c 1 PARENT-6-PROJECT_LOC/components/panics/src/memfault_fault_handling_arm.c memfault_components/memfault_log.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_log.c memfault_components/memfault_metrics.c 1 PARENT-6-PROJECT_LOC/components/metrics/src/memfault_metrics.c memfault_components/memfault_metrics_serializer.c 1 PARENT-6-PROJECT_LOC/components/metrics/src/memfault_metrics_serializer.c memfault_components/memfault_metrics_reliability.c 1 PARENT-6-PROJECT_LOC/components/metrics/src/memfault_metrics_reliability.c memfault_components/memfault_minimal_cbor.c 1 PARENT-6-PROJECT_LOC/components/util/src/memfault_minimal_cbor.c memfault_components/memfault_ram_reboot_info_tracking.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_ram_reboot_info_tracking.c memfault_components/memfault_reboot_tracking_serializer.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_reboot_tracking_serializer.c memfault_components/memfault_rle.c 1 PARENT-6-PROJECT_LOC/components/util/src/memfault_rle.c memfault_components/memfault_sdk_assert.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_sdk_assert.c memfault_components/memfault_serializer_helper.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_serializer_helper.c memfault_components/memfault_trace_event.c 1 PARENT-6-PROJECT_LOC/components/core/src/memfault_trace_event.c memfault_components/memfault_varint.c 1 PARENT-6-PROJECT_LOC/components/util/src/memfault_varint.c memfault_dialog/memfault_platform_core.c 1 PARENT-6-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_core.c memfault_dialog/memfault_platform_coredump_regions.c 1 PARENT-6-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_coredump_regions.c memfault_dialog/memfault_platform_coredump_storage.c 1 PARENT-6-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_coredump_storage.c memfault_dialog/memfault_platform_debug_log.c 1 PARENT-6-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_debug_log.c memfault_dialog/memfault_platform_metrics.c 1 PARENT-6-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_metrics.c memfault_dialog/reset_reboot_tracking.c 1 PARENT-6-PROJECT_LOC/ports/dialog/da145xx/reset_reboot_tracking.c sdk_app/app.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_common/app.c sdk_app/app_customs.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_custs/app_customs.c sdk_app/app_customs_common.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_custs/app_customs_common.c sdk_app/app_customs_task.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_custs/app_customs_task.c sdk_app/app_default_handlers.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_default_hnd/app_default_handlers.c sdk_app/app_diss.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_diss/app_diss.c sdk_app/app_diss_task.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_diss/app_diss_task.c sdk_app/app_easy_msg_utils.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_easy/app_easy_msg_utils.c sdk_app/app_easy_timer.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_easy/app_easy_timer.c sdk_app/app_entry_point.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_entry/app_entry_point.c sdk_app/app_msg_utils.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_common/app_msg_utils.c sdk_app/app_task.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_common/app_task.c sdk_app/app_utils.c 1 PARENT-9-PROJECT_LOC/sdk/app_modules/src/app_common/app_utils.c sdk_arch/arch_console.c 1 PARENT-9-PROJECT_LOC/sdk/platform/core_modules/arch_console/arch_console.c sdk_arch/arch_hibernation.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/main/arch_hibernation.c sdk_arch/arch_main.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/main/arch_main.c sdk_arch/arch_rom.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/main/arch_rom.c sdk_arch/arch_sleep.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/main/arch_sleep.c sdk_arch/arch_system.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/main/arch_system.c sdk_arch/chacha20.c 1 PARENT-9-PROJECT_LOC/third_party/rand/chacha20.c sdk_arch/hash.c 1 PARENT-9-PROJECT_LOC/third_party/hash/hash.c sdk_arch/jump_table.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/main/jump_table.c sdk_arch/nvds.c 1 PARENT-9-PROJECT_LOC/sdk/platform/core_modules/nvds/src/nvds.c sdk_arch/otp_cs.c 1 PARENT-9-PROJECT_LOC/sdk/platform/utilities/otp_cs/otp_cs.c sdk_arch/otp_hdr.c 1 PARENT-9-PROJECT_LOC/sdk/platform/utilities/otp_hdr/otp_hdr.c sdk_ble/ble_arp.c 1 PARENT-9-PROJECT_LOC/sdk/platform/core_modules/rf/src/ble_arp.c sdk_ble/rf_531.c 1 PARENT-9-PROJECT_LOC/sdk/platform/core_modules/rf/src/rf_531.c sdk_ble/rf_585.c 1 PARENT-9-PROJECT_LOC/sdk/platform/core_modules/rf/src/rf_585.c sdk_ble/rwble.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/rwble/rwble.c sdk_ble/rwip.c 1 PARENT-9-PROJECT_LOC/sdk/platform/core_modules/rwip/src/rwip.c sdk_boot/hardfault_handler.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/main/hardfault_handler.c sdk_boot/ivtable_DA14531.S 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/boot/GCC/ivtable_DA14531.S sdk_boot/ivtable_DA14585_586.S 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/boot/GCC/ivtable_DA14585_586.S sdk_boot/nmi_handler.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/main/nmi_handler.c sdk_boot/startup_DA14531.S 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/boot/GCC/startup_DA14531.S sdk_boot/startup_DA14585_586.S 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/boot/GCC/startup_DA14585_586.S sdk_boot/system_DA14531.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/boot/system_DA14531.c sdk_boot/system_DA14585_586.c 1 PARENT-9-PROJECT_LOC/sdk/platform/arch/boot/system_DA14585_586.c sdk_driver/adc_531.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/adc/adc_531.c sdk_driver/adc_58x.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/adc/adc_58x.c sdk_driver/gpio.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/gpio/gpio.c sdk_driver/hw_otpc_531.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/hw_otpc/hw_otpc_531.c sdk_driver/hw_otpc_58x.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/hw_otpc/hw_otpc_58x.c sdk_driver/spi_531.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/spi/spi_531.c sdk_driver/spi_58x.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/spi/spi_58x.c sdk_driver/spi_flash.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/spi_flash/spi_flash.c sdk_driver/syscntl.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/syscntl/syscntl.c sdk_driver/trng.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/trng/trng.c sdk_driver/uart.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/uart/uart.c sdk_driver/wkupct_quadec.c 1 PARENT-9-PROJECT_LOC/sdk/platform/driver/wkupct_quadec/wkupct_quadec.c sdk_profiles/attm_db_128.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/host/att/attm/attm_db_128.c sdk_profiles/custom_common.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/profiles/custom/custom_common.c sdk_profiles/custs1.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/profiles/custom/custs/src/custs1.c sdk_profiles/custs1_task.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/profiles/custom/custs/src/custs1_task.c sdk_profiles/diss.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/profiles/dis/diss/src/diss.c sdk_profiles/diss_task.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/profiles/dis/diss/src/diss_task.c sdk_profiles/prf.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/profiles/prf.c sdk_profiles/prf_utils.c 1 PARENT-9-PROJECT_LOC/sdk/ble_stack/profiles/prf_utils.c user_app/memfault_platform_device_info.c 1 PARENT-1-PROJECT_LOC/src/memfault_platform_device_info.c user_app/user_app.c 1 PARENT-1-PROJECT_LOC/src/user_app.c user_config/da1458x_config_advanced.h 1 PARENT-1-PROJECT_LOC/src/config/da1458x_config_advanced.h user_config/da1458x_config_basic.h 1 PARENT-1-PROJECT_LOC/src/config/da1458x_config_basic.h user_config/memfault_metrics_heartbeat_config.def 1 PARENT-1-PROJECT_LOC/src/config/memfault_metrics_heartbeat_config.def user_config/memfault_platform_config.h 1 PARENT-1-PROJECT_LOC/src/config/memfault_platform_config.h user_config/memfault_trace_reason_user_config.def 1 PARENT-1-PROJECT_LOC/src/config/memfault_trace_reason_user_config.def user_config/user_callback_config.h 1 PARENT-1-PROJECT_LOC/src/config/user_callback_config.h user_config/user_config.h 1 PARENT-1-PROJECT_LOC/src/config/user_config.h user_config/user_modules_config.h 1 PARENT-1-PROJECT_LOC/src/config/user_modules_config.h user_config/user_periph_setup.h 1 PARENT-1-PROJECT_LOC/src/config/user_periph_setup.h user_config/user_profiles_config.h 1 PARENT-1-PROJECT_LOC/src/config/user_profiles_config.h user_custom_profile/user_custs1_def.c 1 PARENT-1-PROJECT_LOC/src/custom_profile/user_custs1_def.c user_custom_profile/user_custs_config.c 1 PARENT-1-PROJECT_LOC/src/custom_profile/user_custs_config.c user_platform/user_periph_setup.c 1 PARENT-1-PROJECT_LOC/src/platform/user_periph_setup.c ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/Eclipse/makefile.targets ================================================ .PHONY: main-build pre-build generate_ldscripts FORCE main-build : | pre-build FORCE: INC_PARAMS=$(foreach d, $(LDSCRIPT_INCLUDE_DIR), -I "$d") generate_ldscripts : $(LDSCRIPT_FILES) %.lds : $(LDSCRIPT_PATH)/%.lds.S FORCE "$(CC)" ${INC_PARAMS} $(PRE_BUILD_EXTRA_DEFS) $(LD_DEFS) -E -P -c "$<" -o "$@" ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/Keil_5/memfault_demo_app.uvoptx ================================================ 1.0
### uVision Project, (C) Keil Software
*.c *.s*; *.src; *.a* *.obj; *.o *.lib *.txt; *.h; *.inc; *.md *.plm *.cpp 0 0 0 DA14585 0x4 ARM-ADS 12000000 1 1 0 1 0 1 65535 0 0 0 79 66 8 .\DA14585\Listings\ 1 1 1 0 1 1 0 1 0 0 0 0 1 1 1 1 1 1 1 0 0 1 0 0 7 0 1 0 1 1 1 1 1 1 0 1 1 1 1 0 1 0 1 1 0 0 1 0 0 4 ..\..\..\..\..\..\..\..\..\sdk\common_project_files\misc\sysram_case23.ini Segger\JL2CM3.dll 0 ARMRTXEVENTFLAGS -L70 -Z18 -C0 -M0 -T1 0 DLGTARM (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0) 0 ARMDBGFLAGS 0 JL2CM3 -U480066255 -O78 -S2 -ZTIFSpeedSel5000 -A0 -C0 -JU1 -JI127.0.0.1 -JP0 -RST0 -N00("ARM CoreSight SW-DP") -D00(0BB11477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -TB1 -TFE0 -FO7 -FD20000000 -FC1000 -FN1 -FF0NEW_DEVICE.FLM -FS00 -FL040000 -FP0($$Device:ARMCM0$Device\ARM\Flash\NEW_DEVICE.FLM) 0 UL2CM3 UL2CM3(-S0 -C0 -P0 ) -FN1 -FC1000 -FD20000000 -FF0NEW_DEVICE -FL040000 -FS00 -FP0($$Device:ARMCM0$Device\ARM\Flash\NEW_DEVICE.FLM) 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DA14586 0x4 ARM-ADS 12000000 1 1 0 1 0 1 65535 0 0 0 79 66 8 .\DA14586\Listings\ 1 1 1 0 1 1 0 1 0 0 0 0 1 1 1 1 1 1 1 0 0 1 0 0 7 0 1 0 1 1 1 1 1 1 0 1 1 1 1 0 1 0 1 1 0 0 1 0 0 4 ..\..\..\..\..\..\..\..\..\sdk\common_project_files\misc\sysram_case23.ini Segger\JL2CM3.dll 0 ARMRTXEVENTFLAGS -L70 -Z18 -C0 -M0 -T1 0 DLGTARM (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0) 0 ARMDBGFLAGS 0 JL2CM3 -U480066255 -O78 -S2 -ZTIFSpeedSel5000 -A0 -C0 -JU1 -JI127.0.0.1 -JP0 -RST0 -N00("ARM CoreSight SW-DP") -D00(0BB11477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -TB1 -TFE0 -FO7 -FD20000000 -FC1000 -FN1 -FF0NEW_DEVICE.FLM -FS00 -FL040000 -FP0($$Device:ARMCM0$Device\ARM\Flash\NEW_DEVICE.FLM) 0 UL2CM3 UL2CM3(-S0 -C0 -P0 ) -FN1 -FC1000 -FD20000000 -FF0NEW_DEVICE -FL040000 -FS00 -FP0($$Device:ARMCM0$Device\ARM\Flash\NEW_DEVICE.FLM) 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DA14531 0x4 ARM-ADS 12000000 1 1 0 1 0 1 65535 0 0 0 79 66 8 .\DA14531\Listings\ 1 1 1 0 1 1 0 1 0 0 0 0 1 1 1 1 1 1 1 0 0 1 0 1 7 0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 0 1 1 0 0 1 0 0 4 ..\..\..\..\..\..\..\..\..\sdk\common_project_files\misc\jlink_DA14531.ini Segger\JL2CM3.dll 0 UL2CM3 UL2CM3(-S0 -C0 -P0 ) -FC1000 -FD20000000 0 JL2CM3 -U480071962 -O78 -S4 -ZTIFSpeedSel2000 -A0 -C0 -JU1 -JI127.0.0.1 -JP0 -RST0 -N00("ARM CoreSight SW-DP") -D00(0BC11477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8004 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -TB1 -TFE0 -FO7 -FD20000000 -FC1000 -FN0 0 ARMRTXEVENTFLAGS -L70 -Z18 -C0 -M0 -T1 0 DLGTARM (1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0) 0 ARMDBGFLAGS 0 0 273 1
133955940
0 0 0 0 0 1 ..\..\..\..\..\..\components\panics\src\memfault_fault_handling_arm.c \\memfault_demo_app_531\../../../../../../components/panics/src/memfault_fault_handling_arm.c\273
0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
memfault_components 0 0 0 0 1 1 1 0 0 0 ..\..\..\..\..\..\components\core\src\arch_arm_cortex_m.c arch_arm_cortex_m.c 0 0 1 2 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_batched_events.c memfault_batched_events.c 0 0 1 3 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_build_id.c memfault_build_id.c 0 0 1 4 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_core_utils.c memfault_core_utils.c 0 0 1 5 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_data_export.c memfault_data_export.c 0 0 1 6 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_data_packetizer.c memfault_data_packetizer.c 0 0 1 7 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_data_source_rle.c memfault_data_source_rle.c 0 0 1 8 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_event_storage.c memfault_event_storage.c 0 0 1 9 1 1 0 0 ..\..\..\..\..\..\components\core\src\memfault_log.c memfault_log.c 0 0 1 10 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_ram_reboot_info_tracking.c memfault_ram_reboot_info_tracking.c 0 0 1 11 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_reboot_tracking_serializer.c memfault_reboot_tracking_serializer.c 0 0 1 12 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_sdk_assert.c memfault_sdk_assert.c 0 0 1 13 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_serializer_helper.c memfault_serializer_helper.c 0 0 1 14 1 0 0 0 ..\..\..\..\..\..\components\core\src\memfault_trace_event.c memfault_trace_event.c 0 0 1 15 1 0 0 0 ..\..\..\..\..\..\components\metrics\src\memfault_metrics.c memfault_metrics.c 0 0 1 16 1 0 0 0 ..\..\..\..\..\..\components\metrics\src\memfault_metrics_serializer.c memfault_metrics_serializer.c 0 0 1 17 1 0 0 0 ..\..\..\..\..\..\components\panics\src\memfault_coredump.c memfault_coredump.c 0 0 1 18 1 0 0 0 ..\..\..\..\..\..\components\panics\src\memfault_coredump_regions_armv7.c memfault_coredump_regions_armv7.c 0 0 1 19 1 0 0 0 ..\..\..\..\..\..\components\panics\src\memfault_coredump_sdk_regions.c memfault_coredump_sdk_regions.c 0 0 1 20 1 0 0 0 ..\..\..\..\..\..\components\panics\src\memfault_coredump_storage_debug.c memfault_coredump_storage_debug.c 0 0 1 21 1 0 0 0 ..\..\..\..\..\..\components\panics\src\memfault_coredump_utils.c memfault_coredump_utils.c 0 0 1 22 1 0 0 0 ..\..\..\..\..\..\components\panics\src\memfault_fault_handling_arm.c memfault_fault_handling_arm.c 0 0 1 23 1 0 0 0 ..\..\..\..\..\..\components\util\src\memfault_base64.c memfault_base64.c 0 0 1 24 1 0 0 0 ..\..\..\..\..\..\components\util\src\memfault_chunk_transport.c memfault_chunk_transport.c 0 0 1 25 1 0 0 0 ..\..\..\..\..\..\components\util\src\memfault_circular_buffer.c memfault_circular_buffer.c 0 0 1 26 1 0 0 0 ..\..\..\..\..\..\components\util\src\memfault_crc16_ccitt.c memfault_crc16_ccitt.c 0 0 1 27 1 0 0 0 ..\..\..\..\..\..\components\util\src\memfault_minimal_cbor.c memfault_minimal_cbor.c 0 0 1 28 1 0 0 0 ..\..\..\..\..\..\components\util\src\memfault_rle.c memfault_rle.c 0 0 1 29 1 0 0 0 ..\..\..\..\..\..\components\util\src\memfault_varint.c memfault_varint.c 0 0 1 30 1 0 0 0 ..\..\..\..\..\..\components\demo\src\memfault_demo_cli_trace_event.c memfault_demo_cli_trace_event.c 0 0 1 31 1 0 0 0 ..\..\..\..\..\..\components\demo\src\memfault_demo_core.c memfault_demo_core.c 0 0 1 32 1 0 0 0 ..\..\..\..\..\..\components\demo\src\panics\memfault_demo_cli_aux.c memfault_demo_cli_aux.c 0 0 1 33 1 0 0 0 ..\..\..\..\..\..\components\demo\src\panics\memfault_demo_panics.c memfault_demo_panics.c 0 0 1 34 1 0 0 0 ..\..\..\..\..\..\components\demo\src\memfault_demo_shell.c memfault_demo_shell.c 0 0 memfault_dialog 0 0 0 0 2 35 1 0 0 0 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_core.c memfault_platform_core.c 0 0 2 36 1 0 0 0 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_coredump_regions.c memfault_platform_coredump_regions.c 0 0 2 37 1 0 0 0 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_coredump_storage.c memfault_platform_coredump_storage.c 0 0 2 38 1 0 0 0 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_debug_log.c memfault_platform_debug_log.c 0 0 2 39 1 0 0 0 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_metrics.c memfault_platform_metrics.c 0 0 2 40 1 0 0 0 ..\..\..\..\..\..\ports\dialog\da145xx\reset_reboot_tracking.c reset_reboot_tracking.c 0 0 sdk_app 0 0 0 0 3 41 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_default_hnd\app_default_handlers.c app_default_handlers.c 0 0 3 42 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app.c app.c 0 0 3 43 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_task.c app_task.c 0 0 3 44 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_diss\app_diss.c app_diss.c 0 0 3 45 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_diss\app_diss_task.c app_diss_task.c 0 0 3 46 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_entry\app_entry_point.c app_entry_point.c 0 0 3 47 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_msg_utils.c app_msg_utils.c 0 0 3 48 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_easy\app_easy_msg_utils.c app_easy_msg_utils.c 0 0 3 49 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_easy\app_easy_timer.c app_easy_timer.c 0 0 3 50 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs.c app_customs.c 0 0 3 51 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs_task.c app_customs_task.c 0 0 3 52 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs_common.c app_customs_common.c 0 0 3 53 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_utils.c app_utils.c 0 0 sdk_arch 0 0 0 0 4 54 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\arch_console\arch_console.c arch_console.c 0 0 4 55 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\nvds\src\nvds.c nvds.c 0 0 4 56 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_main.c arch_main.c 0 0 4 57 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\jump_table.c jump_table.c 0 0 4 58 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_sleep.c arch_sleep.c 0 0 4 59 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_system.c arch_system.c 0 0 4 60 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_hibernation.c arch_hibernation.c 0 0 4 61 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_rom.c arch_rom.c 0 0 4 62 1 0 0 0 .\..\..\..\..\..\..\..\..\..\third_party\rand\chacha20.c chacha20.c 0 0 4 63 1 0 0 0 .\..\..\..\..\..\..\..\..\..\third_party\hash\hash.c hash.c 0 0 4 64 4 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\output\Keil_5\da14585_586.lib da14585_586.lib 0 0 4 65 4 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\output\Keil_5\da14531.lib da14531.lib 0 0 4 66 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_cs\otp_cs.c otp_cs.c 0 0 4 67 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_hdr\otp_hdr.c otp_hdr.c 0 0 sdk_ble 0 0 0 0 5 68 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\rf_585.c rf_585.c 0 0 5 69 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble\rwble.c rwble.c 0 0 5 70 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rwip\src\rwip.c rwip.c 0 0 5 71 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\ble_arp.c ble_arp.c 0 0 5 72 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\rf_531.c rf_531.c 0 0 sdk_boot 0 0 0 0 6 73 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\system_DA14585_586.c system_DA14585_586.c 0 0 6 74 2 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM\startup_DA14585_586.s startup_DA14585_586.s 0 0 6 75 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\system_DA14531.c system_DA14531.c 0 0 6 76 2 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM\startup_DA14531.s startup_DA14531.s 0 0 6 77 1 0 0 0 ..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\hardfault_handler.c hardfault_handler.c 0 0 6 78 1 0 0 0 ..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\nmi_handler.c nmi_handler.c 0 0 sdk_driver 0 0 0 0 7 79 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\syscntl\syscntl.c syscntl.c 0 0 7 80 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\gpio\gpio.c gpio.c 0 0 7 81 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\wkupct_quadec\wkupct_quadec.c wkupct_quadec.c 0 0 7 82 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc\adc_58x.c adc_58x.c 0 0 7 83 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc\adc_531.c adc_531.c 0 0 7 84 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\trng\trng.c trng.c 0 0 7 85 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi\spi_58x.c spi_58x.c 0 0 7 86 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi\spi_531.c spi_531.c 0 0 7 87 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_flash\spi_flash.c spi_flash.c 0 0 7 88 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\uart\uart.c uart.c 0 0 7 89 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc\hw_otpc_58x.c hw_otpc_58x.c 0 0 7 90 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc\hw_otpc_531.c hw_otpc_531.c 0 0 sdk_profiles 0 0 0 0 8 91 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attm\attm_db_128.c attm_db_128.c 0 0 8 92 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\src\diss.c diss.c 0 0 8 93 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\src\diss_task.c diss_task.c 0 0 8 94 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custom_common.c custom_common.c 0 0 8 95 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\src\custs1.c custs1.c 0 0 8 96 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\src\custs1_task.c custs1_task.c 0 0 8 97 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prf.c prf.c 0 0 8 98 1 0 0 0 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prf_utils.c prf_utils.c 0 0 user_config 1 0 0 0 9 99 5 0 0 0 .\..\src\config\da1458x_config_advanced.h da1458x_config_advanced.h 0 0 9 100 5 0 0 0 .\..\src\config\da1458x_config_basic.h da1458x_config_basic.h 0 0 9 101 5 0 0 0 .\..\src\config\user_callback_config.h user_callback_config.h 0 0 9 102 5 0 0 0 .\..\src\config\user_config.h user_config.h 0 0 9 103 5 0 0 0 .\..\src\config\user_modules_config.h user_modules_config.h 0 0 9 104 5 0 0 0 .\..\src\config\user_periph_setup.h user_periph_setup.h 0 0 9 105 5 0 0 0 .\..\src\config\user_profiles_config.h user_profiles_config.h 0 0 9 106 5 0 0 0 ..\src\config\memfault_platform_config.h memfault_platform_config.h 0 0 user_custom_profile 1 0 0 0 10 107 1 0 0 0 ..\src\custom_profile\user_custs_config.c user_custs_config.c 0 0 10 108 1 0 0 0 ..\src\custom_profile\user_custs1_def.c user_custs1_def.c 0 0 user_platform 0 0 0 0 11 109 1 0 0 0 .\..\src\platform\user_periph_setup.c user_periph_setup.c 0 0 user_app 1 0 0 0 12 110 1 0 0 0 ..\src\user_app.c user_app.c 0 0 12 111 1 0 0 0 ..\src\memfault_platform_device_info.c memfault_platform_device_info.c 0 0
================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/Keil_5/memfault_demo_app.uvprojx ================================================ 2.1
### uVision Project, (C) Keil Software
DA14585 0x4 ARM-ADS 5060750::V5.06 update 6 (build 750)::ARMCC 0 ARMCM0 ARM ARM.CMSIS.5.7.0 http://www.keil.com/pack/ IROM(0x0,0x0) IRAM(0x0,0x0) CPUTYPE("Cortex-M0") CLOCK(12000000) ESEL ELITTLE UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0NEW_DEVICE -FS00 -FL040000 -FP0($$Device:ARMCM0$Device\ARM\Flash\NEW_DEVICE.FLM)) 0 $$Device:ARMCM0$Device\ARM\ARMCM0\Include\ARMCM0.h $$Device:ARMCM0$Device\ARM\SVD\ARMCM0.svd 0 0 0 0 0 0 1 .\DA14585\Objects\ memfault_demo_app_585 1 0 1 1 1 .\DA14585\Listings\ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 python ..\..\..\..\..\..\scripts\fw_build_id.py .\DA14585\Objects\@L.axf fromelf --bincombined --output=".\DA14585\Objects\@L.bin" "!L" 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 3 1 SARMCM3.DLL DARMCM1.DLL -pCM0 SARMCM3.DLL TARMCM1.DLL -pCM0 1 0 0 0 16 0 1 0 1 1 4096 1 BIN\UL2CM3.DLL "" () 0 0 1 1 1 1 1 1 1 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 "Cortex-M0" 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 1 0x0 0x0 0 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 1 4 0 0 0 0 0 0 0 0 2 0 0 1 0 0 0 0 1 1 0 0 0 --thumb -c --preinclude da1458x_config_basic.h --preinclude da1458x_config_advanced.h --preinclude user_config.h --bss_threshold=0 --feedback=".\unused_585.txt" .\..\..\..\..\..\..\..\..\..\sdk\app_modules\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\em;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\llc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\lld;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\llm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\ea\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\em\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\hci\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\hci\src;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\atts;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap\gapc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap\gapm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt\gattc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt\gattm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\l2c\l2cc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\l2c\l2cm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp\smpc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp\smpm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anc\ancc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp\anpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp\anps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bas\basc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bas\bass\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs\bcsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs\bcss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp\blpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp\blps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms\bmsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms\bmss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp\cppc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp\cpps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp\cscpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp\cscps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts\ctsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts\ctss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\disc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find\findl\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find\findt\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\gatt\gatt_client\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp\glpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp\glps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogpbh\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogpd\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogprh\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp\hrpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp\hrps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp\htpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp\htpt\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan\lanc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan\lans\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp\paspc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp\pasps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prox\proxm\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prox\proxr\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp\rscpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp\rscps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp\scppc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp\scpps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\suota\suotar\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip\tipc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip\tips\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds\udsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds\udss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss\wssc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss\wsss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble_hl;.\..\..\..\..\..\..\..\..\..\sdk\common_project_files;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\GCC;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler\ARM;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler\GCC;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\ll;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\arch_console;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\common\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\crypto;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\dbg\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\gtl\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\gtl\src;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\h4tl\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\ke\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\ke\src;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\nvds\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rwip\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\battery;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\ble;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\dma;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\gpio;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\i2c;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\i2c_eeprom;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\pdm;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\reg;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\rtc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_flash;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_hci;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\syscntl;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\systick;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\timer;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\trng;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\uart;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\wkupct_quadec;.\..\..\..\..\..\..\..\..\..\sdk\platform\include;.\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\include;.\..\..\..\..\..\..\..\..\..\third_party\hash;.\..\..\..\..\..\..\..\..\..\third_party\rand;.\..\..\..\..\..\..\ports\include;.\..\..\..\..\..\..\components\include;.\..\src;.\..\src\config;.\..\src\custom_profile;.\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_hdr;.\..\..\..\..\..\..\..\..\..\sdk\platform\include\CMSIS\5.6.0\Include 1 0 0 1 0 0 0 0 0 4 0 0 0 0 1 0 0x00000000 0x00000000 .\..\..\..\..\..\..\..\..\..\sdk\common_project_files\scatterfiles\DA14585_586.sct --feedback=".\unused_585.txt" .\..\..\..\..\..\..\..\..\..\sdk\common_project_files\misc\da14585_symbols.txt --symdefs=memfault_demo_app_585_symdef.txt --any_placement=best_fit --datacompressor off --predefine="-I ..\..\..\..\..\..\..\..\..\sdk\common_project_files" memfault_components arch_arm_cortex_m.c 1 ..\..\..\..\..\..\components\core\src\arch_arm_cortex_m.c memfault_batched_events.c 1 ..\..\..\..\..\..\components\core\src\memfault_batched_events.c memfault_build_id.c 1 ..\..\..\..\..\..\components\core\src\memfault_build_id.c memfault_core_utils.c 1 ..\..\..\..\..\..\components\core\src\memfault_core_utils.c memfault_data_export.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_export.c memfault_data_packetizer.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_packetizer.c memfault_data_source_rle.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_source_rle.c memfault_event_storage.c 1 ..\..\..\..\..\..\components\core\src\memfault_event_storage.c memfault_log.c 1 ..\..\..\..\..\..\components\core\src\memfault_log.c memfault_ram_reboot_info_tracking.c 1 ..\..\..\..\..\..\components\core\src\memfault_ram_reboot_info_tracking.c memfault_reboot_tracking_serializer.c 1 ..\..\..\..\..\..\components\core\src\memfault_reboot_tracking_serializer.c memfault_sdk_assert.c 1 ..\..\..\..\..\..\components\core\src\memfault_sdk_assert.c memfault_serializer_helper.c 1 ..\..\..\..\..\..\components\core\src\memfault_serializer_helper.c memfault_trace_event.c 1 ..\..\..\..\..\..\components\core\src\memfault_trace_event.c memfault_metrics.c 1 ..\..\..\..\..\..\components\metrics\src\memfault_metrics.c memfault_metrics_serializer.c 1 ..\..\..\..\..\..\components\metrics\src\memfault_metrics_serializer.c memfault_coredump.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump.c memfault_coredump_regions_armv7.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_regions_armv7.c memfault_coredump_sdk_regions.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_sdk_regions.c memfault_coredump_storage_debug.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_storage_debug.c memfault_coredump_utils.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_utils.c memfault_fault_handling_arm.c 1 ..\..\..\..\..\..\components\panics\src\memfault_fault_handling_arm.c memfault_base64.c 1 ..\..\..\..\..\..\components\util\src\memfault_base64.c memfault_chunk_transport.c 1 ..\..\..\..\..\..\components\util\src\memfault_chunk_transport.c memfault_circular_buffer.c 1 ..\..\..\..\..\..\components\util\src\memfault_circular_buffer.c memfault_crc16_ccitt.c 1 ..\..\..\..\..\..\components\util\src\memfault_crc16_ccitt.c memfault_minimal_cbor.c 1 ..\..\..\..\..\..\components\util\src\memfault_minimal_cbor.c memfault_rle.c 1 ..\..\..\..\..\..\components\util\src\memfault_rle.c memfault_varint.c 1 ..\..\..\..\..\..\components\util\src\memfault_varint.c memfault_demo_cli_trace_event.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_cli_trace_event.c memfault_demo_core.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_core.c memfault_demo_cli_aux.c 1 ..\..\..\..\..\..\components\demo\src\panics\memfault_demo_cli_aux.c memfault_demo_panics.c 1 ..\..\..\..\..\..\components\demo\src\panics\memfault_demo_panics.c memfault_demo_shell.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_shell.c memfault_dialog memfault_platform_core.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_core.c memfault_platform_coredump_regions.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_coredump_regions.c memfault_platform_coredump_storage.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_coredump_storage.c memfault_platform_debug_log.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_debug_log.c memfault_platform_metrics.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_metrics.c reset_reboot_tracking.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\reset_reboot_tracking.c sdk_app app_default_handlers.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_default_hnd\app_default_handlers.c app.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app.c app_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_task.c app_diss.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_diss\app_diss.c app_diss_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_diss\app_diss_task.c app_entry_point.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_entry\app_entry_point.c app_msg_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_msg_utils.c app_easy_msg_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_easy\app_easy_msg_utils.c app_easy_timer.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_easy\app_easy_timer.c app_customs.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs.c app_customs_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs_task.c app_customs_common.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs_common.c app_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_utils.c sdk_arch arch_console.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\arch_console\arch_console.c nvds.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\nvds\src\nvds.c arch_main.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_main.c jump_table.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\jump_table.c arch_sleep.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_sleep.c arch_system.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_system.c arch_hibernation.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_hibernation.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 arch_rom.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_rom.c chacha20.c 1 .\..\..\..\..\..\..\..\..\..\third_party\rand\chacha20.c hash.c 1 .\..\..\..\..\..\..\..\..\..\third_party\hash\hash.c da14585_586.lib 4 .\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\output\Keil_5\da14585_586.lib da14531.lib 4 .\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\output\Keil_5\da14531.lib 2 0 0 0 0 0 2 2 2 2 11 1 otp_cs.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_cs\otp_cs.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 otp_hdr.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_hdr\otp_hdr.c sdk_ble rf_585.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\rf_585.c rwble.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble\rwble.c rwip.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rwip\src\rwip.c ble_arp.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\ble_arp.c rf_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\rf_531.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 sdk_boot system_DA14585_586.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\system_DA14585_586.c startup_DA14585_586.s 2 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM\startup_DA14585_586.s system_DA14531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\system_DA14531.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 startup_DA14531.s 2 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM\startup_DA14531.s 2 0 0 0 0 0 0 2 2 2 11 1 2 2 2 2 2 2 2 2 2 0 hardfault_handler.c 1 ..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\hardfault_handler.c nmi_handler.c 1 ..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\nmi_handler.c sdk_driver syscntl.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\syscntl\syscntl.c gpio.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\gpio\gpio.c wkupct_quadec.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\wkupct_quadec\wkupct_quadec.c adc_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc\adc_58x.c adc_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc\adc_531.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 trng.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\trng\trng.c spi_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi\spi_58x.c spi_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi\spi_531.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 spi_flash.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_flash\spi_flash.c uart.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\uart\uart.c hw_otpc_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc\hw_otpc_58x.c hw_otpc_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc\hw_otpc_531.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 sdk_profiles attm_db_128.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attm\attm_db_128.c diss.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\src\diss.c diss_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\src\diss_task.c custom_common.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custom_common.c custs1.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\src\custs1.c custs1_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\src\custs1_task.c prf.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prf.c prf_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prf_utils.c user_config da1458x_config_advanced.h 5 .\..\src\config\da1458x_config_advanced.h da1458x_config_basic.h 5 .\..\src\config\da1458x_config_basic.h user_callback_config.h 5 .\..\src\config\user_callback_config.h user_config.h 5 .\..\src\config\user_config.h user_modules_config.h 5 .\..\src\config\user_modules_config.h user_periph_setup.h 5 .\..\src\config\user_periph_setup.h user_profiles_config.h 5 .\..\src\config\user_profiles_config.h memfault_platform_config.h 5 ..\src\config\memfault_platform_config.h user_custom_profile user_custs_config.c 1 ..\src\custom_profile\user_custs_config.c user_custs1_def.c 1 ..\src\custom_profile\user_custs1_def.c user_platform user_periph_setup.c 1 .\..\src\platform\user_periph_setup.c user_app user_app.c 1 ..\src\user_app.c memfault_platform_device_info.c 1 ..\src\memfault_platform_device_info.c DA14586 0x4 ARM-ADS 5060750::V5.06 update 6 (build 750)::ARMCC 0 ARMCM0 ARM ARM.CMSIS.5.7.0 http://www.keil.com/pack/ IROM(0x0,0x0) IRAM(0x0,0x0) CPUTYPE("Cortex-M0") CLOCK(12000000) ESEL ELITTLE UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0NEW_DEVICE -FS00 -FL040000 -FP0($$Device:ARMCM0$Device\ARM\Flash\NEW_DEVICE.FLM)) 0 $$Device:ARMCM0$Device\ARM\ARMCM0\Include\ARMCM0.h $$Device:ARMCM0$Device\ARM\SVD\ARMCM0.svd 0 0 0 0 0 0 1 .\DA14586\Objects\ memfault_demo_app_586 1 0 1 1 1 .\DA14586\Listings\ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 python ..\..\..\..\..\..\scripts\fw_build_id.py .\DA14586\Objects\@L.axf fromelf --bincombined --output=".\DA14586\Objects\@L.bin" "!L" 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 3 1 SARMCM3.DLL DARMCM1.DLL -pCM0 SARMCM3.DLL TARMCM1.DLL -pCM0 1 0 0 0 16 0 1 0 1 1 4096 1 BIN\UL2CM3.DLL "" () 0 0 1 1 1 1 1 1 1 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 "Cortex-M0" 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 1 0x0 0x0 0 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 1 4 0 0 0 0 0 0 0 0 2 0 0 1 0 0 0 0 1 1 0 0 0 --thumb -c --preinclude da1458x_config_basic.h --preinclude da1458x_config_advanced.h --preinclude user_config.h --bss_threshold=0 --feedback=".\unused_586.txt" __DA14586__ .\..\..\..\..\..\..\..\..\..\sdk\app_modules\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\em;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\llc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\lld;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\llm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\ea\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\em\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\hci\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\hci\src;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\atts;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap\gapc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap\gapm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt\gattc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt\gattm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\l2c\l2cc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\l2c\l2cm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp\smpc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp\smpm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anc\ancc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp\anpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp\anps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bas\basc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bas\bass\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs\bcsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs\bcss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp\blpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp\blps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms\bmsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms\bmss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp\cppc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp\cpps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp\cscpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp\cscps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts\ctsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts\ctss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\disc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find\findl\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find\findt\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\gatt\gatt_client\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp\glpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp\glps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogpbh\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogpd\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogprh\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp\hrpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp\hrps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp\htpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp\htpt\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan\lanc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan\lans\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp\paspc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp\pasps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prox\proxm\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prox\proxr\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp\rscpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp\rscps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp\scppc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp\scpps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\suota\suotar\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip\tipc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip\tips\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds\udsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds\udss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss\wssc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss\wsss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble_hl;.\..\..\..\..\..\..\..\..\..\sdk\common_project_files;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\GCC;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler\ARM;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler\GCC;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\ll;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\arch_console;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\common\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\crypto;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\dbg\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\gtl\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\gtl\src;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\h4tl\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\ke\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\ke\src;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\nvds\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rwip\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\battery;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\ble;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\dma;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\gpio;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\i2c;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\i2c_eeprom;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\pdm;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\reg;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\rtc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_flash;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_hci;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\syscntl;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\systick;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\timer;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\trng;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\uart;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\wkupct_quadec;.\..\..\..\..\..\..\..\..\..\sdk\platform\include;.\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\include;.\..\..\..\..\..\..\..\..\..\third_party\hash;.\..\..\..\..\..\..\..\..\..\third_party\rand;.\..\..\..\..\..\..\ports\include;.\..\..\..\..\..\..\components\include;.\..\src;.\..\src\config;.\..\src\custom_profile;.\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_hdr;.\..\..\..\..\..\..\..\..\..\sdk\platform\include\CMSIS\5.6.0\Include 1 0 0 1 0 0 0 0 0 4 0 0 0 0 1 0 0x00000000 0x00000000 .\..\..\..\..\..\..\..\..\..\sdk\common_project_files\scatterfiles\DA14585_586.sct --feedback=".\unused_586.txt" .\..\..\..\..\..\..\..\..\..\sdk\common_project_files\misc\da14585_symbols.txt --symdefs=memfault_demo_app_586_symdef.txt --any_placement=best_fit --datacompressor off --predefine="-I ..\..\..\..\..\..\..\..\..\sdk\common_project_files" memfault_components arch_arm_cortex_m.c 1 ..\..\..\..\..\..\components\core\src\arch_arm_cortex_m.c memfault_batched_events.c 1 ..\..\..\..\..\..\components\core\src\memfault_batched_events.c memfault_build_id.c 1 ..\..\..\..\..\..\components\core\src\memfault_build_id.c memfault_core_utils.c 1 ..\..\..\..\..\..\components\core\src\memfault_core_utils.c memfault_data_export.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_export.c memfault_data_packetizer.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_packetizer.c memfault_data_source_rle.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_source_rle.c memfault_event_storage.c 1 ..\..\..\..\..\..\components\core\src\memfault_event_storage.c memfault_log.c 1 ..\..\..\..\..\..\components\core\src\memfault_log.c memfault_ram_reboot_info_tracking.c 1 ..\..\..\..\..\..\components\core\src\memfault_ram_reboot_info_tracking.c memfault_reboot_tracking_serializer.c 1 ..\..\..\..\..\..\components\core\src\memfault_reboot_tracking_serializer.c memfault_sdk_assert.c 1 ..\..\..\..\..\..\components\core\src\memfault_sdk_assert.c memfault_serializer_helper.c 1 ..\..\..\..\..\..\components\core\src\memfault_serializer_helper.c memfault_trace_event.c 1 ..\..\..\..\..\..\components\core\src\memfault_trace_event.c memfault_metrics.c 1 ..\..\..\..\..\..\components\metrics\src\memfault_metrics.c memfault_metrics_serializer.c 1 ..\..\..\..\..\..\components\metrics\src\memfault_metrics_serializer.c memfault_coredump.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump.c memfault_coredump_regions_armv7.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_regions_armv7.c memfault_coredump_sdk_regions.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_sdk_regions.c memfault_coredump_storage_debug.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_storage_debug.c memfault_coredump_utils.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_utils.c memfault_fault_handling_arm.c 1 ..\..\..\..\..\..\components\panics\src\memfault_fault_handling_arm.c memfault_base64.c 1 ..\..\..\..\..\..\components\util\src\memfault_base64.c memfault_chunk_transport.c 1 ..\..\..\..\..\..\components\util\src\memfault_chunk_transport.c memfault_circular_buffer.c 1 ..\..\..\..\..\..\components\util\src\memfault_circular_buffer.c memfault_crc16_ccitt.c 1 ..\..\..\..\..\..\components\util\src\memfault_crc16_ccitt.c memfault_minimal_cbor.c 1 ..\..\..\..\..\..\components\util\src\memfault_minimal_cbor.c memfault_rle.c 1 ..\..\..\..\..\..\components\util\src\memfault_rle.c memfault_varint.c 1 ..\..\..\..\..\..\components\util\src\memfault_varint.c memfault_demo_cli_trace_event.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_cli_trace_event.c memfault_demo_core.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_core.c memfault_demo_cli_aux.c 1 ..\..\..\..\..\..\components\demo\src\panics\memfault_demo_cli_aux.c memfault_demo_panics.c 1 ..\..\..\..\..\..\components\demo\src\panics\memfault_demo_panics.c memfault_demo_shell.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_shell.c memfault_dialog memfault_platform_core.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_core.c memfault_platform_coredump_regions.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_coredump_regions.c memfault_platform_coredump_storage.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_coredump_storage.c memfault_platform_debug_log.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_debug_log.c memfault_platform_metrics.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_metrics.c reset_reboot_tracking.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\reset_reboot_tracking.c sdk_app app_default_handlers.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_default_hnd\app_default_handlers.c app.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app.c app_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_task.c app_diss.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_diss\app_diss.c app_diss_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_diss\app_diss_task.c app_entry_point.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_entry\app_entry_point.c app_msg_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_msg_utils.c app_easy_msg_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_easy\app_easy_msg_utils.c app_easy_timer.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_easy\app_easy_timer.c app_customs.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs.c app_customs_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs_task.c app_customs_common.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs_common.c app_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_utils.c sdk_arch arch_console.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\arch_console\arch_console.c nvds.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\nvds\src\nvds.c arch_main.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_main.c jump_table.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\jump_table.c arch_sleep.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_sleep.c arch_system.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_system.c arch_hibernation.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_hibernation.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 arch_rom.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_rom.c chacha20.c 1 .\..\..\..\..\..\..\..\..\..\third_party\rand\chacha20.c hash.c 1 .\..\..\..\..\..\..\..\..\..\third_party\hash\hash.c da14585_586.lib 4 .\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\output\Keil_5\da14585_586.lib da14531.lib 4 .\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\output\Keil_5\da14531.lib 2 0 0 0 0 0 2 2 2 2 11 1 otp_cs.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_cs\otp_cs.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 otp_hdr.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_hdr\otp_hdr.c sdk_ble rf_585.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\rf_585.c rwble.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble\rwble.c rwip.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rwip\src\rwip.c ble_arp.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\ble_arp.c rf_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\rf_531.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 sdk_boot system_DA14585_586.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\system_DA14585_586.c startup_DA14585_586.s 2 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM\startup_DA14585_586.s system_DA14531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\system_DA14531.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 startup_DA14531.s 2 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM\startup_DA14531.s 2 0 0 0 0 0 0 2 2 2 11 1 2 2 2 2 2 2 2 2 2 0 hardfault_handler.c 1 ..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\hardfault_handler.c nmi_handler.c 1 ..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\nmi_handler.c sdk_driver syscntl.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\syscntl\syscntl.c gpio.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\gpio\gpio.c wkupct_quadec.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\wkupct_quadec\wkupct_quadec.c adc_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc\adc_58x.c adc_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc\adc_531.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 trng.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\trng\trng.c spi_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi\spi_58x.c spi_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi\spi_531.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 spi_flash.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_flash\spi_flash.c uart.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\uart\uart.c hw_otpc_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc\hw_otpc_58x.c hw_otpc_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc\hw_otpc_531.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 sdk_profiles attm_db_128.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attm\attm_db_128.c diss.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\src\diss.c diss_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\src\diss_task.c custom_common.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custom_common.c custs1.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\src\custs1.c custs1_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\src\custs1_task.c prf.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prf.c prf_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prf_utils.c user_config da1458x_config_advanced.h 5 .\..\src\config\da1458x_config_advanced.h da1458x_config_basic.h 5 .\..\src\config\da1458x_config_basic.h user_callback_config.h 5 .\..\src\config\user_callback_config.h user_config.h 5 .\..\src\config\user_config.h user_modules_config.h 5 .\..\src\config\user_modules_config.h user_periph_setup.h 5 .\..\src\config\user_periph_setup.h user_profiles_config.h 5 .\..\src\config\user_profiles_config.h memfault_platform_config.h 5 ..\src\config\memfault_platform_config.h user_custom_profile user_custs_config.c 1 ..\src\custom_profile\user_custs_config.c user_custs1_def.c 1 ..\src\custom_profile\user_custs1_def.c user_platform user_periph_setup.c 1 .\..\src\platform\user_periph_setup.c user_app user_app.c 1 ..\src\user_app.c memfault_platform_device_info.c 1 ..\src\memfault_platform_device_info.c DA14531 0x4 ARM-ADS 5060960::V5.06 update 7 (build 960)::.\ARMCC 0 ARMCM0P_MPU ARM ARM.CMSIS.5.7.0 http://www.keil.com/pack/ IRAM(0x20000000,0x00020000) IROM(0x00000000,0x00040000) CPUTYPE("Cortex-M0+") CLOCK(12000000) ESEL ELITTLE UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000) 0 $$Device:ARMCM0P_MPU$Device\ARM\ARMCM0plus\Include\ARMCM0plus_MPU.h 0 0 0 0 0 0 1 .\DA14531\Objects\ memfault_demo_app_531 1 0 1 1 1 .\DA14531\Listings\ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 python ..\..\..\..\..\..\scripts\fw_build_id.py .\DA14531\Objects\@L.axf fromelf --bincombined --output=".\DA14531\Objects\@L.bin" "!L" 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 3 1 SARMCM3.DLL DARMCM1.DLL -pCM0+ SARMCM3.DLL TARMCM1.DLL -pCM0+ 1 0 0 0 16 0 1 0 1 1 4096 1 BIN\UL2CM3.DLL 0 0 1 1 1 1 1 1 1 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 "Cortex-M0+" 0 0 0 1 1 0 0 0 0 0 0 0 8 1 1 0 0 3 3 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x20000000 0x20000 1 0x0 0x40000 0 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x0 1 0x0 0x40000 1 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x0 0x0 0 0x20000000 0x20000 0 0x0 0x0 1 4 0 0 0 0 0 0 0 0 2 0 0 1 0 0 0 0 1 1 0 0 0 --thumb -c --preinclude da1458x_config_basic.h --preinclude da1458x_config_advanced.h --preinclude user_config.h --bss_threshold=0 --feedback=".\unused_531.txt" __DA14531__ .\..\..\..\..\..\..\..\..\..\sdk\app_modules\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\em;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\llc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\lld;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\controller\llm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\ea\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\em\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\hci\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\hci\src;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\atts;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap\gapc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gap\gapm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt\gattc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\gatt\gattm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\l2c\l2cc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\l2c\l2cm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp\smpc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\smp\smpm;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anc;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anc\ancc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp\anpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\anp\anps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bas\basc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bas\bass\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs\bcsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bcs\bcss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp\blpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\blp\blps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms\bmsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\bms\bmss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp\cppc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cpp\cpps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp\cscpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cscp\cscps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts\ctsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\cts\ctss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\disc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find\findl\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\find\findt\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\gatt\gatt_client\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp\glpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\glp\glps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogpbh\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogpd\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hogp\hogprh\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp\hrpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\hrp\hrps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp\htpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\htp\htpt\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan\lanc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\lan\lans\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp\paspc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\pasp\pasps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prox\proxm\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prox\proxr\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp\rscpc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\rscp\rscps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp\scppc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\scpp\scpps\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\suota\suotar\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip\tipc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\tip\tips\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds\udsc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\uds\udss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss\wssc\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\wss\wsss\api;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble;.\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble_hl;.\..\..\..\..\..\..\..\..\..\sdk\common_project_files;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\GCC;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler\ARM;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\compiler\GCC;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\ll;.\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\arch_console;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\common\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\crypto;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\dbg\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\gtl\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\gtl\src;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\h4tl\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\ke\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\ke\src;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\nvds\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rwip\api;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\battery;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\ble;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\dma;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\gpio;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\i2c;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\i2c_eeprom;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\pdm;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\reg;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\rtc;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_flash;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_hci;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\syscntl;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\systick;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\timer;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\trng;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\uart;.\..\..\..\..\..\..\..\..\..\sdk\platform\driver\wkupct_quadec;.\..\..\..\..\..\..\..\..\..\sdk\platform\include;.\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\include;.\..\..\..\..\..\..\..\..\..\third_party\hash;.\..\..\..\..\..\..\..\..\..\third_party\irng;.\..\..\..\..\..\..\..\..\..\third_party\rand;.\..\..\..\..\..\..\ports\include;.\..\..\..\..\..\..\components\include;.\..\src;.\..\src\config;.\..\src\custom_profile;.\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_cs;.\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_hdr;.\..\..\..\..\..\..\..\..\..\sdk\platform\include\CMSIS\5.6.0\Include 1 0 0 1 0 0 0 0 0 4 0 0 0 0 1 0 0x00000000 0x00000000 .\..\..\..\..\..\..\..\..\..\sdk\common_project_files\scatterfiles\DA14531.sct --feedback=".\unused_531.txt" .\..\..\..\..\..\..\..\..\..\sdk\common_project_files\misc\da14531_symbols.txt --symdefs=memfault_demo_app_531_symdef.txt --any_placement=best_fit --datacompressor off --predefine="-I ..\..\..\..\..\..\..\..\..\sdk\common_project_files" memfault_components arch_arm_cortex_m.c 1 ..\..\..\..\..\..\components\core\src\arch_arm_cortex_m.c memfault_batched_events.c 1 ..\..\..\..\..\..\components\core\src\memfault_batched_events.c memfault_build_id.c 1 ..\..\..\..\..\..\components\core\src\memfault_build_id.c memfault_core_utils.c 1 ..\..\..\..\..\..\components\core\src\memfault_core_utils.c memfault_data_export.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_export.c memfault_data_packetizer.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_packetizer.c memfault_data_source_rle.c 1 ..\..\..\..\..\..\components\core\src\memfault_data_source_rle.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 memfault_event_storage.c 1 ..\..\..\..\..\..\components\core\src\memfault_event_storage.c memfault_log.c 1 ..\..\..\..\..\..\components\core\src\memfault_log.c memfault_ram_reboot_info_tracking.c 1 ..\..\..\..\..\..\components\core\src\memfault_ram_reboot_info_tracking.c memfault_reboot_tracking_serializer.c 1 ..\..\..\..\..\..\components\core\src\memfault_reboot_tracking_serializer.c memfault_sdk_assert.c 1 ..\..\..\..\..\..\components\core\src\memfault_sdk_assert.c memfault_serializer_helper.c 1 ..\..\..\..\..\..\components\core\src\memfault_serializer_helper.c memfault_trace_event.c 1 ..\..\..\..\..\..\components\core\src\memfault_trace_event.c memfault_metrics.c 1 ..\..\..\..\..\..\components\metrics\src\memfault_metrics.c memfault_metrics_serializer.c 1 ..\..\..\..\..\..\components\metrics\src\memfault_metrics_serializer.c memfault_coredump.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump.c memfault_coredump_regions_armv7.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_regions_armv7.c memfault_coredump_sdk_regions.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_sdk_regions.c memfault_coredump_storage_debug.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_storage_debug.c memfault_coredump_utils.c 1 ..\..\..\..\..\..\components\panics\src\memfault_coredump_utils.c memfault_fault_handling_arm.c 1 ..\..\..\..\..\..\components\panics\src\memfault_fault_handling_arm.c memfault_base64.c 1 ..\..\..\..\..\..\components\util\src\memfault_base64.c memfault_chunk_transport.c 1 ..\..\..\..\..\..\components\util\src\memfault_chunk_transport.c memfault_circular_buffer.c 1 ..\..\..\..\..\..\components\util\src\memfault_circular_buffer.c memfault_crc16_ccitt.c 1 ..\..\..\..\..\..\components\util\src\memfault_crc16_ccitt.c memfault_minimal_cbor.c 1 ..\..\..\..\..\..\components\util\src\memfault_minimal_cbor.c memfault_rle.c 1 ..\..\..\..\..\..\components\util\src\memfault_rle.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 memfault_varint.c 1 ..\..\..\..\..\..\components\util\src\memfault_varint.c memfault_demo_cli_trace_event.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_cli_trace_event.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 memfault_demo_core.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_core.c memfault_demo_cli_aux.c 1 ..\..\..\..\..\..\components\demo\src\panics\memfault_demo_cli_aux.c memfault_demo_panics.c 1 ..\..\..\..\..\..\components\demo\src\panics\memfault_demo_panics.c memfault_demo_shell.c 1 ..\..\..\..\..\..\components\demo\src\memfault_demo_shell.c memfault_dialog memfault_platform_core.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_core.c memfault_platform_coredump_regions.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_coredump_regions.c memfault_platform_coredump_storage.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_coredump_storage.c memfault_platform_debug_log.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_debug_log.c memfault_platform_metrics.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\memfault_platform_metrics.c reset_reboot_tracking.c 1 ..\..\..\..\..\..\ports\dialog\da145xx\reset_reboot_tracking.c sdk_app app_default_handlers.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_default_hnd\app_default_handlers.c app.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app.c app_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_task.c app_diss.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_diss\app_diss.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 app_diss_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_diss\app_diss_task.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 app_entry_point.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_entry\app_entry_point.c app_msg_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_msg_utils.c app_easy_msg_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_easy\app_easy_msg_utils.c app_easy_timer.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_easy\app_easy_timer.c app_customs.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 app_customs_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs_task.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 app_customs_common.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_custs\app_customs_common.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 app_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\app_modules\src\app_common\app_utils.c sdk_arch arch_console.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\arch_console\arch_console.c nvds.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\nvds\src\nvds.c arch_main.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_main.c jump_table.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\jump_table.c arch_sleep.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_sleep.c arch_system.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_system.c arch_hibernation.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_hibernation.c arch_rom.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\arch_rom.c chacha20.c 1 .\..\..\..\..\..\..\..\..\..\third_party\rand\chacha20.c hash.c 1 .\..\..\..\..\..\..\..\..\..\third_party\hash\hash.c da14585_586.lib 4 .\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\output\Keil_5\da14585_586.lib 2 0 0 0 0 0 2 2 2 2 11 1 da14531.lib 4 .\..\..\..\..\..\..\..\..\..\sdk\platform\system_library\output\Keil_5\da14531.lib otp_cs.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_cs\otp_cs.c otp_hdr.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\utilities\otp_hdr\otp_hdr.c sdk_ble rf_585.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\rf_585.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 rwble.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\rwble\rwble.c rwip.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rwip\src\rwip.c ble_arp.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\ble_arp.c rf_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\core_modules\rf\src\rf_531.c sdk_boot system_DA14585_586.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\system_DA14585_586.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 startup_DA14585_586.s 2 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM\startup_DA14585_586.s 2 0 0 0 0 0 0 2 2 2 11 1 2 2 2 2 2 2 2 2 2 0 system_DA14531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\system_DA14531.c startup_DA14531.s 2 .\..\..\..\..\..\..\..\..\..\sdk\platform\arch\boot\ARM\startup_DA14531.s hardfault_handler.c 1 ..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\hardfault_handler.c nmi_handler.c 1 ..\..\..\..\..\..\..\..\..\sdk\platform\arch\main\nmi_handler.c sdk_driver syscntl.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\syscntl\syscntl.c gpio.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\gpio\gpio.c wkupct_quadec.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\wkupct_quadec\wkupct_quadec.c adc_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc\adc_58x.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 adc_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\adc\adc_531.c trng.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\trng\trng.c spi_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi\spi_58x.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 spi_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi\spi_531.c spi_flash.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\spi_flash\spi_flash.c uart.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\uart\uart.c hw_otpc_58x.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc\hw_otpc_58x.c 2 0 0 0 0 0 0 0 0 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 hw_otpc_531.c 1 .\..\..\..\..\..\..\..\..\..\sdk\platform\driver\hw_otpc\hw_otpc_531.c sdk_profiles attm_db_128.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\host\att\attm\attm_db_128.c diss.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\src\diss.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 diss_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\dis\diss\src\diss_task.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 custom_common.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custom_common.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 custs1.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\src\custs1.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 custs1_task.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\custom\custs\src\custs1_task.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 prf.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prf.c prf_utils.c 1 .\..\..\..\..\..\..\..\..\..\sdk\ble_stack\profiles\prf_utils.c user_config da1458x_config_advanced.h 5 .\..\src\config\da1458x_config_advanced.h da1458x_config_basic.h 5 .\..\src\config\da1458x_config_basic.h user_callback_config.h 5 .\..\src\config\user_callback_config.h user_config.h 5 .\..\src\config\user_config.h user_modules_config.h 5 .\..\src\config\user_modules_config.h user_periph_setup.h 5 .\..\src\config\user_periph_setup.h user_profiles_config.h 5 .\..\src\config\user_profiles_config.h memfault_platform_config.h 5 ..\src\config\memfault_platform_config.h user_custom_profile user_custs_config.c 1 ..\src\custom_profile\user_custs_config.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 user_custs1_def.c 1 ..\src\custom_profile\user_custs1_def.c 2 0 0 0 0 0 2 2 2 2 11 1 2 0 2 2 2 2 2 2 2 2 0 2 2 2 2 2 0 0 2 2 2 2 2 user_platform user_periph_setup.c 1 .\..\src\platform\user_periph_setup.c user_app user_app.c 1 ..\src\user_app.c memfault_platform_device_info.c 1 ..\src\memfault_platform_device_info.c memfault_demo_app 1
================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/Keil_5/unused_531.txt ================================================ ;## ARM Linker, 5060960: Last Updated: Sun May 09 10:25:09 2021 ;VERSION 0.2 ;FILE adc_531.o adc_correct_sample <= USED 0 adc_disable <= USED 0 adc_get_sample <= USED 0 adc_get_temp_async <= USED 0 adc_input_shift_config <= USED 0 adc_input_shift_disable <= USED 0 adc_input_shift_enable <= USED 0 adc_offset_calibrate <= USED 0 adc_register_interrupt <= USED 0 adc_reset <= USED 0 adc_unregister_interrupt <= USED 0 ;FILE app.o app_easy_gap_advertise_with_timeout_stop <= USED 0 app_easy_gap_dev_config_get_active <= USED 0 app_easy_gap_directed_advertise_get_active <= USED 0 app_easy_gap_directed_advertise_start <= USED 0 app_easy_gap_get_peer_features <= USED 0 app_easy_gap_non_connectable_advertise_get_active <= USED 0 app_easy_gap_non_connectable_advertise_start <= USED 0 app_easy_gap_param_update_get_active <= USED 0 app_easy_gap_param_update_start <= USED 0 app_easy_gap_start_connection_to <= USED 0 app_easy_gap_start_connection_to_get_active <= USED 0 app_easy_gap_start_connection_to_set <= USED 0 app_easy_gap_undirected_advertise_get_active <= USED 0 app_easy_gap_undirected_advertise_with_timeout_start <= USED 0 ;FILE app_default_handlers.o default_advertise_operation <= USED 0 default_app_generate_unique_static_random_addr <= USED 0 ;FILE app_easy_msg_utils.o app_easy_msg_free_callback <= USED 0 app_easy_msg_modify <= USED 0 app_easy_msg_set <= USED 0 app_easy_wakeup <= USED 0 app_easy_wakeup_free <= USED 0 app_easy_wakeup_set <= USED 0 ;FILE app_easy_timer.o app_easy_timer_cancel <= USED 0 app_easy_timer_cancel_all <= USED 0 app_easy_timer_modify <= USED 0 ;FILE app_msg_utils.o app_msg_send_wakeup_ble <= USED 0 ;FILE app_task.o ;FILE app_utils.o app_fill_random_byte_array <= USED 0 app_get_address_type <= USED 0 ;FILE arch_arm_cortex_m.o memfault_arch_is_inside_isr <= USED 0 ;FILE arch_hibernation.o arch_hibernation_restore <= USED 0 arch_set_hibernation <= USED 0 arch_set_stateful_hibernation <= USED 0 ;FILE arch_main.o ;FILE arch_rom.o ;FILE arch_sleep.o arch_ble_ext_wakeup_off <= USED 0 arch_ble_ext_wakeup_on <= USED 0 arch_last_rwble_evt_get <= USED 0 arch_set_deep_sleep <= USED 0 ;FILE arch_system.o arch_set_pxact_gpio <= USED 0 arch_wkupct_tweak_deb_time <= USED 0 check_sys_startup_period <= USED 0 ;FILE ble_arp.o ;FILE gpio.o GPIO_ConfigurePinPower <= USED 0 GPIO_DisablePorPin <= USED 0 GPIO_EnableIRQ <= USED 0 GPIO_EnablePorPin <= USED 0 GPIO_GetIRQInputLevel <= USED 0 GPIO_GetPinFunction <= USED 0 GPIO_GetPinStatus <= USED 0 GPIO_GetPorTime <= USED 0 GPIO_RegisterCallback <= USED 0 GPIO_SetIRQInputLevel <= USED 0 GPIO_SetPorTime <= USED 0 GPIO_is_valid <= USED 0 ;FILE hardfault_handler.o ;FILE hash.o hash <= USED 0 ;FILE hw_otpc_531.o hw_otpc_prog <= USED 0 hw_otpc_prog_and_verify <= USED 0 hw_otpc_read <= USED 0 hw_otpc_word_prog_and_verify <= USED 0 hw_otpc_word_read <= USED 0 ;FILE jump_table.o dummyf <= USED 0 ;FILE memfault_base64.o ;FILE memfault_batched_events.o memfault_batched_events_build_header <= USED 0 ;FILE memfault_chunk_transport.o ;FILE memfault_circular_buffer.o memfault_circular_buffer_get_read_pointer <= USED 0 memfault_circular_buffer_read_with_callback <= USED 0 ;FILE memfault_core_utils.o ;FILE memfault_coredump.o memfault_coredump_get_save_size <= USED 0 ;FILE memfault_coredump_regions_armv7.o ;FILE memfault_coredump_sdk_regions.o ;FILE memfault_coredump_storage_debug.o memfault_coredump_storage_debug_test_begin <= USED 0 memfault_coredump_storage_debug_test_finish <= USED 0 ;FILE memfault_coredump_utils.o memfault_coredump_storage_check_size <= USED 0 ;FILE memfault_crc16_ccitt.o ;FILE memfault_data_export.o ;FILE memfault_data_packetizer.o memfault_packetizer_abort <= USED 0 memfault_packetizer_data_available <= USED 0 ;FILE memfault_demo_core.o memfault_demo_cli_cmd_get_device_info <= USED 0 ;FILE memfault_demo_panics.o memfault_demo_cli_cmd_clear_core <= USED 0 memfault_demo_cli_cmd_get_core <= USED 0 ;FILE memfault_demo_shell.o ;FILE memfault_event_storage.o memfault_event_storage_bytes_free <= USED 0 memfault_event_storage_bytes_used <= USED 0 memfault_event_storage_persist <= USED 0 ;FILE memfault_fault_handling_arm.o BusFault_Handler <= USED 0 MemfaultWatchdog_Handler <= USED 0 MemoryManagement_Handler <= USED 0 UsageFault_Handler <= USED 0 memfault_coredump_storage_compute_size_required <= USED 0 ;FILE memfault_log.o memfault_log_boot <= USED 0 memfault_log_get_regions <= USED 0 memfault_log_handle_saved_callback <= USED 0 memfault_log_iter_copy_msg <= USED 0 memfault_log_iter_update_entry <= USED 0 memfault_log_iterate <= USED 0 memfault_log_read <= USED 0 memfault_log_reset <= USED 0 memfault_log_save <= USED 0 memfault_log_save_preformatted <= USED 0 memfault_log_set_min_save_level <= USED 0 memfault_vlog_save <= USED 0 ;FILE memfault_metrics.o memfault_metrics_heartbeat_add <= USED 0 memfault_metrics_heartbeat_debug_print <= USED 0 memfault_metrics_heartbeat_debug_trigger <= USED 0 memfault_metrics_heartbeat_read_signed <= USED 0 memfault_metrics_heartbeat_read_unsigned <= USED 0 memfault_metrics_heartbeat_set_signed <= USED 0 memfault_metrics_heartbeat_timer_read <= USED 0 memfault_metrics_heartbeat_timer_stop <= USED 0 ;FILE memfault_metrics_serializer.o ;FILE memfault_minimal_cbor.o memfault_cbor_encode_long_signed_integer <= USED 0 memfault_cbor_encode_string_add <= USED 0 memfault_cbor_encode_string_begin <= USED 0 memfault_cbor_encode_uint64_as_double <= USED 0 ;FILE memfault_platform_core.o ;FILE memfault_platform_coredump_regions.o memfault_platform_sanitize_address_range <= USED 0 ;FILE memfault_platform_coredump_storage.o ;FILE memfault_platform_debug_log.o memfault_platform_log_raw <= USED 0 ;FILE memfault_platform_device_info.o ;FILE memfault_platform_metrics.o ;FILE memfault_ram_reboot_info_tracking.o memfault_reboot_tracking_reset_crash_count <= USED 0 ;FILE memfault_reboot_tracking_serializer.o ;FILE memfault_sdk_assert.o ;FILE memfault_serializer_helper.o memfault_serializer_helper_read_drop_count <= USED 0 ;FILE memfault_trace_event.o memfault_trace_event_capture <= USED 0 memfault_trace_event_reset <= USED 0 memfault_trace_event_try_flush_isr_event <= USED 0 memfault_trace_event_with_log_capture <= USED 0 memfault_trace_event_with_status_capture <= USED 0 ;FILE memfault_varint.o memfault_encode_varint_si32 <= USED 0 ;FILE nmi_handler.o ;FILE otp_cs.o otp_cs_get_adc_diff_ge <= USED 0 otp_cs_get_adc_diff_offset <= USED 0 otp_cs_get_adc_offsh_ge <= USED 0 otp_cs_get_adc_offsh_offset <= USED 0 otp_cs_get_adc_single_ge <= USED 0 otp_cs_get_adc_single_offset <= USED 0 otp_cs_get_low_power_clock <= USED 0 ;FILE otp_hdr.o ;FILE patch.o ;FILE prf.o ;FILE reset_reboot_tracking.o ;FILE rf_531.o dcoff_calibration <= USED 0 dis_kdco_cal <= USED 0 en_hclk <= USED 0 en_kdtc_cal_mod1 <= USED 0 kdco_cal_end <= USED 0 kdco_cal_init <= USED 0 kdco_calibration <= USED 0 rf_adplldig_ldo_on <= USED 0 rf_adplldig_txmod <= USED 0 rf_ldo_cont_mode_en <= USED 0 rf_nfm_disable <= USED 0 rf_nfm_enable <= USED 0 rf_power_down <= USED 0 ;FILE rwble.o ;FILE rwip.o ;FILE spi_531.o spi_disable <= USED 0 spi_master_transfer <= USED 0 spi_receive <= USED 0 spi_register_receive_cb <= USED 0 spi_register_send_cb <= USED 0 spi_register_transfer_cb <= USED 0 spi_send <= USED 0 spi_set_cs_mode <= USED 0 spi_transfer <= USED 0 ;FILE spi_flash.o spi_flash_auto_detect <= USED 0 spi_flash_block_erase_no_wait <= USED 0 spi_flash_chip_erase <= USED 0 spi_flash_chip_erase_forced <= USED 0 spi_flash_configure_memory_protection <= USED 0 spi_flash_enable <= USED 0 spi_flash_enable_with_autodetect <= USED 0 spi_flash_erase_fail <= USED 0 spi_flash_fill <= USED 0 spi_flash_get_power_mode <= USED 0 spi_flash_is_empty <= USED 0 spi_flash_is_page_empty <= USED 0 spi_flash_is_sector_empty <= USED 0 spi_flash_page_erase <= USED 0 spi_flash_page_fill <= USED 0 spi_flash_page_program_buffer <= USED 0 spi_flash_power_down <= USED 0 spi_flash_program_fail <= USED 0 spi_flash_read_config_reg <= USED 0 spi_flash_read_data_buffer <= USED 0 spi_flash_read_jedec_id <= USED 0 spi_flash_read_rems_id <= USED 0 spi_flash_read_security_reg <= USED 0 spi_flash_read_unique_id <= USED 0 spi_flash_release_from_power_down <= USED 0 spi_flash_set_power_mode <= USED 0 spi_flash_set_write_disable <= USED 0 spi_flash_set_write_enable <= USED 0 spi_flash_write_data_buffer <= USED 0 spi_flash_write_enable_volatile <= USED 0 spi_flash_write_status_config_reg <= USED 0 spi_flash_write_status_reg <= USED 0 ;FILE startup_da14531.o ;FILE syscntl.o syscntl_dcdc_turn_off <= USED 0 syscntl_dcdc_turn_on_in_buck <= USED 0 syscntl_por_vbat_high_cfg <= USED 0 syscntl_por_vbat_low_cfg <= USED 0 ;FILE system_da14531.o SystemCoreClockUpdate <= USED 0 ;FILE trng.o ;FILE uart.o uart_disable <= USED 0 uart_disable_flow_control <= USED 0 uart_enable_flow_control <= USED 0 uart_read_buffer <= USED 0 uart_receive <= USED 0 uart_register_err_cb <= USED 0 uart_register_rx_cb <= USED 0 ;FILE user_app.o ;FILE user_periph_setup.o ;FILE wkupct_quadec.o quad_decoder_clear_irq <= USED 0 quad_decoder_disable_irq <= USED 0 quad_decoder_enable_irq <= USED 0 quad_decoder_init <= USED 0 quad_decoder_register_callback <= USED 0 quad_decoder_release <= USED 0 wkupct2_disable_irq <= USED 0 wkupct2_enable_irq <= USED 0 wkupct2_register_callback <= USED 0 wkupct_disable_irq <= USED 0 wkupct_enable_irq <= USED 0 wkupct_register_callback <= USED 0 ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/Keil_5/unused_585.txt ================================================ ;## ARM Linker, 5060750: Last Updated: Thu May 06 11:34:07 2021 ;VERSION 0.2 ;FILE adc_58x.o adc_disable <= USED 0 adc_enable <= USED 0 adc_offset_calibrate <= USED 0 adc_register_interrupt <= USED 0 adc_reset <= USED 0 adc_unregister_interrupt <= USED 0 ;FILE app.o active_conhdl_to_conidx <= USED 0 active_conidx_to_conhdl <= USED 0 app_easy_gap_advertise_stop <= USED 0 app_easy_gap_advertise_with_timeout_stop <= USED 0 app_easy_gap_dev_config_get_active <= USED 0 app_easy_gap_directed_advertise_get_active <= USED 0 app_easy_gap_directed_advertise_start <= USED 0 app_easy_gap_disconnect <= USED 0 app_easy_gap_get_peer_features <= USED 0 app_easy_gap_non_connectable_advertise_get_active <= USED 0 app_easy_gap_non_connectable_advertise_start <= USED 0 app_easy_gap_param_update_get_active <= USED 0 app_easy_gap_param_update_start <= USED 0 app_easy_gap_set_data_packet_length <= USED 0 app_easy_gap_start_connection_to <= USED 0 app_easy_gap_start_connection_to_get_active <= USED 0 app_easy_gap_start_connection_to_set <= USED 0 app_easy_gap_undirected_advertise_get_active <= USED 0 app_easy_gap_undirected_advertise_with_timeout_start <= USED 0 app_easy_gap_update_adv_data <= USED 0 app_gattc_svc_changed_cmd_send <= USED 0 app_set_prf_srv_perm <= USED 0 app_timer_set <= USED 0 ;FILE app_customs.o app_custs1_init <= USED 0 ;FILE app_customs_common.o app_custs1_val_wr_validate <= USED 0 ;FILE app_customs_task.o ;FILE app_default_handlers.o default_advertise_operation <= USED 0 default_app_generate_static_random_addr <= USED 0 default_app_generate_unique_static_random_addr <= USED 0 ;FILE app_diss.o ;FILE app_diss_task.o ;FILE app_easy_msg_utils.o app_easy_msg_free_callback <= USED 0 app_easy_msg_modify <= USED 0 app_easy_msg_set <= USED 0 app_easy_wakeup <= USED 0 app_easy_wakeup_free <= USED 0 app_easy_wakeup_set <= USED 0 ;FILE app_easy_timer.o app_easy_timer_cancel <= USED 0 app_easy_timer_cancel_all <= USED 0 app_easy_timer_modify <= USED 0 ;FILE app_entry_point.o ;FILE app_msg_utils.o app_msg_send_wakeup_ble <= USED 0 ;FILE app_task.o ;FILE app_utils.o app_fill_random_byte_array <= USED 0 app_get_address_type <= USED 0 ;FILE arch_arm_cortex_m.o ;FILE arch_console.o arch_puts <= USED 0 ;FILE arch_main.o ;FILE arch_rom.o ;FILE arch_sleep.o arch_ble_ext_wakeup_off <= USED 0 arch_ble_ext_wakeup_on <= USED 0 arch_last_rwble_evt_get <= USED 0 arch_set_deep_sleep <= USED 0 ;FILE arch_system.o __ARM_common_ll_muluu <= USED 0 arch_set_pxact_gpio <= USED 0 arch_wkupct_tweak_deb_time <= USED 0 calibrate_rcx20 <= USED 0 lld_sleep_lpcycles_2_us_rcx_func <= USED 0 lld_sleep_us_2_lpcycles_rcx_func <= USED 0 read_rcx_freq <= USED 0 ;FILE attm_db_128.o ;FILE custom_common.o ;FILE custs1.o ;FILE custs1_task.o ;FILE diss.o ;FILE gpio.o GPIO_ConfigurePinPower <= USED 0 GPIO_DisablePorPin <= USED 0 GPIO_EnableIRQ <= USED 0 GPIO_EnablePorPin <= USED 0 GPIO_GetIRQInputLevel <= USED 0 GPIO_GetPinFunction <= USED 0 GPIO_GetPinStatus <= USED 0 GPIO_GetPorTime <= USED 0 GPIO_RegisterCallback <= USED 0 GPIO_SetIRQInputLevel <= USED 0 GPIO_SetPorTime <= USED 0 GPIO_is_valid <= USED 0 ;FILE hardfault_handler.o ;FILE hash.o hash <= USED 0 ;FILE hw_otpc_58x.o hw_otpc_blank <= USED 0 hw_otpc_cancel_prepare <= USED 0 hw_otpc_dma_prog <= USED 0 hw_otpc_dma_read <= USED 0 hw_otpc_fifo_prog <= USED 0 hw_otpc_fifo_read <= USED 0 hw_otpc_manual_prog <= USED 0 hw_otpc_manual_read_off <= USED 0 hw_otpc_manual_word_prog <= USED 0 hw_otpc_num_of_rr <= USED 0 hw_otpc_power_save <= USED 0 hw_otpc_prepare <= USED 0 hw_otpc_set_speed <= USED 0 hw_otpc_tdec <= USED 0 hw_otpc_twr <= USED 0 hw_otpc_write_rr <= USED 0 ;FILE jump_table.o dummyf <= USED 0 ;FILE memfault_base64.o ;FILE memfault_batched_events.o memfault_batched_events_build_header <= USED 0 ;FILE memfault_chunk_transport.o ;FILE memfault_circular_buffer.o memfault_circular_buffer_get_read_pointer <= USED 0 ;FILE memfault_core_utils.o ;FILE memfault_coredump.o memfault_coredump_get_save_size <= USED 0 ;FILE memfault_coredump_regions_armv7.o ;FILE memfault_coredump_sdk_regions.o ;FILE memfault_coredump_storage_debug.o ;FILE memfault_coredump_utils.o memfault_coredump_storage_check_size <= USED 0 ;FILE memfault_crc16_ccitt.o ;FILE memfault_data_export.o ;FILE memfault_data_packetizer.o memfault_packetizer_abort <= USED 0 memfault_packetizer_data_available <= USED 0 ;FILE memfault_data_source_rle.o ;FILE memfault_demo_cli_trace_event.o ;FILE memfault_demo_core.o ;FILE memfault_demo_panics.o ;FILE memfault_event_storage.o memfault_event_storage_persist <= USED 0 ;FILE memfault_fault_handling_arm.o BusFault_Handler <= USED 0 MemfaultWatchdog_Handler <= USED 0 MemoryManagement_Handler <= USED 0 UsageFault_Handler <= USED 0 memfault_coredump_storage_compute_size_required <= USED 0 ;FILE memfault_log.o memfault_log_boot <= USED 0 memfault_log_get_regions <= USED 0 memfault_log_read <= USED 0 memfault_log_reset <= USED 0 memfault_log_set_min_save_level <= USED 0 ;FILE memfault_metrics.o memfault_metrics_heartbeat_add <= USED 0 memfault_metrics_heartbeat_debug_print <= USED 0 memfault_metrics_heartbeat_debug_trigger <= USED 0 memfault_metrics_heartbeat_read_signed <= USED 0 memfault_metrics_heartbeat_read_unsigned <= USED 0 memfault_metrics_heartbeat_set_signed <= USED 0 memfault_metrics_heartbeat_timer_read <= USED 0 memfault_metrics_heartbeat_timer_stop <= USED 0 ;FILE memfault_metrics_serializer.o ;FILE memfault_minimal_cbor.o memfault_cbor_encode_long_signed_integer <= USED 0 memfault_cbor_encode_uint64_as_double <= USED 0 ;FILE memfault_platform_core.o ;FILE memfault_platform_coredump_regions.o memfault_platform_sanitize_address_range <= USED 0 ;FILE memfault_platform_coredump_storage.o ;FILE memfault_platform_debug_log.o memfault_platform_log_raw <= USED 0 ;FILE memfault_platform_device_info.o ;FILE memfault_platform_metrics.o ;FILE memfault_ram_reboot_info_tracking.o memfault_reboot_tracking_reset_crash_count <= USED 0 ;FILE memfault_reboot_tracking_serializer.o ;FILE memfault_rle.o ;FILE memfault_sdk_assert.o ;FILE memfault_serializer_helper.o memfault_serializer_helper_read_drop_count <= USED 0 ;FILE memfault_trace_event.o memfault_trace_event_reset <= USED 0 memfault_trace_event_with_log_capture <= USED 0 memfault_trace_event_with_status_capture <= USED 0 ;FILE memfault_varint.o ;FILE nmi_handler.o ;FILE nvds.o ;FILE otp_hdr.o ;FILE patch.o ;FILE pll_vcocal_lut.o GPADC_init <= USED 0 MedianOfFive <= USED 0 check_pll_lock <= USED 0 clear_HW_LUT <= USED 0 find_initial_calcap_ranges <= USED 0 meas_precharge_freq <= USED 0 min <= USED 0 pll_vcocal_LUT_InitUpdate <= USED 0 save_configure_restore <= USED 0 set_rf_cal_cap <= USED 0 update_LUT <= USED 0 update_calcap_max_channel <= USED 0 update_calcap_min_channel <= USED 0 update_calcap_ranges <= USED 0 write_HW_LUT <= USED 0 write_one_SW_LUT_entry <= USED 0 ;FILE prf.o prf_reset_func <= USED 0 ;FILE prf_utils.o prf_pack_date_time <= USED 0 prf_unpack_date_time <= USED 0 ;FILE reset_reboot_tracking.o ;FILE rf_585.o rf_nfm_disable <= USED 0 rf_nfm_enable <= USED 0 rf_nfm_is_enabled <= USED 0 ;FILE rf_calibration.o DCoffsetCalibration_580 <= USED 0 IffCalibration_580 <= USED 0 enable_rf_diag_irq <= USED 0 rf_calibration_580 <= USED 0 set_gauss_modgain <= USED 0 ;FILE rwble.o ;FILE rwip.o ;FILE smpc_util.o JT_llm_p256_start_func <= USED 0 ;FILE spi_58x.o spi_disable <= USED 0 spi_receive <= USED 0 spi_send <= USED 0 spi_set_cp_mode <= USED 0 spi_set_cs_pad <= USED 0 spi_set_irq_mode <= USED 0 spi_set_speed <= USED 0 spi_transfer <= USED 0 ;FILE spi_flash.o spi_flash_auto_detect <= USED 0 spi_flash_block_erase_no_wait <= USED 0 spi_flash_chip_erase <= USED 0 spi_flash_chip_erase_forced <= USED 0 spi_flash_configure_memory_protection <= USED 0 spi_flash_enable <= USED 0 spi_flash_enable_with_autodetect <= USED 0 spi_flash_erase_fail <= USED 0 spi_flash_fill <= USED 0 spi_flash_get_power_mode <= USED 0 spi_flash_is_empty <= USED 0 spi_flash_is_page_empty <= USED 0 spi_flash_is_sector_empty <= USED 0 spi_flash_page_erase <= USED 0 spi_flash_page_fill <= USED 0 spi_flash_page_program_buffer <= USED 0 spi_flash_power_down <= USED 0 spi_flash_program_fail <= USED 0 spi_flash_read_config_reg <= USED 0 spi_flash_read_data_buffer <= USED 0 spi_flash_read_jedec_id <= USED 0 spi_flash_read_rems_id <= USED 0 spi_flash_read_security_reg <= USED 0 spi_flash_read_unique_id <= USED 0 spi_flash_release_from_power_down <= USED 0 spi_flash_set_power_mode <= USED 0 spi_flash_set_write_disable <= USED 0 spi_flash_set_write_enable <= USED 0 spi_flash_write_data_buffer <= USED 0 spi_flash_write_enable_volatile <= USED 0 spi_flash_write_status_config_reg <= USED 0 spi_flash_write_status_reg <= USED 0 ;FILE startup_da14585_586.o ;FILE syscntl.o syscntl_set_dcdc_vbat3v_level <= USED 0 ;FILE system_da14585_586.o SystemCoreClockUpdate <= USED 0 ;FILE trng.o ;FILE uart.o uart_disable <= USED 0 uart_disable_flow_control <= USED 0 uart_enable_flow_control <= USED 0 uart_read_buffer <= USED 0 uart_receive <= USED 0 uart_register_err_cb <= USED 0 uart_register_rx_cb <= USED 0 ;FILE user_app.o ;FILE user_periph_setup.o ;FILE wkupct_quadec.o quad_decoder_disable_irq <= USED 0 quad_decoder_enable_irq <= USED 0 quad_decoder_init <= USED 0 quad_decoder_register_callback <= USED 0 quad_decoder_release <= USED 0 wkupct_disable_irq <= USED 0 wkupct_enable_irq <= USED 0 wkupct_register_callback <= USED 0 ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/Keil_5/unused_586.txt ================================================ ;## ARM Linker, 5060750: Last Updated: Thu Apr 22 14:28:00 2021 ;VERSION 0.2 ;FILE adc_58x.o adc_disable <= USED 0 adc_enable <= USED 0 adc_offset_calibrate <= USED 0 adc_register_interrupt <= USED 0 adc_reset <= USED 0 adc_unregister_interrupt <= USED 0 ;FILE app.o active_conhdl_to_conidx <= USED 0 active_conidx_to_conhdl <= USED 0 app_easy_gap_advertise_stop <= USED 0 app_easy_gap_advertise_with_timeout_stop <= USED 0 app_easy_gap_dev_config_get_active <= USED 0 app_easy_gap_directed_advertise_get_active <= USED 0 app_easy_gap_directed_advertise_start <= USED 0 app_easy_gap_disconnect <= USED 0 app_easy_gap_get_peer_features <= USED 0 app_easy_gap_non_connectable_advertise_get_active <= USED 0 app_easy_gap_non_connectable_advertise_start <= USED 0 app_easy_gap_param_update_get_active <= USED 0 app_easy_gap_param_update_start <= USED 0 app_easy_gap_set_data_packet_length <= USED 0 app_easy_gap_start_connection_to <= USED 0 app_easy_gap_start_connection_to_get_active <= USED 0 app_easy_gap_start_connection_to_set <= USED 0 app_easy_gap_undirected_advertise_get_active <= USED 0 app_easy_gap_undirected_advertise_with_timeout_start <= USED 0 app_easy_gap_update_adv_data <= USED 0 app_gattc_svc_changed_cmd_send <= USED 0 app_set_prf_srv_perm <= USED 0 app_timer_set <= USED 0 ;FILE app_customs.o app_custs1_init <= USED 0 ;FILE app_customs_common.o app_custs1_val_wr_validate <= USED 0 ;FILE app_customs_task.o ;FILE app_default_handlers.o default_advertise_operation <= USED 0 default_app_generate_static_random_addr <= USED 0 default_app_generate_unique_static_random_addr <= USED 0 ;FILE app_diss.o ;FILE app_diss_task.o ;FILE app_easy_msg_utils.o app_easy_msg_free_callback <= USED 0 app_easy_msg_modify <= USED 0 app_easy_msg_set <= USED 0 app_easy_wakeup <= USED 0 app_easy_wakeup_free <= USED 0 app_easy_wakeup_set <= USED 0 ;FILE app_easy_timer.o app_easy_timer_cancel <= USED 0 app_easy_timer_cancel_all <= USED 0 app_easy_timer_modify <= USED 0 ;FILE app_entry_point.o ;FILE app_msg_utils.o app_msg_send_wakeup_ble <= USED 0 ;FILE app_task.o ;FILE app_utils.o app_fill_random_byte_array <= USED 0 app_get_address_type <= USED 0 ;FILE arch_arm_cortex_m.o ;FILE arch_console.o arch_puts <= USED 0 ;FILE arch_main.o ;FILE arch_rom.o ;FILE arch_sleep.o arch_ble_ext_wakeup_off <= USED 0 arch_ble_ext_wakeup_on <= USED 0 arch_last_rwble_evt_get <= USED 0 arch_set_deep_sleep <= USED 0 ;FILE arch_system.o __ARM_common_ll_muluu <= USED 0 arch_set_pxact_gpio <= USED 0 arch_wkupct_tweak_deb_time <= USED 0 calibrate_rcx20 <= USED 0 lld_sleep_lpcycles_2_us_rcx_func <= USED 0 lld_sleep_us_2_lpcycles_rcx_func <= USED 0 read_rcx_freq <= USED 0 ;FILE attm_db_128.o ;FILE custom_common.o ;FILE custs1.o ;FILE custs1_task.o ;FILE diss.o ;FILE gpio.o GPIO_ConfigurePinPower <= USED 0 GPIO_DisablePorPin <= USED 0 GPIO_EnableIRQ <= USED 0 GPIO_EnablePorPin <= USED 0 GPIO_GetIRQInputLevel <= USED 0 GPIO_GetPinFunction <= USED 0 GPIO_GetPinStatus <= USED 0 GPIO_GetPorTime <= USED 0 GPIO_RegisterCallback <= USED 0 GPIO_SetIRQInputLevel <= USED 0 GPIO_SetPorTime <= USED 0 GPIO_is_valid <= USED 0 ;FILE hash.o hash <= USED 0 ;FILE hw_otpc_58x.o hw_otpc_blank <= USED 0 hw_otpc_cancel_prepare <= USED 0 hw_otpc_dma_prog <= USED 0 hw_otpc_dma_read <= USED 0 hw_otpc_fifo_prog <= USED 0 hw_otpc_fifo_read <= USED 0 hw_otpc_manual_prog <= USED 0 hw_otpc_manual_read_off <= USED 0 hw_otpc_manual_word_prog <= USED 0 hw_otpc_num_of_rr <= USED 0 hw_otpc_power_save <= USED 0 hw_otpc_prepare <= USED 0 hw_otpc_set_speed <= USED 0 hw_otpc_tdec <= USED 0 hw_otpc_twr <= USED 0 hw_otpc_write_rr <= USED 0 ;FILE jump_table.o dummyf <= USED 0 ;FILE memfault_base64.o ;FILE memfault_batched_events.o memfault_batched_events_build_header <= USED 0 ;FILE memfault_chunk_transport.o ;FILE memfault_circular_buffer.o memfault_circular_buffer_get_read_pointer <= USED 0 ;FILE memfault_core_utils.o ;FILE memfault_coredump.o memfault_coredump_get_save_size <= USED 0 ;FILE memfault_coredump_regions_armv7.o ;FILE memfault_coredump_sdk_regions.o ;FILE memfault_coredump_storage_debug.o ;FILE memfault_coredump_utils.o memfault_coredump_storage_check_size <= USED 0 ;FILE memfault_crc16_ccitt.o ;FILE memfault_data_export.o ;FILE memfault_data_packetizer.o memfault_packetizer_abort <= USED 0 ;FILE memfault_data_source_rle.o ;FILE memfault_demo_cli_print_chunk.o ;FILE memfault_demo_cli_trace_event.o ;FILE memfault_demo_core.o ;FILE memfault_demo_panics.o ;FILE memfault_event_storage.o memfault_event_storage_persist <= USED 0 ;FILE memfault_fault_handling_arm.o BusFault_Handler <= USED 0 MemfaultWatchdog_Handler <= USED 0 MemoryManagement_Handler <= USED 0 UsageFault_Handler <= USED 0 memfault_coredump_storage_compute_size_required <= USED 0 ;FILE memfault_log.o memfault_log_boot <= USED 0 memfault_log_get_regions <= USED 0 memfault_log_read <= USED 0 memfault_log_reset <= USED 0 memfault_log_set_min_save_level <= USED 0 ;FILE memfault_metrics.o memfault_metrics_heartbeat_add <= USED 0 memfault_metrics_heartbeat_debug_print <= USED 0 memfault_metrics_heartbeat_debug_trigger <= USED 0 memfault_metrics_heartbeat_read_signed <= USED 0 memfault_metrics_heartbeat_read_unsigned <= USED 0 memfault_metrics_heartbeat_set_signed <= USED 0 memfault_metrics_heartbeat_timer_read <= USED 0 memfault_metrics_heartbeat_timer_stop <= USED 0 ;FILE memfault_metrics_serializer.o ;FILE memfault_minimal_cbor.o memfault_cbor_encode_long_signed_integer <= USED 0 memfault_cbor_encode_uint64_as_double <= USED 0 ;FILE memfault_platform_core.o ;FILE memfault_platform_coredump_regions.o memfault_platform_sanitize_address_range <= USED 0 ;FILE memfault_platform_coredump_storage.o ;FILE memfault_platform_debug_log.o ;FILE memfault_platform_device_info.o ;FILE memfault_platform_metrics.o ;FILE memfault_ram_reboot_info_tracking.o memfault_reboot_tracking_reset_crash_count <= USED 0 ;FILE memfault_reboot_tracking_serializer.o ;FILE memfault_rle.o ;FILE memfault_sdk_assert.o ;FILE memfault_serializer_helper.o memfault_serializer_helper_read_drop_count <= USED 0 ;FILE memfault_trace_event.o memfault_trace_event_reset <= USED 0 memfault_trace_event_with_log_capture <= USED 0 memfault_trace_event_with_status_capture <= USED 0 ;FILE memfault_varint.o ;FILE nvds.o ;FILE otp_hdr.o ;FILE patch.o ;FILE pll_vcocal_lut.o GPADC_init <= USED 0 MedianOfFive <= USED 0 check_pll_lock <= USED 0 clear_HW_LUT <= USED 0 find_initial_calcap_ranges <= USED 0 meas_precharge_freq <= USED 0 min <= USED 0 pll_vcocal_LUT_InitUpdate <= USED 0 save_configure_restore <= USED 0 set_rf_cal_cap <= USED 0 update_LUT <= USED 0 update_calcap_max_channel <= USED 0 update_calcap_min_channel <= USED 0 update_calcap_ranges <= USED 0 write_HW_LUT <= USED 0 write_one_SW_LUT_entry <= USED 0 ;FILE prf.o prf_reset_func <= USED 0 ;FILE prf_utils.o prf_pack_date_time <= USED 0 prf_unpack_date_time <= USED 0 ;FILE reset_reboot_tracking.o ;FILE rf_585.o rf_nfm_disable <= USED 0 rf_nfm_enable <= USED 0 rf_nfm_is_enabled <= USED 0 ;FILE rf_calibration.o DCoffsetCalibration_580 <= USED 0 IffCalibration_580 <= USED 0 enable_rf_diag_irq <= USED 0 rf_calibration_580 <= USED 0 set_gauss_modgain <= USED 0 ;FILE rwble.o ;FILE rwip.o ;FILE smpc_util.o JT_llm_p256_start_func <= USED 0 ;FILE spi_58x.o spi_receive <= USED 0 spi_send <= USED 0 spi_set_cp_mode <= USED 0 spi_set_cs_pad <= USED 0 spi_set_irq_mode <= USED 0 spi_set_speed <= USED 0 spi_transfer <= USED 0 ;FILE spi_flash.o spi_flash_auto_detect <= USED 0 spi_flash_block_erase_no_wait <= USED 0 spi_flash_chip_erase <= USED 0 spi_flash_chip_erase_forced <= USED 0 spi_flash_configure_memory_protection <= USED 0 spi_flash_enable <= USED 0 spi_flash_enable_with_autodetect <= USED 0 spi_flash_erase_fail <= USED 0 spi_flash_fill <= USED 0 spi_flash_get_power_mode <= USED 0 spi_flash_is_empty <= USED 0 spi_flash_is_page_empty <= USED 0 spi_flash_is_sector_empty <= USED 0 spi_flash_page_erase <= USED 0 spi_flash_page_fill <= USED 0 spi_flash_page_program_buffer <= USED 0 spi_flash_program_fail <= USED 0 spi_flash_read_config_reg <= USED 0 spi_flash_read_data_buffer <= USED 0 spi_flash_read_jedec_id <= USED 0 spi_flash_read_rems_id <= USED 0 spi_flash_read_security_reg <= USED 0 spi_flash_read_unique_id <= USED 0 spi_flash_set_power_mode <= USED 0 spi_flash_set_write_disable <= USED 0 spi_flash_set_write_enable <= USED 0 spi_flash_write_data_buffer <= USED 0 spi_flash_write_enable_volatile <= USED 0 spi_flash_write_status_config_reg <= USED 0 spi_flash_write_status_reg <= USED 0 ;FILE startup_da14585_586.o ;FILE syscntl.o syscntl_set_dcdc_vbat3v_level <= USED 0 ;FILE system_da14585_586.o SystemCoreClockUpdate <= USED 0 ;FILE trng.o ;FILE uart.o uart_disable <= USED 0 uart_disable_flow_control <= USED 0 uart_enable_flow_control <= USED 0 uart_read_buffer <= USED 0 uart_receive <= USED 0 uart_register_err_cb <= USED 0 uart_register_rx_cb <= USED 0 ;FILE user_app.o ;FILE user_periph_setup.o ;FILE wkupct_quadec.o quad_decoder_disable_irq <= USED 0 quad_decoder_enable_irq <= USED 0 quad_decoder_init <= USED 0 quad_decoder_register_callback <= USED 0 quad_decoder_release <= USED 0 wkupct_disable_irq <= USED 0 wkupct_enable_irq <= USED 0 wkupct_register_callback <= USED 0 ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/da1458x_config_advanced.h ================================================ /** **************************************************************************************** * * @file da1458x_config_advanced.h * * @brief Advanced compile configuration file. * * Copyright (C) 2014-2020 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _DA1458X_CONFIG_ADVANCED_H_ #define _DA1458X_CONFIG_ADVANCED_H_ #include "da1458x_stack_config.h" #if !defined(__DA14531__) /****************************************************************************************************************/ /* Low Power clock selection. */ /* -LP_CLK_XTAL32 External XTAL32K oscillator */ /* -LP_CLK_RCX20 Internal RCX clock */ /* -LP_CLK_FROM_OTP Use the selection in the corresponding field of OTP Header */ /* */ /* NOTE: Disable CFG_XTAL16M_ADAPTIVE_SETTLING flag when RCX is chosen as the LP clock either from * the OTP */ /* header or from the SDK. */ /****************************************************************************************************************/ #define CFG_LP_CLK LP_CLK_XTAL32 /****************************************************************************************************************/ /* If defined the application uses a hardcoded value for XTAL16M trimming. Should be disabled for * devices */ /* where XTAL16M is calibrated and trim value is stored in OTP. */ /* Important note. The hardcoded value is the average value of the trimming values giving the * optimal results */ /* for DA14585 DK devices. May not be applicable in other designs */ /****************************************************************************************************************/ #define CFG_USE_DEFAULT_XTAL16M_TRIM_VALUE_IF_NOT_CALIBRATED /****************************************************************************************************************/ /* Periodic wakeup period to poll GTL iface. Time in msec. */ /****************************************************************************************************************/ #define CFG_MAX_SLEEP_DURATION_PERIODIC_WAKEUP_MS 500 // 0.5s /****************************************************************************************************************/ /* Periodic wakeup period if GTL iface is not enabled. Time in msec. */ /****************************************************************************************************************/ #define CFG_MAX_SLEEP_DURATION_EXTERNAL_WAKEUP_MS 600000 // 600s /****************************************************************************************************************/ /* Wakeup from external processor running host application. */ /****************************************************************************************************************/ #undef CFG_EXTERNAL_WAKEUP /****************************************************************************************************************/ /* Wakeup external processor when a message is sent to GTL */ /****************************************************************************************************************/ #undef CFG_WAKEUP_EXT_PROCESSOR /****************************************************************************************************************/ /* Enables True Random Number Generator. A true random number, generated at system initialization, * is used to */ /* seed any random number generator (C standard library, ChaCha20, etc.). The following supported * options */ /* provide a trade-off between code size and start-up latency. */ /* - undefined (or 0): TRNG is disabled. */ /* - 32: Enables TRNG with 32 Bytes Buffer. */ /* - 64: Enables TRNG with 64 Bytes Buffer. */ /* - 128: Enables TRNG with 128 Bytes Buffer. */ /* - 256: Enables TRNG with 256 Bytes Buffer. */ /* - 512: Enables TRNG with 512 Bytes Buffer. */ /* - 1024: Enables TRNG with 1024 Bytes Buffer. */ /****************************************************************************************************************/ #define CFG_TRNG (1024) /****************************************************************************************************************/ /* Creation of private and public keys using Elliptic Curve Diffie Hellman algorithms. */ /* - defined: Creation of ECDH keys and secure connection feature is enabled. */ /* - undefined: Creation of ECDH keys and secure connection feature is disabled. If application * does not */ /* support secure connections, it is recommended to undefine CFG_ENABLE_SMP_SECURE in * order to */ /* enable faster start-up time and reduce code size. */ /****************************************************************************************************************/ #define CFG_ENABLE_SMP_SECURE /****************************************************************************************************************/ /* Uses ChaCha20 random number generator instead of the C standard library random number * generator. */ /****************************************************************************************************************/ #undef CFG_USE_CHACHA20_RAND /****************************************************************************************************************/ /* Custom heap sizes */ /****************************************************************************************************************/ // #define DB_HEAP_SZ 1024 // #define ENV_HEAP_SZ 4928 // #define MSG_HEAP_SZ 6880 // #define NON_RET_HEAP_SZ 2048 /****************************************************************************************************************/ /* NVDS configuration */ /* - CFG_NVDS_TAG_BD_ADDRESS Default bdaddress. If bdaddress is written in OTP header * this value is */ /* ignored */ /* - CFG_NVDS_TAG_LPCLK_DRIFT Low power clock drift. Permitted values in ppm are: */ /* + DRIFT_20PPM */ /* + DRIFT_30PPM */ /* + DRIFT_50PPM */ /* + DRIFT_75PPM */ /* + DRIFT_100PPM */ /* + DRIFT_150PPM */ /* + DRIFT_250PPM */ /* + DRIFT_500PPM Default value (500 ppm) */ /* - CFG_NVDS_TAG_BLE_CA_TIMER_DUR Channel Assessment Timer duration (Multiple of 10ms) */ /* - CFG_NVDS_TAG_BLE_CRA_TIMER_DUR Channel Reassessment Timer duration (Multiple of CA timer * duration) */ /* - CFG_NVDS_TAG_BLE_CA_MIN_RSSI Minimum RSSI Threshold */ /* - CFG_NVDS_TAG_BLE_CA_NB_PKT Number of packets to receive for statistics */ /* - CFG_NVDS_TAG_BLE_CA_NB_BAD_PKT Number of bad packets needed to remove a channel */ /****************************************************************************************************************/ #define CFG_NVDS_TAG_BD_ADDRESS { 0x01, 0x00, 0x70, 0xCA, 0xEA, 0x80 } #define CFG_NVDS_TAG_LPCLK_DRIFT DRIFT_500PPM #define CFG_NVDS_TAG_BLE_CA_TIMER_DUR 2000 #define CFG_NVDS_TAG_BLE_CRA_TIMER_DUR 6 #define CFG_NVDS_TAG_BLE_CA_MIN_RSSI 0x40 #define CFG_NVDS_TAG_BLE_CA_NB_PKT 100 #define CFG_NVDS_TAG_BLE_CA_NB_BAD_PKT 50 /****************************************************************************************************************/ /* Enables the logging of heap memories usage. The feature can be used in development/debug mode. */ /* Application must be executed in Keil debugger environment and "da14585_586.lib" must be * replaced with */ /* "da14585_586_with_heap_logging.lib" in project structure under sdk_arch. Developer must stop * execution */ /* and type disp_heaplog() in debugger's command window. Heap memory statistics will be displayed * on window */ /****************************************************************************************************************/ #undef CFG_LOG_HEAP_USAGE /****************************************************************************************************************/ /* Enables the BLE statistics measurement feature. */ /****************************************************************************************************************/ #undef CFG_BLE_METRICS /****************************************************************************************************************/ /* Output the Hardfault arguments to serial/UART interface. */ /****************************************************************************************************************/ #undef CFG_PRODUCTION_DEBUG_OUTPUT /****************************************************************************************************************/ /* Maximum supported TX data packet length (supportedMaxTxOctets value, as defined in 4.2 * Specification). */ /* Range: 27 - 251 octets. */ /* NOTE 1: Even number of octets are not supported. A selected even number will be automatically * converted to */ /* the next odd one. */ /* NOTE 2: The supportedMaxTxTime value is automatically calculated by the ROM code, according to * the following */ /* equation: */ /* supportedMaxTxTime = (supportedMaxTxOctets + 11 + 3 ) * 8 */ /* Range: 328 - 2120 usec. */ /****************************************************************************************************************/ #define CFG_MAX_TX_PACKET_LENGTH (251) /****************************************************************************************************************/ /* Maximum supported RX data packet length (supportedMaxRxOctets value, as defined in 4.2 * Specification). */ /* Range: 27 - 251 octets. */ /* NOTE 1: Even number of octets are not supported. A selected even number will be automatically * converted to */ /* the next odd one. */ /* NOTE 2: The supportedMaxRxTime value is automatically calculated by the ROM code, according to * the following */ /* equation: */ /* supportedMaxRxTime = (supportedMaxRxOctets + 11 + 3 ) * 8 */ /* Range: 328 - 2120 usec. */ /****************************************************************************************************************/ #define CFG_MAX_RX_PACKET_LENGTH (251) /****************************************************************************************************************/ /* Select external application/host transport layer: */ /* - 0 = GTL (auto) */ /* - 1 = HCI (auto) */ /* - 8 = GTL (fixed) */ /* - 9 = HCI (fixed) */ /****************************************************************************************************************/ #define CFG_USE_H4TL (0) /****************************************************************************************************************/ /* Duplicate filter max value for the scan report list. The maximum value shall be 100. */ /****************************************************************************************************************/ #define CFG_BLE_DUPLICATE_FILTER_MAX (10) /****************************************************************************************************************/ /* Duplicate filter flag for the scan report list. This flag controls what will be reported if the */ /* CFG_BLE_DUPLICATE_FILTER_MAX number is exceeded. */ /* - If the flag is defined, the extra devices are considered to be in the list and will not * be reported. */ /****************************************************************************************************************/ #undef CFG_BLE_DUPLICATE_FILTER_FOUND /****************************************************************************************************************/ /* Resolving list maximum size. */ /****************************************************************************************************************/ #define CFG_LLM_RESOLVING_LIST_MAX LLM_RESOLVING_LIST_MAX /****************************************************************************************************************/ /* Enables automatic data packet length negotiation. */ /* NOTE: Enable only if peer device supports data length extension!! */ /****************************************************************************************************************/ #undef AUTO_DATA_LENGTH_NEGOTIATION_UPON_NEW_CONNECTION /****************************************************************************************************************/ /* Maximum retention memory in bytes. The base address of the retention data is calculated from * the selected */ /* size. */ /****************************************************************************************************************/ #define CFG_RET_DATA_SIZE (2048) /****************************************************************************************************************/ /* Maximum uninitialized retained data required by the application. */ /****************************************************************************************************************/ #define CFG_RET_DATA_UNINIT_SIZE (64) /****************************************************************************************************************/ /* The Keil scatter file may be provided by the user. If the user provides his own scatter file, * the system has */ /* to be aware which RAM blocks has to retain. The 4th RAM block is always retained, since it * contains the ROM */ /* data. */ /* - CFG_RETAIN_RAM_1_BLOCK: if defined, the 1st RAM block must be retained. */ /* - CFG_RETAIN_RAM_2_BLOCK: if defined, the 2nd RAM block must be retained. */ /* - CFG_RETAIN_RAM_3_BLOCK: if defined, the 3rd RAM block must be retained. */ /* */ /* If the CFG_CUSTOM_SCATTER_FILE flag is undefined, the system knows which blocks to retain based * on the */ /* default SDK scatter file. */ /****************************************************************************************************************/ #undef CFG_CUSTOM_SCATTER_FILE #ifdef CFG_CUSTOM_SCATTER_FILE #define CFG_RETAIN_RAM_1_BLOCK #define CFG_RETAIN_RAM_2_BLOCK #define CFG_RETAIN_RAM_3_BLOCK #endif /****************************************************************************************************************/ /* Code location selection. */ /* - CFG_CODE_LOCATION_EXT: Code is loaded from SPI flash / I2C EEPROM / UART */ /* - CFG_CODE_LOCATION_OTP: Code is burned in the OTP */ /* The above options are mutually exclusive and exactly one of them must be enabled. */ /****************************************************************************************************************/ #define CFG_CODE_LOCATION_EXT #undef CFG_CODE_LOCATION_OTP /****************************************************************************************************************/ /* Temperature range selection. */ /* - CFG_HIGH_TEMPERATURE: Device is configured to operate at high temperature range (-40C * to +105C). */ /* - CFG_AMB_TEMPERATURE: Device is configured to operate at ambient temperature range * (-40C to +40C). */ /* - CFG_MID_TEMPERATURE: Device is configured to operate at mid temperature range (-40C * to +60C). */ /* - CFG_EXT_TEMPERATURE: Device is configured to operate at ext temperature range (-40C * to +85C). */ /* NOTE 1: High temperature support is not compatible with power optimizations. User shall * undefine the */ /* CFG_POWER_OPTIMIZATIONS flag, if device is to support the high temperature range * feature. */ /****************************************************************************************************************/ #define CFG_AMB_TEMPERATURE /****************************************************************************************************************/ /* Enable power optimizations using the XTAL16M adaptive settling algorithm. */ /* NOTE: The XTAL16M adaptive settling algorithm works only with XTAL32K and not with RCX, as the * LP clock. */ /****************************************************************************************************************/ #define CFG_XTAL16M_ADAPTIVE_SETTLING #else /****************************************************************************************************************/ /* Low Power clock selection. */ /* -LP_CLK_XTAL32 External XTAL32K oscillator */ /* -LP_CLK_RCX20 Internal RCX clock */ /* -LP_CLK_FROM_OTP Use the selection in the corresponding field of OTP Header */ /****************************************************************************************************************/ #define CFG_LP_CLK LP_CLK_RCX20 /****************************************************************************************************************/ /* Periodic wakeup period to poll GTL iface. Time in msec. */ /****************************************************************************************************************/ #define CFG_MAX_SLEEP_DURATION_PERIODIC_WAKEUP_MS 500 // 0.5s /****************************************************************************************************************/ /* Periodic wakeup period if GTL iface is not enabled. Time in msec. */ /****************************************************************************************************************/ #define CFG_MAX_SLEEP_DURATION_EXTERNAL_WAKEUP_MS 600000 // 600s /****************************************************************************************************************/ /* Wakeup from external processor running host application. */ /****************************************************************************************************************/ #undef CFG_EXTERNAL_WAKEUP /****************************************************************************************************************/ /* Wakeup external processor when a message is sent to GTL */ /****************************************************************************************************************/ #undef CFG_WAKEUP_EXT_PROCESSOR /****************************************************************************************************************/ /* Enables True Random Number Generator. A true random number, generated at system initialization, * is used to */ /* seed any random number generator (C standard library, ChaCha20, etc.). */ /****************************************************************************************************************/ #define CFG_TRNG /****************************************************************************************************************/ /* Creation of private and public keys using Elliptic Curve Diffie Hellman algorithms. */ /* - defined: Creation of ECDH keys and secure connection feature is enabled. */ /* - undefined: Creation of ECDH keys and secure connection feature is disabled. If application * does not */ /* support secure connections, it is recommended to undefine CFG_ENABLE_SMP_SECURE in * order to */ /* enable faster start-up time and reduce code size. */ /****************************************************************************************************************/ #undef CFG_ENABLE_SMP_SECURE /****************************************************************************************************************/ /* Uses ChaCha20 random number generator instead of the C standard library random number * generator. */ /****************************************************************************************************************/ #undef CFG_USE_CHACHA20_RAND /****************************************************************************************************************/ /* Custom heap sizes */ /****************************************************************************************************************/ // #define DB_HEAP_SZ 1024 // #define ENV_HEAP_SZ 4928 // #define MSG_HEAP_SZ 6880 // #define NON_RET_HEAP_SZ 2048 /****************************************************************************************************************/ /* NVDS configuration */ /* - CFG_NVDS_TAG_BD_ADDRESS Default bdaddress. If bdaddress is written in OTP header * this value is */ /* ignored */ /* - CFG_NVDS_TAG_LPCLK_DRIFT Low power clock drift. Permitted values in ppm are: */ /* + DRIFT_20PPM */ /* + DRIFT_30PPM */ /* + DRIFT_50PPM */ /* + DRIFT_75PPM */ /* + DRIFT_100PPM */ /* + DRIFT_150PPM */ /* + DRIFT_250PPM */ /* + DRIFT_500PPM Default value (500 ppm) */ /* - CFG_NVDS_TAG_BLE_CA_TIMER_DUR Channel Assessment Timer duration (Multiple of 10ms) */ /* - CFG_NVDS_TAG_BLE_CRA_TIMER_DUR Channel Reassessment Timer duration (Multiple of CA timer * duration) */ /* - CFG_NVDS_TAG_BLE_CA_MIN_RSSI Minimum RSSI Threshold */ /* - CFG_NVDS_TAG_BLE_CA_NB_PKT Number of packets to receive for statistics */ /* - CFG_NVDS_TAG_BLE_CA_NB_BAD_PKT Number of bad packets needed to remove a channel */ /****************************************************************************************************************/ #define CFG_NVDS_TAG_BD_ADDRESS { 0x01, 0x00, 0x70, 0xCA, 0xEA, 0x80 } #define CFG_NVDS_TAG_LPCLK_DRIFT DRIFT_500PPM #define CFG_NVDS_TAG_BLE_CA_TIMER_DUR 2000 #define CFG_NVDS_TAG_BLE_CRA_TIMER_DUR 6 #define CFG_NVDS_TAG_BLE_CA_MIN_RSSI 0x40 #define CFG_NVDS_TAG_BLE_CA_NB_PKT 100 #define CFG_NVDS_TAG_BLE_CA_NB_BAD_PKT 50 /****************************************************************************************************************/ /* Enables the logging of heap memories usage. The feature can be used in development/debug mode. */ /* Application must be executed in Keil debugger environment and "da14531.lib" must be replaced * with */ /* "da14531_with_heap_logging.lib" in project structure under sdk_arch. Developer must stop * execution */ /* and type disp_heaplog() in debugger's command window. Heap memory statistics will be displayed * on window */ /****************************************************************************************************************/ #undef CFG_LOG_HEAP_USAGE /****************************************************************************************************************/ /* Enables the BLE statistics measurement feature. */ /****************************************************************************************************************/ #undef CFG_BLE_METRICS /****************************************************************************************************************/ /* Output the Hardfault arguments to serial/UART interface. */ /****************************************************************************************************************/ #undef CFG_PRODUCTION_DEBUG_OUTPUT /****************************************************************************************************************/ /* Maximum supported TX data packet length (supportedMaxTxOctets value, as defined in 4.2 * Specification). */ /* Range: 27 - 251 octets. */ /* NOTE 1: Even number of octets are not supported. A selected even number will be automatically * converted to */ /* the next odd one. */ /* NOTE 2: The supportedMaxTxTime value is automatically calculated by the ROM code, according to * the following */ /* equation: */ /* supportedMaxTxTime = (supportedMaxTxOctets + 11 + 3 ) * 8 */ /* Range: 328 - 2120 usec. */ /****************************************************************************************************************/ #define CFG_MAX_TX_PACKET_LENGTH (251) /****************************************************************************************************************/ /* Maximum supported RX data packet length (supportedMaxRxOctets value, as defined in 4.2 * Specification). */ /* Range: 27 - 251 octets. */ /* NOTE 1: Even number of octets are not supported. A selected even number will be automatically * converted to */ /* the next odd one. */ /* NOTE 2: The supportedMaxRxTime value is automatically calculated by the ROM code, according to * the following */ /* equation: */ /* supportedMaxRxTime = (supportedMaxRxOctets + 11 + 3 ) * 8 */ /* Range: 328 - 2120 usec. */ /****************************************************************************************************************/ #define CFG_MAX_RX_PACKET_LENGTH (251) /****************************************************************************************************************/ /* Select external application/host transport layer: */ /* - 0 = GTL (auto) */ /* - 1 = HCI (auto) */ /* - 8 = GTL (fixed) */ /* - 9 = HCI (fixed) */ /****************************************************************************************************************/ #define CFG_USE_H4TL (0) /****************************************************************************************************************/ /* Duplicate filter max value for the scan report list. The maximum value shall be 100. */ /****************************************************************************************************************/ #define CFG_BLE_DUPLICATE_FILTER_MAX (10) /****************************************************************************************************************/ /* Duplicate filter flag for the scan report list. This flag controls what will be reported if the */ /* CFG_BLE_DUPLICATE_FILTER_MAX number is exceeded. */ /* - If the flag is defined, the extra devices are considered to be in the list and will not * be reported. */ /****************************************************************************************************************/ #undef CFG_BLE_DUPLICATE_FILTER_FOUND /****************************************************************************************************************/ /* Resolving list maximum size. */ /****************************************************************************************************************/ #define CFG_LLM_RESOLVING_LIST_MAX LLM_RESOLVING_LIST_MAX /****************************************************************************************************************/ /* Enables automatic data packet length negotiation. */ /* NOTE: Enable only if peer device supports data length extension!! */ /****************************************************************************************************************/ #undef AUTO_DATA_LENGTH_NEGOTIATION_UPON_NEW_CONNECTION /****************************************************************************************************************/ /* Maximum retention memory in bytes. The base address of the retention data is calculated from * the selected */ /* size. */ /****************************************************************************************************************/ #define CFG_RET_DATA_SIZE (2048) /****************************************************************************************************************/ /* Maximum uninitialized retained data required by the application. */ /****************************************************************************************************************/ // Note: There's two possible allocations we need here for Memfault // 1. MEMFAULT_REBOOT_TRACKING_REGION_SIZE (64 bytes) - used to hold info about resets taking // place // 2. MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE if and only if // MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_RAM=1 // We recommend storing coredumps to flash since there is more space typically available #define CFG_RET_DATA_UNINIT_SIZE (64) /****************************************************************************************************************/ /* The Keil scatter file may be provided by the user. If the user provides his own scatter file, * the system has */ /* to be aware which RAM blocks has to retain. The 3rd RAM block is always retained, since it * contains the ROM */ /* data. */ /* - CFG_RETAIN_RAM_1_BLOCK: if defined, the 1st RAM block must be retained. */ /* - CFG_RETAIN_RAM_2_BLOCK: if defined, the 2nd RAM block must be retained. */ /* */ /* If the CFG_CUSTOM_SCATTER_FILE flag is undefined, the system knows which blocks to retain based * on the */ /* default SDK scatter file. */ /****************************************************************************************************************/ #undef CFG_CUSTOM_SCATTER_FILE #ifdef CFG_CUSTOM_SCATTER_FILE #define CFG_RETAIN_RAM_1_BLOCK #define CFG_RETAIN_RAM_2_BLOCK #endif /****************************************************************************************************************/ /* Code location selection. */ /* - CFG_CODE_LOCATION_EXT: Code is loaded from SPI flash / I2C EEPROM / UART */ /* - CFG_CODE_LOCATION_OTP: Code is burned in the OTP */ /* The above options are mutually exclusive and exactly one of them must be enabled. */ /****************************************************************************************************************/ #define CFG_CODE_LOCATION_EXT #undef CFG_CODE_LOCATION_OTP /****************************************************************************************************************/ /* Temperature range selection. */ /* - CFG_HIGH_TEMPERATURE: Device is configured to operate at high temperature range (-40C * to +105C). */ /* - CFG_AMB_TEMPERATURE: Device is configured to operate at ambient temperature range * (-40C to +40C). */ /* - CFG_MID_TEMPERATURE: Device is configured to operate at mid temperature range (-40C * to +60C). */ /* - CFG_EXT_TEMPERATURE: Device is configured to operate at ext temperature range (-40C * to +85C). */ /****************************************************************************************************************/ #define CFG_AMB_TEMPERATURE /****************************************************************************************************************/ /* Disable quadrature decoder on start up. The quadrature decoder is by default enabled on system * power up and */ /* it may count events. This leads to WKUP_QUADEC_IRQn pending interrupts. */ /****************************************************************************************************************/ #define CFG_DISABLE_QUADEC_ON_START_UP #endif #endif // _DA1458X_CONFIG_ADVANCED_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/da1458x_config_basic.h ================================================ /** **************************************************************************************** * * @file da1458x_config_basic.h * * @brief Basic compile configuration file. * * Copyright (C) 2014-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _DA1458X_CONFIG_BASIC_H_ #define _DA1458X_CONFIG_BASIC_H_ #include "da1458x_stack_config.h" #include "user_profiles_config.h" #if !defined(__DA14531__) /***************************************************************************************************************/ /* Integrated or external processor configuration */ /* -defined Integrated processor mode. Host application runs in DA14585 processor. Host * application */ /* is the TASK_APP kernel task. */ /* -undefined External processor mode. Host application runs on an external processor. * Communicates with */ /* BLE application through GTL protocol over a signalling iface (UART, SPI etc) */ /***************************************************************************************************************/ #define CFG_APP /****************************************************************************************************************/ /* Enables the BLE security functionality in TASK_APP. If not defined BLE security related code is * compiled out.*/ /****************************************************************************************************************/ #undef CFG_APP_SECURITY /****************************************************************************************************************/ /* Enables WatchDog timer. */ /****************************************************************************************************************/ #define CFG_WDOG /****************************************************************************************************************/ /* Watchdog timer behavior in production mode: */ /* Flag is not defined: Watchdog timer generates NMI at value 0. */ /* Flag is defined : Watchdog timer generates a WDOG (SYS) reset at value 0. */ /****************************************************************************************************************/ #undef CFG_WDG_TRIGGER_HW_RESET_IN_PRODUCTION_MODE /****************************************************************************************************************/ /* Determines maximum concurrent connections supported by application. It configures the heap * memory allocated */ /* to service multiple connections. It is used for GAP central role applications. For GAP * peripheral role it */ /* should be set to 1 for optimizing memory utilization. */ /* - MAX value for DA14585: 8 */ /****************************************************************************************************************/ #define CFG_MAX_CONNECTIONS (1) /****************************************************************************************************************/ /* Enables development/debug mode. For production mode builds it must be disabled. */ /* When enabled the following debugging features are enabled */ /* - Allows the emulation of the OTP mirroring to System RAM. No actual writing to RAM is * done, but the */ /* exact same amount of time is spend as if the mirroring would take place. This is to * mimic the */ /* behavior as if the System Code is already in OTP, and the mirroring takes place after * waking up, */ /* but the (development) code still resides in an external source. */ /* - Validation of GPIO reservations. */ /* - Enables Debug module and sets code execution in breakpoint in Hardfault and NMI * (Watchdog) handlers.*/ /* It allows developer to hot attach debugger and get debug information */ /****************************************************************************************************************/ #define CFG_DEVELOPMENT_DEBUG /****************************************************************************************************************/ /* UART Console Print. If CFG_PRINTF is defined, serial interface logging mechanism will be * enabled. */ /* If CFG_PRINTF_UART2 is defined, then serial interface logging mechanism is implemented using * UART2, else UART1 */ /* will be used. */ /****************************************************************************************************************/ #define CFG_PRINTF #ifdef CFG_PRINTF #define CFG_PRINTF_UART2 #endif /****************************************************************************************************************/ /* UART1 Driver Implementation. If CFG_UART1_SDK is defined, UART1 ROM driver will be overridden * and UART SDK */ /* driver will be used, else ROM driver will be used for UART1 module. */ /****************************************************************************************************************/ #undef CFG_UART1_SDK /****************************************************************************************************************/ /* Select external memory device for data storage */ /* SPI FLASH (#define CFG_SPI_FLASH_ENABLE) */ /* I2C EEPROM (#define CFG_I2C_EEPROM_ENABLE) */ /****************************************************************************************************************/ #undef CFG_SPI_FLASH_ENABLE #undef CFG_I2C_EEPROM_ENABLE /****************************************************************************************************************/ /* Enables/Disables the DMA Support for the following interfaces: */ /* - UART */ /* - SPI */ /* - I2C */ /****************************************************************************************************************/ #undef CFG_UART_DMA_SUPPORT #undef CFG_SPI_DMA_SUPPORT #undef CFG_I2C_DMA_SUPPORT #else /***************************************************************************************************************/ /* Integrated or external processor configuration */ /* -defined Integrated processor mode. Host application runs in DA14585 processor. Host * application */ /* is the TASK_APP kernel task. */ /* -undefined External processor mode. Host application runs on an external processor. * Communicates with */ /* BLE application through GTL protocol over a signalling iface (UART, SPI etc) */ /***************************************************************************************************************/ #define CFG_APP /****************************************************************************************************************/ /* Enables the BLE security functionality in TASK_APP. If not defined BLE security related code is * compiled out.*/ /****************************************************************************************************************/ #undef CFG_APP_SECURITY /****************************************************************************************************************/ /* Enables WatchDog timer. */ /****************************************************************************************************************/ #define CFG_WDOG /****************************************************************************************************************/ /* Watchdog timer behavior in production mode: */ /* Flag is not defined: Watchdog timer generates NMI at value 0. */ /* Flag is defined : Watchdog timer generates a WDOG (SYS) reset at value 0. */ /****************************************************************************************************************/ #undef CFG_WDG_TRIGGER_HW_RESET_IN_PRODUCTION_MODE /****************************************************************************************************************/ /* Determines maximum concurrent connections supported by application. It configures the heap * memory allocated */ /* to service multiple connections. It is used for GAP central role applications. For GAP * peripheral role it */ /* should be set to 1 for optimizing memory utilization. */ /* - MAX value for DA14531: 3 */ /****************************************************************************************************************/ #define CFG_MAX_CONNECTIONS (1) /****************************************************************************************************************/ /* Enables development/debug mode. For production mode builds it must be disabled. */ /* When enabled the following debugging features are enabled */ /* - Allows the emulation of the OTP mirroring to System RAM. No actual writing to RAM is * done, but the */ /* exact same amount of time is spend as if the mirroring would take place. This is to * mimic the */ /* behavior as if the System Code is already in OTP, and the mirroring takes place after * waking up, */ /* but the (development) code still resides in an external source. */ /* - Validation of GPIO reservations. */ /* - Enables Debug module and sets code execution in breakpoint in Hardfault and NMI * (Watchdog) handlers.*/ /* It allows developer to hot attach debugger and get debug information */ /****************************************************************************************************************/ #define CFG_DEVELOPMENT_DEBUG /****************************************************************************************************************/ /* UART Console Print. If CFG_PRINTF is defined, serial interface logging mechanism will be * enabled. */ /* If CFG_PRINTF_UART2 is defined, then serial interface logging mechanism is implemented using * UART2, else UART1 */ /* will be used. */ /****************************************************************************************************************/ #define CFG_PRINTF #ifdef CFG_PRINTF #define CFG_PRINTF_UART2 #endif /****************************************************************************************************************/ /* UART1 Driver Implementation. If CFG_UART1_SDK is defined, UART1 ROM driver will be overridden * and UART SDK */ /* driver will be used, else ROM driver will be used for UART1 module. */ /****************************************************************************************************************/ #undef CFG_UART1_SDK /****************************************************************************************************************/ /* Select external memory device for data storage */ /* SPI FLASH (#define CFG_SPI_FLASH_ENABLE) */ /* I2C EEPROM (#define CFG_I2C_EEPROM_ENABLE) */ /****************************************************************************************************************/ #undef CFG_SPI_FLASH_ENABLE #undef CFG_I2C_EEPROM_ENABLE /****************************************************************************************************************/ /* Enables/Disables the DMA Support for the following interfaces: */ /* - UART */ /* - SPI */ /* - I2C */ /* - ADC */ /****************************************************************************************************************/ #undef CFG_UART_DMA_SUPPORT #undef CFG_SPI_DMA_SUPPORT #undef CFG_I2C_DMA_SUPPORT #undef CFG_ADC_DMA_SUPPORT /****************************************************************************************************************/ /* Notify the SDK about the fixed power mode (currently used only for Bypass): */ /* - CFG_POWER_MODE_BYPASS = Bypass mode */ /****************************************************************************************************************/ #undef CFG_POWER_MODE_BYPASS #endif #endif // _DA1458X_CONFIG_BASIC_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/memfault_metrics_heartbeat_config.def ================================================ //! Define custom system metrics to track. For example, // MEMFAULT_METRICS_KEY_DEFINE(main_task_stack_hwm, kMemfaultMetricType_Unsigned) // MEMFAULT_METRICS_KEY_DEFINE(ble_min_rssi, kMemfaultMetricType_Signed) // MEMFAULT_METRICS_KEY_DEFINE(mcu_sleep_time_ms, kMemfaultMetricType_Timer) //! @file memfault_metrics_heartbeat_config.def MEMFAULT_METRICS_KEY_DEFINE(MainTaskWakeups, kMemfaultMetricType_Unsigned) ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" #ifdef __cplusplus extern "C" { #endif // Save coredumps to external flash. #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH 1 #define MEMFAULT_COREDUMP_STORAGE_START_ADDR 0x20000 #define MEMFAULT_COREDUMP_STORAGE_END_ADDR 0x30000 #define MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL 1 // Allows for log buffer to be captured outside of coredump. Leave disabled for now to save a // little codespace #define MEMFAULT_LOG_DATA_SOURCE_ENABLED 0 // For example, decide if you want to use the Gnu Build ID. #if defined(__GNUC__) #define MEMFAULT_USE_GNU_BUILD_ID 1 #endif #if defined(__DA14531__) // Tune some parameters to save additional RAM space on the DA1531 #define MEMFAULT_DATA_SOURCE_RLE_ENABLED 0 #define MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED 0 #define MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED 0 #define MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED 0 #define MEMFAULT_SDK_LOG_SAVE_DISABLE 1 #endif #ifdef __cplusplus } #endif ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/memfault_trace_reason_user_config.def ================================================ //! Define custom error reasons that can be filtered & searched //! on in the Memfault UI, i.e MEMFAULT_TRACE_REASON_DEFINE(critical_error) ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/user_callback_config.h ================================================ /** **************************************************************************************** * * @file user_callback_config.h * * @brief Callback functions configuration file. * * Copyright (C) 2015-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _USER_CALLBACK_CONFIG_H_ #define _USER_CALLBACK_CONFIG_H_ /* * INCLUDE FILES **************************************************************************************** */ #include "app_api.h" #include "app_bass.h" #include "app_callback.h" #include "app_findme.h" #include "app_prf_types.h" #include "app_proxr.h" #include "app_suotar.h" #if (BLE_APP_SEC) #include "app_bond_db.h" #endif // (BLE_APP_SEC) #include "user_app.h" /* * FUNCTION DECLARATIONS **************************************************************************************** */ /** **************************************************************************************** * @brief Function to be called on the advertising completion event. * @param[in] uint8_t GAP Error code **************************************************************************************** */ void app_advertise_complete(const uint8_t); /** **************************************************************************************** * @brief SUOTAR session start or stop event handler. * @param[in] suotar_event SUOTAR_START/SUOTAR_STOP **************************************************************************************** */ void on_suotar_status_change(const uint8_t suotar_event); /* * LOCAL VARIABLE DEFINITIONS **************************************************************************************** */ #if (BLE_BATT_SERVER) static const struct app_bass_cb user_app_bass_cb = { .on_batt_level_upd_rsp = NULL, .on_batt_level_ntf_cfg_ind = NULL, }; #endif #if (BLE_FINDME_TARGET) static const struct app_findt_cb user_app_findt_cb = { .on_findt_alert_ind = default_findt_alert_ind_handler, }; #endif #if (BLE_PROX_REPORTER) static const struct app_proxr_cb user_app_proxr_cb = { .on_proxr_alert_ind = default_proxr_alert_ind_handler, }; #endif #if (BLE_SUOTA_RECEIVER) static const struct app_suotar_cb user_app_suotar_cb = { .on_suotar_status_change = on_suotar_status_change, }; #endif static const struct app_callbacks user_app_callbacks = { .app_on_connection = user_on_connection, .app_on_disconnect = user_on_disconnect, .app_on_update_params_rejected = NULL, .app_on_update_params_complete = NULL, .app_on_set_dev_config_complete = default_app_on_set_dev_config_complete, .app_on_adv_nonconn_complete = NULL, .app_on_adv_undirect_complete = NULL, .app_on_adv_direct_complete = NULL, .app_on_db_init_complete = default_app_on_db_init_complete, .app_on_scanning_completed = NULL, .app_on_adv_report_ind = NULL, .app_on_get_dev_name = default_app_on_get_dev_name, .app_on_get_dev_appearance = default_app_on_get_dev_appearance, .app_on_get_dev_slv_pref_params = default_app_on_get_dev_slv_pref_params, .app_on_set_dev_info = default_app_on_set_dev_info, .app_on_data_length_change = NULL, .app_on_update_params_request = default_app_update_params_request, .app_on_generate_static_random_addr = default_app_generate_static_random_addr, .app_on_svc_changed_cfg_ind = NULL, .app_on_get_peer_features = NULL, #if (BLE_APP_SEC) .app_on_pairing_request = default_app_on_pairing_request, .app_on_tk_exch = default_app_on_tk_exch, .app_on_irk_exch = NULL, .app_on_csrk_exch = NULL, .app_on_ltk_exch = default_app_on_ltk_exch, .app_on_pairing_succeeded = NULL, .app_on_encrypt_ind = NULL, .app_on_encrypt_req_ind = NULL, .app_on_security_req_ind = NULL, .app_on_addr_solved_ind = NULL, .app_on_addr_resolve_failed = NULL, .app_on_ral_cmp_evt = NULL, .app_on_ral_size_ind = NULL, .app_on_ral_addr_ind = NULL, #endif // (BLE_APP_SEC) }; #if (BLE_APP_SEC) static const struct app_bond_db_callbacks user_app_bond_db_callbacks = { .app_bdb_init = NULL, .app_bdb_get_size = NULL, .app_bdb_add_entry = NULL, .app_bdb_remove_entry = NULL, .app_bdb_search_entry = NULL, .app_bdb_get_number_of_stored_irks = NULL, .app_bdb_get_stored_irks = NULL, .app_bdb_get_device_info_from_slot = NULL, }; #endif // (BLE_APP_SEC) /* * "app_process_catch_rest_cb" symbol handling: * - Use #define if "user_catch_rest_hndl" is defined by the user * - Use const declaration if "user_catch_rest_hndl" is NULL */ // #define app_process_catch_rest_cb user_catch_rest_hndl // static const catch_rest_event_func_t app_process_catch_rest_cb = NULL; #if defined(__CC_ARM) static const catch_rest_event_func_t app_process_catch_rest_cb = NULL; #elif defined(__GNUC__) #define app_process_catch_rest_cb ((const catch_rest_event_func_t)NULL) #endif static const struct default_app_operations user_default_app_operations = { .default_operation_adv = default_advertise_operation, }; static const struct arch_main_loop_callbacks user_app_main_loop_callbacks = { .app_on_init = user_app_on_init, // By default the watchdog timer is reloaded and resumed when the system wakes up. // The user has to take into account the watchdog timer handling (keep it running, // freeze it, reload it, resume it, etc), when the app_on_ble_powered() is being // called and may potentially affect the main loop. .app_on_ble_powered = NULL, // By default the watchdog timer is reloaded and resumed when the system wakes up. // The user has to take into account the watchdog timer handling (keep it running, // freeze it, reload it, resume it, etc), when the app_on_system_powered() is being // called and may potentially affect the main loop. .app_on_system_powered = user_app_on_system_powered, .app_before_sleep = NULL, .app_validate_sleep = NULL, .app_going_to_sleep = NULL, .app_resume_from_sleep = NULL, }; // place in this structure the app__db_create and app__enable functions // for SIG profiles that do not have this function already implemented in the SDK // or if you want to override the functionality. Check the prf_func array in the SDK // for your reference of which profiles are supported. static const struct prf_func_callbacks user_prf_funcs[] = { { TASK_ID_INVALID, NULL, NULL } // DO NOT MOVE. Must always be last }; #endif // _USER_CALLBACK_CONFIG_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/user_config.h ================================================ /** **************************************************************************************** * * @file user_config.h * * @brief User configuration file. * * Copyright (C) 2015-2020 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _USER_CONFIG_H_ #define _USER_CONFIG_H_ /***************************************************************************************/ /* Override SDK based assertion checks with memfault versions allowing logging. */ /***************************************************************************************/ #include "memfault/panics/assert.h" #define ASSERT_ERROR(x) MEMFAULT_ASSERT(x); #define ASSERT_WARNING(x) MEMFAULT_ASSERT(x); /* * INCLUDE FILES **************************************************************************************** */ #include "app_adv_data.h" #include "app_default_handlers.h" #include "app_user_config.h" #include "arch_api.h" #include "co_bt.h" /* * LOCAL VARIABLES **************************************************************************************** */ /* **************************************************************************************** * * Privacy / Addressing configuration * **************************************************************************************** */ /************************************************************************* * Privacy Capabilities and address configuration of local device: * - APP_CFG_ADDR_PUB No Privacy, Public BDA * - APP_CFG_ADDR_STATIC No Privacy, Random Static BDA * - APP_CFG_HOST_PRIV_RPA Host Privacy, RPA, Public Identity * - APP_CFG_HOST_PRIV_NRPA Host Privacy, NRPA (non-connectable ONLY) * - APP_CFG_CNTL_PRIV_RPA_PUB Controller Privacy, RPA or PUB, Public Identity * - APP_CFG_CNTL_PRIV_RPA_RAND Controller Privacy, RPA, Public Identity * * Select only one option for privacy / addressing configuration. ************************************************************************** */ #define USER_CFG_ADDRESS_MODE APP_CFG_ADDR_PUB /************************************************************************* * Controller Privacy Mode: * - APP_CFG_CNTL_PRIV_MODE_NETWORK Controller Privacy Network mode (default) * - APP_CFG_CNTL_PRIV_MODE_DEVICE Controller Privacy Device mode * * Select only one option for controller privacy mode configuration. ************************************************************************** */ #define USER_CFG_CNTL_PRIV_MODE APP_CFG_CNTL_PRIV_MODE_NETWORK /****************************************** * Default sleep mode. Possible values are: * * - ARCH_SLEEP_OFF * - ARCH_EXT_SLEEP_ON * - ARCH_EXT_SLEEP_OTP_COPY_ON * ****************************************** */ static const sleep_state_t app_default_sleep_mode = ARCH_SLEEP_OFF; /* **************************************************************************************** * * Advertising configuration * **************************************************************************************** */ static const struct advertise_configuration user_adv_conf = { .addr_src = APP_CFG_ADDR_SRC(USER_CFG_ADDRESS_MODE), /// Minimum interval for advertising .intv_min = MS_TO_BLESLOTS(687.5), // 687.5ms /// Maximum interval for advertising .intv_max = MS_TO_BLESLOTS(687.5), // 687.5ms /** * Advertising channels map: * - ADV_CHNL_37_EN: Advertising channel map for channel 37. * - ADV_CHNL_38_EN: Advertising channel map for channel 38. * - ADV_CHNL_39_EN: Advertising channel map for channel 39. * - ADV_ALL_CHNLS_EN: Advertising channel map for channel 37, 38 and 39. */ .channel_map = ADV_ALL_CHNLS_EN, /************************* * Advertising information ************************* */ /// Host information advertising data (GAPM_ADV_NON_CONN and GAPM_ADV_UNDIRECT) /// Advertising mode : /// - GAP_NON_DISCOVERABLE: Non discoverable mode /// - GAP_GEN_DISCOVERABLE: General discoverable mode /// - GAP_LIM_DISCOVERABLE: Limited discoverable mode /// - GAP_BROADCASTER_MODE: Broadcaster mode .mode = GAP_GEN_DISCOVERABLE, /// Host information advertising data (GAPM_ADV_NON_CONN and GAPM_ADV_UNDIRECT) /// - ADV_ALLOW_SCAN_ANY_CON_ANY: Allow both scan and connection requests from anyone /// - ADV_ALLOW_SCAN_ANY_CON_WLST: Allow both scan req from anyone and connection req from /// White List devices only .adv_filt_policy = ADV_ALLOW_SCAN_ANY_CON_ANY, /// Address of peer device /// NOTE: Meant for directed advertising (ADV_DIRECT_IND) .peer_addr = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 }, /// Address type of peer device (0=public/1=random) /// NOTE: Meant for directed advertising (ADV_DIRECT_IND) .peer_addr_type = 0, }; /* **************************************************************************************** * * Advertising or scan response data for the following cases: * * - ADV_IND: Connectable undirected advertising event. * - The maximum length of the user defined advertising data shall be 28 bytes. * - The Flags data type are written by the related ROM function, hence the user shall * not include them in the advertising data. The related ROM function adds 3 bytes in * the start of the advertising data that are to be transmitted over the air. * - The maximum length of the user defined response data shall be 31 bytes. * * - ADV_NONCONN_IND: Non-connectable undirected advertising event. * - The maximum length of the user defined advertising data shall be 31 bytes. * - The Flags data type may be omitted, hence the user can use all the 31 bytes for * data. * - The scan response data shall be empty. * * - ADV_SCAN_IND: Scannable undirected advertising event. * - The maximum length of the user defined advertising data shall be 31 bytes. * - The Flags data type may be omitted, hence the user can use all the 31 bytes for * data. * - The maximum length of the user defined response data shall be 31 bytes. **************************************************************************************** */ /// Advertising data #define USER_ADVERTISE_DATA "" /// Advertising data length - maximum 28 bytes, 3 bytes are reserved to set #define USER_ADVERTISE_DATA_LEN (sizeof(USER_ADVERTISE_DATA) - 1) /// Scan response data #define USER_ADVERTISE_SCAN_RESPONSE_DATA "" /// Scan response data length- maximum 31 bytes #define USER_ADVERTISE_SCAN_RESPONSE_DATA_LEN (sizeof(USER_ADVERTISE_SCAN_RESPONSE_DATA) - 1) /* **************************************************************************************** * * Device name. * * - If there is space left in the advertising or scan response data the device name is * copied there. The device name can be anytime read by a connected peer, if the * application supports it. * - The Bluetooth device name can be up to 248 bytes. * **************************************************************************************** */ /// Device name #define USER_DEVICE_NAME "DIALOG-TMPL" /// Device name length #define USER_DEVICE_NAME_LEN (sizeof(USER_DEVICE_NAME) - 1) /* **************************************************************************************** * * GAPM configuration * **************************************************************************************** */ static const struct gapm_configuration user_gapm_conf = { /// Device Role: Central, Peripheral, Observer, Broadcaster or All roles. (@see enum gap_role) .role = GAP_ROLE_PERIPHERAL, /// Maximal MTU. Shall be set to 23 if Legacy Pairing is used, 65 if Secure Connection is used, /// more if required by the application .max_mtu = 23, /// Device Address Type .addr_type = APP_CFG_ADDR_TYPE(USER_CFG_ADDRESS_MODE), /// Duration before regenerate the random private address when privacy is enabled .renew_dur = 15000, // 15000 * 10ms = 150s is the minimum value /*********************** * Privacy configuration *********************** */ /// Private static address // NOTE: The address shall comply with the following requirements: // - the two most significant bits of the address shall be equal to 1, // - all the remaining bits of the address shall NOT be equal to 1, // - all the remaining bits of the address shall NOT be equal to 0. // In case the {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} null address is used, a // random static address will be automatically generated. .addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /// Device IRK used for resolvable random BD address generation (LSB first) .irk = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, /**************************** * ATT database configuration **************************** */ /// Attribute database configuration (@see enum gapm_att_cfg_flag) /// 7 6 5 4 3 2 1 0 /// +-----+-----+----+-----+-----+----+----+----+ /// | DBG | RFU | SC | PCP | APP_PERM |NAME_PERM| /// +-----+-----+----+-----+-----+----+----+----+ /// - Bit [0-1]: Device Name write permission requirements for peer device (@see /// device_name_write_perm) /// - Bit [2-3]: Device Appearance write permission requirements for peer device (@see /// device_appearance_write_perm) /// - Bit [4] : Slave Preferred Connection Parameters present /// - Bit [5] : Service change feature present in GATT attribute database. /// - Bit [6] : Reserved /// - Bit [7] : Enable Debug Mode .att_cfg = GAPM_MASK_ATT_SVC_CHG_EN, /// GAP service start handle .gap_start_hdl = 0, /// GATT service start handle .gatt_start_hdl = 0, /************************************************** * Data packet length extension configuration (4.2) ************************************************** */ /// Maximal MPS .max_mps = 0, /// Maximal Tx octets (connInitialMaxTxOctets value, as defined in 4.2 Specification) .max_txoctets = 0, /// Maximal Tx time (connInitialMaxTxTime value, as defined in 4.2 Specification) .max_txtime = 0, }; /* **************************************************************************************** * * Parameter update configuration * **************************************************************************************** */ static const struct connection_param_configuration user_connection_param_conf = { /// Connection interval minimum measured in ble double slots (1.25ms) /// use the macro MS_TO_DOUBLESLOTS to convert from milliseconds (ms) to double slots .intv_min = MS_TO_DOUBLESLOTS(10), /// Connection interval maximum measured in ble double slots (1.25ms) /// use the macro MS_TO_DOUBLESLOTS to convert from milliseconds (ms) to double slots .intv_max = MS_TO_DOUBLESLOTS(20), /// Latency measured in connection events .latency = 0, /// Supervision timeout measured in timer units (10 ms) /// use the macro MS_TO_TIMERUNITS to convert from milliseconds (ms) to timer units .time_out = MS_TO_TIMERUNITS(1250), /// Minimum Connection Event Duration measured in ble double slots (1.25ms) /// use the macro MS_TO_DOUBLESLOTS to convert from milliseconds (ms) to double slots .ce_len_min = MS_TO_DOUBLESLOTS(0), /// Maximum Connection Event Duration measured in ble double slots (1.25ms) /// use the macro MS_TO_DOUBLESLOTS to convert from milliseconds (ms) to double slots .ce_len_max = MS_TO_DOUBLESLOTS(0), }; /* **************************************************************************************** * * Default handlers configuration (applies only for @app_default_handlers.c) * **************************************************************************************** */ static const struct default_handlers_configuration user_default_hnd_conf = { // Configure the advertise operation used by the default handlers // Possible values: // - DEF_ADV_FOREVER // - DEF_ADV_WITH_TIMEOUT .adv_scenario = DEF_ADV_FOREVER, // Configure the advertise period in case of DEF_ADV_WITH_TIMEOUT. // It is measured in timer units (3 min). Use MS_TO_TIMERUNITS macro to convert // from milliseconds (ms) to timer units. .advertise_period = MS_TO_TIMERUNITS(180000), // Configure the security start operation of the default handlers // if the security is enabled (CFG_APP_SECURITY) // Possible values: // - DEF_SEC_REQ_NEVER // - DEF_SEC_REQ_ON_CONNECT .security_request_scenario = DEF_SEC_REQ_NEVER }; /* **************************************************************************************** * * Central configuration (not used by current example) * **************************************************************************************** */ static const struct central_configuration user_central_conf = { /// GAPM requested operation: /// - GAPM_CONNECTION_DIRECT: Direct connection operation /// - GAPM_CONNECTION_AUTO: Automatic connection operation /// - GAPM_CONNECTION_SELECTIVE: Selective connection operation /// - GAPM_CONNECTION_NAME_REQUEST: Name Request operation (requires to start a direct /// connection) .code = GAPM_CONNECTION_DIRECT, /// Own BD address source of the device: .addr_src = APP_CFG_ADDR_SRC(USER_CFG_ADDRESS_MODE), /// Scan interval .scan_interval = 0x180, /// Scan window size .scan_window = 0x160, /// Minimum of connection interval .con_intv_min = 100, /// Maximum of connection interval .con_intv_max = 100, /// Connection latency .con_latency = 0, /// Link supervision timeout .superv_to = 0x1F4, /// Minimum CE length .ce_len_min = 0, /// Maximum CE length .ce_len_max = 0x5, /************************************************************************************** * Peer device information (maximum number of peers = 8) ************************************************************************************** */ /// BD Address of device .peer_addr_0 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, /// Address type of the device 0=public/1=random .peer_addr_0_type = 0, /// BD Address of device .peer_addr_1 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, /// Address type of the device 0=public/1=random .peer_addr_1_type = 0, /// BD Address of device .peer_addr_2 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, /// Address type of the device 0=public/1=random .peer_addr_2_type = 0, /// BD Address of device .peer_addr_3 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, /// Address type of the device 0=public/1=random .peer_addr_3_type = 0, /// BD Address of device .peer_addr_4 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, /// Address type of the device 0=public/1=random .peer_addr_4_type = 0, /// BD Address of device .peer_addr_5 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, /// Address type of the device 0=public/1=random .peer_addr_5_type = 0, /// BD Address of device .peer_addr_6 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, /// Address type of the device 0=public/1=random .peer_addr_6_type = 0, /// BD Address of device .peer_addr_7 = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, /// Address type of the device 0=public/1=random .peer_addr_7_type = 0, }; /* **************************************************************************************** * * Security related configuration * **************************************************************************************** */ static const struct security_configuration user_security_conf = { // IO Capabilities #if defined(USER_CFG_FEAT_IO_CAP) .iocap = USER_CFG_FEAT_IO_CAP, #else .iocap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT, #endif // OOB Capabilities #if defined(USER_CFG_FEAT_OOB) .oob = USER_CFG_FEAT_OOB, #else .oob = GAP_OOB_AUTH_DATA_NOT_PRESENT, #endif // Authentication Requirements #if defined(USER_CFG_FEAT_AUTH_REQ) .auth = USER_CFG_FEAT_AUTH_REQ, #else .auth = GAP_AUTH_NONE, #endif // LTK size #if defined(USER_CFG_FEAT_KEY_SIZE) .key_size = USER_CFG_FEAT_KEY_SIZE, #else .key_size = KEY_LEN, #endif // Initiator key distribution #if defined(USER_CFG_FEAT_INIT_KDIST) .ikey_dist = USER_CFG_FEAT_INIT_KDIST, #else .ikey_dist = GAP_KDIST_NONE, #endif // Responder key distribution #if defined(USER_CFG_FEAT_RESP_KDIST) .rkey_dist = USER_CFG_FEAT_RESP_KDIST, #else .rkey_dist = GAP_KDIST_ENCKEY, #endif // Security requirements (minimum security level) #if defined(USER_CFG_FEAT_SEC_REQ) .sec_req = USER_CFG_FEAT_SEC_REQ, #else .sec_req = GAP_NO_SEC, #endif }; #endif // _USER_CONFIG_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/user_modules_config.h ================================================ /** **************************************************************************************** * * @file user_modules_config.h * * @brief User modules configuration file. * * Copyright (C) 2015-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _USER_MODULES_CONFIG_H_ #define _USER_MODULES_CONFIG_H_ /** **************************************************************************************** * @addtogroup APP * @ingroup RICOW * * @brief User modules configuration. * * @{ **************************************************************************************** */ /* * DEFINES **************************************************************************************** */ /***************************************************************************************/ /* Exclude or not a module in user's application code. */ /* */ /* (0) - The module is included. The module's messages are handled by the SDK. */ /* */ /* (1) - The module is excluded. The user must handle the module's messages. */ /* */ /* Note: */ /* This setting has no effect if the respective module is a BLE Profile */ /* that is not used included in the user's application. */ /***************************************************************************************/ #define EXCLUDE_DLG_GAP (0) #define EXCLUDE_DLG_TIMER (0) #define EXCLUDE_DLG_MSG (0) #define EXCLUDE_DLG_SEC (0) #define EXCLUDE_DLG_DISS (0) #define EXCLUDE_DLG_PROXR (0) #define EXCLUDE_DLG_BASS (0) #define EXCLUDE_DLG_FINDL (0) #define EXCLUDE_DLG_FINDT (0) #define EXCLUDE_DLG_SUOTAR (0) #define EXCLUDE_DLG_CUSTS1 (0) #define EXCLUDE_DLG_CUSTS2 (0) /// @} APP #endif // _USER_MODULES_CONFIG_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/user_periph_setup.h ================================================ /** **************************************************************************************** * * @file user_periph_setup.h * * @brief Peripherals setup header file. * * Copyright (C) 2015-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _USER_PERIPH_SETUP_H_ #define _USER_PERIPH_SETUP_H_ /* * INCLUDE FILES **************************************************************************************** */ #include "arch.h" #include "gpio.h" #include "uart.h" /* * DEFINES **************************************************************************************** */ /****************************************************************************************/ /* UART2 configuration to use with arch_console print messages */ /****************************************************************************************/ // Define UART2 Tx Pad #if defined(__DA14531__) #define UART2_TX_PORT GPIO_PORT_0 #define UART2_TX_PIN GPIO_PIN_5 #define UART2_RX_PORT GPIO_PORT_0 #define UART2_RX_PIN GPIO_PIN_6 #else #define UART2_TX_PORT GPIO_PORT_2 #define UART2_TX_PIN GPIO_PIN_5 #define UART2_RX_PORT GPIO_PORT_2 #define UART2_RX_PIN GPIO_PIN_6 #endif // Define UART2 Settings #define UART2_BAUDRATE UART_BAUDRATE_115200 #define UART2_DATABITS UART_DATABITS_8 #define UART2_PARITY UART_PARITY_NONE #define UART2_STOPBITS UART_STOPBITS_1 #define UART2_AFCE UART_AFCE_DIS #define UART2_FIFO UART_FIFO_EN #define UART2_TX_FIFO_LEVEL UART_TX_FIFO_LEVEL_0 #define UART2_RX_FIFO_LEVEL UART_RX_FIFO_LEVEL_0 /****************************************************************************************/ /* SPI configuration */ /****************************************************************************************/ #if defined(__DA14531__) #define SPI_EN_PORT GPIO_PORT_0 #define SPI_EN_PIN GPIO_PIN_1 #define SPI_CLK_PORT GPIO_PORT_0 #define SPI_CLK_PIN GPIO_PIN_4 #define SPI_DO_PORT GPIO_PORT_0 #define SPI_DO_PIN GPIO_PIN_0 #define SPI_DI_PORT GPIO_PORT_0 #define SPI_DI_PIN GPIO_PIN_3 #else #define SPI_EN_PORT GPIO_PORT_0 #define SPI_EN_PIN GPIO_PIN_3 #define SPI_CLK_PORT GPIO_PORT_0 #define SPI_CLK_PIN GPIO_PIN_0 #define SPI_DO_PORT GPIO_PORT_0 #define SPI_DO_PIN GPIO_PIN_6 #define SPI_DI_PORT GPIO_PORT_0 #define SPI_DI_PIN GPIO_PIN_5 #endif /***************************************************************************************/ /* Production debug output configuration */ /***************************************************************************************/ #if PRODUCTION_DEBUG_OUTPUT #if defined(__DA14531__) #define PRODUCTION_DEBUG_PORT GPIO_PORT_0 #define PRODUCTION_DEBUG_PIN GPIO_PIN_11 #else #define PRODUCTION_DEBUG_PORT GPIO_PORT_2 #define PRODUCTION_DEBUG_PIN GPIO_PIN_5 #endif #endif /* * FUNCTION DECLARATIONS **************************************************************************************** */ #if DEVELOPMENT_DEBUG /** **************************************************************************************** * @brief Reserves application's specific GPIOs * @details Used only in Development mode (#if DEVELOPMENT_DEBUG) * i.e. to reserve P0_1 as Generic Purpose I/O: * RESERVE_GPIO(DESCRIPTIVE_NAME, GPIO_PORT_0, GPIO_PIN_1, PID_GPIO); **************************************************************************************** */ void GPIO_reservations(void); #endif /** **************************************************************************************** * @brief Sets the functionality of application pads * @details i.e. to set P0_1 as Generic purpose Output: * GPIO_ConfigurePin(GPIO_PORT_0, GPIO_PIN_1, OUTPUT, PID_GPIO, false); **************************************************************************************** */ void set_pad_functions(void); /** **************************************************************************************** * @brief Initializes application's peripherals and pins **************************************************************************************** */ void periph_init(void); #endif // _USER_PERIPH_SETUP_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/config/user_profiles_config.h ================================================ /** **************************************************************************************** * * @file user_profiles_config.h * * @brief Configuration file for the profiles used in the application. * * Copyright (C) 2015-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _USER_PROFILES_CONFIG_H_ #define _USER_PROFILES_CONFIG_H_ /** **************************************************************************************** * @defgroup APP_CONFIG * @ingroup APP * @brief Application configuration file * * This file contains the configuration of the profiles used by the application. * * @{ **************************************************************************************** */ /* * DEFINITIONS **************************************************************************************** */ /***************************************************************************************/ /* Used BLE profiles (used by "rwprf_config.h"). */ /***************************************************************************************/ #if !defined(__DA14531__) // Disable profiles to save space for features #define CFG_PRF_CUST1 #define CFG_PRF_DISS #endif /***************************************************************************************/ /* Profile application configuration section */ /***************************************************************************************/ /* **************************************************************************************** * DISS application profile configuration **************************************************************************************** */ #define APP_DIS_FEATURES \ (DIS_MANUFACTURER_NAME_CHAR_SUP | DIS_MODEL_NB_STR_CHAR_SUP | DIS_SYSTEM_ID_CHAR_SUP | \ DIS_SW_REV_STR_CHAR_SUP | DIS_FIRM_REV_STR_CHAR_SUP | DIS_PNP_ID_CHAR_SUP) /// Manufacturer Name (up to 18 chars) #define APP_DIS_MANUFACTURER_NAME ("Dialog Semi") #define APP_DIS_MANUFACTURER_NAME_LEN (11) /// Model Number String (up to 18 chars) #if defined(__DA14586__) #define APP_DIS_MODEL_NB_STR ("DA14586") #elif defined(__DA14531__) #define APP_DIS_MODEL_NB_STR ("DA14531") #else #define APP_DIS_MODEL_NB_STR ("DA14585") #endif #define APP_DIS_MODEL_NB_STR_LEN (7) /// System ID - LSB -> MSB #define APP_DIS_SYSTEM_ID ("\x12\x34\x56\xFF\xFE\x9A\xBC\xDE") #define APP_DIS_SYSTEM_ID_LEN (8) #define APP_DIS_SW_REV SDK_VERSION #define APP_DIS_FIRM_REV SDK_VERSION /// Serial Number #define APP_DIS_SERIAL_NB_STR ("1.0.0.0-LE") #define APP_DIS_SERIAL_NB_STR_LEN (10) /// Hardware Revision String #if defined(__DA14586__) #define APP_DIS_HARD_REV_STR ("DA14586") #elif defined(__DA14531__) #define APP_DIS_HARD_REV_STR ("DA14531") #else #define APP_DIS_HARD_REV_STR ("DA14585") #endif #define APP_DIS_HARD_REV_STR_LEN (7) /// Firmware Revision #define APP_DIS_FIRM_REV_STR SDK_VERSION #define APP_DIS_FIRM_REV_STR_LEN (sizeof(APP_DIS_FIRM_REV_STR) - 1) /// Software Revision String #define APP_DIS_SW_REV_STR SDK_VERSION #define APP_DIS_SW_REV_STR_LEN (sizeof(APP_DIS_SW_REV_STR) - 1) /// IEEE #define APP_DIS_IEEE ("\xFF\xEE\xDD\xCC\xBB\xAA") #define APP_DIS_IEEE_LEN (6) /** * PNP ID Value - LSB -> MSB * Vendor ID Source : 0x02 (USB Implementers Forum assigned Vendor ID value) * Vendor ID : 0x045E (Microsoft Corp) * Product ID : 0x0040 * Product Version : 0x0300 * e.g. #define APP_DIS_PNP_ID ("\x02\x5E\x04\x40\x00\x00\x03") */ #define APP_DIS_PNP_ID ("\x01\xD2\x00\x80\x05\x00\x01") #define APP_DIS_PNP_ID_LEN (7) /// @} APP_CONFIG #endif // _USER_PROFILES_CONFIG_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/custom_profile/user_custs1_def.c ================================================ /** **************************************************************************************** * * @file user_custs1_def.c * * @brief Custom Server 1 (CUSTS1) profile database definitions. * * Copyright (C) 2016-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ /** **************************************************************************************** * @defgroup USER_CONFIG * @ingroup USER * @brief Custom server 1 (CUSTS1) profile database definitions. * * @{ **************************************************************************************** */ /* * INCLUDE FILES **************************************************************************************** */ #include "user_custs1_def.h" #include #include "attm_db_128.h" #include "co_utils.h" #include "prf_types.h" /* * LOCAL VARIABLE DEFINITIONS **************************************************************************************** */ // Service 1 of the custom server 1 static const att_svc_desc128_t custs1_svc1 = DEF_SVC1_UUID_128; static const uint8_t SVC1_CTRL_POINT_UUID_128[ATT_UUID_128_LEN] = DEF_SVC1_CTRL_POINT_UUID_128; static const uint8_t SVC1_LED_STATE_UUID_128[ATT_UUID_128_LEN] = DEF_SVC1_LED_STATE_UUID_128; static const uint8_t SVC1_ADC_VAL_1_UUID_128[ATT_UUID_128_LEN] = DEF_SVC1_ADC_VAL_1_UUID_128; static const uint8_t SVC1_ADC_VAL_2_UUID_128[ATT_UUID_128_LEN] = DEF_SVC1_ADC_VAL_2_UUID_128; static const uint8_t SVC1_BUTTON_STATE_UUID_128[ATT_UUID_128_LEN] = DEF_SVC1_BUTTON_STATE_UUID_128; static const uint8_t SVC1_INDICATEABLE_UUID_128[ATT_UUID_128_LEN] = DEF_SVC1_INDICATEABLE_UUID_128; static const uint8_t SVC1_LONG_VALUE_UUID_128[ATT_UUID_128_LEN] = DEF_SVC1_LONG_VALUE_UUID_128; // Service 2 of the custom server 1 static const att_svc_desc128_t custs1_svc2 = DEF_SVC2_UUID_128; static const uint8_t SVC2_WRITE_VAL_1_UUID_128[ATT_UUID_128_LEN] = DEF_SVC2_WRITE_VAL_1_UUID_128; static const uint8_t SVC2_WRITE_VAL_2_UUID_128[ATT_UUID_128_LEN] = DEF_SVC2_WRITE_VAL_2_UUID_128; // Service 3 of the custom server 1 static const att_svc_desc128_t custs1_svc3 = DEF_SVC3_UUID_128; static const uint8_t SVC3_READ_VAL_1_UUID_128[ATT_UUID_128_LEN] = DEF_SVC3_READ_VAL_1_UUID_128; static const uint8_t SVC3_READ_VAL_2_UUID_128[ATT_UUID_128_LEN] = DEF_SVC3_READ_VAL_2_UUID_128; static const uint8_t SVC3_READ_VAL_3_UUID_128[ATT_UUID_128_LEN] = DEF_SVC3_READ_VAL_3_UUID_128; // Attribute specifications static const uint16_t att_decl_svc = ATT_DECL_PRIMARY_SERVICE; static const uint16_t att_decl_char = ATT_DECL_CHARACTERISTIC; static const uint16_t att_desc_cfg = ATT_DESC_CLIENT_CHAR_CFG; static const uint16_t att_desc_user_desc = ATT_DESC_CHAR_USER_DESCRIPTION; /* * GLOBAL VARIABLE DEFINITIONS **************************************************************************************** */ const uint8_t custs1_services[] = { SVC1_IDX_SVC, SVC2_IDX_SVC, SVC3_IDX_SVC, CUSTS1_IDX_NB }; const uint8_t custs1_services_size = ARRAY_LEN(custs1_services) - 1; const uint16_t custs1_att_max_nb = CUSTS1_IDX_NB; /// Full CUSTS1 Database Description - Used to add attributes into the database const struct attm_desc_128 custs1_att_db[CUSTS1_IDX_NB] = { /************************* * Service 1 configuration ************************* */ // Service 1 Declaration [SVC1_IDX_SVC] = { (uint8_t *)&att_decl_svc, ATT_UUID_128_LEN, PERM(RD, ENABLE), sizeof(custs1_svc1), sizeof(custs1_svc1), (uint8_t *)&custs1_svc1 }, // Control Point Characteristic Declaration [SVC1_IDX_CONTROL_POINT_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Control Point Characteristic Value [SVC1_IDX_CONTROL_POINT_VAL] = { SVC1_CTRL_POINT_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE), DEF_SVC1_CTRL_POINT_CHAR_LEN, 0, NULL }, // Control Point Characteristic User Description [SVC1_IDX_CONTROL_POINT_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC1_CONTROL_POINT_USER_DESC) - 1, sizeof(DEF_SVC1_CONTROL_POINT_USER_DESC) - 1, (uint8_t *)DEF_SVC1_CONTROL_POINT_USER_DESC }, // LED State Characteristic Declaration [SVC1_IDX_LED_STATE_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // LED State Characteristic Value [SVC1_IDX_LED_STATE_VAL] = { SVC1_LED_STATE_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_COMMAND, ENABLE), PERM(RI, ENABLE) | DEF_SVC1_LED_STATE_CHAR_LEN, 0, NULL }, // LED State Characteristic User Description [SVC1_IDX_LED_STATE_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC1_LED_STATE_USER_DESC) - 1, sizeof(DEF_SVC1_LED_STATE_USER_DESC) - 1, (uint8_t *)DEF_SVC1_LED_STATE_USER_DESC }, // ADC Value 1 Characteristic Declaration [SVC1_IDX_ADC_VAL_1_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // ADC Value 1 Characteristic Value [SVC1_IDX_ADC_VAL_1_VAL] = { SVC1_ADC_VAL_1_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE) | PERM(NTF, ENABLE), DEF_SVC1_ADC_VAL_1_CHAR_LEN, 0, NULL }, // ADC Value 1 Client Characteristic Configuration Descriptor [SVC1_IDX_ADC_VAL_1_NTF_CFG] = { (uint8_t *)&att_desc_cfg, ATT_UUID_16_LEN, PERM(RD, ENABLE) | PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE), sizeof(uint16_t), 0, NULL }, // ADC Value 1 Characteristic User Description [SVC1_IDX_ADC_VAL_1_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC1_ADC_VAL_1_USER_DESC) - 1, sizeof(DEF_SVC1_ADC_VAL_1_USER_DESC) - 1, (uint8_t *)DEF_SVC1_ADC_VAL_1_USER_DESC }, // ADC Value 2 Characteristic Declaration [SVC1_IDX_ADC_VAL_2_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // ADC Value 2 Characteristic Value [SVC1_IDX_ADC_VAL_2_VAL] = { SVC1_ADC_VAL_2_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE), DEF_SVC1_ADC_VAL_2_CHAR_LEN, 0, NULL }, // ADC Value 2 Characteristic User Description [SVC1_IDX_ADC_VAL_2_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC1_ADC_VAL_2_USER_DESC) - 1, sizeof(DEF_SVC1_ADC_VAL_2_USER_DESC) - 1, (uint8_t *)DEF_SVC1_ADC_VAL_2_USER_DESC }, // Button State Characteristic Declaration [SVC1_IDX_BUTTON_STATE_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Button State Characteristic Value [SVC1_IDX_BUTTON_STATE_VAL] = { SVC1_BUTTON_STATE_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE) | PERM(NTF, ENABLE), DEF_SVC1_BUTTON_STATE_CHAR_LEN, 0, NULL }, // Button State Client Characteristic Configuration Descriptor [SVC1_IDX_BUTTON_STATE_NTF_CFG] = { (uint8_t *)&att_desc_cfg, ATT_UUID_16_LEN, PERM(RD, ENABLE) | PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE), sizeof(uint16_t), 0, NULL }, // Button State Characteristic User Description [SVC1_IDX_BUTTON_STATE_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC1_BUTTON_STATE_USER_DESC) - 1, sizeof(DEF_SVC1_BUTTON_STATE_USER_DESC) - 1, (uint8_t *)DEF_SVC1_BUTTON_STATE_USER_DESC }, // Indicateable Characteristic Declaration [SVC1_IDX_INDICATEABLE_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Indicateable Characteristic Value [SVC1_IDX_INDICATEABLE_VAL] = { SVC1_INDICATEABLE_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE) | PERM(IND, ENABLE), DEF_SVC1_INDICATEABLE_CHAR_LEN, 0, NULL }, // Indicateable Client Characteristic Configuration Descriptor [SVC1_IDX_INDICATEABLE_IND_CFG] = { (uint8_t *)&att_desc_cfg, ATT_UUID_16_LEN, PERM(RD, ENABLE) | PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE), sizeof(uint16_t), 0, NULL }, // Indicateable Characteristic User Description [SVC1_IDX_INDICATEABLE_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC1_INDICATEABLE_USER_DESC) - 1, sizeof(DEF_SVC1_INDICATEABLE_USER_DESC) - 1, (uint8_t *)DEF_SVC1_INDICATEABLE_USER_DESC }, // Long Value Characteristic Declaration [SVC1_IDX_LONG_VALUE_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Long Value Characteristic Value [SVC1_IDX_LONG_VALUE_VAL] = { SVC1_LONG_VALUE_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE) | PERM(WR, ENABLE) | PERM(NTF, ENABLE) | PERM(WRITE_REQ, ENABLE), DEF_SVC1_LONG_VALUE_CHAR_LEN, 0, NULL }, // Long Value Client Characteristic Configuration Descriptor [SVC1_IDX_LONG_VALUE_NTF_CFG] = { (uint8_t *)&att_desc_cfg, ATT_UUID_16_LEN, PERM(RD, ENABLE) | PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE), sizeof(uint16_t), 0, NULL }, // Long Value Characteristic User Description [SVC1_IDX_LONG_VALUE_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC1_LONG_VALUE_CHAR_USER_DESC) - 1, sizeof(DEF_SVC1_LONG_VALUE_CHAR_USER_DESC) - 1, (uint8_t *)DEF_SVC1_LONG_VALUE_CHAR_USER_DESC }, /************************* * Service 2 configuration ************************* */ // Service 2 Declaration [SVC2_IDX_SVC] = { (uint8_t *)&att_decl_svc, ATT_UUID_128_LEN, PERM(RD, ENABLE), sizeof(custs1_svc2), sizeof(custs1_svc2), (uint8_t *)&custs1_svc2 }, // Write 1 Characteristic Declaration [SVC2_WRITE_1_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Write 1 Characteristic Value [SVC2_WRITE_1_VAL] = { SVC2_WRITE_VAL_1_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE), PERM(RI, ENABLE) | DEF_SVC2_WRITE_VAL_1_CHAR_LEN, 0, NULL }, // Write 1 Characteristic User Description [SVC2_WRITE_1_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC2_WRITE_VAL_1_USER_DESC) - 1, sizeof(DEF_SVC2_WRITE_VAL_1_USER_DESC) - 1, (uint8_t *)DEF_SVC2_WRITE_VAL_1_USER_DESC }, // Write 2 Characteristic Declaration [SVC2_WRITE_2_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Write 2 Characteristic Value [SVC2_WRITE_2_VAL] = { SVC2_WRITE_VAL_2_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_COMMAND, ENABLE), DEF_SVC2_WRITE_VAL_2_CHAR_LEN, 0, NULL }, // Write 2 Characteristic User Description [SVC2_WRITE_2_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC2_WRITE_VAL_2_USER_DESC) - 1, sizeof(DEF_SVC2_WRITE_VAL_2_USER_DESC) - 1, (uint8_t *)DEF_SVC2_WRITE_VAL_2_USER_DESC }, /************************* * Service 3 configuration ************************* */ // Service 3 Declaration [SVC3_IDX_SVC] = { (uint8_t *)&att_decl_svc, ATT_UUID_128_LEN, PERM(RD, ENABLE), sizeof(custs1_svc3), sizeof(custs1_svc3), (uint8_t *)&custs1_svc3 }, // Read 1 Characteristic Declaration [SVC3_IDX_READ_1_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Read 1 Characteristic Value [SVC3_IDX_READ_1_VAL] = { SVC3_READ_VAL_1_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE) | PERM(NTF, ENABLE), DEF_SVC3_READ_VAL_1_CHAR_LEN, 0, NULL }, // Read 1 Client Characteristic Configuration Descriptor [SVC3_IDX_READ_1_NTF_CFG] = { (uint8_t *)&att_desc_cfg, ATT_UUID_16_LEN, PERM(RD, ENABLE) | PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE), sizeof(uint16_t), 0, NULL }, // Read 1 Characteristic User Description [SVC3_IDX_READ_1_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC3_READ_VAL_1_USER_DESC) - 1, sizeof(DEF_SVC3_READ_VAL_1_USER_DESC) - 1, (uint8_t *)DEF_SVC3_READ_VAL_1_USER_DESC }, // Read 2 Characteristic Declaration [SVC3_IDX_READ_2_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Read 2 Characteristic Value [SVC3_IDX_READ_2_VAL] = { SVC3_READ_VAL_2_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE), DEF_SVC3_READ_VAL_2_CHAR_LEN, 0, NULL }, // Read 2 Characteristic User Description [SVC3_IDX_READ_2_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC3_READ_VAL_2_USER_DESC) - 1, sizeof(DEF_SVC3_READ_VAL_2_USER_DESC) - 1, (uint8_t *)DEF_SVC3_READ_VAL_2_USER_DESC }, // Read 3 Characteristic Declaration [SVC3_IDX_READ_3_CHAR] = { (uint8_t *)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE), 0, 0, NULL }, // Read 3 Characteristic Value [SVC3_IDX_READ_3_VAL] = { SVC3_READ_VAL_3_UUID_128, ATT_UUID_128_LEN, PERM(RD, ENABLE) | PERM(IND, ENABLE), DEF_SVC3_READ_VAL_3_CHAR_LEN, 0, NULL }, // Read 3 Client Characteristic Configuration Descriptor [SVC3_IDX_READ_3_IND_CFG] = { (uint8_t *)&att_desc_cfg, ATT_UUID_16_LEN, PERM(RD, ENABLE) | PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE), sizeof(uint16_t), 0, NULL }, // Read 3 Characteristic User Description [SVC3_IDX_READ_3_USER_DESC] = { (uint8_t *)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE), sizeof(DEF_SVC3_READ_VAL_3_USER_DESC) - 1, sizeof(DEF_SVC3_READ_VAL_3_USER_DESC) - 1, (uint8_t *)DEF_SVC3_READ_VAL_3_USER_DESC }, }; /// @} USER_CONFIG ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/custom_profile/user_custs1_def.h ================================================ /** **************************************************************************************** * * @file user_custs1_def.h * * @brief Custom Server 1 (CUSTS1) profile database definitions. * * Copyright (C) 2016-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _USER_CUSTS1_DEF_H_ #define _USER_CUSTS1_DEF_H_ /** **************************************************************************************** * @defgroup USER_CONFIG * @ingroup USER * @brief Custom Server 1 (CUSTS1) profile database definitions. * * @{ **************************************************************************************** */ /* * INCLUDE FILES **************************************************************************************** */ #include "attm_db_128.h" /* * DEFINES **************************************************************************************** */ // Service 1 of the custom server 1 #define DEF_SVC1_UUID_128 \ { 0x59, 0x5a, 0x08, 0xe4, 0x86, 0x2a, 0x9e, 0x8f, 0xe9, 0x11, 0xbc, 0x7c, 0x98, 0x43, 0x42, 0x18 } #define DEF_SVC1_CTRL_POINT_UUID_128 \ { 0x20, 0xEE, 0x8D, 0x0C, 0xE1, 0xF0, 0x4A, 0x0C, 0xB3, 0x25, 0xDC, 0x53, 0x6A, 0x68, 0x86, 0x2D } #define DEF_SVC1_LED_STATE_UUID_128 \ { 0x4F, 0x43, 0x31, 0x3C, 0x93, 0x92, 0x42, 0xE6, 0xA8, 0x76, 0xFA, 0x3B, 0xEF, 0xB4, 0x87, 0x5A } #define DEF_SVC1_ADC_VAL_1_UUID_128 \ { 0x17, 0xB9, 0x67, 0x98, 0x4C, 0x66, 0x4C, 0x01, 0x96, 0x33, 0x31, 0xB1, 0x91, 0x59, 0x00, 0x15 } #define DEF_SVC1_ADC_VAL_2_UUID_128 \ { 0x23, 0x68, 0xEC, 0x52, 0x1E, 0x62, 0x44, 0x74, 0x9A, 0x1B, 0xD1, 0x8B, 0xAB, 0x75, 0xB6, 0x6E } #define DEF_SVC1_BUTTON_STATE_UUID_128 \ { 0x9E, 0xE7, 0xBA, 0x08, 0xB9, 0xA9, 0x48, 0xAB, 0xA1, 0xAC, 0x03, 0x1C, 0x2E, 0x0D, 0x29, 0x6C } #define DEF_SVC1_INDICATEABLE_UUID_128 \ { 0x28, 0xD5, 0xE1, 0xC1, 0xE1, 0xC5, 0x47, 0x29, 0xB5, 0x57, 0x65, 0xC3, 0xBA, 0x47, 0x15, 0x9E } #define DEF_SVC1_LONG_VALUE_UUID_128 \ { 0x8C, 0x09, 0xE0, 0xD1, 0x81, 0x54, 0x42, 0x40, 0x8E, 0x4F, 0xD2, 0xB3, 0x77, 0xE3, 0x2A, 0x77 } #define DEF_SVC1_CTRL_POINT_CHAR_LEN 1 #define DEF_SVC1_LED_STATE_CHAR_LEN 1 #define DEF_SVC1_ADC_VAL_1_CHAR_LEN 2 #define DEF_SVC1_ADC_VAL_2_CHAR_LEN 2 #define DEF_SVC1_BUTTON_STATE_CHAR_LEN 1 #define DEF_SVC1_INDICATEABLE_CHAR_LEN 20 #define DEF_SVC1_LONG_VALUE_CHAR_LEN 50 #define DEF_SVC1_CONTROL_POINT_USER_DESC "Control Point" #define DEF_SVC1_LED_STATE_USER_DESC "LED State" #define DEF_SVC1_ADC_VAL_1_USER_DESC "ADC Value 1" #define DEF_SVC1_ADC_VAL_2_USER_DESC "ADC Value 2" #define DEF_SVC1_BUTTON_STATE_USER_DESC "Button State" #define DEF_SVC1_INDICATEABLE_USER_DESC "Indicateable" #define DEF_SVC1_LONG_VALUE_CHAR_USER_DESC "Long Value" // Service 2 of the custom server 1 #define DEF_SVC2_UUID_128 \ { 0x59, 0x5a, 0x08, 0xe4, 0x86, 0x2a, 0x9e, 0x8f, 0xe9, 0x11, 0xbc, 0x7c, 0x7c, 0x46, 0x42, 0x18 } #define DEF_SVC2_WRITE_VAL_1_UUID_128 \ { 0x20, 0xEE, 0x8D, 0x0C, 0xE1, 0xF0, 0x4A, 0x0C, 0xB3, 0x25, 0xDC, 0x53, 0x6A, 0x68, 0x86, 0x2C } #define DEF_SVC2_WRITE_VAL_2_UUID_128 \ { 0x4F, 0x43, 0x31, 0x3C, 0x93, 0x92, 0x42, 0xE6, 0xA8, 0x76, 0xFA, 0x3B, 0xEF, 0xB4, 0x87, 0x59 } #define DEF_SVC2_WRITE_VAL_1_CHAR_LEN 1 #define DEF_SVC2_WRITE_VAL_2_CHAR_LEN 1 #define DEF_SVC2_WRITE_VAL_1_USER_DESC "Write me" #define DEF_SVC2_WRITE_VAL_2_USER_DESC "Write me (no rsp)" // Service 3 of the custom server 1 #define DEF_SVC3_UUID_128 \ { 0x59, 0x5a, 0x08, 0xe4, 0x86, 0x2a, 0x9e, 0x8, 0xe9, 0x11, 0xbc, 0x7c, 0xd0, 0x47, 0x42, 0x18 } #define DEF_SVC3_READ_VAL_1_UUID_128 \ { 0x17, 0xB9, 0x67, 0x98, 0x4C, 0x66, 0x4C, 0x01, 0x96, 0x33, 0x31, 0xB1, 0x91, 0x59, 0x00, 0x14 } #define DEF_SVC3_READ_VAL_2_UUID_128 \ { 0x23, 0x68, 0xEC, 0x52, 0x1E, 0x62, 0x44, 0x74, 0x9A, 0x1B, 0xD1, 0x8B, 0xAB, 0x75, 0xB6, 0x6D } #define DEF_SVC3_READ_VAL_3_UUID_128 \ { 0x28, 0xD5, 0xE1, 0xC1, 0xE1, 0xC5, 0x47, 0x29, 0xB5, 0x57, 0x65, 0xC3, 0xBA, 0x47, 0x15, 0x9D } #define DEF_SVC3_READ_VAL_1_CHAR_LEN 2 #define DEF_SVC3_READ_VAL_2_CHAR_LEN 2 #define DEF_SVC3_READ_VAL_3_CHAR_LEN 2 #define DEF_SVC3_READ_VAL_1_USER_DESC "Read me (notify)" #define DEF_SVC3_READ_VAL_2_USER_DESC "Read me" #define DEF_SVC3_READ_VAL_3_USER_DESC "Read me (indicate)" /// Custom1 Service Data Base Characteristic enum enum { // Custom Service 1 SVC1_IDX_SVC = 0, SVC1_IDX_CONTROL_POINT_CHAR, SVC1_IDX_CONTROL_POINT_VAL, SVC1_IDX_CONTROL_POINT_USER_DESC, SVC1_IDX_LED_STATE_CHAR, SVC1_IDX_LED_STATE_VAL, SVC1_IDX_LED_STATE_USER_DESC, SVC1_IDX_ADC_VAL_1_CHAR, SVC1_IDX_ADC_VAL_1_VAL, SVC1_IDX_ADC_VAL_1_NTF_CFG, SVC1_IDX_ADC_VAL_1_USER_DESC, SVC1_IDX_ADC_VAL_2_CHAR, SVC1_IDX_ADC_VAL_2_VAL, SVC1_IDX_ADC_VAL_2_USER_DESC, SVC1_IDX_BUTTON_STATE_CHAR, SVC1_IDX_BUTTON_STATE_VAL, SVC1_IDX_BUTTON_STATE_NTF_CFG, SVC1_IDX_BUTTON_STATE_USER_DESC, SVC1_IDX_INDICATEABLE_CHAR, SVC1_IDX_INDICATEABLE_VAL, SVC1_IDX_INDICATEABLE_IND_CFG, SVC1_IDX_INDICATEABLE_USER_DESC, SVC1_IDX_LONG_VALUE_CHAR, SVC1_IDX_LONG_VALUE_VAL, SVC1_IDX_LONG_VALUE_NTF_CFG, SVC1_IDX_LONG_VALUE_USER_DESC, // Custom Service 2 SVC2_IDX_SVC, SVC2_WRITE_1_CHAR, SVC2_WRITE_1_VAL, SVC2_WRITE_1_USER_DESC, SVC2_WRITE_2_CHAR, SVC2_WRITE_2_VAL, SVC2_WRITE_2_USER_DESC, // Custom Service 3 SVC3_IDX_SVC, SVC3_IDX_READ_1_CHAR, SVC3_IDX_READ_1_VAL, SVC3_IDX_READ_1_NTF_CFG, SVC3_IDX_READ_1_USER_DESC, SVC3_IDX_READ_2_CHAR, SVC3_IDX_READ_2_VAL, SVC3_IDX_READ_2_USER_DESC, SVC3_IDX_READ_3_CHAR, SVC3_IDX_READ_3_VAL, SVC3_IDX_READ_3_IND_CFG, SVC3_IDX_READ_3_USER_DESC, CUSTS1_IDX_NB }; /// @} USER_CONFIG #endif // _USER_CUSTS1_DEF_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/custom_profile/user_custs_config.c ================================================ /** **************************************************************************************** * * @file user_custs_config.c * * @brief Custom1/2 Server (CUSTS1/2) profile database structure and initialization. * * Copyright (C) 2016-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ /** **************************************************************************************** * @defgroup USER_CONFIG * @ingroup USER * @brief Custom1/2 Server (CUSTS1/2) profile database structure and initialization. * * @{ **************************************************************************************** */ /* * INCLUDE FILES **************************************************************************************** */ #include "app_customs.h" #include "app_prf_types.h" #include "user_custs1_def.h" /* * GLOBAL VARIABLE DEFINITIONS **************************************************************************************** */ #if (BLE_CUSTOM1_SERVER) extern const struct attm_desc_128 custs1_att_db[CUSTS1_IDX_NB]; #endif /// Custom1/2 server function callback table const struct cust_prf_func_callbacks cust_prf_funcs[] = { #if (BLE_CUSTOM1_SERVER) { TASK_ID_CUSTS1, custs1_att_db, CUSTS1_IDX_NB, #if (BLE_APP_PRESENT) app_custs1_create_db, NULL, #else NULL, NULL, #endif NULL, NULL, }, #endif #if (BLE_CUSTOM2_SERVER) { TASK_ID_CUSTS2, NULL, 0, #if (BLE_APP_PRESENT) app_custs2_create_db, NULL, #else NULL, NULL, #endif NULL, NULL, }, #endif { TASK_ID_INVALID, NULL, 0, NULL, NULL, NULL, NULL }, // DO NOT MOVE. Must always be last }; /// @} USER_CONFIG ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/custom_profile/user_custs_config.h ================================================ /** **************************************************************************************** * * @file user_custs_config.h * * @brief Custom1/2 Server (CUSTS1/2) profile database initialization. * * Copyright (C) 2016-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _USER_CUSTS_CONFIG_H_ #define _USER_CUSTS_CONFIG_H_ /** **************************************************************************************** * @defgroup USER_CONFIG * @ingroup USER * @brief Custom1/2 Server (CUSTS1/2) profile database initialization. * * @{ **************************************************************************************** */ /* * INCLUDE FILES **************************************************************************************** */ #include "app_prf_types.h" /* * GLOBAL VARIABLE DECLARATIONS **************************************************************************************** */ extern const struct cust_prf_func_callbacks cust_prf_funcs[]; /// @} USER_CONFIG #endif // _USER_CUSTS_CONFIG_H_ ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/memfault_platform_device_info.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Example populating memfault_platform_get_device_info() implementation for DA145xx #include #include "memfault/components.h" #include "nvds.h" static void prv_get_device_serial(char *buf, size_t buf_len) { nvds_tag_len_t tag_len; struct bd_addr dev_bdaddr; if (nvds_get(NVDS_TAG_BD_ADDRESS, &tag_len, (uint8_t *)&dev_bdaddr) == NVDS_OK) { (void)snprintf(buf, buf_len, "%02X%02X%02X%02X%02X%02X", dev_bdaddr.addr[5], dev_bdaddr.addr[4], dev_bdaddr.addr[3], dev_bdaddr.addr[2], dev_bdaddr.addr[1], dev_bdaddr.addr[0]); } } void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { static char s_device_serial[12 + 1]; /* \0 */ static char s_fw_version[16] = "1.0.0+"; static bool s_init = false; if (!s_init) { prv_get_device_serial(s_device_serial, sizeof(s_device_serial)); const size_t version_len = strlen(s_fw_version); // We will use 6 characters of the build id to make our versions unique and // identifiable between releases const size_t build_id_chars = 6 + 1 /* '\0' */; const size_t build_id_num_chars = MEMFAULT_MIN(build_id_chars, sizeof(s_fw_version) - version_len - 1); memfault_build_id_get_string(&s_fw_version[version_len], build_id_num_chars); s_init = true; } *info = (struct MemfaultDeviceInfo){ .device_serial = s_device_serial, .hardware_version = APP_DIS_HARD_REV_STR, .software_version = s_fw_version, #if defined(__DA14531__) .software_type = "da14531-demo-app", #elif defined(__DA14586__) .software_type = "da14586-demo-app", #else .software_type = "da14585-demo-app", #endif }; } ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/platform/user_periph_setup.c ================================================ /** **************************************************************************************** * * @file user_periph_setup.c * * @brief Peripherals setup and initialization. * * Copyright (C) 2015-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ /* * INCLUDE FILES **************************************************************************************** */ #include "user_periph_setup.h" #include "datasheet.h" #include "gpio.h" #include "rwip_config.h" #include "spi.h" #include "spi_flash.h" #include "syscntl.h" #include "system_library.h" #include "uart.h" /* * GLOBAL VARIABLE DEFINITIONS **************************************************************************************** */ #if DEVELOPMENT_DEBUG void GPIO_reservations(void) { /* i.e. to reserve P0_1 as Generic Purpose I/O: RESERVE_GPIO(DESCRIPTIVE_NAME, GPIO_PORT_0, GPIO_PIN_1, PID_GPIO); */ RESERVE_GPIO(SPI_FLASH_CS, SPI_EN_PORT, SPI_EN_PIN, PID_SPI_EN); RESERVE_GPIO(SPI_FLASH_CLK, SPI_CLK_PORT, SPI_CLK_PIN, PID_SPI_CLK); RESERVE_GPIO(SPI_FLASH_DO, SPI_DO_PORT, SPI_DO_PIN, PID_SPI_DO); RESERVE_GPIO(SPI_FLASH_DI, SPI_DI_PORT, SPI_DI_PIN, PID_SPI_DI); #if defined(CFG_PRINTF_UART2) RESERVE_GPIO(UART2_TX, UART2_TX_PORT, UART2_TX_PIN, PID_UART2_TX); RESERVE_GPIO(UART2_RX, UART2_RX_PORT, UART2_RX_PIN, PID_UART2_RX); #endif } #endif void set_pad_functions(void) { /* i.e. to set P0_1 as Generic purpose Output: GPIO_ConfigurePin(GPIO_PORT_0, GPIO_PIN_1, OUTPUT, PID_GPIO, false); */ #if defined(__DA14531__) /* P0_0 is used as both the SPI data out and the reset input. */ GPIO_Disable_HW_Reset(); #endif GPIO_ConfigurePin(SPI_EN_PORT, SPI_EN_PIN, OUTPUT, PID_SPI_EN, true); GPIO_ConfigurePin(SPI_CLK_PORT, SPI_CLK_PIN, OUTPUT, PID_SPI_CLK, false); GPIO_ConfigurePin(SPI_DO_PORT, SPI_DO_PIN, OUTPUT, PID_SPI_DO, false); GPIO_ConfigurePin(SPI_DI_PORT, SPI_DI_PIN, INPUT, PID_SPI_DI, false); #if defined(__DA14586__) // Disallow spontaneous DA14586 SPI Flash wake-up GPIO_ConfigurePin(GPIO_PORT_2, GPIO_PIN_3, OUTPUT, PID_GPIO, true); #endif #if defined(CFG_PRINTF_UART2) // Configure UART2 TX Pad GPIO_ConfigurePin(UART2_TX_PORT, UART2_TX_PIN, OUTPUT, PID_UART2_TX, false); GPIO_ConfigurePin(UART2_RX_PORT, UART2_RX_PIN, OUTPUT, PID_UART2_RX, false); #endif } #if defined(CFG_PRINTF_UART2) // Configuration struct for UART2 static const uart_cfg_t uart_cfg = { .baud_rate = UART2_BAUDRATE, .data_bits = UART2_DATABITS, .parity = UART2_PARITY, .stop_bits = UART2_STOPBITS, .auto_flow_control = UART2_AFCE, .use_fifo = UART2_FIFO, .tx_fifo_tr_lvl = UART2_TX_FIFO_LEVEL, .rx_fifo_tr_lvl = UART2_RX_FIFO_LEVEL, .intr_priority = 2, }; #endif /* Default SPI configuration */ static const spi_cfg_t spi_cfg = { .spi_ms = SPI_MS_MODE_MASTER, .spi_cp = SPI_CP_MODE_0, .spi_speed = SPI_SPEED_MODE_4MHz, .spi_wsz = SPI_MODE_8BIT, .spi_cs = SPI_CS_0, .cs_pad.port = SPI_EN_PORT, .cs_pad.pin = SPI_EN_PIN, #if defined(__DA14531__) .spi_capture = SPI_MASTER_EDGE_CAPTURE, #endif }; /* SPI flash configuration - assumes use of a Macronix MXR2035F as this is present on the DA145xx PRO development kit */ static const spi_flash_cfg_t spi_flash_cfg = { .dev_index = MX25R2035F_DEV_INDEX, .jedec_id = MX25V2035F_JEDEC_ID, .chip_size = MX25V2035F_CHIP_SIZE, }; void periph_init(void) { #if defined(__DA14531__) // In Boost mode enable the DCDC converter to supply VBAT_HIGH for the used GPIOs syscntl_dcdc_turn_on_in_boost(SYSCNTL_DCDC_LEVEL_3V0); #else // Power up peripherals' power domain SetBits16(PMU_CTRL_REG, PERIPH_SLEEP, 0); while (!(GetWord16(SYS_STAT_REG) & PER_IS_UP)); SetBits16(CLK_16M_REG, XTAL16_BIAS_SH_ENABLE, 1); #endif // ROM patch patch_func(); // Initialize peripherals #if defined(CFG_PRINTF_UART2) // Initialize UART2 uart_initialize(UART2, &uart_cfg); #endif // Initialize interface to SPI flash so we can use it from within the application spi_flash_configure_env(&spi_flash_cfg); spi_initialize(&spi_cfg); // Set pad functionality set_pad_functions(); // Enable the pads GPIO_set_pad_latch_en(true); } ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/user_app.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A minimal example app for exercising and trying out the Memfault SDK with the DA145xx SDK. #include "user_app.h" #include "app_api.h" #ifdef CFG_PRINTF #include "arch_console.h" #include "uart.h" #endif #include "memfault/components.h" static int prv_send_char(char c) { arch_printf("%c", c); return 0; } //! A custom callback invoked by the Dialog SDK on bootup. See user_callback_config.h for //! configuration. void user_app_on_init(void) { memfault_platform_boot(); //! We will use the Memfault CLI shell as a very basic debug interface const sMemfaultShellImpl impl = { .send_char = prv_send_char, }; memfault_demo_shell_boot(&impl); default_app_on_init(); } void user_on_connection(uint8_t connection_idx, struct gapc_connection_req_ind const *param) { default_app_on_connection(connection_idx, param); } void user_on_disconnect(struct gapc_disconnect_ind const *param) { default_app_on_disconnect(param); } #if !defined(__DA14531__) static int prv_test_storage(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { GLOBAL_INT_DISABLE(); memfault_coredump_storage_debug_test_begin(); GLOBAL_INT_RESTORE(); memfault_coredump_storage_debug_test_finish(); return 0; } #endif static int prv_export_data(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { memfault_data_export_dump_chunks(); return 0; } static const sMemfaultShellCommand s_memfault_shell_commands[] = { { "crash", memfault_demo_cli_cmd_crash, "" }, #if !defined(__DA14531__) // Don't use on DA14531 to reduce memory usage { "trace", memfault_demo_cli_cmd_trace_event_capture, "" }, { "get_core", memfault_demo_cli_cmd_get_core, "" }, { "clear_core", memfault_demo_cli_cmd_clear_core, "" }, { "test_storage", prv_test_storage, "" }, { "get_device_info", memfault_demo_cli_cmd_get_device_info, "" }, #endif { "reboot", memfault_demo_cli_cmd_system_reboot, "" }, { "export", prv_export_data, "" }, { "help", memfault_shell_help_handler, "" }, }; const sMemfaultShellCommand *const g_memfault_shell_commands = s_memfault_shell_commands; const size_t g_memfault_num_shell_commands = MEMFAULT_ARRAY_SIZE(s_memfault_shell_commands); //! Called by the DA145xx scheduler arch_main_loop_callback_ret_t user_app_on_system_powered(void) { while (uart_data_ready_getf(UART2) > 0) { uint8_t rx_byte = uart_read_byte(UART2); memfault_demo_shell_receive_char((char)rx_byte); arch_printf_process(); } return GOTO_SLEEP; } ================================================ FILE: examples/dialog/da145xx/apps/memfault_demo_app/src/user_app.h ================================================ /** **************************************************************************************** * * @file user_app.h * * @brief Memfault example application header file. * * Copyright (C) 2012-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef _USER_APP_H_ #define _USER_APP_H_ /** **************************************************************************************** * @addtogroup APP * @ingroup RICOW * * @brief * * @{ **************************************************************************************** */ /* * INCLUDE FILES **************************************************************************************** */ #include "app.h" // application definitions #include "app_task.h" // application task #include "arch_api.h" #include "co_error.h" // error code definitions #include "gapc_task.h" // gap functions and messages #include "gapm_task.h" // gap functions and messages #include "rwble_config.h" /**************************************************************************** Add here supported profiles' application header files. i.e. #if (BLE_DIS_SERVER) #include "app_dis.h" #include "app_dis_task.h" #endif *****************************************************************************/ /* * TYPE DEFINITIONS **************************************************************************************** */ /* * DEFINES **************************************************************************************** */ /* * FUNCTION DECLARATIONS **************************************************************************************** */ void user_app_on_init(void); arch_main_loop_callback_ret_t user_app_on_system_powered(void); void user_on_connection(uint8_t connection_idx, struct gapc_connection_req_ind const *param); void user_on_disconnect(struct gapc_disconnect_ind const *param); /// @} APP #endif // _USER_APP_H_ ================================================ FILE: examples/dialog/da1469x/README.md ================================================ # Memfault for the DA1469x Family of Wireless Microcontrollers This example application shows an integration with Dialog Semiconductors SDK10 for the DA1469x family of Wireless Microcontrollers. ## Getting Started We assume you have a working setup for firmware development on the Dialog DA1469x family of devices: - have Dialogs SmartSnippets Studio installed - have installed the latest version of the DA1469x SDK (this example was tested with version 10.0.10.118) ### Adding Memfault to the Dialog SDK Create a directory called `memfault` in the `middleware` folder of the Dialog SDK and then clone the `memfault-firmware-sdk` repository into this folder. The resulting directory structure should look like the following: ``` ├── binaries ├── config ├── doc ├── projects ├── sdk │   ├── bsp │   ├── free_rtos │   ├── interfaces │   └── middleware │   ├── adapters │   ├── cli │   ├── config │   ├── console │   ├── dgtl │   ├── haptics |   ├── hw_tools │   ├── logging │   ├── mcif │   ├── memfault │   │   └── memfault-firmware-sdk │   │   ├── cmake │   │   ├── components │   │   ├── examples │   │   ├── makefiles │   │   ├── ports │   │   ├── scripts │   │   ├── tasks │   │   └── tests │   ├── monitoring │   ├── osal │   └── segger_tools └── utilities ``` ## Using the demo app The demo app is compatible with the following DA1469x Development Kit boards: [DA14695 Development Kit USB](https://www.dialog-semiconductor.com/products/bluetooth-low-energy/da14695-development-kit-usb) [DA14695 Development Kit PRO](https://www.dialog-semiconductor.com/products/bluetooth-low-energy/da14695-development-kit-pro) ### Building the demo app The demo application can be built using the Eclipse/GCC (SmartSnippets Studio) toolchain. First, a few small changes must be made to the Dialog SDK to enable integration of Memfault support. To make these changes navigate to the root of the Dialog SDK and apply the following patches: `git apply --ignore-whitespace ./sdk/middleware/memfault/memfault-firmware-sdk/ports/dialog/da1469x/gnu-build-id.patch` `git apply --ignore-whitespace ./sdk/middleware/memfault/memfault-firmware-sdk/ports/dialog/da1469x/freertos-config.patch` `git apply --ignore-whitespace ./sdk/middleware/memfault/memfault-firmware-sdk/ports/dialog/da1469x/fault-handlers.patch` Next, open SmartSnippet Studio and import the demo application project, which can be found in the following location: `/sdk/middleware/memfault/memfault-firmware-sdk/examples/dialog/da1469x/apps/memfault_demo_app` Build the application and then program into the flash memory contained on the Development Kit board. ### Running the demo app The demo app is a simple console based app that has commands to cause a crash in several ways. Once a coredump is captured, it can be sent to Memfault's web services to get analyzed. Once the DA1469x Development Kit has been programmed, connect to it using a Terminal emulator (such as TeraTerm etc.) at 115200-8-N-1. **NOTE:** The DA1469x will enumerate two COM ports when connected to a PC, select the lowest numbered of the two ports when connecting via a terminal emulator. The following commands are supported by the demo app and can be initiated by sending the command listed: - crash: Generate different types of crashes with crash [0..3] - trace: Generate a trace event - get_core: Get coredump - clear_core: Clear coredump - test_storage: Test coredump storage - get_device_info: Display device information - reboot: Reboot & record event - export: Export chunk - help: show list of available commands ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/.cproject ================================================ ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/.gitignore ================================================ .settings/ jlink.log DA1469x-00-Debug_QSPI/ DA1469x-00-Release_QSPI/ ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/.project ================================================ memfault_demo_app org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder full,incremental, org.eclipse.cdt.core.cnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature memfault_platform_device_info.c 1 PROJECT_LOC/memfault_platform_device_info.c sdk 2 virtual:/virtual startup 2 SDKROOT/sdk/bsp/startup sdk/FreeRTOS 2 SDKROOT/sdk/free_rtos sdk/adapters 2 SDKROOT/sdk/middleware/adapters sdk/bsp_include 2 SDKROOT/sdk/bsp/include sdk/config 2 SDKROOT/sdk/bsp/config sdk/ldscripts 2 SDKROOT/sdk/bsp/ldscripts/non_ble_projects sdk/memfault_components 2 virtual:/virtual sdk/memfault_dialog 2 SDKROOT/sdk/middleware/memfault/memfault-firmware-sdk/ports/dialog/da1469x sdk/memory 2 SDKROOT/sdk/bsp/memory sdk/middleware_config 2 SDKROOT/sdk/middleware/config sdk/middleware_console 2 SDKROOT/sdk/middleware/console sdk/osal 2 SDKROOT/sdk/middleware/osal sdk/peripherals 2 SDKROOT/sdk/bsp/peripherals sdk/snc 2 SDKROOT/sdk/bsp/snc sdk/sys_man 2 SDKROOT/sdk/bsp/system/sys_man sdk/util 2 SDKROOT/sdk/bsp/util sdk/memfault_components/arch_arm_cortex_m.c 1 PARENT-5-PROJECT_LOC/components/core/src/arch_arm_cortex_m.c sdk/memfault_components/memfault_base64.c 1 PARENT-5-PROJECT_LOC/components/util/src/memfault_base64.c sdk/memfault_components/memfault_build_id.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_build_id.c sdk/memfault_components/memfault_chunk_transport.c 1 PARENT-5-PROJECT_LOC/components/util/src/memfault_chunk_transport.c sdk/memfault_components/memfault_circular_buffer.c 1 PARENT-5-PROJECT_LOC/components/util/src/memfault_circular_buffer.c sdk/memfault_components/memfault_core_freertos.c 1 PARENT-5-PROJECT_LOC/ports/freertos/src/memfault_core_freertos.c sdk/memfault_components/memfault_core_utils.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_core_utils.c sdk/memfault_components/memfault_coredump.c 1 PARENT-5-PROJECT_LOC/components/panics/src/memfault_coredump.c sdk/memfault_components/memfault_coredump_regions_armv7.c 1 PARENT-5-PROJECT_LOC/components/panics/src/memfault_coredump_regions_armv7.c sdk/memfault_components/memfault_coredump_sdk_regions.c 1 PARENT-5-PROJECT_LOC/components/panics/src/memfault_coredump_sdk_regions.c sdk/memfault_components/memfault_coredump_storage_debug.c 1 PARENT-5-PROJECT_LOC/components/panics/src/memfault_coredump_storage_debug.c sdk/memfault_components/memfault_crc16_ccitt.c 1 PARENT-5-PROJECT_LOC/components/util/src/memfault_crc16_ccitt.c sdk/memfault_components/memfault_data_export.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_data_export.c sdk/memfault_components/memfault_data_packetizer.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_data_packetizer.c sdk/memfault_components/memfault_demo_cli_aux.c 1 PARENT-5-PROJECT_LOC/components/demo/src/panics/memfault_demo_cli_aux.c sdk/memfault_components/memfault_demo_cli_trace_event.c 1 PARENT-5-PROJECT_LOC/components/demo/src/memfault_demo_cli_trace_event.c sdk/memfault_components/memfault_demo_core.c 1 PARENT-5-PROJECT_LOC/components/demo/src/memfault_demo_core.c sdk/memfault_components/memfault_demo_panics.c 1 PARENT-5-PROJECT_LOC/components/demo/src/panics/memfault_demo_panics.c sdk/memfault_components/memfault_demo_shell.c 1 PARENT-5-PROJECT_LOC/components/demo/src/memfault_demo_shell.c sdk/memfault_components/memfault_event_storage.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_event_storage.c sdk/memfault_components/memfault_fault_handling_arm.c 1 PARENT-5-PROJECT_LOC/components/panics/src/memfault_fault_handling_arm.c sdk/memfault_components/memfault_freertos_ram_regions.c 1 PARENT-5-PROJECT_LOC/ports/freertos/src/memfault_freertos_ram_regions.c sdk/memfault_components/memfault_log.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_log.c sdk/memfault_components/memfault_log_data_source.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_log_data_source.c sdk/memfault_components/memfault_metrics.c 1 PARENT-5-PROJECT_LOC/components/metrics/src/memfault_metrics.c sdk/memfault_components/memfault_metrics_freertos.c 1 PARENT-5-PROJECT_LOC/ports/freertos/src/memfault_metrics_freertos.c sdk/memfault_components/memfault_metrics_reliability.c 1 PARENT-5-PROJECT_LOC/components/metrics/src/memfault_metrics_reliability.c sdk/memfault_components/memfault_metrics_serializer.c 1 PARENT-5-PROJECT_LOC/components/metrics/src/memfault_metrics_serializer.c sdk/memfault_components/memfault_minimal_cbor.c 1 PARENT-5-PROJECT_LOC/components/util/src/memfault_minimal_cbor.c sdk/memfault_components/memfault_panics_freertos.c 1 PARENT-5-PROJECT_LOC/ports/freertos/src/memfault_panics_freertos.c sdk/memfault_components/memfault_ram_reboot_info_tracking.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_ram_reboot_info_tracking.c sdk/memfault_components/memfault_reboot_tracking_serializer.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_reboot_tracking_serializer.c sdk/memfault_components/memfault_sdk_assert.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_sdk_assert.c sdk/memfault_components/memfault_serializer_helper.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_serializer_helper.c sdk/memfault_components/memfault_trace_event.c 1 PARENT-5-PROJECT_LOC/components/core/src/memfault_trace_event.c sdk/memfault_components/memfault_varint.c 1 PARENT-5-PROJECT_LOC/components/util/src/memfault_varint.c SDKROOT $%7BPARENT-9-PROJECT_LOC%7D ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/config/custom_config_qspi.h ================================================ /** **************************************************************************************** * * @file custom_config_qspi.h * * @brief Board Support Package. User Configuration file for cached QSPI mode. * * Copyright (C) 2015-2019 Dialog Semiconductor. * This computer program includes Confidential, Proprietary Information * of Dialog Semiconductor. All Rights Reserved. * **************************************************************************************** */ #ifndef CUSTOM_CONFIG_QSPI_H_ #define CUSTOM_CONFIG_QSPI_H_ #include "bsp_definitions.h" #define CONFIG_RETARGET #define dg_configUART_ADAPTER (1) #define dg_configUSE_CONSOLE (1) #define dg_configUSE_MEMFAULT (1) #define dg_configUSE_LP_CLK (LP_CLK_32768) #define dg_configEXEC_MODE MODE_IS_CACHED #define dg_configCODE_LOCATION NON_VOLATILE_IS_FLASH #define dg_configFLASH_CONNECTED_TO (FLASH_CONNECTED_TO_1V8) #define dg_configFLASH_POWER_DOWN (0) #define dg_configPOWER_1V8_ACTIVE (1) #define dg_configPOWER_1V8_SLEEP (1) #define dg_configUSE_WDOG (0) #define dg_configUSE_SW_CURSOR (1) #define dg_configRETAINED_UNINIT_SECTION_SIZE (4096) #define dg_configDISABLE_BACKGROUND_FLASH_OPS (1) /*************************************************************************************************\ * FreeRTOS specific config */ #define OS_FREERTOS /* Define this to use FreeRTOS */ #define configTOTAL_HEAP_SIZE 24000 /* This is the FreeRTOS Total Heap Size */ /*************************************************************************************************\ * Peripheral specific config */ #define dg_configRF_ENABLE_RECALIBRATION (0) #define dg_configFLASH_ADAPTER (0) #define dg_configNVMS_ADAPTER (0) #define dg_configNVMS_VES (0) /* Include bsp default values */ #include "bsp_defaults.h" /* Include middleware default values */ #include "middleware_defaults.h" #endif /* CUSTOM_CONFIG_QSPI_H_ */ ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/config/memfault_metrics_heartbeat_config.def ================================================ MEMFAULT_METRICS_KEY_DEFINE(MainTaskWakeups, kMemfaultMetricType_Unsigned) ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH 1 // Note: The default location coredumps are saved is NVMS log storage. // This size can be adjusted depending on the amount of RAM regions collected // in memfault_platform_coredump_get_regions() #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES (32 * 1024) #define MEMFAULT_USE_GNU_BUILD_ID 1 #ifdef __cplusplus } #endif ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/config/memfault_trace_reason_user_config.def ================================================ //! Define custom error reasons that can be filtered & searched //! on in the Memfault UI, i.e MEMFAULT_TRACE_REASON_DEFINE(critical_error) ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/main.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A minimal example app for exercising the Memfault SDK with the DA1469x SDK. #include #include #include #include "console.h" #include "hw_cpm.h" #include "hw_gpio.h" #include "hw_watchdog.h" #include "memfault/components.h" #include "osal.h" #include "resmgmt.h" #include "sys_clock_mgr.h" #include "sys_power_mgr.h" static int prv_send_char(char c) { printf("%c", c); return 0; } static int prv_test_storage(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { GLOBAL_INT_DISABLE(); memfault_coredump_storage_debug_test_begin(); GLOBAL_INT_RESTORE(); memfault_coredump_storage_debug_test_finish(); return 0; } static int prv_export_data(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) { memfault_data_export_dump_chunks(); return 0; } static const sMemfaultShellCommand s_memfault_shell_commands[] = { { "crash", memfault_demo_cli_cmd_crash, "" }, { "trace", memfault_demo_cli_cmd_trace_event_capture, "" }, { "get_core", memfault_demo_cli_cmd_get_core, "" }, { "clear_core", memfault_demo_cli_cmd_clear_core, "" }, { "test_storage", prv_test_storage, "" }, { "get_device_info", memfault_demo_cli_cmd_get_device_info, "" }, { "reboot", memfault_demo_cli_cmd_system_reboot, "" }, { "export", prv_export_data, "" }, { "help", memfault_shell_help_handler, "" }, }; const sMemfaultShellCommand *const g_memfault_shell_commands = s_memfault_shell_commands; const size_t g_memfault_num_shell_commands = MEMFAULT_ARRAY_SIZE(s_memfault_shell_commands); static void cli_task(void *pvParameters) { //! We will use the Memfault CLI shell as a very basic debug interface const sMemfaultShellImpl impl = { .send_char = prv_send_char, }; memfault_demo_shell_boot(&impl); while (1) { char rx_byte; console_read(&rx_byte, 1); memfault_demo_shell_receive_char(rx_byte); } } static void periph_init(void) { } static void prvSetupHardware(void) { pm_system_init(periph_init); } static OS_TASK s_main_task_hdl; static void system_init(void *pvParameters) { OS_TASK task_h = NULL; cm_sys_clk_init(sysclk_XTAL32M); cm_apb_set_clock_divider(apb_div1); cm_ahb_set_clock_divider(ahb_div1); cm_lp_clk_init(); /* Prepare the hardware to run this demo. */ prvSetupHardware(); #if defined CONFIG_RETARGET extern void retarget_init(void); retarget_init(); #endif memfault_platform_boot(); // Configure sleep modes pm_sleep_mode_set(pm_mode_extended_sleep); pm_set_sys_wakeup_mode(pm_sys_wakeup_mode_fast); OS_TASK_CREATE("CLI", cli_task, NULL, 1024 * OS_STACK_WORD_SIZE, OS_TASK_PRIORITY_NORMAL, task_h); OS_ASSERT(task_h); OS_TASK_DELETE(s_main_task_hdl); } int main(void) { OS_BASE_TYPE status; status = OS_TASK_CREATE("SysInit", system_init, (void *)0, 1024 * OS_STACK_WORD_SIZE, OS_TASK_PRIORITY_HIGHEST, s_main_task_hdl); OS_ASSERT(status == OS_TASK_CREATE_SUCCESS); vTaskStartScheduler(); // Scheduler should have started. If we get here an error took place // such as not enough memory to allocate required contexts in FreeRTOS heap MEMFAULT_ASSERT(0); } void vApplicationMallocFailedHook(void) { MEMFAULT_ASSERT(0); } void vApplicationIdleHook(void) { } void vApplicationTickHook(void) { } ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/makefile.targets ================================================ LDSCRIPT_PATH=../ldscripts .PHONY: main-build pre-build generate_ldscripts FORCE main-build : | pre-build FORCE: generate_ldscripts : mem.ld sections.ld %.ld : $(LDSCRIPT_PATH)/%.ld.h FORCE "$(CC)" -I "$(BSP_CONFIG_DIR)" -I "$(MIDDLEWARE_CONFIG_DIR)" $(PRE_BUILD_EXTRA_DEFS) -imacros "$(APP_CONFIG_H)" $(LD_DEFS) -Ddg_configDEVICE=$(DEVICE) -E -P -c "$<" -o "$@" ================================================ FILE: examples/dialog/da1469x/apps/memfault_demo_app/memfault_platform_device_info.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Example populating memfault_platform_get_device_info() implementation for DA1469x #include #include #include "memfault/components.h" void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { *info = (struct MemfaultDeviceInfo){ .device_serial = "DEMOSERIAL", .software_version = memfault_create_unique_version_string("1.0.0"), .hardware_version = "DA14695", .software_type = "da1469x-demo-app", }; } ================================================ FILE: examples/esp32/README.md ================================================ # Memfault for ESP32 This example application shows an integration with the ESP-IDF v5.0.2 SDK where a saved coredump is posted to the Memfault cloud for analysis. If you already have an ESP-IDF project based on the v5.x, v4.x, or v3.x SDK, a step by step getting started guide can be found [here](https://mflt.io/esp-tutorial). The Memfault SDK has been tested to be compatible with these versions of ESP-IDF: - v3.x release series - v4.x release series v5.x release series through v5.0 Other versions may be also be compatible but have not been verified by Memfault. The memfault SDK hooks into the pre-existing mechanism for [saving coredumps](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/core_dump.html). We save the Memfault coredump data in the exact same coredump partition the default implementation would use in the Memfault coredump format. This yields several main benefits: - The memory regions which are collected collected is highly configurable (via `memfault_platform_coredump_get_regions()`). This means all of RAM can be collected or just key globals and statics depending on your use case. In the Memfault cloud you will be able to inspect any of the variables collected, just like you would in an IDE or GDB. - FreeRTOS thread recovery and traversal can take place in the Memfault cloud. By moving the recovery off the device, the fault handling logic is simplified and it's more likely for data to be recovered in the case where the crash happened as a result of memory corruption. - Data can be posted directly from device to Memfault cloud for deduplication and analysis The demo app is tested on the [ESP-WROVER-KIT](https://www.espressif.com/en/products/hardware/esp-wrover-kit/overview). The instructions below assume this development board is being used. ## Getting started Make sure you have read the instructions in the `README.md` in the root of the SDK and performed the installation steps that are mentioned there. We assume you have a working setup for the [v5.0.2 SDK](https://docs.espressif.com/projects/esp-idf/en/v5.0.2/): - have a version of CMAKE installed - installed the xtensa [toolchain](https://docs.espressif.com/projects/esp-idf/en/v5.0.2/get-started/index.html#setup-toolchain) and added it to your path ### Adding Memfault to the ESP-IDF SDK 1. Delete the dummy esp-idf directory (if present) and clone a copy of the v5.2.1 SDK. ```bash cd examples/esp32/ rm -rf esp-idf git clone -b v5.2.1 --recursive https://github.com/espressif/esp-idf.git esp-idf cd esp-idf export IDF_TOOLS_PATH=$(pwd) # you may need to install the sdk tools by running ./install.sh here source ./export.sh ``` 1. Install the memfault build id utility: ```bash pip install mflt-build-id ``` That's it! You should be good to go! ### Memfault Project Key An API key will need to be configured for Memfault HTTP client to communicate with Memfault's web services. Go to , navigate to the project you want to use and select 'Settings'. Copy the 'Project Key', and configure it; either by running `idf.py menuconfig` and setting the `MEMFAULT_PROJECT_KEY` config value, or by inserting to `sdkconfig.defaults`: ```kconfig CONFIG_MEMFAULT_PROJECT_KEY="" ``` > Note: when doing a clean build, or a build in CI, another option is to place > the Project Key in a second `sdkconfig.defaults` file, for example: > > ```bash > ❯ echo CONFIG_MEMFAULT_PROJECT_KEY=\"\" > sdkconfig.extra > ❯ idf.py build -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.extra" > ``` ### Building the demo app using pyinvoke: ```bash invoke esp32.build ``` or use the Espressif command: ```bash cd examples/esp32/apps/memfault_demo_app && idf.py build ``` ### Flashing the demo app ```bash invoke esp32.flash ``` or use the Espressif command: ```bash cd examples/esp32/apps/memfault_demo_app && idf.py -p flash ``` ### Console ```bash invoke esp32.console ``` or ```bash miniterm.py ftdi://ftdi:2232/2 115200 ``` Press enter and you should see the `esp32>` prompt. Enter the `help` command to see a list of commands from the [Memfault demo CLI](https://mflt.io/demo-cli). ## Using the demo app The demo app is a simple console based app that has commands to cause a crash in several ways. Once a coredump is captured, it can be sent via WiFi to Memfault's web services to get analyzed and desymbolicated. The `memfault/http` component of the Memfault SDK is used to talk to Memfault's web services. Let's walk through this process step by step: ### Memfault Project Key An API key will need to be baked into the demo app to enable it to communicate with Memfault's web services. Go to , navigate to the project you want to use and select 'Settings'. Copy the 'Project Key' and paste it into `esp32/apps/memfault_demo_app/main/config.c`, replacing `` with your API key. Save the file and rebuild, reflash the project and attach the debug console (see instruction above). ### Checking the device info As a sanity check, let's request the device info from the debug console, enter `get_device_info` and press enter: ```plaintext esp32> get_device_info I (55239) mflt: S/N: 30AEA44AFF28 I (55239) mflt: SW type: esp32-main I (55239) mflt: SW version: 1.0.0-dev I (55239) mflt: HW version: esp32-proto ``` In the platform reference implementation for ESP32, the hardware version is hard-coded to `esp32-proto`, software type is hard-coded to `esp32-main` and the WiFi MAC address is used as serial number. You can change this to match your requirements (see [memfault_platform_device_info.c](apps/memfault_demo_app/main/memfault_platform_device_info.c)) ### Causing a crash Command `crash 1` will trigger a hard fault due to a bad instruction fetch at a non-existing address, `0xbadcafe`: ```plaintext esp32> crash 0 abort() was called at PC 0x400e54a3 on core 0 Backtrace: 0x4008fef0:0x3ffbc310 0x400900c7:0x3ffbc330 0x400e54a3:0x3ffbc350 0x400e5c0e:0x3ffbc370 0x4010a587:0x3ffbc390 0x400e4d09:0x3ffbc3b0 0x400d28b6:0x3ffbc3e0 Saving Memfault Coredump! Rebooting... ``` Upon crashing, the coredump will be written to SPI flash. Note this can take a several seconds. Once done, the board should reboot and show the console prompt again. To check whether the coredump has been captured, try running the `get_core` command: ```plaintext esp32> get_core I (77840) mflt: Has coredump with size: 768 ``` ### Posting the coredump to Memfault for analysis #### Uploading Symbols Memfault needs the symbols for the firmware in order to analyze the coredump. The ESP32 SDK demo app symbol file can be found in the build folder: `apps/memfault_demo_app/build/memfault-esp32-demo-app.elf.memfault_log_fmt` > Note: the file to be uploaded is > `memfault-esp32-demo-app.elf.memfault_log_fmt`, _not_ > `memfault-esp32-demo-app.elf`, when using compact logs! This ELF file contains the symbols (debug information) amongst other things. [More information on Build Ids and uploading Symbol Files can be found here](https://mflt.io/symbol-file-build-ids). #### Post coredump To post the collected crash to the Memfault cloud, first join a WiFi network: ```plaintext esp32> join I (116450) connect: Connecting to '' ``` Then post the data: ```plaintext esp32> post_chunks I (12419) mflt: Posting Memfault Data... I (12419) mflt: Result: 0 ``` ### Clearing a coredump New coredumps are only written if the coredump storage area is not already occupied. Typically coredumps are cleared once they have been posted to the memfault cloud by using the `memfault_platform_coredump_storage_clear` function. You can invoke this command from the cli by running `clear_core`. ### Checking coredump storage capacity Memfault coredumps will be written to the `coredump` partition by default. Use the `coredump_size` command to see the computed maximum coredump size, and the available storage capacity: ```bash esp32> coredump_size coredump storage capacity: 262144B coredump size required: 456988B ``` ### Testing OTA The following steps can be used to exercise OTA functionality: 1. Build and flash the default image per [the instructions above](#getting-started). Confirm by running `get_device_info` in the `idf.py monitor` console that the software version is `1.0.0-dev`: ```bash esp32> get_device_info I (8358) mflt: S/N: A8032AEBF8E4 I (8358) mflt: SW type: esp32-main I (8358) mflt: SW version: 1.0.1-dev I (8358) mflt: HW version: esp32-proto ``` 2. If not already done, post some data from the device to register it in your Memfault project's fleet: ```bash esp32> join ... I (294468) connect: Connected esp32> post_chunks D (26028) mflt: Posting Memfault Data D (33288) mflt: Posting Memfault Data Complete! I (33288) mflt: Result: 0 ``` Optionally, persist these settings to non-volatile memory so your device auto-connects to your network on boot: ```bash esp32> wifi_config ``` 3. Use `idf.py menuconfig` to change the value of `MEMFAULT_DEVICE_INFO_SOFTWARE_VERSION` config variable to `"1.0.1"`, and rebuild (`idf.py build`), but don't load it to the device. We'll use this build as our OTA payload! 4. In the Memfault web app, go to "Software->OTA Releases" and create an OTA release for `1.0.1-dev`. Click "Add OTA Payload to Release", select `esp32-proto` as the "Hardware Version" ("Software Type" should auto fill to `esp32-main`), and upload the `apps/memfault_demo_app/build/memfault-esp32-demo-app.bin` file as the payload (note: this step can also be done with the `memfault` cli, see [here](https://mflt.io/release-mgmt) for more information) 5. Deploy the release to your device; add it to a cohort under "Fleet->Cohorts", and set the "Target Release" to the `1.0.1-dev` version just created. 6. Back to the esp32 console, run `memfault_ota_check` to confirm the release is available for our device: ```bash esp32> memfault_ota_check D (274688) mflt: Checking for OTA Update D (276338) mflt: OTA Update Available D (276338) mflt: Up to date! ``` 7. Now trigger the OTA release. The board will download the release and install it to the inactive OTA slot, then reboot (example output below truncated for brevity): ```bash esp32> memfault_ota_perform D (1455118) mflt: Checking for OTA Update D (1456618) mflt: OTA Update Available I (1456628) mflt: Starting OTA download ... I (1457798) esp_https_ota: Starting OTA... I (1457798) esp_https_ota: Writing to partition subtype 16 at offset 0x1e0000 I (1476958) esp_https_ota: Connection closed ... I (1477758) mflt: OTA Update Complete, Rebooting System ... Rebooting... ... ``` 8. When the console is back up, confirm that the update was applied by checking the Software Version: ```bash esp32> get_device_info I (8358) mflt: S/N: A8032AEBF8E4 I (8358) mflt: SW type: esp32-main I (8358) mflt: SW version: 1.0.1-dev I (8358) mflt: HW version: esp32-proto ``` For more details on how to use the CLI to explore each of Memfault's subsystems, see the [Memfault docs](https://mflt.io/demo-cli). ================================================ FILE: examples/esp32/apps/memfault_demo_app/.gitignore ================================================ /dependencies.lock /managed_components/ /sdkconfig /sdkconfig.extra /sdkconfig.old ================================================ FILE: examples/esp32/apps/memfault_demo_app/CMakeLists.txt ================================================ # The following lines of boilerplate have to be in your project's CMakeLists # in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) set(PROJECT_NAME memfault-esp32-demo-app) set(CMAKE_PROJECT_NAME ${PROJECT_NAME}) include($ENV{IDF_PATH}/tools/cmake/version.cmake OPTIONAL) # This contains the implementation for the led_strip driver. In ESP-IDF v5 it # moved to a "managed component" and no longer needs to be manually added. if(DEFINED IDF_VERSION_MAJOR) if(IDF_VERSION_MAJOR VERSION_LESS 5) set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/led_strip) endif() endif() get_filename_component(memfault_firmware_sdk_dir ../../../../ ABSOLUTE) # If we found the Memfault SDK, include it in the build if(EXISTS ${memfault_firmware_sdk_dir}/ports/esp_idf/memfault.cmake) include(${memfault_firmware_sdk_dir}/ports/esp_idf/memfault.cmake) else() # Otherwise, append this to the main/idf_component.yml file: # memfault/memfault-firmware-sdk: "*" # Ideally we'd push an environment variable and use a conditional dependency # https://docs.espressif.com/projects/idf-component-manager/en/latest/reference/manifest_file.html#conditional-dependencies # But that requires idf-component-manager v2+, which is not available on all # esp-idf versions # if the string "memfault/memfault-firmware-sdk" isn't in the file, append it: file(READ ${CMAKE_CURRENT_SOURCE_DIR}/main/idf_component.yml idf_component_yml) if(NOT idf_component_yml MATCHES "memfault/memfault-firmware-sdk") file(APPEND ${CMAKE_CURRENT_SOURCE_DIR}/main/idf_component.yml " memfault/memfault-firmware-sdk: \"*\"\n" ) endif() endif() # NOTE: This include also applies global compiler options, make sure # this happens first before defining other targets! # The esp-idf project() macro creates a project_name.elf target: include($ENV{IDF_PATH}/tools/cmake/project.cmake) # Wrap the mbedtls netif read/write functions, for instrumentation. # These MUST be before the project() function below, or they are ignored if(IDF_VERSION_MAJOR VERSION_GREATER_EQUAL 4) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=mbedtls_net_send,--wrap=mbedtls_net_recv" APPEND) endif() project(${PROJECT_NAME}) # Enable a compilation error if any deprecated APIs are used. This helps detect # bleeding edge changes in the ESP-IDF. if (IDF_VERSION_MAJOR VERSION_GREATER_EQUAL 5) idf_build_set_property(COMPILE_OPTIONS "-Werror=deprecated-declarations" APPEND) endif() ================================================ FILE: examples/esp32/apps/memfault_demo_app/README.md ================================================ # esp32 Demo Application This Demo App is based on the console example from ESP-IDF, which can be found here relative to the ESP-IDF SDK root folder: - `examples/system/console/advanced/` ## Configuring for MQTT This application includes an option to send Memfault data over MQTT. This option requires a few extra pieces to set up. You can either follow the steps outlined here or use your own MQTT setup. ### Broker Setup 1. Install a local installation of Cedalo by following the [installation guide](https://docs.cedalo.com/management-center/installation/) 2. Login to Cedalo at 3. Create a new client login for the device - Ensure device client has the "client" role to allow publishing data 4. Create a new client login for the Python service - Ensure Python service client has "client" role to allow subscribing to data ### Service Setup 1. Modify the script found in [Docs->Best Practices->MQTT](https://docs.memfault.com/docs/best-practices/mqtt-with-memfault#service-examples) with Memfault with the the following: 1. The service client login information previously created 2. Connection info for your local broker 3. Map of Memfault projects to project keys 2. Start the service by running `python mqtt.py` ### Device Setup 1. Make the following modifications to `main/app_memfault_transport_mqtt.c`: 1. Update `MEMFAULT_PROJECT` macro with your project's name 2. Update `s_mqtt_config` with your setup's IP address, and MQTT client username and password 2. Clean your existing build with `idf.py fullclean && rm sdkconfig` 3. Set your target: `idf.py set-target ` 4. Build your image: `idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.mqtt" build` 5. Flash to your device using `idf.py flash` ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/CMakeLists.txt ================================================ list(APPEND COMPONENT_SRCS button.c led.c main.c ) if (CONFIG_MEMFAULT) list(APPEND COMPONENT_SRCS cmd_app.c cmd_system.c deep_sleep.c metrics.c ota_session_metrics.c ) if (CONFIG_APP_MEMFAULT_TRANSPORT_HTTP) list(APPEND COMPONENT_SRCS app_memfault_transport_http.c) elseif(CONFIG_APP_MEMFAULT_TRANSPORT_MQTT) list(APPEND COMPONENT_SRCS app_memfault_transport_mqtt.c) endif() # the 'cmd_wifi.c' implementation is different for ESP-IDF v5+ if("${IDF_VERSION_MAJOR}" VERSION_GREATER_EQUAL 5) list(APPEND COMPONENT_SRCS cmd_wifi.c ) else() list(APPEND COMPONENT_SRCS cmd_wifi_legacy.c ) endif() # include settings.c only on idf >= 4 if("${IDF_VERSION_MAJOR}" VERSION_GREATER_EQUAL 4) list(APPEND COMPONENT_SRCS settings.c ) endif() set(COMPONENT_ADD_INCLUDEDIRS . config ) endif() register_component() ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/Kconfig.projbuild ================================================ menu "Memfault App Configuration" config STORE_HISTORY bool "Store command history in flash" default n help Linenoise line editing library provides functions to save and load command history. If this option is enabled, initializes a FAT filesystem and uses it to store command history. Note that this adds a ~22kB heap allocation on system boot. config MEMFAULT_APP_WIFI_AUTOJOIN bool "Enable automatic WiFi connection" default y depends on MEMFAULT help Automatically join if credentials are configured. config MEMFAULT_APP_HEAP_TRACING bool "Print allocation tracing information at runtime" default n depends on HEAP_USE_HOOKS choice APP_MEMFAULT_TRANSPORT prompt "Protocol to send chunks over" default APP_MEMFAULT_TRANSPORT_HTTP config APP_MEMFAULT_TRANSPORT_HTTP bool "HTTP" config APP_MEMFAULT_TRANSPORT_MQTT bool "MQTT" select MQTT_PROTOCOL_5 depends on !MEMFAULT_HTTP_PERIODIC_UPLOAD endchoice config MEMFAULT_APP_AUTO_DEEP_SLEEP bool "Enable deep sleep" default n select MEMFAULT_DEEP_SLEEP_SUPPORT help Enable deep sleep functionality. This will put the device into deep sleep periodically, and auto wake up. config MEMFAULT_APP_AUTO_DEEP_SLEEP_PERIOD_SECONDS int "Deep sleep period in seconds" default 60 depends on MEMFAULT_APP_AUTO_DEEP_SLEEP help The period in seconds to put the device into deep sleep. This is only used if MEMFAULT_APP_AUTO_DEEP_SLEEP is enabled. config MEMFAULT_APP_TASK_WDT_TIMEOUT_S int "Task Watchdog timeout in seconds" default ESP_TASK_WDT_TIMEOUT_S default 5 help Helper symbol to make it simple to get the Task Watchdog timeout value whether it's enabled or not. if APP_MEMFAULT_TRANSPORT_MQTT config MEMFAULT_DEVICE_INFO_SOFTWARE_TYPE string "Override default software version for the application" default "esp32-main-mqtt" endif # These LED settings are taken from ESP-IDF: # examples/get-started/blink/main/blink_example_main.c choice BLINK_LED prompt "Blink LED type" default BLINK_LED_GPIO if IDF_TARGET_ESP32 || !SOC_RMT_SUPPORTED default BLINK_LED_RMT help Defines the default peripheral for blink example config BLINK_LED_GPIO bool "GPIO" config BLINK_LED_RMT bool "RMT - Addressable LED" endchoice config BLINK_LED_RMT_CHANNEL depends on BLINK_LED_RMT int "RMT Channel" range 0 7 default 0 help Note: this is only used if ESP-IDF version is < 5.0. See led.c. Set the RMT peripheral channel. ESP32 RMT channel from 0 to 7 ESP32-S2 RMT channel from 0 to 3 ESP32-S3 RMT channel from 0 to 3 ESP32-C3 RMT channel from 0 to 1 config BLINK_GPIO int "Blink GPIO number" range 0 48 default 5 if IDF_TARGET_ESP32 default 18 if IDF_TARGET_ESP32S2 default 48 if IDF_TARGET_ESP32S3 default 8 help GPIO number (IOxx) to blink on and off or the RMT signal for the addressable LED. Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink. config BUTTON_GPIO int "Input pin for the EN button on the dev board" # https://dl.espressif.com/dl/schematics/esp32_devkitc_v4_sch.pdf # https://dl.espressif.com/dl/schematics/esp-idf/SCH_ESP32-S2-DEVKITC-1_V1_20220817.pdf # https://dl.espressif.com/dl/schematics/SCH_ESP32-S3-DevKitC-1_V1.1_20221130.pdf default 0 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 # https://dl.espressif.com/dl/schematics/SCH_ESP32-C3-DEVKITC-02_V1_1_20210126A.pdf # https://dl.espressif.com/dl/schematics/SCH_ESP32-C3-DEVKITM-1_V1_20200915A.pdf # https://docs.espressif.com/projects/esp-dev-kits/en/latest/_static/esp32-c6-devkitc-1/schematics/esp32-c6-devkitc-1-schematics_v1.4.pdf default 9 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 default 0 help GPIO pin number for the BOOT button. Used to trigger a crash for testing. # esp32c3/c6 specific settings if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 # Default optimize for size due to increase in binary size # This can be changed in root -> Compiler Options # The choice and prompt lines are required and must match the esp-idf definition choice COMPILER_OPTIMIZATION prompt "Optimization Level" default COMPILER_OPTIMIZATION_SIZE endchoice endif endmenu ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/app_memfault_transport.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #ifdef __cplusplus extern "C" { #endif //! Initializes any components needed for configured transport //! //! Transport is selected via CONFIG_APP_MEMFAULT_TRANSPORT choices void app_memfault_transport_init(void); //! Sends all available Memfault chunks over configured transport //! //! Transport is selected via CONFIG_APP_MEMFAULT_TRANSPORT choices //! //! @return 0 on success or non-zero on error int app_memfault_transport_send_chunks(void); #ifdef __cplusplus } #endif ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/app_memfault_transport_http.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "app_memfault_transport.h" #include "memfault/esp_port/http_client.h" void app_memfault_transport_init(void) { } int app_memfault_transport_send_chunks(void) { return memfault_esp_port_http_client_post_data(); } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/app_memfault_transport_mqtt.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include #include #include "app_memfault_transport.h" #include "esp_log.h" #include "memfault/components.h" #include "mqtt_client.h" // TODO: Fill in with device's Memfault project #define MEMFAULT_PROJECT "my_project" static const char *TAG = "app_memfault_transport_mqtt"; // TODO: Fill in with broker connection configuration static esp_mqtt_client_config_t s_mqtt_config = { .broker.address.uri = "mqtt://192.168.50.88", .credentials.username = "test", .credentials.authentication.password = "test1234", .session.protocol_ver = MQTT_PROTOCOL_V_5, }; static SemaphoreHandle_t s_mqtt_connected = NULL; static esp_mqtt_client_handle_t s_mqtt_client = NULL; static esp_mqtt5_publish_property_config_t s_publish_property = { .topic_alias = 1, }; static char s_topic_string[128] = { 0 }; static uint8_t s_chunk_data[1024] = { 0 }; static void mqtt_event_handler(MEMFAULT_UNUSED void *handler_args, MEMFAULT_UNUSED esp_event_base_t base, MEMFAULT_UNUSED int32_t event_id, void *event_data) { esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data; switch (event->event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "Connected to MQTT broker"); xSemaphoreGive(s_mqtt_connected); break; default: ESP_LOGE(TAG, "Unknown MQTT event received[%d]", event->event_id); break; } } static void prv_close_client(void) { int rv = esp_mqtt_client_disconnect(s_mqtt_client); if (rv) { ESP_LOGW(TAG, "Failed to disconnect[%d]", rv); } rv = esp_mqtt_client_destroy(s_mqtt_client); if (rv) { ESP_LOGW(TAG, "Failed to destroy client[%d]", rv); } MEMFAULT_METRIC_TIMER_STOP(mqtt_conn_uptime); s_mqtt_client = NULL; } static int prv_create_client(void) { if (s_mqtt_client) { return 0; } s_mqtt_client = esp_mqtt_client_init(&s_mqtt_config); if (s_mqtt_client == NULL) { ESP_LOGE(TAG, "MQTT client failed to initialize"); return -1; } int rv = esp_mqtt_client_register_event(s_mqtt_client, MQTT_EVENT_CONNECTED, mqtt_event_handler, NULL); if (rv) { ESP_LOGE(TAG, "MQTT event handler registration failed[%d]", rv); } rv = esp_mqtt_client_start(s_mqtt_client); if (rv) { ESP_LOGE(TAG, "MQTT client start failed[%d]", rv); return -1; } // Wait for successful connection rv = xSemaphoreTake(s_mqtt_connected, (1000 * 10) / portTICK_PERIOD_MS); if (rv != pdTRUE) { ESP_LOGE(TAG, "MQTT client failed to connect[%d]", rv); MEMFAULT_METRIC_TIMER_START(mqtt_conn_downtime); prv_close_client(); return -1; } // Update connection metrics when connected MEMFAULT_METRIC_TIMER_STOP(mqtt_conn_downtime); MEMFAULT_METRIC_TIMER_START(mqtt_conn_uptime); // Set topic alias before publishing rv = esp_mqtt5_client_set_publish_property(s_mqtt_client, &s_publish_property); if (rv != 0) { ESP_LOGW(TAG, "MQTT client could not set publish property [%d]", rv); } return 0; } static const char *prv_get_device_serial(void) { sMemfaultDeviceInfo info = { 0 }; memfault_platform_get_device_info(&info); return info.device_serial; } void prv_build_topic_string(void) { // String already built if (strlen(s_topic_string) > 0) { return; } const char *device_serial = prv_get_device_serial(); snprintf(s_topic_string, MEMFAULT_ARRAY_SIZE(s_topic_string), "memfault/" MEMFAULT_PROJECT "/%s/chunks", device_serial); } void app_memfault_transport_init(void) { #if MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION != 0 static StaticSemaphore_t s_mqtt_connected_buf; s_mqtt_connected = xSemaphoreCreateBinaryStatic(&s_mqtt_connected_buf); #else s_mqtt_connected = xSemaphoreCreateBinary(); #endif } int app_memfault_transport_send_chunks(void) { int rv = prv_create_client(); if (rv) { return rv; } prv_build_topic_string(); ESP_LOGD(TAG, "Checking for packetizer data"); while (memfault_packetizer_data_available()) { size_t chunk_size = MEMFAULT_ARRAY_SIZE(s_chunk_data); bool chunk_filled = memfault_packetizer_get_chunk(s_chunk_data, &chunk_size); if (!chunk_filled) { ESP_LOGW(TAG, "No chunk data produced"); break; } rv = esp_mqtt_client_publish(s_mqtt_client, s_topic_string, (char *)s_chunk_data, chunk_size, 1, 0); if (rv < 0) { ESP_LOGE(TAG, "MQTT failed to publish[%d]", rv); memfault_packetizer_abort(); break; } MEMFAULT_METRIC_ADD(mqtt_publish_bytes, chunk_size); MEMFAULT_METRIC_ADD(mqtt_publish_count, 1); ESP_LOGD(TAG, "chunk[%d], len[%zu] published to %s", rv, chunk_size, s_topic_string); } prv_close_client(); return rv; } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/button.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Button setup and handling #include "button.h" #include "driver/gpio.h" #include "esp_attr.h" #include "esp_log.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" static const char *TAG = "button"; static portMUX_TYPE s_button_spinlock = portMUX_INITIALIZER_UNLOCKED; static void IRAM_ATTR prv_gpio_isr_handler(void *arg) { // Make volatile to prevent compiler optimization in while loop volatile uint32_t gpio_num = (uint32_t)arg; // Grab a spinlock to ensure this ISR does not get interrupted portENTER_CRITICAL_ISR(&s_button_spinlock); // Hang the interrupt to trigger a fault from the interrupt watchdog while (true) { gpio_num++; } } // The flex glitch filter is only available on 5.1. Skip it for earlier SDKs. #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) #include "driver/gpio_filter.h" static void prv_button_glitch_filter_enable(void) { #if SOC_GPIO_FLEX_GLITCH_FILTER_NUM > 0 gpio_glitch_filter_handle_t filter; gpio_flex_glitch_filter_config_t filter_cfg = { .clk_src = GLITCH_FILTER_CLK_SRC_DEFAULT, .gpio_num = CONFIG_BUTTON_GPIO, .window_thres_ns = 500, .window_width_ns = 500, }; esp_err_t err = gpio_new_flex_glitch_filter(&filter_cfg, &filter); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to create glitch filter: %s", esp_err_to_name(err)); return; } err = gpio_glitch_filter_enable(filter); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to enable glitch filter: %s", esp_err_to_name(err)); return; } #endif } #else // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) static void prv_button_glitch_filter_enable(void) { // No-op } #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) void button_setup(void) { // configure the button as an input gpio_config_t io_conf = { .intr_type = GPIO_INTR_NEGEDGE, .mode = GPIO_MODE_INPUT, .pin_bit_mask = 1ULL << CONFIG_BUTTON_GPIO, .pull_down_en = GPIO_PULLDOWN_DISABLE, .pull_up_en = GPIO_PULLUP_ENABLE, }; esp_err_t err = gpio_config(&io_conf); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to configure button: %s", esp_err_to_name(err)); return; } prv_button_glitch_filter_enable(); // install gpio isr service err = gpio_install_isr_service(0); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to install gpio isr service: %s", esp_err_to_name(err)); return; } // install isr handler for specific gpio pin err = gpio_isr_handler_add(CONFIG_BUTTON_GPIO, prv_gpio_isr_handler, (void *)CONFIG_BUTTON_GPIO); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to add isr handler for button: %s", esp_err_to_name(err)); return; } } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/button.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Button setup and handling #pragma once void button_setup(void); ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/cmd_app.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! Miscellaneous commands specific to this example app //! #include #include #include #include "cmd_decl.h" #include "deep_sleep.h" #include "esp_console.h" #include "esp_heap_task_info.h" #include "esp_log.h" #include "memfault/esp_port/core.h" #if defined(CONFIG_ESP_TASK_WDT_EN) #include "esp_task_wdt.h" #endif #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/task.h" #include "memfault/components.h" #include "sdkconfig.h" #if MEMFAULT_TASK_WATCHDOG_ENABLE //! cause the task watchdog to fire by deadlocking the example task static int test_task_watchdog(int argc, char **argv) { (void)argc, (void)argv; ESP_LOGI(__func__, "Setting the example_task to stuck, will cause the task watchdog to fire"); xSemaphoreTakeRecursive(g_example_task_lock, portMAX_DELAY); return 0; } #endif #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) // 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 // Add a new variable to the stack uint8_t temp = 0; // Calculate bytes remaining between current stack pointer (address of temp) // and the lowest address in the stack. Stack is full descending, so subtract // 4 bytes to account for currently used location. size_t stack_remaining = (uintptr_t)&temp - (uintptr_t)s_overflow_task_stack - 4; uint8_t stack_array[stack_remaining]; // Use the array in some manner to prevent optimization for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(stack_array); i++) { stack_array[i] = i; taskYIELD(); } #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) } /** * Function to run stack overflow task, reads the lowest stack address * to see if canary values have been clobbered */ static void prv_overflow_task(MEMFAULT_UNUSED void *params) { while (true) { ulTaskNotifyTake(pdFALSE, portMAX_DELAY); prv_trigger_stack_overflow(); // For debugging purposes, print the lowest stack memory to see if canary // value is still present ESP_LOGD(__func__, "overflow triggered, lowest stack memory contents: %x", s_overflow_task_stack[0]); } } /** * Crashes overflow_task by modifying a canary byte in the stack */ static int prv_test_stack_overflow(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char **argv) { ESP_LOGI(__func__, "triggering stack overflow"); xTaskNotifyGive(s_overflow_task_handle); return 0; } static void prv_init_stack_overflow_test(void) { // Create a task with static memory which we will trigger stack overflow // checks on command s_overflow_task_handle = xTaskCreateStatic( prv_overflow_task, "overflow_task", MEMFAULT_ARRAY_SIZE(s_overflow_task_stack), NULL, tskIDLE_PRIORITY, s_overflow_task_stack, &s_overflow_task_tcb); const esp_console_cmd_t test_stack_overflow_cmd = { .command = "test_stack_overflow", .help = "Demonstrate a coredump triggered by stack overflow detection", .hint = NULL, .func = &prv_test_stack_overflow, }; 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) #if defined(CONFIG_HEAP_TASK_TRACKING) // Print out per-task heap allocations. This is lifted from the example here: // https://github.com/espressif/esp-idf/blob/v5.1.2/examples/system/heap_task_tracking/main/heap_task_tracking_main.c static int prv_heap_task_stats(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char **argv) { #define MAX_TASK_NUM 10 // Max number of per tasks info that it can store #define MAX_BLOCK_NUM 10 // Max number of per block info that it can store static size_t s_prepopulated_num = 0; static heap_task_totals_t s_totals_arr[MAX_TASK_NUM]; static heap_task_block_t s_block_arr[MAX_BLOCK_NUM]; heap_task_info_params_t heap_info = { 0 }; heap_info.caps[0] = MALLOC_CAP_8BIT; // Gets heap with CAP_8BIT capabilities heap_info.mask[0] = MALLOC_CAP_8BIT; heap_info.caps[1] = MALLOC_CAP_32BIT; // Gets heap info with CAP_32BIT capabilities heap_info.mask[1] = MALLOC_CAP_32BIT; heap_info.tasks = NULL; // Passing NULL captures heap info for all tasks heap_info.num_tasks = 0; heap_info.totals = s_totals_arr; // Gets task wise allocation details heap_info.num_totals = &s_prepopulated_num; heap_info.max_totals = MAX_TASK_NUM; // Maximum length of "s_totals_arr" heap_info.blocks = s_block_arr; // Gets block wise allocation details. For each block, gets owner // task, address and size heap_info.max_blocks = MAX_BLOCK_NUM; // Maximum length of "s_block_arr" heap_caps_get_per_task_info(&heap_info); for (int i = 0; i < *heap_info.num_totals; i++) { printf( "Task: %s -> CAP_8BIT: %d CAP_32BIT: %d\n", heap_info.totals[i].task ? pcTaskGetName(heap_info.totals[i].task) : "Pre-Scheduler allocs", heap_info.totals[i].size[0], // Heap size with CAP_8BIT capabilities heap_info.totals[i].size[1]); // Heap size with CAP32_BIT capabilities } printf("\n\n"); return 0; } #endif // CONFIG_HEAP_TASK_TRACKING #if defined(CONFIG_ESP_TASK_WDT_EN) static void prv_stuck_task(void *pvParameters) { // Register a task watchdog for the current task esp_err_t res = esp_task_wdt_add(NULL); MEMFAULT_ASSERT(res == ESP_OK); ESP_LOGI(__func__, "Stuck task started!"); while (1) { vTaskDelay(portMAX_DELAY); } // This code never runs- the task is stuck! esp_task_wdt_reset(); } static int prv_esp_task_watchdog(int argc, char **argv) { // optional argument to specify the CPU to register the idle hook for BaseType_t cpuid = 0; if (argc > 1) { cpuid = atoi(argv[1]); } // Create and start a task that will stall ESP_LOGI(__func__, "Starting stuck task on core %d", cpuid); ESP_LOGI(__func__, "Task watchdog will fire in %d seconds", CONFIG_MEMFAULT_APP_TASK_WDT_TIMEOUT_S); const portBASE_TYPE res = xTaskCreatePinnedToCore( prv_stuck_task, "stuck task", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, cpuid); MEMFAULT_ASSERT(res == pdTRUE); return 0; } #endif // CONFIG_ESP_TASK_WDT_EN #if !defined(CONFIG_MEMFAULT_RECORD_REBOOT_ON_BOOT) static int prv_collect_reboot_info(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char **argv) { memfault_esp_port_collect_reset_info(); return 0; } #endif static int prv_deep_sleep(int argc, char **argv) { if (argc < 2) { ESP_LOGE(__func__, "Usage: deep_sleep "); return -1; } uint32_t sleep_seconds = strtoul(argv[1], NULL, 0); return deep_sleep_start(sleep_seconds); } void register_app(void) { #if MEMFAULT_TASK_WATCHDOG_ENABLE const esp_console_cmd_t test_watchdog_cmd = { .command = "test_task_watchdog", .help = "Demonstrate the task watchdog tripping on a deadlock", .hint = NULL, .func = &test_task_watchdog, }; ESP_ERROR_CHECK(esp_console_cmd_register(&test_watchdog_cmd)); #endif #if defined(CONFIG_HEAP_TASK_TRACKING) const esp_console_cmd_t heap_task_stats_cmd = { .command = "heap_task_stats", .help = "Prints heap usage per task", .hint = NULL, .func = &prv_heap_task_stats, }; ESP_ERROR_CHECK(esp_console_cmd_register(&heap_task_stats_cmd)); #endif #if defined(CONFIG_ESP_TASK_WDT_EN) const esp_console_cmd_t esp_task_watchdog_cmd = { .command = "esp_task_watchdog", .help = "Register an idle hook that stalls the system", .hint = "", .func = &prv_esp_task_watchdog, }; ESP_ERROR_CHECK(esp_console_cmd_register(&esp_task_watchdog_cmd)); #endif #if !defined(CONFIG_MEMFAULT_RECORD_REBOOT_ON_BOOT) const esp_console_cmd_t collect_reboot_info_cmd = { .command = "collect_reboot_info", .help = "Serialize the reboot reason event into Memfault event storage. Use when " "CONFIG_MEMFAULT_RECORD_REBOOT_ON_BOOT=n and device info is now available.", .hint = NULL, .func = &prv_collect_reboot_info, }; ESP_ERROR_CHECK(esp_console_cmd_register(&collect_reboot_info_cmd)); #endif const esp_console_cmd_t deep_sleep_cmd = { .command = "deep_sleep", .help = "Enter deep sleep mode", .hint = NULL, .func = &prv_deep_sleep, }; ESP_ERROR_CHECK(esp_console_cmd_register(&deep_sleep_cmd)); #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) } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/cmd_decl.h ================================================ /* Console example - declarations of command registration functions. This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #pragma once #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" // Register system console commands void register_system(void); // Register WiFi console commands void register_wifi(void); // Attempt to join a wifi network bool wifi_join(const char *ssid, const char *pass); void wifi_load_creds(char **ssid, char **password); #define MEMFAULT_PROJECT_KEY_LEN 32 // if gcc < 11, disable -Wattributes, "access" attribute is not supported #if __GNUC__ < 11 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" #endif __attribute__((access(write_only, 1, 2))) int wifi_get_project_key(char *project_key, size_t project_key_len); #if __GNUC__ < 11 #pragma GCC diagnostic pop #endif // Register app-specific console commands void register_app(void); // This semaphore is used to control the Task Watchdog example task extern SemaphoreHandle_t g_example_task_lock; ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/cmd_system.c ================================================ /* Console example - various system commands This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include #include #include #include #include "argtable3/argtable3.h" #include "cmd_decl.h" #include "esp_console.h" #include "esp_heap_caps.h" #include "esp_log.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "sdkconfig.h" #ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS #define WITH_TASKS_INFO 1 #endif static void register_free(); static void register_heap_dump(); static void register_restart(); #if WITH_TASKS_INFO static void register_tasks(); #endif void register_system() { register_free(); register_heap_dump(); register_restart(); #if WITH_TASKS_INFO register_tasks(); #endif } /** 'restart' command restarts the program */ static int restart(int argc, char **argv) { ESP_LOGI(__func__, "Restarting"); esp_restart(); } static void register_restart() { const esp_console_cmd_t cmd = { .command = "restart", .help = "Restart the program", .hint = NULL, .func = &restart, }; ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); } /** 'free' command prints available heap memory */ static int free_mem(int argc, char **argv) { printf("%" PRIu32 "\n", esp_get_free_heap_size()); return 0; } static int dump_heap(int argc, char **argv) { heap_caps_print_heap_info(0); return 0; } static void register_free() { const esp_console_cmd_t cmd = { .command = "free", .help = "Get the total size of heap memory available", .hint = NULL, .func = &free_mem, }; ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); } static void register_heap_dump() { const esp_console_cmd_t cmd = { .command = "heap_dump", .help = "Dump the current heap stats", .hint = NULL, .func = &dump_heap, }; ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); } /** 'tasks' command prints the list of tasks and related information */ #if WITH_TASKS_INFO static int tasks_info(int argc, char **argv) { const size_t bytes_per_task = 40; /* see vTaskList description */ char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task); if (task_list_buffer == NULL) { ESP_LOGE(__func__, "failed to allocate buffer for vTaskList output"); return 1; } fputs("Task Name\tStatus\tPrio\tStkFree\tTask Number\n", stdout); vTaskList(task_list_buffer); fputs(task_list_buffer, stdout); free(task_list_buffer); return 0; } static void register_tasks() { const esp_console_cmd_t cmd = { .command = "tasks", .help = "Get information about running tasks", .hint = NULL, .func = &tasks_info, }; ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); } #endif // WITH_TASKS_INFO ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/cmd_wifi.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Wifi-specific commands for the ESP32 console. #include #include #include #include "argtable3/argtable3.h" #include "cmd_decl.h" #include "esp_console.h" #include "esp_event.h" #include "esp_wifi.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "settings.h" // enable for more verbose debug logs #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include "esp_log.h" // max length of saved ssid / pw, including null term #define WIFI_CREDS_MAX_SIZE 65 // cache the creds so we don't have to read NVS every time they're requested static char s_wifi_ssid[WIFI_CREDS_MAX_SIZE]; static char s_wifi_pass[WIFI_CREDS_MAX_SIZE]; #define EXAMPLE_ESP_MAXIMUM_RETRY 10 // CONFIG_ESP_MAXIMUM_RETRY #if CONFIG_ESP_WIFI_AUTH_OPEN #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN #elif CONFIG_ESP_WIFI_AUTH_WEP #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP #elif CONFIG_ESP_WIFI_AUTH_WPA_PSK #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK #elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK #elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK #elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK #elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK #elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK #else #define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK #endif /* FreeRTOS event group to signal when we are connected*/ static EventGroupHandle_t s_wifi_event_group; /* The event group allows multiple bits for each event, but we only care about * two events: * - we are connected to the AP with an IP * - we failed to connect after the maximum amount of retries */ #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 static const char *TAG = "wifi station"; static int s_retry_num = 0; static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_num++; ESP_LOGD(TAG, "retry to connect to the AP"); } else { xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); } ESP_LOGD(TAG, "connect to the AP fail"); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { wifi_event_sta_connected_t *event = (wifi_event_sta_connected_t *)event_data; ESP_LOGD(TAG, "connected to ap SSID:%s", event->ssid); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); s_retry_num = 0; xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); } } bool wifi_join(const char *ssid, const char *pass) { static bool connected_successfully = false; static bool one_time_init = false; if (!one_time_init) { one_time_init = true; s_wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); // Try to create default event loop. If it's already created, it returns // ESP_ERR_INVALID_STATE, equivalent to ESP_OK for our purposes. esp_err_t err = esp_event_loop_create_default(); ESP_ERROR_CHECK_WITHOUT_ABORT(err); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); esp_event_handler_instance_t instance_any_id; esp_event_handler_instance_t instance_got_ip; ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); } wifi_config_t wifi_config = { .sta = { /* Setting a password implies station will connect to all security * modes including WEP/WPA. However these modes are deprecated and * not advisable to be used. Incase your Access point doesn't * support WPA2, these mode can be enabled by commenting out the line * below */ .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD, }, }; if (connected_successfully) { // reconnect attempt ESP_LOGD(TAG, "wifi already connected, reconnecting ..."); esp_wifi_disconnect(); esp_err_t err = esp_wifi_connect(); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_connect failed: %s", esp_err_to_name(err)); return false; } } else { strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1); strncpy((char *)wifi_config.sta.password, pass, sizeof(wifi_config.sta.password) - 1); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGD(TAG, "wifi_init_sta finished."); } /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or * connection failed for the maximum number of re-tries (WIFI_FAIL_BIT). The * bits are set by event_handler() (see above) */ EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); /* xEventGroupWaitBits() returns the bits before the call returned, hence we * can test which event actually happened. */ if (bits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "connected to ap SSID:%s ", ssid); connected_successfully = true; return true; } else if (bits & WIFI_FAIL_BIT) { ESP_LOGD(TAG, "Failed to connect to SSID:%s", pass); } else { ESP_LOGE(TAG, "UNEXPECTED EVENT"); } return false; } static int wifi_disconnect(int argc, char **argv) { (void)argc, (void)argv; return esp_wifi_disconnect(); } /** Arguments used by 'join' function */ static struct { struct arg_str *ssid; struct arg_str *password; struct arg_end *end; } join_args; static int connect(int argc, char **argv) { int nerrors = arg_parse(argc, argv, (void **)&join_args); if (nerrors != 0) { arg_print_errors(stderr, join_args.end, argv[0]); return 1; } ESP_LOGI(__func__, "Connecting to '%s'", join_args.ssid->sval[0]); bool connected = wifi_join(join_args.ssid->sval[0], join_args.password->sval[0]); if (!connected) { ESP_LOGW(__func__, "Connection timed out"); return 1; } ESP_LOGI(__func__, "Connected"); return 0; } //! Since the creds are set via cli, we can safely assume there's no null bytes //! in the password, despite being theoretically permissible by the spec static void prv_save_wifi_creds(const char *ssid, const char *password) { // length arg is ignored for string-type settings esp_err_t err = settings_set(kSettingsWifiSsid, ssid, 0); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) writing ssid to NVS!", esp_err_to_name(err)); } err = settings_set(kSettingsWifiPassword, password, 0); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) writing password to NVS!", esp_err_to_name(err)); } if (err == ESP_OK) { ESP_LOGD(__func__, "Successfully saved new wifi creds"); strncpy(s_wifi_ssid, ssid, sizeof(s_wifi_ssid) - 1); strncpy(s_wifi_pass, password, sizeof(s_wifi_pass) - 1); } } //! Return ssid + password pointers, or NULL if not found void wifi_load_creds(char **ssid, char **password) { // first check if the creds are already loaded if ((strlen(s_wifi_ssid) > 0) && (strlen(s_wifi_pass) > 0)) { *ssid = s_wifi_ssid; *password = s_wifi_pass; return; } // clear output first *ssid = NULL; *password = NULL; ESP_LOGD(__func__, "Reading wifi creds ... "); size_t len = sizeof(s_wifi_ssid); esp_err_t err = settings_get(kSettingsWifiSsid, s_wifi_ssid, &len); if (err != ESP_OK) { ESP_LOGE(__func__, "failed reading ssid"); return; } len = sizeof(s_wifi_pass); err = settings_get(kSettingsWifiPassword, s_wifi_pass, &len); if (err != ESP_OK) { ESP_LOGE(__func__, "failed reading pw"); return; } *ssid = s_wifi_ssid; *password = s_wifi_pass; } // argtable3 requires this data structure to be valid through the entire command // session, so we can't just use a local variable. static struct { struct arg_str *ssid; struct arg_str *password; struct arg_end *end; } wifi_creds_args; static int wifi_creds_set(int argc, char **argv) { if (argc == 1) { char *ssid; char *password; wifi_load_creds(&ssid, &password); if (!ssid || !password) { ESP_LOGE(__func__, "failed to load wifi creds"); return 1; } printf("ssid: %s pw: %s\n", ssid, password); return 0; } int nerrors = arg_parse(argc, argv, (void **)&wifi_creds_args); if (nerrors != 0) { arg_print_errors(stderr, wifi_creds_args.end, argv[0]); return 1; } const char *ssid = wifi_creds_args.ssid->sval[0]; if (strlen(ssid) > 32) { ESP_LOGE(__func__, "SSID must be 32 characters or less"); return 1; } const char *password = wifi_creds_args.password->sval[0]; if (strlen(password) > 64) { ESP_LOGE(__func__, "Password must be 64 characters or less"); return 1; } prv_save_wifi_creds(ssid, password); return 0; } static struct { struct arg_str *projectkey; struct arg_end *end; } memfault_args; __attribute__((access(write_only, 1, 2))) int wifi_get_project_key(char *project_key, size_t project_key_len) { if (project_key_len != MEMFAULT_PROJECT_KEY_LEN + 1) { ESP_LOGE(__func__, "Destination buffer must be sized exactly to %zu bytes", MEMFAULT_PROJECT_KEY_LEN + 1); return 1; } size_t len = project_key_len; return settings_get(kSettingsProjectKey, project_key, &len); } __attribute__((access(read_only, 1, 2))) static esp_err_t prv_set_project_key( const char *project_key, size_t project_key_len) { // should never happen assert((project_key_len == MEMFAULT_PROJECT_KEY_LEN) && (strlen(project_key) == MEMFAULT_PROJECT_KEY_LEN)); return settings_set(kSettingsProjectKey, project_key, project_key_len); } static int project_key_set(int argc, char **argv) { if (argc == 1) { char project_key[MEMFAULT_PROJECT_KEY_LEN + 1]; esp_err_t err = wifi_get_project_key(project_key, sizeof(project_key)); if (err != ESP_OK) { ESP_LOGE(__func__, "failed to load wifi creds"); return 1; } printf("%.*s\n", MEMFAULT_PROJECT_KEY_LEN, project_key); return 0; } int nerrors = arg_parse(argc, argv, (void **)&memfault_args); if (nerrors != 0) { arg_print_errors(stderr, memfault_args.end, argv[0]); return 1; } // set the project key to nvs const char *projectkey = memfault_args.projectkey->sval[0]; if (strlen(projectkey) != MEMFAULT_PROJECT_KEY_LEN) { ESP_LOGE(__func__, "Project key must be %d characters", MEMFAULT_PROJECT_KEY_LEN); return ESP_ERR_INVALID_ARG; } esp_err_t err = prv_set_project_key(projectkey, strlen(projectkey)); return err; } void register_wifi(void) { join_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); join_args.password = arg_str0(NULL, NULL, "", "PSK of AP"); join_args.end = arg_end(2); const esp_console_cmd_t join_cmd = { .command = "join", .help = "Join WiFi AP as a station", .hint = NULL, .func = &connect, .argtable = &join_args }; ESP_ERROR_CHECK(esp_console_cmd_register(&join_cmd)); wifi_creds_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); wifi_creds_args.password = arg_str1(NULL, NULL, "", "PSK of AP"); wifi_creds_args.end = arg_end(2); const esp_console_cmd_t config_cmd = { .command = "wifi_config", .help = "Save WiFi ssid + password to NVS", .hint = NULL, .func = &wifi_creds_set, .argtable = &wifi_creds_args }; ESP_ERROR_CHECK(esp_console_cmd_register(&config_cmd)); memfault_args.projectkey = arg_str1(NULL, NULL, "", "Memfault Project Key"); memfault_args.end = arg_end(1); const esp_console_cmd_t project_key_cmd = { .command = "project_key", .help = "Save Memfault Project Key to NVS", .hint = NULL, .func = &project_key_set, .argtable = &memfault_args }; ESP_ERROR_CHECK(esp_console_cmd_register(&project_key_cmd)); const esp_console_cmd_t disconnect_cmd = { .command = "wifi_disconnect", .help = "Disconnect from WiFi AP", .hint = NULL, .func = &wifi_disconnect, .argtable = NULL }; ESP_ERROR_CHECK(esp_console_cmd_register(&disconnect_cmd)); } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/cmd_wifi_legacy.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Version of cmd_wifi.c that uses the legacy wifi API, for ESP-IDF v3. #include #include #include #include "argtable3/argtable3.h" #include "cmd_decl.h" #include "esp_console.h" #include "esp_event_loop.h" #include "esp_wifi.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "nvs.h" #include "nvs_flash.h" #include "tcpip_adapter.h" // enable for more verbose debug logs #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include "esp_log.h" // max length of saved ssid / pw, including null term #define WIFI_CREDS_MAX_SIZE 65 // cache the creds so we don't have to read NVS every time they're requested static char s_wifi_ssid[WIFI_CREDS_MAX_SIZE]; static char s_wifi_pass[WIFI_CREDS_MAX_SIZE]; static EventGroupHandle_t wifi_event_group; const int CONNECTED_BIT = BIT0; 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 return 1; } static esp_err_t event_handler(void *ctx, system_event_t *event) { switch (event->event_id) { case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); break; case SYSTEM_EVENT_STA_DISCONNECTED: esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); break; default: break; } return ESP_OK; } static void initialise_wifi(void) { esp_log_level_set("wifi", ESP_LOG_WARN); static bool initialized = false; if (initialized) { return; } tcpip_adapter_init(); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); ESP_ERROR_CHECK(esp_wifi_start()); initialized = true; } bool wifi_join(const char *ssid, const char *pass) { initialise_wifi(); wifi_config_t wifi_config = { 0 }; strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); if (pass) { strncpy((char *)wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); } ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_connect()); const int timeout_ms = 5000; int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 1, 1, timeout_ms / portTICK_PERIOD_MS); return (bits & CONNECTED_BIT) != 0; } /** Arguments used by 'join' function */ static struct { struct arg_str *ssid; struct arg_str *password; struct arg_end *end; } join_args; static int connect(int argc, char **argv) { int nerrors = arg_parse(argc, argv, (void **)&join_args); if (nerrors != 0) { arg_print_errors(stderr, join_args.end, argv[0]); return 1; } ESP_LOGI(__func__, "Connecting to '%s'", join_args.ssid->sval[0]); bool connected = wifi_join(join_args.ssid->sval[0], join_args.password->sval[0]); if (!connected) { ESP_LOGW(__func__, "Connection timed out"); return 1; } ESP_LOGI(__func__, "Connected"); return 0; } //! Since the creds are set via cli, we can safely assume there's no null bytes //! in the password, despite being theoretically permissible by the spec static void prv_save_wifi_creds(const char *ssid, const char *password) { // Initialize NVS esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { // NVS partition was truncated and needs to be erased // Retry nvs_flash_init ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK(err); // Open nvs_handle_t nvs_handle; err = nvs_open("storage", NVS_READWRITE, &nvs_handle); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) opening NVS handle!", esp_err_to_name(err)); } else { ESP_LOGD(__func__, "Opened NVS handle"); // Write err = nvs_set_str(nvs_handle, "wifi_ssid", ssid); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) writing ssid to NVS!", esp_err_to_name(err)); } else { ESP_LOGD(__func__, "Wrote ssid to NVS"); } err = nvs_set_str(nvs_handle, "wifi_password", password); if (err == ESP_OK) { if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) writing password to NVS!", esp_err_to_name(err)); } else { ESP_LOGD(__func__, "Wrote password to NVS"); } } // Commit written value. // After setting any values, nvs_commit() must be called to ensure changes are written // to flash storage. Implementations may write to storage at other times, // but this is not guaranteed. err = nvs_commit(nvs_handle); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) committing NVS!", esp_err_to_name(err)); } else { ESP_LOGD(__func__, "Successfully saved new wifi creds"); strncpy(s_wifi_ssid, ssid, sizeof(s_wifi_ssid)); strncpy(s_wifi_pass, password, sizeof(s_wifi_pass)); } // Close nvs_close(nvs_handle); } } //! Return ssid + password pointers, or NULL if not found void wifi_load_creds(char **ssid, char **password) { // first check if the creds are already loaded if ((strlen(s_wifi_ssid) > 0) && (strlen(s_wifi_pass) > 0)) { *ssid = s_wifi_ssid; *password = s_wifi_pass; return; } // clear output first *ssid = NULL; *password = NULL; // Initialize NVS esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { // NVS partition was truncated and needs to be erased // Retry nvs_flash_init ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK(err); // Open nvs_handle_t nvs_handle; err = nvs_open("storage", NVS_READWRITE, &nvs_handle); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) opening NVS handle!", esp_err_to_name(err)); } else { ESP_LOGD(__func__, "Opened NVS handle"); // Read ESP_LOGD(__func__, "Reading wifi creds ... "); size_t len = sizeof(s_wifi_ssid); err = nvs_get_str(nvs_handle, "wifi_ssid", s_wifi_ssid, &len); if (err == ESP_OK) { ESP_LOGD(__func__, "ssid size: %d", len); } else { ESP_LOGE(__func__, "failed reading ssid"); goto close; } len = sizeof(s_wifi_pass); err = nvs_get_str(nvs_handle, "wifi_password", s_wifi_pass, &len); if (err == ESP_OK) { ESP_LOGD(__func__, "pw size: %d", len); } else { ESP_LOGE(__func__, "failed reading pw"); goto close; } *ssid = s_wifi_ssid; *password = s_wifi_pass; close: nvs_close(nvs_handle); } } // argtable3 requires this data structure to be valid through the entire command // session, so we can't just use a local variable. static struct { struct arg_str *ssid; struct arg_str *password; struct arg_end *end; } wifi_creds_args; static int wifi_creds_set(int argc, char **argv) { if (argc == 1) { char *ssid; char *password; wifi_load_creds(&ssid, &password); if (!ssid || !password) { ESP_LOGE(__func__, "failed to load wifi creds"); return 1; } printf("ssid: %s pw: %s\n", ssid, password); return 0; } int nerrors = arg_parse(argc, argv, (void **)&wifi_creds_args); if (nerrors != 0) { arg_print_errors(stderr, wifi_creds_args.end, argv[0]); return 1; } const char *ssid = wifi_creds_args.ssid->sval[0]; if (strlen(ssid) > 32) { ESP_LOGE(__func__, "SSID must be 32 characters or less"); return 1; } const char *password = wifi_creds_args.password->sval[0]; if (strlen(password) > 64) { ESP_LOGE(__func__, "Password must be 64 characters or less"); return 1; } prv_save_wifi_creds(ssid, password); return 0; } void register_wifi(void) { join_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); join_args.password = arg_str0(NULL, NULL, "", "PSK of AP"); join_args.end = arg_end(2); const esp_console_cmd_t join_cmd = { .command = "join", .help = "Join WiFi AP as a station", .hint = NULL, .func = &connect, .argtable = &join_args }; ESP_ERROR_CHECK(esp_console_cmd_register(&join_cmd)); wifi_creds_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); wifi_creds_args.password = arg_str1(NULL, NULL, "", "PSK of AP"); wifi_creds_args.end = arg_end(2); const esp_console_cmd_t config_cmd = { .command = "wifi_config", .help = "Save WiFi ssid + password to NVS", .hint = NULL, .func = &wifi_creds_set, .argtable = &wifi_creds_args }; ESP_ERROR_CHECK(esp_console_cmd_register(&config_cmd)); } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/config/memfault_metrics_heartbeat_config.def ================================================ #include "sdkconfig.h" // File for holding custom metrics: // https://mflt.io/embedded-metrics MEMFAULT_METRICS_KEY_DEFINE(ota_check_count, kMemfaultMetricType_Unsigned) #if defined(CONFIG_APP_MEMFAULT_TRANSPORT_MQTT) MEMFAULT_METRICS_KEY_DEFINE(mqtt_publish_bytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(mqtt_publish_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(mqtt_conn_downtime, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(mqtt_conn_uptime, kMemfaultMetricType_Timer) #endif // OTA session metrics MEMFAULT_METRICS_SESSION_KEY_DEFINE(ota) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(ota_error_code, kMemfaultMetricType_Signed, ota) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(ota_wifi_rssi, kMemfaultMetricType_Signed, ota) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(ota_tcp_tx_count, kMemfaultMetricType_Unsigned, ota) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(ota_tcp_rx_count, kMemfaultMetricType_Unsigned, ota) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(ota_tls_sent_bytes, kMemfaultMetricType_Unsigned, ota) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(ota_tls_recv_bytes, kMemfaultMetricType_Unsigned, ota) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(ota_mbedtls_mem_max_bytes, kMemfaultMetricType_Unsigned, ota) #if defined(CONFIG_MEMFAULT_METRICS_FLASH_ENABLE) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(flash_spi_erase_bytes, kMemfaultMetricType_Unsigned, ota) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(flash_spi_write_bytes, kMemfaultMetricType_Unsigned, ota) #endif // Thread stack usage metrics. // Note: We test this application in CI in a "zero-config" form, by removing // memfault_platform_config.h. This enables the default set of thread metrics, // so to avoid a name conflict, don't enable them here. #if !MEMFAULT_METRICS_THREADS_DEFAULTS MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_main_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_idle0_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #if !defined(CONFIG_FREERTOS_UNICORE) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_idle1_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #endif MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_example_task_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_tit_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_mflt_periodic_u_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_tmr_svc_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #if !defined(CONFIG_FREERTOS_UNICORE) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_ipc0_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_ipc1_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #endif MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_ota_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_sys_evt_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_wifi_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_esp_timer_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_overflow_task_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #endif // !MEMFAULT_METRICS_THREADS_DEFAULTS ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" #define MEMFAULT_TASK_WATCHDOG_ENABLE 1 //! Disable the default set of built-in FreeRTOS thread metrics. ESP-IDF links //! the application from a set of static libraries, so we are unable to override //! the weak definition of the default thread metrics //! (MEMFAULT_METRICS_DEFINE_THREAD_METRICS()). Instead, explicitly disable the //! defaults here and define a custom set of thread metrics in the application. #define MEMFAULT_METRICS_THREADS_DEFAULTS_INDEX 0 #define MEMFAULT_METRICS_THREADS_DEFAULTS 0 ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/config/memfault_reboot_reason_user_config.def ================================================ MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(CustomExpectedReboot) MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(CustomUnexpectedReboot) ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/config/memfault_task_watchdog_config.def ================================================ //! @file MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(example_task) ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/config/memfault_trace_reason_user_config.def ================================================ // File for holding custom error traces: // https://mflt.io/error-tracing MEMFAULT_TRACE_REASON_DEFINE(ota_install_failure) ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/deep_sleep.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Deep sleep handling #include "deep_sleep.h" #include #include #include "esp_log.h" #include "esp_sleep.h" #include "freertos/FreeRTOS.h" #include "freertos/timers.h" #include "memfault/components.h" #include "memfault/esp_port/deep_sleep.h" #include "sdkconfig.h" static const char *TAG = "deep_sleep"; esp_err_t deep_sleep_start(uint32_t sleep_seconds) { esp_err_t err = esp_sleep_enable_timer_wakeup(sleep_seconds * 1000 * 1000); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to enable timer wakeup: %s", esp_err_to_name(err)); return ESP_FAIL; } #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT) memfault_platform_deep_sleep_save_state(); #endif ESP_LOGI(TAG, "🛌 Entering deep sleep for %" PRIu32 " seconds", sleep_seconds); esp_deep_sleep_start(); // This code should never run, but if it does, return an error ESP_LOGE(TAG, "Failed to enter deep sleep"); return ESP_FAIL; } #if defined(CONFIG_MEMFAULT_APP_AUTO_DEEP_SLEEP) static void prv_auto_deep_sleep_start_cb(MEMFAULT_UNUSED TimerHandle_t handle) { ESP_LOGI(TAG, "⏰ Deep sleep timer expired"); (void)deep_sleep_start(CONFIG_MEMFAULT_APP_AUTO_DEEP_SLEEP_PERIOD_SECONDS); } #endif void deep_sleep_wakeup(void) { #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT) memfault_platform_deep_sleep_restore_state(); #endif // CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT #if defined(CONFIG_MEMFAULT_APP_AUTO_DEEP_SLEEP) // start a FreeRTOS timer to trigger deep sleep const TickType_t deep_sleep_interval = pdMS_TO_TICKS(CONFIG_MEMFAULT_APP_AUTO_DEEP_SLEEP_PERIOD_SECONDS * 1000); TimerHandle_t timer = xTimerCreate("AutoDeepSleep", deep_sleep_interval, pdFALSE, NULL, prv_auto_deep_sleep_start_cb); if (timer == NULL) { ESP_LOGE(TAG, "Failed to create auto deep sleep timer"); } else { // start the timer ESP_LOGI(TAG, "⏱️ Starting auto deep sleep timer for %d seconds", CONFIG_MEMFAULT_APP_AUTO_DEEP_SLEEP_PERIOD_SECONDS); xTimerStart(timer, 0); } #endif } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/deep_sleep.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Deep sleep handling #pragma once #include #include "esp_err.h" //! Call this to start deep sleep //! @param sleep_seconds The number of seconds to sleep for esp_err_t deep_sleep_start(uint32_t sleep_seconds); //! Called on boot to process wakeup from deep sleep. Should be called on every //! boot. void deep_sleep_wakeup(void); ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/idf_component.yml ================================================ # This file is required as of ESP-IDF v5.0 to use the led_strip component (which # used to be just located inside the ESP-IDF repository). # # See here for more information: # https://github.com/espressif/idf-component-manager/tree/dd6f5452a835342429ca5f3ec813bce4f9de6308#defining-dependencies-in-the-manifest dependencies: espressif/led_strip: # esp-idf v6+ requires led_strip v3.0.0+, but that is incompatible with # esp-idf v4.x, so we use a looser dependency spec here. version: "<4.0.0" rules: # will add dependency only when all if clauses are True - if: "idf_version >=5.0" # supports all SimpleSpec grammars (https://python-semanticversion.readthedocs.io/en/latest/reference.html#semantic_version.SimpleSpec) - if: "target not in [esp32]" # supports boolean operator ==, !=, in, not in. espressif/mqtt: version: "^1.0.0" rules: # moved to a component manager component in this commit: # https://github.com/espressif/esp-idf/commit/448714b056ec79172bcbf8724a23f3acca7a4667 - if: "idf_version >=6.0" ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/led.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Module for controlling the RGB LED. Implementation varies depending on which //! target board; the ESP32-WROVER has a 3-gpio LED, while the ESP32-S3-DevKitC //! has an addressable LED. #include "led.h" #include #include #include #include "sdkconfig.h" #if defined(CONFIG_MEMFAULT) #include "memfault/components.h" #include "settings.h" #endif #if CONFIG_BLINK_LED_RMT #include "led_strip.h" #endif #include "driver/gpio.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/timers.h" // System state LED color: // Red: System is running, has not checked in to memfault (wifi might be bad) // Green: System is running, has checked in to memfault // Blue: System is performing an OTA update static int s_led_color = kLedColor_Red; static int32_t s_led_brightness = 5; void led_set_color(enum LED_COLORS color) { s_led_color = color; } #if CONFIG_BLINK_LED_RMT struct rgb_led_s { uint8_t r; uint8_t g; uint8_t b; }; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) static led_strip_handle_t led_strip; static void led_config(void) { /* LED strip initialization with the GPIO and pixels number*/ led_strip_config_t strip_config = { .strip_gpio_num = CONFIG_BLINK_GPIO, .max_leds = 1, // at least one LED on board }; led_strip_rmt_config_t rmt_config = { .resolution_hz = 10 * 1000 * 1000, // 10MHz }; ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); /* Set all LED off to clear all pixels */ led_strip_clear(led_strip); } static void prv_set_pixel(struct rgb_led_s rgb, bool set) { /* If the addressable LED is enabled */ if (set) { /* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */ led_strip_set_pixel(led_strip, 0, rgb.r, rgb.g, rgb.b); /* Refresh the strip to send data */ led_strip_refresh(led_strip); } else { /* Set all LED off to clear all pixels */ led_strip_clear(led_strip); } } #else // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) static led_strip_t *pStrip_a; static void prv_set_pixel(struct rgb_led_s rgb, bool set) { if (set) { /* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */ pStrip_a->set_pixel(pStrip_a, 0, rgb.r, rgb.g, rgb.b); /* Refresh the strip to send data */ pStrip_a->refresh(pStrip_a, 100); } else { /* Set all LED off to clear all pixels */ pStrip_a->clear(pStrip_a, 50); } } static void led_config(void) { /* LED strip initialization with the GPIO and pixels number*/ pStrip_a = led_strip_init(CONFIG_BLINK_LED_RMT_CHANNEL, CONFIG_BLINK_GPIO, 1); /* Set all LED off to clear all pixels */ pStrip_a->clear(pStrip_a, 50); } #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) static void prv_heartbeat_led_callback(TimerHandle_t handle) { (void)handle; static bool s_led_state = true; /* If the addressable LED is enabled */ struct rgb_led_s rgb_led; switch (s_led_color) { default: case kLedColor_Red: rgb_led.r = s_led_brightness; rgb_led.g = 0; rgb_led.b = 0; break; case kLedColor_Green: rgb_led.r = 0; rgb_led.g = s_led_brightness; rgb_led.b = 0; break; case kLedColor_Blue: rgb_led.r = 0; rgb_led.g = 0; rgb_led.b = s_led_brightness; break; } prv_set_pixel(rgb_led, s_led_state); s_led_state = !s_led_state; } #else // CONFIG_BLINK_LED_RMT static void prv_heartbeat_led_callback(TimerHandle_t handle) { (void)handle; static bool s_led_state = false; s_led_state = !s_led_state; const gpio_num_t leds[] = { kLedColor_Red, kLedColor_Green, kLedColor_Blue }; for (size_t i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) { if (leds[i] == s_led_color) { gpio_set_level(s_led_color, s_led_state); } else { gpio_set_level(leds[i], 0); } } } static void led_config(void) { const gpio_num_t leds[] = { kLedColor_Red, kLedColor_Green, kLedColor_Blue }; for (size_t i = 0; i < sizeof(leds) / sizeof(leds[0]); i++) { gpio_reset_pin(leds[i]); gpio_set_direction(leds[i], GPIO_MODE_OUTPUT); gpio_set_level(leds[i], 0); } } #endif // CONFIG_BLINK_LED_RMT void led_init(void) { led_config(); int32_t led_interval_ms = 500; #if defined(CONFIG_MEMFAULT) size_t len = sizeof(s_led_brightness); (void)settings_get(kSettingsLedBrightness, &s_led_brightness, &len); len = sizeof(led_interval_ms); (void)settings_get(kSettingsLedBlinkIntervalMs, &led_interval_ms, &len); #endif ESP_LOGI(__func__, "LED brightness: %" PRIi32, s_led_brightness); ESP_LOGI(__func__, "LED blink interval: %" PRIi32, led_interval_ms); // create a timer that blinks the LED, indicating the app is alive const char *const pcTimerName = "HeartbeatLED"; const TickType_t xTimerPeriodInTicks = pdMS_TO_TICKS(led_interval_ms); TimerHandle_t timer; #if defined(CONFIG_MEMFAULT) && (MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION != 0) static StaticTimer_t s_heartbeat_led_timer_context; timer = xTimerCreateStatic(pcTimerName, xTimerPeriodInTicks, pdTRUE, NULL, prv_heartbeat_led_callback, &s_heartbeat_led_timer_context); #else timer = xTimerCreate(pcTimerName, xTimerPeriodInTicks, pdTRUE, NULL, prv_heartbeat_led_callback); #endif assert(timer != 0); xTimerStart(timer, 0); } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/led.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! LED control module header. #pragma once enum LED_COLORS { kLedColor_Red = 0, kLedColor_Green = 2, kLedColor_Blue = 4, }; void led_set_color(enum LED_COLORS color); void led_init(void); ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/main.c ================================================ /* Console example This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include #include #include #include "app_memfault_transport.h" #include "argtable3/argtable3.h" #include "button.h" #include "cmd_decl.h" #include "deep_sleep.h" #include "driver/uart.h" #include "esp_console.h" #include "esp_log.h" #include "esp_system.h" #include "esp_task.h" #include "esp_task_wdt.h" #include "esp_vfs_dev.h" #include "esp_vfs_fat.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/timers.h" #include "led.h" #include "linenoise/linenoise.h" #include "nvs.h" #include "nvs_flash.h" #include "ota_session_metrics.h" #include "sdkconfig.h" #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) #include "driver/uart_vfs.h" #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 "settings.h" #endif // Conditionally enable the logging tag variable only when it's used #if defined(CONFIG_STORE_HISTORY) || defined(CONFIG_MEMFAULT_APP_HEAP_TRACING) static const char *TAG = "main"; #endif /* Console command history can be stored to and loaded from a file. * The easiest way to do this is to use FATFS filesystem on top of * wear_levelling library. */ #if CONFIG_STORE_HISTORY #define MOUNT_PATH "/data" #define HISTORY_PATH MOUNT_PATH "/history.txt" static void initialize_filesystem() { static wl_handle_t wl_handle; const esp_vfs_fat_mount_config_t mount_config = { .max_files = 4, .format_if_mount_failed = true }; esp_err_t err = #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) esp_vfs_fat_spiflash_mount_rw_wl #else esp_vfs_fat_spiflash_mount #endif (MOUNT_PATH, "storage", &mount_config, &wl_handle); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err)); return; } } #endif // CONFIG_STORE_HISTORY static void initialize_nvs() { esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK(err); } 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. #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); #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); #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_ESP_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0)); /* Tell VFS to use UART driver */ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) uart_vfs_dev_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); #else esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); #endif /* Initialize the console */ esp_console_config_t console_config = { .max_cmdline_args = 8, .max_cmdline_length = 256, #if CONFIG_LOG_COLORS .hint_color = atoi(LOG_COLOR_CYAN), #endif }; ESP_ERROR_CHECK(esp_console_init(&console_config)); /* Configure linenoise line completion library */ /* Enable multiline editing. If not set, long commands will scroll within * single line. */ linenoiseSetMultiLine(1); /* Tell linenoise where to get command completions and hints */ linenoiseSetCompletionCallback(&esp_console_get_completion); linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint); /* Set command history size */ linenoiseHistorySetMaxLen(10); #if CONFIG_STORE_HISTORY /* Load command history from filesystem */ linenoiseHistoryLoad(HISTORY_PATH); #endif } #if defined(CONFIG_MEMFAULT) // Put this buffer in the IRAM region. Accesses on the instruction bus must be word-aligned // while data accesses don't have to be. See "1.3.1 Address Mapping" in the ESP32 technical // reference manual. MEMFAULT_ALIGNED(4) static IRAM_ATTR uint8_t s_my_buf[10]; void *g_unaligned_buffer; #if defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA_CUSTOM_CBS) static bool prv_handle_ota_upload_available(void *user_ctx) { // set blue when performing update led_set_color(kLedColor_Blue); MEMFAULT_LOG_INFO("Starting OTA download ..."); // Begin the OTA metrics session ota_session_metrics_start(); return true; } static bool prv_handle_ota_download_complete(void *user_ctx) { MEMFAULT_LOG_INFO("OTA Update Complete, Rebooting System"); // Successful OTA update, end the metrics session ota_session_metrics_end(0); MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_FirmwareUpdate); esp_restart(); MEMFAULT_LOG_ERROR("OTA reboot failed"); return false; } static void prv_handle_ota_done(int status, void *user_ctx) { // count the number of times the check has run MEMFAULT_METRIC_ADD(ota_check_count, 1); #if defined(CONFIG_MEMFAULT_METRICS_SYNC_SUCCESS) // Record the OTA check result using the built-in sync success metric if (status == 0 || status == 1) { memfault_metrics_connectivity_record_sync_success(); } else { memfault_metrics_connectivity_record_sync_failure(); } #endif if (status == 0) { MEMFAULT_LOG_INFO("OTA up to date!"); led_set_color(kLedColor_Green); } else if (status == 1) { MEMFAULT_LOG_INFO("OTA update available, not installed"); } else if (status < 0) { MEMFAULT_LOG_ERROR("OTA update failed, status=%d", status); // record a Trace Event when this happens, and freeze the log buffer to be // uploaded for diagnosis MEMFAULT_TRACE_EVENT_WITH_LOG(ota_install_failure, "error code=%d", status); memfault_log_trigger_collection(); // End the OTA metric session, passing the non-zero error code. This will // also trigger a chunk post to upload data to Memfault, so it should be // last in the error path here. ota_session_metrics_end(status); led_set_color(kLedColor_Red); } } sMemfaultOtaUpdateHandler g_memfault_ota_update_handler = { .user_ctx = NULL, .handle_update_available = prv_handle_ota_upload_available, .handle_download_complete = prv_handle_ota_download_complete, .handle_ota_done = prv_handle_ota_done, }; #endif // CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA_CUSTOM_CBS #if CONFIG_MEMFAULT_APP_WIFI_AUTOJOIN void memfault_esp_port_wifi_autojoin(void) { if (memfault_esp_port_wifi_connected()) { return; } char *ssid, *pass; wifi_load_creds(&ssid, &pass); if ((ssid == NULL) || (pass == NULL) || (strnlen(ssid, 64) == 0) || (strnlen(pass, 64) == 0)) { MEMFAULT_LOG_DEBUG("No WiFi credentials found"); return; } MEMFAULT_LOG_DEBUG("Starting WiFi Autojoin ..."); bool result = wifi_join(ssid, pass); if (!result) { MEMFAULT_LOG_DEBUG("Failed to join WiFi network"); } } #endif // CONFIG_MEMFAULT_APP_WIFI_AUTOJOIN // Periodically attempt to join wifi, if configured static void prv_ota_task(void *args) { const TickType_t wifi_join_interval = pdMS_TO_TICKS(5 * 60 * 1000); MEMFAULT_LOG_INFO("WiFi autojoin task up and running every %" PRIu32 "s.", wifi_join_interval); while (true) { // attempt to autojoin wifi, if configured #if defined(CONFIG_MEMFAULT_APP_WIFI_AUTOJOIN) memfault_esp_port_wifi_autojoin(); #endif if (!memfault_esp_port_wifi_connected()) { // Set LED to red led_set_color(kLedColor_Red); } // sleep vTaskDelay(wifi_join_interval); } } // Example showing how to use the task watchdog #if MEMFAULT_TASK_WATCHDOG_ENABLE SemaphoreHandle_t g_example_task_lock; static void prv_example_task(void *args) { (void)args; // set up the semaphore used to programmatically make this task stuck #if MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION != 0 static StaticSemaphore_t s_memfault_lock_context; g_example_task_lock = xSemaphoreCreateRecursiveMutexStatic(&s_memfault_lock_context); #else g_example_task_lock = xSemaphoreCreateRecursiveMutex(); #endif MEMFAULT_ASSERT(g_example_task_lock != NULL); // this task runs every 250ms and gets/puts a semaphore. if the semaphore is // claimed, the task watchdog will eventually trip and mark this task as stuck const uint32_t interval_ms = 250; MEMFAULT_LOG_INFO("Task watchdog example task running every %" PRIu32 "ms.", interval_ms); while (true) { // enable the task watchdog MEMFAULT_TASK_WATCHDOG_START(example_task); // get the semaphore. if we can't get it, the task watchdog should // eventually trip xSemaphoreTakeRecursive(g_example_task_lock, portMAX_DELAY); xSemaphoreGiveRecursive(g_example_task_lock); // disable the task watchdog now that this task is done in this run MEMFAULT_TASK_WATCHDOG_STOP(example_task); vTaskDelay(interval_ms / portTICK_PERIOD_MS); } } static void prv_task_watchdog_timer_callback(MEMFAULT_UNUSED TimerHandle_t handle) { memfault_task_watchdog_check_all(); } static void prv_initialize_task_watchdog(void) { memfault_task_watchdog_init(); // create a timer that runs the watchdog check once a second const char *const pcTimerName = "TaskWatchdogTimer"; const TickType_t xTimerPeriodInTicks = pdMS_TO_TICKS(1000); TimerHandle_t timer; #if MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION != 0 static StaticTimer_t s_task_watchdog_timer_context; timer = xTimerCreateStatic(pcTimerName, xTimerPeriodInTicks, pdTRUE, NULL, prv_task_watchdog_timer_callback, &s_task_watchdog_timer_context); #else timer = xTimerCreate(pcTimerName, xTimerPeriodInTicks, pdTRUE, NULL, prv_task_watchdog_timer_callback); #endif MEMFAULT_ASSERT(timer != 0); xTimerStart(timer, 0); // create and start the example task const portBASE_TYPE res = xTaskCreate(prv_example_task, "example_task", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL); MEMFAULT_ASSERT(res == pdTRUE); } #else static void prv_initialize_task_watchdog(void) { // task watchdog disabled, do nothing } #endif #endif // defined(CONFIG_MEMFAULT) #if defined(CONFIG_MEMFAULT_APP_HEAP_TRACING) // This callback is triggered when a heap allocation is made. It prints large // allocations for debugging heap usage from the serial log. void esp_heap_trace_alloc_hook(void *ptr, size_t size, uint32_t caps) { // In our app, there's a periodic 1696 byte alloc. Filter out anything that // size or smaller from this log, otherwise it's quite spammy if (size > 1696) { ESP_LOGI(TAG, "Large alloc: %p, size: %d, caps: %lu", ptr, size, caps); multi_heap_info_t heap_info = { 0 }; heap_caps_get_info(&heap_info, MALLOC_CAP_DEFAULT); ESP_LOGI(TAG, "Total free bytes: %d", heap_info.total_free_bytes); } } #endif #if defined(CONFIG_MEMFAULT) && !defined(CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK) // Demonstrate a custom vprintf hook that prefixes all log messages with // "[IDF]", before invoking the Memfault log hook, and then printing to stdout. static int prv_vprintf_hook(const char *fmt, va_list args) { char *fmt_annotated; int rv = asprintf(&fmt_annotated, "[IDF] %s", fmt); if ((rv < 0) || (fmt_annotated == NULL)) { return -1; } #if defined(CONFIG_MEMFAULT) (void)memfault_esp_port_vprintf_log_hook(fmt_annotated, args); #endif rv = vprintf(fmt_annotated, args); free(fmt_annotated); return rv; } #endif #if defined(CONFIG_MEMFAULT) // This banner is printed when the app boots up. Declare it here so it isn't // allocated on the main thread stack. static const char s_mflt_banner[] = "\n\n" MEMFAULT_BANNER_COLORIZED; static char s_mflt_project_key[MEMFAULT_PROJECT_KEY_LEN + 1] = { 0 }; static char s_mflt_chunks_url[128] = { 0 }; static char s_mflt_device_url[128] = { 0 }; static void prv_load_memfault_settings_from_nvs(void) { // Attempt to load project key from nvs int err = wifi_get_project_key(s_mflt_project_key, sizeof(s_mflt_project_key)); if (err == 0) { s_mflt_project_key[sizeof(s_mflt_project_key) - 1] = '\0'; g_mflt_http_client_config.api_key = s_mflt_project_key; } // Load chunks + device URLs from NVS too. Need to persist for the lifetime of the program size_t s_mflt_chunks_url_len = sizeof(s_mflt_chunks_url); size_t s_mflt_device_url_len = sizeof(s_mflt_device_url); if ((settings_get(kSettingsChunksUrl, s_mflt_chunks_url, &s_mflt_chunks_url_len) == 0) && (settings_get(kSettingsDeviceUrl, s_mflt_device_url, &s_mflt_device_url_len) == 0)) { g_mflt_http_client_config.chunks_api.host = (s_mflt_chunks_url_len > 1) ? s_mflt_chunks_url : NULL; g_mflt_http_client_config.device_api.host = (s_mflt_device_url_len > 1) ? s_mflt_device_url : NULL; } } #if defined(CONFIG_MEMFAULT_TIME_SINCE_BOOT_CUSTOM) // Simulate a user-defined memfault_platform_get_time_since_boot_ms // implementation, just as an example uint64_t memfault_platform_get_time_since_boot_ms(void) { const int64_t time_since_boot_us = esp_timer_get_time(); return (uint64_t)(time_since_boot_us / 1000) /* us per ms */; } #endif #endif // defined(CONFIG_MEMFAULT) static void prv_conditionally_init_task_wdt(void) { #if defined(CONFIG_ESP_TASK_WDT_INIT) // note: we would need to also run esp_task_wdt_reconfigure() if // CONFIG_ESP_TASK_WDT_PANIC=n, but leaving that out for simplicity #if !defined(CONFIG_ESP_TASK_WDT_PANIC) #warning "CONFIG_ESP_TASK_WDT_PANIC=n is not fully supported by this example" #endif #else // Initialize now // API changed in ESP-IDF v5.0.0 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) esp_task_wdt_config_t twdt_config = { .timeout_ms = CONFIG_MEMFAULT_APP_TASK_WDT_TIMEOUT_S * 1000, .idle_core_mask = 0, .trigger_panic = true, }; #if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 twdt_config.idle_core_mask |= (1 << 0); #endif #if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 twdt_config.idle_core_mask |= (1 << 1); #endif ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config)); #else // Pre-5.0.0 API ESP_ERROR_CHECK(esp_task_wdt_init(CONFIG_MEMFAULT_APP_TASK_WDT_TIMEOUT_S * 1000, true)); #endif #endif } // This task started by cpu_start.c::start_cpu0_default(). void app_main() { // keep before memfault_boot() prv_conditionally_init_task_wdt(); #if defined(CONFIG_MEMFAULT) #if !defined(CONFIG_MEMFAULT_AUTOMATIC_INIT) memfault_boot(); #endif // suppress some noisy wifi logs esp_log_level_set("wifi", ESP_LOG_ERROR); esp_log_level_set("wifi_init", ESP_LOG_WARN); #if !defined(CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK) esp_log_set_vprintf(prv_vprintf_hook); ESP_LOGD("main", "debug log 🕵️"); ESP_LOGI("main", "info log 🐢"); ESP_LOGW("main", "warning log ⚠️"); ESP_LOGE("main", "error log 🔥"); #endif memfault_device_info_dump(); g_unaligned_buffer = &s_my_buf[1]; #endif initialize_nvs(); #if CONFIG_STORE_HISTORY initialize_filesystem(); #endif initialize_console(); led_init(); /* Register commands */ esp_console_register_help_command(); #if defined(CONFIG_MEMFAULT) prv_initialize_task_watchdog(); // We need another task to check for OTA since we block waiting for user // input in this task. const portBASE_TYPE res = xTaskCreate(prv_ota_task, "ota", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL); assert(res == pdTRUE); // Register the app commands register_system(); register_wifi(); register_app(); settings_register_shell_commands(); prv_load_memfault_settings_from_nvs(); deep_sleep_wakeup(); #if MEMFAULT_COMPACT_LOG_ENABLE MEMFAULT_COMPACT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, "This is a compact log example"); #endif puts(s_mflt_banner); #endif // defined(CONFIG_MEMFAULT) /* Prompt to be printed before each line. * This can be customized, made dynamic, etc. */ const char *prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR; /* Figure out if the terminal supports escape sequences */ int probe_status = linenoiseProbe(); if (probe_status) { /* zero indicates success */ printf("\n" "Your terminal application does not support escape sequences.\n" "Line editing and history features are disabled.\n" "On Windows, try using Putty instead.\n"); linenoiseSetDumbMode(1); #if CONFIG_LOG_COLORS /* Since the terminal doesn't support escape sequences, * don't use color codes in the prompt. */ prompt = "esp32> "; #endif // CONFIG_LOG_COLORS } button_setup(); /* Main loop */ while (true) { /* Get a line using linenoise (blocking call). * The line is returned when ENTER is pressed. */ char *line = linenoise(prompt); if (line == NULL) { /* Ignore empty lines */ continue; } /* Add the command to the history */ linenoiseHistoryAdd(line); #if CONFIG_STORE_HISTORY /* Save command history to filesystem */ linenoiseHistorySave(HISTORY_PATH); #endif /* Try to run the command */ int ret; esp_err_t err = esp_console_run(line, &ret); if (err == ESP_ERR_NOT_FOUND) { printf("Unrecognized command\n"); } else if (err == ESP_ERR_INVALID_ARG) { // command was empty } else if (err == ESP_OK && ret != ESP_OK) { printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret)); } else if (err != ESP_OK) { printf("Internal error: %s\n", esp_err_to_name(err)); } /* linenoise allocates line buffer on the heap, so need to free it */ linenoiseFree(line); } } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Example application metrics. #include "esp_idf_version.h" #include "memfault/ports/freertos/thread_metrics.h" // We test this application in CI in a "zero-config" form, by removing // memfault_platform_config.h. This enables the default set of thread metrics, // so to avoid a name conflict, don't enable them here. #if !MEMFAULT_METRICS_THREADS_DEFAULTS #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) // Provide callbacks to get the IDLE task handle for each core static TaskHandle_t prv_get_idle0_task_handle_for_core(void) { return xTaskGetIdleTaskHandleForCPU(0); } #if !defined(CONFIG_FREERTOS_UNICORE) static TaskHandle_t prv_get_idle1_task_handle_for_core(void) { return xTaskGetIdleTaskHandleForCPU(1); } #endif #endif MEMFAULT_METRICS_DEFINE_THREAD_METRICS( { .thread_name = "main", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_main_pct_max), }, #if defined(CONFIG_FREERTOS_UNICORE) { .thread_name = "IDLE", #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) .get_task_handle = prv_get_idle0_task_handle_for_core, #endif .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle0_pct_max), }, #else { .thread_name = "IDLE0", #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) .get_task_handle = prv_get_idle0_task_handle_for_core, #endif .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle0_pct_max), }, { .thread_name = "IDLE1", #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) .get_task_handle = prv_get_idle1_task_handle_for_core, #endif .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle1_pct_max), }, #endif { .thread_name = "example_task", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_example_task_pct_max), }, { .thread_name = "tiT", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_tit_pct_max), }, { .thread_name = "mflt_periodic_u", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_mflt_periodic_u_pct_max), }, { .thread_name = "Tmr Svc", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_tmr_svc_pct_max), }, #if !defined(CONFIG_FREERTOS_UNICORE) { .thread_name = "ipc0", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_ipc0_pct_max), }, { .thread_name = "ipc1", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_ipc1_pct_max), }, #endif { .thread_name = "ota", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_ota_pct_max), }, { .thread_name = "sys_evt", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_sys_evt_pct_max), }, { .thread_name = "wifi", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_wifi_pct_max), }, { .thread_name = "esp_timer", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_esp_timer_pct_max), }, { .thread_name = "overflow_task", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_overflow_task_pct_max), }); #endif // !MEMFAULT_METRICS_THREADS_DEFAULTS ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/ota_session_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Implementation for Memfault OTA Session Metrics #include "ota_session_metrics.h" #include #include "app_memfault_transport.h" #include "esp_idf_version.h" #include "esp_wifi.h" #include "lwip/stats.h" #include "memfault/components.h" #include "memfault/esp_port/metrics.h" #include "memfault/ports/mbedtls/metrics.h" #include "sdkconfig.h" static struct ota_session_metrics_lwip_stats_prev { uint32_t tx; uint32_t rx; } s_ota_session_metrics_lwip_stats_prev; static struct ota_session_metrics_mbedtls_stats { uint32_t sent; uint32_t recv; } s_ota_session_metrics_mbedtls_stats; #if defined(CONFIG_MEMFAULT_METRICS_FLASH_ENABLE) static sMfltFlashCounters s_ota_session_metrics_flash_counters = { 0 }; #endif void ota_session_metrics_start(void) { // save starting tcp_tx_count and tcp_rx_count metrics s_ota_session_metrics_lwip_stats_prev = (struct ota_session_metrics_lwip_stats_prev){ .tx = lwip_stats.tcp.xmit, .rx = lwip_stats.tcp.recv, }; // reset mbedtls tx/rx stats s_ota_session_metrics_mbedtls_stats = (struct ota_session_metrics_mbedtls_stats){ 0 }; #if defined(CONFIG_MEMFAULT_METRICS_FLASH_ENABLE) // initialize baseline for flash counters (void)memfault_platform_metrics_get_flash_counters(&s_ota_session_metrics_flash_counters); #endif (void)MEMFAULT_METRICS_SESSION_START(ota); } extern int __real_mbedtls_net_send(void *ctx, unsigned char const *buf, size_t len); int __wrap_mbedtls_net_send(void *ctx, unsigned char const *buf, size_t len) { int rv = __real_mbedtls_net_send(ctx, buf, len); if (rv > 0) { s_ota_session_metrics_mbedtls_stats.sent += rv; } return rv; } extern int __real_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len); int __wrap_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len) { int rv = __real_mbedtls_net_recv(ctx, buf, len); if (rv > 0) { s_ota_session_metrics_mbedtls_stats.recv += rv; } return rv; } void ota_session_metrics_end(int ota_error_code) { // error code MEMFAULT_METRIC_SESSION_SET_SIGNED(ota_error_code, ota, ota_error_code); // Read last recorded station RSSI. this is reset every heartbeat in // ports/esp_idf/memfault/common/memfault_platform_metrics.c, so this gives us // a reasonably recent RSSI reading. Note: esp_wifi_sta_get_rssi() was added // in v5.2 and back-ported to 4.4.8/5.0.7/5.1.5, so there's no version check // here. int rssi; esp_err_t err = esp_wifi_sta_get_rssi(&rssi); if (err == ESP_OK) { MEMFAULT_METRIC_SESSION_SET_SIGNED(ota_wifi_rssi, ota, rssi); } // lwip TCP tx + rx packet counts uint32_t tx_count = lwip_stats.tcp.xmit - s_ota_session_metrics_lwip_stats_prev.tx; uint32_t rx_count = lwip_stats.tcp.recv - s_ota_session_metrics_lwip_stats_prev.rx; MEMFAULT_METRIC_SESSION_SET_UNSIGNED(ota_tcp_tx_count, ota, tx_count); MEMFAULT_METRIC_SESSION_SET_UNSIGNED(ota_tcp_rx_count, ota, rx_count); // mbedtls net sent + received byte counts MEMFAULT_METRIC_SESSION_SET_UNSIGNED(ota_tls_sent_bytes, ota, s_ota_session_metrics_mbedtls_stats.sent); MEMFAULT_METRIC_SESSION_SET_UNSIGNED(ota_tls_recv_bytes, ota, s_ota_session_metrics_mbedtls_stats.recv); #if defined(CONFIG_MEMFAULT_MBEDTLS_METRICS) // mbedtls max heap usage sMemfaultMbedtlsMetricData mbedtls_stats; memfault_mbedtls_heartbeat_get_data(&mbedtls_stats); MEMFAULT_METRIC_SESSION_SET_UNSIGNED(ota_mbedtls_mem_max_bytes, ota, mbedtls_stats.mem_max_bytes); #endif // CONFIG_MEMFAULT_MBEDTLS_METRICS #if defined(CONFIG_MEMFAULT_METRICS_FLASH_ENABLE) sMfltFlashCounters flash_counters = memfault_platform_metrics_get_flash_counters(&s_ota_session_metrics_flash_counters); MEMFAULT_METRIC_SESSION_ADD(flash_spi_erase_bytes, ota, flash_counters.erase_bytes); MEMFAULT_METRIC_SESSION_ADD(flash_spi_write_bytes, ota, flash_counters.write_bytes); #endif // CONFIG_MEMFAULT_METRICS_FLASH_ENABLE (void)MEMFAULT_METRICS_SESSION_END(ota); // after session end, issue a chunk post to upload the data to memfault (void)app_memfault_transport_send_chunks(); } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/ota_session_metrics.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! API for Memfault OTA Session Metrics #pragma once void ota_session_metrics_start(void); void ota_session_metrics_end(int ota_error_code); ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/settings.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Implement app settings helpers #include "settings.h" #include #include #include #include "argtable3/argtable3.h" #include "cmd_decl.h" #include "esp_console.h" #include "memfault/components.h" #include "nvs.h" #include "nvs_flash.h" // enable for more verbose debug logs #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include "esp_log.h" //! settings types: string or int enum settings_type { kSettingsTypeString, kSettingsTypeI32, }; //! settings key to {type, key_string, default value} map static const struct { enum settings_type type; const char *key_str; const union { const char *str; const int32_t i32; } default_value; } settings_table[] = { [kSettingsWifiSsid] = { kSettingsTypeString, "wifi_ssid", { .str = "", }, }, [kSettingsWifiPassword] = { kSettingsTypeString, "wifi_password", { .str = "", }, }, [kSettingsProjectKey] = { kSettingsTypeString, "project_key", { .str = CONFIG_MEMFAULT_PROJECT_KEY, }, }, [kSettingsLedBrightness] = { kSettingsTypeI32, "led_brightness", { .i32 = 5, }, }, [kSettingsLedBlinkIntervalMs] = { kSettingsTypeI32, "led_blink_ms", { .i32 = 500, }, }, [kSettingsChunksUrl] = { kSettingsTypeString, "chunks_url", { .str = MEMFAULT_HTTP_CHUNKS_API_HOST, }, }, [kSettingsDeviceUrl] = { kSettingsTypeString, "device_url", { .str = MEMFAULT_HTTP_DEVICE_API_HOST, }, }, }; static bool prv_settings_key_is_valid(enum settings_key key) { return key < (sizeof(settings_table) / sizeof(settings_table[0])); } static esp_err_t prv_open_nvs(nvs_handle_t *nvs_handle) { // Initialize NVS esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { // NVS partition was truncated and needs to be erased // Retry nvs_flash_init ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK(err); // Open err = nvs_open("storage", NVS_READWRITE, nvs_handle); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) opening NVS handle!", esp_err_to_name(err)); return err; } return ESP_OK; } #if __GNUC__ >= 11 __attribute__((access(write_only, 2))) #endif int settings_get(enum settings_key key, void *value, size_t *len) { if (!prv_settings_key_is_valid(key)) { ESP_LOGE(__func__, "Invalid key: %d", key); return ESP_ERR_INVALID_ARG; } // Initialize NVS nvs_handle_t nvs_handle; esp_err_t err = prv_open_nvs(&nvs_handle); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) opening NVS handle!", esp_err_to_name(err)); return err; } ESP_LOGD(__func__, "Opened NVS handle"); // Read switch (settings_table[key].type) { case kSettingsTypeString: err = nvs_get_str(nvs_handle, settings_table[key].key_str, (char *)value, len); break; case kSettingsTypeI32: err = nvs_get_i32(nvs_handle, settings_table[key].key_str, (int32_t *)value); break; default: ESP_LOGE(__func__, "Invalid type: %d", settings_table[key].type); err = ESP_ERR_INVALID_ARG; goto close; break; } if (err == ESP_ERR_NVS_NOT_FOUND) { ESP_LOGD(__func__, "%d not in store, loading default", key); err = ESP_OK; switch (settings_table[key].type) { case kSettingsTypeString: strncpy(value, settings_table[key].default_value.str, *len - 1); *len = strlen(settings_table[key].default_value.str); break; case kSettingsTypeI32: *(int32_t *)value = settings_table[key].default_value.i32; break; default: ESP_LOGE(__func__, "Invalid type: %d", settings_table[key].type); err = ESP_ERR_INVALID_ARG; goto close; break; } } else if (err != ESP_OK) { ESP_LOGE(__func__, "failed reading %d", key); } // Close close: nvs_close(nvs_handle); return err; } #if __GNUC__ >= 11 __attribute__((access(read_only, 2, 3))) #endif int settings_set(enum settings_key key, const void *value, size_t len) { if (!prv_settings_key_is_valid(key)) { ESP_LOGE(__func__, "Invalid key: %d", key); return ESP_ERR_INVALID_ARG; } // Initialize NVS nvs_handle_t nvs_handle; esp_err_t err = prv_open_nvs(&nvs_handle); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) opening NVS handle!", esp_err_to_name(err)); return err; } ESP_LOGD(__func__, "Opened NVS handle"); // Write switch (settings_table[key].type) { case kSettingsTypeString: err = nvs_set_str(nvs_handle, settings_table[key].key_str, value); break; case kSettingsTypeI32: err = nvs_set_i32(nvs_handle, settings_table[key].key_str, *(int32_t *)value); break; default: ESP_LOGE(__func__, "Invalid type: %d", settings_table[key].type); err = ESP_ERR_INVALID_ARG; goto close; break; } if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) writing key %d to NVS!", esp_err_to_name(err), key); } else { ESP_LOGD(__func__, "Wrote key %d to NVS", key); } // Commit written value. // After setting any values, nvs_commit() must be called to ensure changes are written // to flash storage. Implementations may write to storage at other times, // but this is not guaranteed. err = nvs_commit(nvs_handle); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) committing NVS!", esp_err_to_name(err)); } else { ESP_LOGD(__func__, "Successfully wrote key %d to NVS", key); } // Close close: nvs_close(nvs_handle); return err; } static struct { struct arg_str *key; struct arg_end *end; } s_get_args; static struct { struct arg_str *key; struct arg_str *value; struct arg_end *end; } s_set_args; static int prv_string_to_key(const char *str, enum settings_key *key) { for (size_t i = 0; i < sizeof(settings_table) / sizeof(settings_table[0]); i++) { if (strcmp(str, settings_table[i].key_str) == 0) { *key = i; return 0; } } return 1; } static int prv_get_and_print_setting(enum settings_key key) { // settings longer than 100 not supported size_t len = 100; char *value = malloc(len); if (value == NULL) { ESP_LOGE(__func__, "Error allocating %d bytes", len); return 1; } int err = settings_get(key, value, &len); if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) getting key %d", esp_err_to_name(err), key); return 1; } switch (settings_table[key].type) { case kSettingsTypeString: printf("%s: %s\n", settings_table[key].key_str, value); break; case kSettingsTypeI32: printf("%s: %" PRIi32 "\n", settings_table[key].key_str, *(int32_t *)value); break; default: break; } free(value); return 0; } static int prv_get_cmd(int argc, char **argv) { if (argc == 1) { // print every setting in the table for (size_t i = 0; i < sizeof(settings_table) / sizeof(settings_table[0]); i++) { (void)prv_get_and_print_setting(i); } return 0; } int nerrors = arg_parse(argc, argv, (void **)&s_get_args); if (nerrors != 0) { arg_print_errors(stderr, s_get_args.end, argv[0]); return 1; } enum settings_key key; if (prv_string_to_key(s_get_args.key->sval[0], &key)) { ESP_LOGE(__func__, "Invalid key: %s", s_get_args.key->sval[0]); return 1; } return prv_get_and_print_setting(key); } static int prv_set_cmd(int argc, char **argv) { int nerrors = arg_parse(argc, argv, (void **)&s_set_args); if (nerrors != 0) { arg_print_errors(stderr, s_set_args.end, argv[0]); return 1; } enum settings_key key; if (prv_string_to_key(s_set_args.key->sval[0], &key)) { ESP_LOGE(__func__, "Invalid key: %s", s_set_args.key->sval[0]); return 1; } int err = ESP_OK; switch (settings_table[key].type) { case kSettingsTypeString: err = settings_set(key, s_set_args.value->sval[0], strlen(s_set_args.value->sval[0])); break; case kSettingsTypeI32: // convert string to int { int32_t i32; if (sscanf(s_set_args.value->sval[0], "%" SCNi32, &i32) != 1) { ESP_LOGE(__func__, "Invalid value: %s", s_set_args.value->sval[0]); return 1; } err = settings_set(key, &i32, sizeof(i32)); } break; default: return 1; break; } if (err != ESP_OK) { ESP_LOGE(__func__, "Error (%s) setting key %d", esp_err_to_name(err), key); return 1; } return 0; } void settings_register_shell_commands(void) { s_get_args.key = arg_str1(NULL, NULL, "", "Key name to get"); s_get_args.end = arg_end(1); static const esp_console_cmd_t s_get_cmd = { .command = "settings_get", .help = "Get a setting", .hint = NULL, .func = prv_get_cmd, .argtable = &s_get_args, }; ESP_ERROR_CHECK(esp_console_cmd_register(&s_get_cmd)); s_set_args.key = arg_str1(NULL, NULL, "", "Key name to set"); s_set_args.value = arg_str1(NULL, NULL, "", "Value to set (string or i32)"); s_set_args.end = arg_end(2); static const esp_console_cmd_t s_set_cmd = { .command = "settings_set", .help = "Set a setting", .hint = NULL, .func = prv_set_cmd, .argtable = &s_set_args, }; ESP_ERROR_CHECK(esp_console_cmd_register(&s_set_cmd)); } ================================================ FILE: examples/esp32/apps/memfault_demo_app/main/settings.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! App settings helper functions. #pragma once #include #include "esp_idf_version.h" enum settings_key { kSettingsWifiSsid, kSettingsWifiPassword, kSettingsProjectKey, kSettingsLedBrightness, kSettingsLedBlinkIntervalMs, kSettingsChunksUrl, kSettingsDeviceUrl, }; #if __GNUC__ >= 11 __attribute__((access(write_only, 2))) #endif int settings_get(enum settings_key key, void *value, size_t *len); #if __GNUC__ >= 11 __attribute__((access(read_only, 2, 3))) #endif int settings_set(enum settings_key key, const void *value, size_t len); void settings_register_shell_commands(void); ================================================ FILE: examples/esp32/apps/memfault_demo_app/partitions_example.csv ================================================ # Name, Type, SubType, Offset, Size, Flags # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, 0x9000, 0x4000 otadata, data, ota, 0xd000, 0x2000 phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M, storage, data, fat, , 528K, coredump, data, coredump,, 256K, ota_0, app, ota_0, , 1M, ota_1, app, ota_1, , 1M, ================================================ FILE: examples/esp32/apps/memfault_demo_app/sdkconfig.defaults ================================================ # Reduce bootloader log verbosity CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y CONFIG_LOG_BOOTLOADER_LEVEL=2 # Increase main and timer task stack sizes CONFIG_MAIN_TASK_STACK_SIZE=7168 CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3072 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=3072 # Enable watchpoint stack overflow guard CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y # Enable filesystem CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 CONFIG_APP_OFFSET=0x10000 # Enable FreeRTOS stats formatting functions, needed for 'tasks' command CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # Workaround for issue in ESP-IDF v4.2.2 python dependencies, see: # https://github.com/espressif/esp-idf/issues/7631#issuecomment-934212224 # We only need the typical root certs, so this works for our app. CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=y # SoftAP is unused in this example, disabling it saves about 40kB flash CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n # Instead of pre-allocating large 16 + 4kB IN/OUT buffers when setting up the # TLS connection, use dynamic buffers. This reduces the large memory pressure at # the cost of some extra mallocs. CONFIG_MBEDTLS_DYNAMIC_BUFFER=y CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT=y # Trigger an assert if any malloc fails CONFIG_MEMFAULT_ASSERT_ON_ALLOC_FAILURE=y # Enable custom reboot reasons CONFIG_MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE=y # Enable compact logging CONFIG_MEMFAULT_COMPACT_LOG_ENABLE=y # Enable periodic upload thread for posting Memfault data CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=y # Upload logs. Not recommended for production. CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS=y # Support older ESP32-C3 variants CONFIG_ESP32C3_REV_MIN_2=y # Task Watchdog should crash the system CONFIG_ESP_TASK_WDT_PANIC=y # Enable OTA CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA=y CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA_CUSTOM_CBS=y # Enable Memfault Deep Sleep support CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT=y CONFIG_MEMFAULT_DEEP_SLEEP_ENABLE_DEBUG_LOG=y # The app's deep sleep timer function can use significant stack, especially if # non-nano formatting is enabled. Bump this up to accommodate CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 # This compiles out the assert string + file name from asserts, to save code # space CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y ================================================ FILE: examples/esp32/apps/memfault_demo_app/sdkconfig.heaptrace ================================================ # Heap tracing configs CONFIG_HEAP_POISONING_LIGHT=y CONFIG_HEAP_TASK_TRACKING=y CONFIG_HEAP_USE_HOOKS=y ================================================ FILE: examples/esp32/apps/memfault_demo_app/sdkconfig.mqtt ================================================ CONFIG_APP_MEMFAULT_TRANSPORT_MQTT=y CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=n ================================================ FILE: examples/freertos/.gitignore ================================================ /FreeRTOS-Kernel /components /ports ================================================ FILE: examples/freertos/Makefile ================================================ # Error on any uset variable MAKEFLAGS=--warn-undefined-variables # Default to a silent build. Run make with --trace for a verbose build. .SILENT: # Mac and Linux stat have different options required, set them up here ifeq ($(shell uname), Darwin) STAT = "stat -f '%z'" else STAT = "stat -c '%s'" endif BOARD ?= qemu_mps2_an385 $(info Building for $(BOARD)) # Set to 1 to build with templates/memfault_platform_port.c instead of # FreeRTOS platform functions. Used to test compilation with template files MEMFAULT_TEST_USE_PORT_TEMPLATE ?= 0 ifeq ($(MEMFAULT_TEST_USE_PORT_TEMPLATE),1) ifneq (qemu_mps2_an385,$(BOARD)) $(error Testing with the template port only supported on the qemu_mps2_an385 board) endif endif BOARD_DIR := boards/$(BOARD) BUILD_DIR := build/$(BOARD) ELF = $(BUILD_DIR)/main.elf ARM_CC ?= arm-none-eabi-gcc ARM_CXX ?= arm-none-eabi-g++ # if cc isn't set by the user, set it to ARM_CC ifeq ($(origin CC),default) CC := $(ARM_CC) CXX := $(ARM_CXX) endif # use ccache if available CCACHE := $(shell command -v ccache 2> /dev/null) ifdef CCACHE CC := ccache $(CC) CXX := ccache $(CXX) endif OBJCOPY ?= $(shell $(CC) -print-prog-name=objcopy) SIZE ?= $(shell $(CC) -print-prog-name=size) READELF ?= $(shell $(CC) -print-prog-name=readelf) # Check if FreeRTOS location set, add rule to download if not FREERTOS_DIR ?= ifeq ($(FREERTOS_DIR),) FREERTOS_DIR = FreeRTOS-Kernel FREERTOS_VER = V11.2.0 FREERTOS_SHA = 0adc196d4bd52a2d91102b525b0aafc1e14a2386 # Verify the FreeRTOS directory is present and matches expected SHA FREERTOS_STALE = \ $(shell \ if ! (echo "$(FREERTOS_SHA)" | diff -q $(FREERTOS_DIR)/.git/HEAD - > /dev/null 2>&1); then \ echo FREERTOS_STALE; \ fi \ ) .PHONY: FREERTOS_STALE $(FREERTOS_DIR)/.git/HEAD: $(FREERTOS_STALE) $(info FREERTOS_DIR needs updating, deleting stale copy (if present) and fetching from git) rm -rf $(FREERTOS_DIR) git -c advice.detachedHead=false clone --branch $(FREERTOS_VER) --depth 1 https://github.com/FreeRTOS/FreeRTOS-Kernel.git $(FREERTOS_DIR) .DEFAULT_GOAL := endif include $(BOARD_DIR)/Makefile # Gather list of FreeRTOS sources FREERTOS_SRCS += \ $(FREERTOS_DIR)/tasks.c \ $(FREERTOS_DIR)/queue.c \ $(FREERTOS_DIR)/list.c \ $(FREERTOS_DIR)/timers.c \ $(FREERTOS_DIR)/portable/MemMang/heap_4.c \ # Add application sources SRCS += \ src/main.c \ src/compact_log.cpp \ src/console.c \ src/heap_task.c \ src/metrics.c \ src/mpu.c \ src/memfault/memfault_platform_port.c \ $(BOARD_DIR)/startup.c \ $(BOARD_DIR)/memfault_platform_impl.c \ $(FREERTOS_SRCS) \ # Use Memfault SDK worker to gather initial Memfault SDK sources and include dirs MEMFAULT_SDK_ROOT := ../.. MEMFAULT_COMPONENTS ?= core util panics metrics demo include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk # Add CFLAGS defines for each of the memfault components enabled above CFLAGS += $(foreach each, $(MEMFAULT_COMPONENTS), -DMEMFAULT_COMPONENT_$(each)_) # Add additional SDK sources to project for FreeRTOS and RAM-backed coredump. # Intentionally using a wildcard to trap any new features added- it's nice to # have them enabled in this example app. SRCS += \ $(MEMFAULT_COMPONENTS_SRCS) \ $(MEMFAULT_SDK_ROOT)/ports/panics/src/memfault_platform_ram_backed_coredump.c \ ifneq ($(MEMFAULT_TEST_USE_PORT_TEMPLATE), 1) SRCS += \ $(wildcard $(MEMFAULT_SDK_ROOT)/ports/freertos/src/*.c) endif # Fixup build path for objects of the Memfault SDK, all build output kept within build/ OBJS := $(subst $(MEMFAULT_SDK_ROOT),memfault-firmware-sdk,$(SRCS:%=$(BUILD_DIR)/%.o)) INCLUDE_PATHS += \ -I$(FREERTOS_DIR)/include \ -I. \ -Isrc \ -Isrc/memfault \ -I$(MEMFAULT_COMPONENTS_INC_FOLDERS) \ -I$(MEMFAULT_SDK_ROOT)/ports/include \ -I$(MEMFAULT_SDK_ROOT) \ -I$(FREERTOS_DIR)/portable/GCC/$(FREERTOS_PORT) \ # generic (non-arch-specific) CFLAGS CFLAGS += \ -nostartfiles \ -Werror \ -Wall \ -Wextra \ -Werror=undef \ -ffunction-sections \ -fdata-sections \ -ggdb3 \ -Og \ -MD \ -fdebug-prefix-map="$(shell pwd)"=. \ -DBOARD=\"$(BOARD)\" \ $(ARCH_CFLAGS) \ # Enable the self test by default MEMFAULT_DEMO_CLI_SELF_TEST ?= 1 # Enable all device vitals by default MEMFAULT_METRICS_SYNC_SUCCESS ?= 1 MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS ?= 1 MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME ?= 1 MEMFAULT_METRICS_BATTERY_ENABLE ?= 1 CFLAGS += \ -DMEMFAULT_DEMO_CLI_SELF_TEST=$(MEMFAULT_DEMO_CLI_SELF_TEST) \ -DMEMFAULT_TEST_USE_PORT_TEMPLATE=$(MEMFAULT_TEST_USE_PORT_TEMPLATE) \ -DMEMFAULT_METRICS_SYNC_SUCCESS=$(MEMFAULT_METRICS_SYNC_SUCCESS) \ -DMEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS=$(MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS) \ -DMEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME=$(MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME) \ -DMEMFAULT_METRICS_BATTERY_ENABLE=$(MEMFAULT_METRICS_BATTERY_ENABLE) \ LINKER_SCRIPT = $(BOARD_DIR)/linker.ld LDFLAGS += \ -Wl,-T$(LINKER_SCRIPT) \ -Wl,--gc-sections \ --specs=nano.specs \ --specs=rdimon.specs \ -Wl,-lc \ -Wl,-lrdimon \ -Wl,-Map=$(BUILD_DIR)/main.map \ -Wl,--build-id \ -Wl,--print-memory-usage \ # Check if the compiler is = 4.9.3, if so, disable some incopatible flags. The # warnings they emit are due to incomplete support for C99 braced initializers # in this old version of GCC. GCC_VERSION := $(shell $(CC) -dumpversion) ifeq ($(GCC_VERSION),4.9.3) $(info Detected GCC 4.9.3, disabling some flags) CFLAGS += \ -Wno-error=missing-braces \ -Wno-error=missing-field-initializers \ # remove -Wl,--print-memory-usage from LDFLAGS COMMA=, LDFLAGS := $(filter-out -Wl$(COMMA)--print-memory-usage,$(LDFLAGS)) endif .PHONY: all all: $(ELF) # Require clone to complete as prereq for sources $(SRCS): $(FREERTOS_DIR)/.git/HEAD # Store computed cflags in a file; it's a prerequisite for all objects. Use a # shell hack to check if the current cflags are different from the stored file, # and make it out of date if so # If CFLAGS differ from last build, rebuild all files RAW_CFLAGS := $(CFLAGS) $(LDFLAGS) CFLAGS_STALE = \ $(shell \ if ! (echo "$(RAW_CFLAGS)" | diff -q $(BUILD_DIR)/cflags - > /dev/null 2>&1); then \ echo CFLAGS_STALE; \ fi \ ) .PHONY: CFLAGS_STALE $(BUILD_DIR)/cflags: $(CFLAGS_STALE) Makefile mkdir -p $(dir $@) echo "$(RAW_CFLAGS)" > $@ # Add rules for patched build objects from SDK $(BUILD_DIR)/memfault-firmware-sdk/%.c.o: $(MEMFAULT_SDK_ROOT)/%.c $(BUILD_DIR)/cflags mkdir -p $(dir $@) $(info Compiling $<) $(CC) -std=gnu11 $(CFLAGS) $(INCLUDE_PATHS) -c $< -o $@ $(BUILD_DIR)/%.c.o: %.c $(BUILD_DIR)/cflags mkdir -p $(dir $@) $(info Compiling $<) $(CC) -std=gnu11 $(CFLAGS) $(INCLUDE_PATHS) -c $< -o $@ $(BUILD_DIR)/%.cpp.o: %.cpp $(BUILD_DIR)/cflags mkdir -p $(dir $@) $(info Compiling $<) $(CXX) -std=gnu++11 $(CFLAGS) $(INCLUDE_PATHS) -c $< -o $@ $(ELF).uncompressed: $(OBJS) $(LINKER_SCRIPT) $(info Linking $@) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $@ $(ELF): $(ELF).uncompressed echo -n 'Compressing debug info... ' $(OBJCOPY) --compress-debug-sections $^ $@ echo From $$("$(STAT)" $^) to $$("$(STAT)" $@) bytes $(SIZE) $@ $(READELF) -n $@ | grep 'Build ID' -include $(OBJS:.o=.d) .PHONY: debug debug: $(ELF) $(info Starting debugger) $(DEBUG_COMMAND) .PHONY: gdb gdb: $(ELF) $(GDB_COMMAND) .PHONY: run run: $(ELF) $(RUN_COMMAND) # This target dumps the preprocessed output of the memfault_metrics.c file .PHONY: memfault_metrics.c memfault_metrics.c: # build/qemu_mps2_an385/memfault-firmware-sdk/components/metrics/src/memfault_metrics.c.o $(CC) $(CFLAGS:-MD=) $(INCLUDE_PATHS) -c $(MEMFAULT_SDK_ROOT)/components/metrics/src/memfault_metrics.c -E -P -o - .PHONY: clean clean: rm -rf build ================================================ FILE: examples/freertos/README.md ================================================ # Memfault FreeRTOS Demo Application This example demonstrates integrating Memfault with FreeRTOS applications. The example uses the QEMU target, MPS2 Cortex M3 AN385, to run the application and is based on the [MPS2 Cortex M3 AN385 FreeRTOS Demo](https://github.com/FreeRTOS/FreeRTOS/tree/main/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC). This file outlines various components of the example application. For more information on the MCU SDK, please see our documentation at [docs.memfault.com](docs.memfault.com). ## Prerequisites The project requires the following: - git - GNU Make - A version of the [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) - **Note**: The project assumes the toolchain can be found via your environment's PATH, otherwise you may specify by setting `ARM_CC` with the full path to the toolchain `/bin` directory ## FreeRTOS Installation By default the project will clone FreeRTOS V11.2.0 into the project directory. If you would like to use a different version, please set `FREERTOS_DIR` to an existing FreeRTOS directory. ## Building To build, from the project directory run: ```bash make ``` ## Running To run, from the project directory run: ```bash make run ``` With the console open you may test out various features using the demo CLI commands. A simple example of testing assert handling and data export: ```bash MFLT:[INFO] GNU Build ID: 8a45620abae6842848670c379411d8b31846eadc MFLT:[INFO] S/N: freertos-example MFLT:[INFO] SW type: qemu-app MFLT:[INFO] SW version: 1.0.0 MFLT:[INFO] HW version: qemu-mps2-an385 MFLT:[INFO] Memfault Initialized! mflt> test_assert MFLT:[INFO] GNU Build ID: 8a45620abae6842848670c379411d8b31846eadc MFLT:[INFO] S/N: freertos-example MFLT:[INFO] SW type: qemu-app MFLT:[INFO] SW version: 1.0.0 MFLT:[INFO] HW version: qemu-mps2-an385 MFLT:[INFO] Memfault Initialized! mflt> export MFLT:[INFO] MC:SK8SgQlDT1JFAgYAAwwTFAABTAYAIwGAAAD//wAAe30AAEBmACB7fQwACKUHgFMAICilE3BTACA3WAAAhG8KABNhMP8/IHBTACAMDgABFAY=: MFLT:[INFO] MC:wE0AKYpFYgq65oQoSGcMN5QR2LMYRurcAg4AARAGACFmcmVlcnRvcy1leGFtcGxlCg4AAQUGAAsxLjAuMAsOAAEIBgARcWVtdS1hcHAEDgA=: MFLT:[INFO] MC:wJsBAQ8GAB9xZW11LW1wczItYW4zODUHDgABBAYAASgGAAEFDgABBAYACQGAAAABBgAJJO0A4BwSAAEBCAABQCAAAQEGAAkY7QDgDBoABP8=: MFLT:[INFO] MC:wOgBAQEGAAkE4ADgEB4ACQcAAQABBgAJBO0A4AgGAAcD+AAECAABAQYACfztAOAEDgABAQgAB+EA4AQOAAEBCAAH4gDgBA4AAQEIAAfjAOA=: MFLT:[INFO] MC:wLUCBA4AAQEIAAfkAOAgRgABAQYACUhlACAoBgAGARoAAakIACcCAADMWQAgzFkAIAACAABpvQAAAQYA4QLMWQAgAAIAAAE2R05VIEJ1aWw=: # Output clipped ``` The exported output can be pasted into the Memfault web app for processing. See this [page](https://docs.memfault.com/docs/mcu/arm-cortex-m-guide/#post-data-to-cloud-via-local-debug-setup) in our docs. ## Debugging To debug, from the project directory run: ```bash make debug ``` QEMU will start the application and wait for a debugger to attach. From a separate terminal run: ```bash make gdb ``` ## Application Features ### Console and Memfault Demo CLI The application is built with the MCU SDK's demo CLI. This CLI includes commands to trigger faults; collect data from logs, trace events, and metrics; and export data to Memfault's web application for processing. For more info on the commands available use the "help" command in the console or view the source code at `memfault-firmware-sdk/components/demo`. ### Application Metrics Two simple tasks are used to illustrate creating metrics to measure your application's performance. The first task, Heap Task, allocates and deallocates memory periodically. A second tasks, Metrics Task, sets metrics based on the state of the system heap. The two metrics produced are: - `FreeRTOS_HeapFreeBytes`: Measures current total bytes free in the FreeRTOS heap - `FreeRTOS_HeapMinFreeBytes`: Measures the lowest total bytes free in the FreeRTOS heap since boot - `FreeRTOS_Heap_pct_max`: Measures the max percentage of the FreeRTOS heap used since boot These metrics are collected every 60 seconds in this example. This can be changed by modifying `MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS` in `memfault_platform_config.h`. ### Heap Tracing The heap tracing capabilities of the MCU SDK are enabled by default. The SDK will collect a set of the most recent allocations with their location in code and size when a coredump is saved. ### Logging Collection The application is configured to collect the most recent logs along whenever a coredump is collected. ================================================ FILE: examples/freertos/boards/qemu_mps2_an385/Makefile ================================================ # Board-specific build/debug properties FREERTOS_PORT = ARM_CM3 MEMFAULT_ARCH = arm ARCH_CFLAGS += \ -mthumb \ -mcpu=cortex-m3 \ FREERTOS_SRCS += \ $(FREERTOS_DIR)/portable/GCC/$(FREERTOS_PORT)/port.c \ ATTACH_DEBUGGER = -s -S RUN_COMMAND = qemu-system-arm -machine mps2-an385 -monitor null -semihosting \ --semihosting-config enable=on,target=native \ -kernel $(ELF) \ -serial stdio -nographic \ DEBUG_COMMAND = $(RUN_COMMAND) $(ATTACH_DEBUGGER) # Permit overriding the gdb executable GDB ?= gdb GDB_COMMAND = \ $(GDB) --ex 'target extended-remote :1234' $(ELF) ================================================ FILE: examples/freertos/boards/qemu_mps2_an385/linker.ld ================================================ MEMORY { FLASH (xr) : ORIGIN = 0x00000000, LENGTH = 4M RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 1M } ENTRY(Reset_Handler) _Min_Heap_Size = 0x3000 ; /* Required amount of heap. */ _Min_Stack_Size = 0x4000 ; /* Required amount of stack. */ M_VECTOR_RAM_SIZE = (16 + 48) * 4; _estack = ORIGIN(RAM) + LENGTH(RAM); SECTIONS { .isr_vector : { __vector_table = .; KEEP(*(.isr_vector)) . = ALIGN(4); } > FLASH .text : { . = ALIGN(4); *(.text*) KEEP (*(.init)) KEEP (*(.fini)) KEEP(*(.eh_frame)) *(.rodata*) . = ALIGN(4); _etext = .; } > FLASH .note.gnu.build-id : { __start_gnu_build_id_start = .; KEEP(*(.note.gnu.build-id)) } > FLASH .ARM.extab : { . = ALIGN(4); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(4); } >FLASH .ARM : { . = ALIGN(4); __exidx_start = .; *(.ARM.exidx* .gnu.linkonce.armexidx.*) __exidx_end = .; . = ALIGN(4); } >FLASH .interrupts_ram : { . = ALIGN(4); __VECTOR_RAM__ = .; __interrupts_ram_start__ = .; . += M_VECTOR_RAM_SIZE; . = ALIGN(4); __interrupts_ram_end = .; } > RAM _sidata = LOADADDR(.data); .data : /* AT ( _sidata ) */ { . = ALIGN(4); _sdata = .; *(.data*) . = ALIGN(4); _edata = .; } > RAM AT > FLASH .uninitialized (NOLOAD): { . = ALIGN(32); __uninitialized_start = .; *(.uninitialized) KEEP(*(.keep.uninitialized)) . = ALIGN(32); __uninitialized_end = .; } > RAM .bss : { . = ALIGN(4); _sbss = .; __bss_start__ = _sbss; __memfault_capture_bss_start = .; *tasks*.o(.bss COMMON .bss*) *timers*.o(.bss COMMON .bss*) __memfault_capture_bss_end = .; *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; __bss_end__ = _ebss; } >RAM .heap : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); _heap_bottom = .; . = . + _Min_Heap_Size; _heap_top = .; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM /* Set stack top to end of RAM, and stack limit move down by * size of stack_dummy section */ __StackTop = ORIGIN(RAM) + LENGTH(RAM); __StackLimit = __StackTop - _Min_Stack_Size; PROVIDE(__stack = __StackTop); log_fmt 0xF0000000 (INFO): { /* Old ld (GCC ARM 4.9.3 for example) doesn't emit this symbol by default */ __start_log_fmt = ABSOLUTE(.); KEEP(*(*.log_fmt_hdr)) KEEP(*(log_fmt)) } /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack") } ================================================ FILE: examples/freertos/boards/qemu_mps2_an385/memfault_platform_impl.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/components.h" #define SCB_AIRCR_ADDR (0xE000ED0C) #define SCB_AIRCR_VECTKEY_WRITE (0x5FAUL << 16U) #define SCB_AIRCR_SYSRESETREQ (1U << 2) #define SCB_AIRCR_RESET_SYSTEM (SCB_AIRCR_VECTKEY_WRITE | SCB_AIRCR_SYSRESETREQ) #define CMSDK_RSTINFO_ADDR (0x4001F010) #define CMSDK_RSTINFO_MASK (0x07) #define CMSDK_RSTINFO_SYSRESETREQ_MASK (1 << 0) #define CMSDK_RSTINFO_WATCHDOG_MASK (1 << 1) #define CMSDK_RSTINFO_LOCKUP_MASK (1 << 2) #if MEMFAULT_TEST_USE_PORT_TEMPLATE != 1 void memfault_platform_reboot(void) { volatile uint32_t *SCB_AIRCR = (uint32_t *)SCB_AIRCR_ADDR; *SCB_AIRCR = SCB_AIRCR_RESET_SYSTEM; while (1) { } // unreachable } size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { static const struct { uint32_t start_addr; size_t length; } s_mcu_mem_regions[] = { { .start_addr = 0x20000000, .length = 0x800000 }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_mcu_mem_regions); i++) { const uint32_t lower_addr = s_mcu_mem_regions[i].start_addr; const uint32_t upper_addr = lower_addr + s_mcu_mem_regions[i].length; if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); } } return 0; } #endif #if MEMFAULT_TEST_USE_PORT_TEMPLATE != 1 static uint32_t prv_read_reboot_reg(void) { volatile uint32_t *cmsdk_rstinfo = (uint32_t *)CMSDK_RSTINFO_ADDR; return (*cmsdk_rstinfo & CMSDK_RSTINFO_MASK); } static void prv_clear_reboot_reg(void) { volatile uint32_t *cmsdk_rstinfo = (uint32_t *)CMSDK_RSTINFO_ADDR; // Write any bit to clear *cmsdk_rstinfo = 1; } //! Decode the reset info register //! //! NB: QEMU does not follow the behavior specified in AN385 and always returns 0 static eMemfaultRebootReason prv_decode_reboot_reg(uint32_t reboot_reg) { eMemfaultRebootReason result = kMfltRebootReason_Unknown; if (reboot_reg & CMSDK_RSTINFO_SYSRESETREQ_MASK) { result = kMfltRebootReason_SoftwareReset; } else if (reboot_reg & CMSDK_RSTINFO_WATCHDOG_MASK) { result = kMfltRebootReason_HardwareWatchdog; } else if (reboot_reg & CMSDK_RSTINFO_LOCKUP_MASK) { result = kMfltRebootReason_Lockup; } else { result = kMfltRebootReason_PowerOnReset; } prv_clear_reboot_reg(); return result; } void memfault_reboot_reason_get(sResetBootupInfo *reset_info) { uint32_t reboot_reg = prv_read_reboot_reg(); eMemfaultRebootReason reboot_reason = prv_decode_reboot_reg(reboot_reg); *reset_info = (sResetBootupInfo){ .reset_reason_reg = reboot_reg, .reset_reason = reboot_reason, }; } #endif ================================================ FILE: examples/freertos/boards/qemu_mps2_an385/startup.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include "memfault/components.h" typedef struct UART_t { volatile uint32_t DATA; volatile uint32_t STATE; volatile uint32_t CTRL; volatile uint32_t INTSTATUS; volatile uint32_t BAUDDIV; } UART_t; #define UART0_ADDR ((UART_t *)(0x40004000)) #define UART_DR(baseaddr) (*(unsigned int *)(baseaddr)) #define UART_STATE_TXFULL (1 << 0) #define UART_CTRL_TX_EN (1 << 0) #define UART_CTRL_RX_EN (1 << 1) // snagged the below def from // https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_ARM_FM/TARGET_FVP_MPS2/serial_api.c#L356 #define UART_STATE_RXRDY (2 << 0) /** * @brief initializes the UART emulated hardware */ void uart_init(void) { UART0_ADDR->BAUDDIV = 16; UART0_ADDR->CTRL = UART_CTRL_TX_EN | UART_CTRL_RX_EN; } /** * @brief not used anywhere in the code * @todo implement if necessary * */ int _read(__attribute__((unused)) int file, __attribute__((unused)) char *buf, __attribute__((unused)) int len) { for (int i = 0; i < len; i++) { if (UART0_ADDR->STATE & UART_STATE_RXRDY) { char data = UART0_ADDR->DATA; // for some reason, BACKSPACE=0x7f, DEL=0x7e, so remap those data = (data == 0x7f) ? '\b' : data; data = (data == 0x7e) ? 0x7f : data; buf[i] = data; } else { return i; } } return len; } /** * @brief Write bytes to the UART channel to be displayed on the command line * with qemu * @param [in] file ignored * @param [in] buf buffer to send * @param [in] len length of the buffer * @returns the number of bytes written */ int _write(__attribute__((unused)) int file, __attribute__((unused)) char *buf, int len) { int todo; for (todo = 0; todo < len; todo++) { UART_DR(UART0_ADDR) = *buf++; } return len; } extern int main(void); // Following symbols are defined by the linker. // Start address for the initialization values of the .data section. extern uint32_t _sidata; // Start address for the .data section extern uint32_t _sdata; // End address for the .data section extern uint32_t _edata; // Start address for the .bss section extern uint32_t __bss_start__; // End address for the .bss section extern uint32_t __bss_end__; // End address for stack extern void __stack(void); // Prevent inlining to avoid persisting any stack allocations __attribute__((noinline)) static void prv_cinit(void) { // Initialize data and bss // Copy the data segment initializers from flash to SRAM for (uint32_t *dst = &_sdata, *src = &_sidata; dst < &_edata;) { *(dst++) = *(src++); } // Zero fill the bss segment. for (uint32_t *dst = &__bss_start__; (uintptr_t)dst < (uintptr_t)&__bss_end__;) { *(dst++) = 0; } } __attribute__((noreturn)) void Reset_Handler(void) { // enable mem/bus/usage faults #define SCBSHCSR_ (*(uint32_t *)0xE000ED24) SCBSHCSR_ |= (1UL << 16U) | (1UL << 17U) | (1UL << 18U); prv_cinit(); uart_init(); // Call the application's entry point. (void)main(); // shouldn't return while (1) { }; } // These are defined in the FreeRTOS port extern void vPortSVCHandler(void); extern void xPortPendSVHandler(void); extern void xPortSysTickHandler(void); extern void NMI_Handler(void); extern void HardFault_Handler(void); extern void MemoryManagement_Handler(void); extern void BusFault_Handler(void); extern void UsageFault_Handler(void); // A minimal vector table for a Cortex M. __attribute__((section(".isr_vector"))) void (*const g_pfnVectors[])(void) = { __stack, // initial stack pointer Reset_Handler, // 1 NMI_Handler, // 2 HardFault_Handler, // 3 MemoryManagement_Handler, // 4 BusFault_Handler, // 5 UsageFault_Handler, // 6 0, // 7, reserved 0, // 8, reserved 0, // 9, reserved 0, // 10, reserved vPortSVCHandler, // 11 SVC_Handler -5 0, // 12 DebugMon_Handler -4 0, // 13 reserved */ xPortPendSVHandler, // 14 PendSV handler -2 xPortSysTickHandler, // 15 SysTick_Handler -1 0, // 16 UART 0 0, // 17 UART 0 0, // 18 UART 1 0, // 19 UART 1 0, // 20 UART 2 0, // 21 UART 2 0, // 22 GPIO 0 0, // 23 GPIO 1 MEMFAULT_EXC_HANDLER_WATCHDOG, // 24 Timer 0 }; ================================================ FILE: examples/freertos/boards/qemu_mps2_an386/Makefile ================================================ # Board-specific build/debug properties FREERTOS_PORT = ARM_CM4F MEMFAULT_ARCH = arm ARCH_CFLAGS += \ -mthumb \ -mcpu=cortex-m4 \ -mfloat-abi=hard \ -mfpu=fpv4-sp-d16 \ -DconfigENABLE_FPU=1 \ FREERTOS_SRCS += \ $(FREERTOS_DIR)/portable/GCC/$(FREERTOS_PORT)/port.c \ ATTACH_DEBUGGER = -s -S RUN_COMMAND = qemu-system-arm -machine mps2-an386 -monitor null -semihosting \ --semihosting-config enable=on,target=native \ -kernel $(ELF) \ -serial stdio -nographic \ DEBUG_COMMAND = $(RUN_COMMAND) $(ATTACH_DEBUGGER) # Permit overriding the gdb executable GDB ?= gdb GDB_COMMAND = \ $(GDB) --ex 'target extended-remote :1234' $(ELF) ================================================ FILE: examples/freertos/boards/qemu_mps2_an386/linker.ld ================================================ MEMORY { FLASH (xr) : ORIGIN = 0x00000000, LENGTH = 4M RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 1M } ENTRY(Reset_Handler) _Min_Heap_Size = 0x3000 ; /* Required amount of heap. */ _Min_Stack_Size = 0x4000 ; /* Required amount of stack. */ M_VECTOR_RAM_SIZE = (16 + 48) * 4; _estack = ORIGIN(RAM) + LENGTH(RAM); SECTIONS { .isr_vector : { __vector_table = .; KEEP(*(.isr_vector)) . = ALIGN(4); } > FLASH .text : { . = ALIGN(4); *(.text*) KEEP (*(.init)) KEEP (*(.fini)) KEEP(*(.eh_frame)) *(.rodata*) . = ALIGN(4); _etext = .; } > FLASH .note.gnu.build-id : { __start_gnu_build_id_start = .; KEEP(*(.note.gnu.build-id)) } > FLASH .ARM.extab : { . = ALIGN(4); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(4); } >FLASH .ARM : { . = ALIGN(4); __exidx_start = .; *(.ARM.exidx* .gnu.linkonce.armexidx.*) __exidx_end = .; . = ALIGN(4); } >FLASH .interrupts_ram : { . = ALIGN(4); __VECTOR_RAM__ = .; __interrupts_ram_start__ = .; . += M_VECTOR_RAM_SIZE; . = ALIGN(4); __interrupts_ram_end = .; } > RAM _sidata = LOADADDR(.data); .data : /* AT ( _sidata ) */ { . = ALIGN(4); _sdata = .; *(.data*) . = ALIGN(4); _edata = .; } > RAM AT > FLASH .uninitialized (NOLOAD): { . = ALIGN(32); __uninitialized_start = .; *(.uninitialized) KEEP(*(.keep.uninitialized)) . = ALIGN(32); __uninitialized_end = .; } > RAM .bss : { . = ALIGN(4); _sbss = .; __bss_start__ = _sbss; __memfault_capture_bss_start = .; *tasks*.o(.bss COMMON .bss*) *timers*.o(.bss COMMON .bss*) __memfault_capture_bss_end = .; *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; __bss_end__ = _ebss; } >RAM .heap : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); _heap_bottom = .; . = . + _Min_Heap_Size; _heap_top = .; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM /* Set stack top to end of RAM, and stack limit move down by * size of stack_dummy section */ __StackTop = ORIGIN(RAM) + LENGTH(RAM); __StackLimit = __StackTop - _Min_Stack_Size; PROVIDE(__stack = __StackTop); log_fmt 0xF0000000 (INFO): { /* Old ld (GCC ARM 4.9.3 for example) doesn't emit this symbol by default */ __start_log_fmt = ABSOLUTE(.); KEEP(*(*.log_fmt_hdr)) KEEP(*(log_fmt)) } /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack") } ================================================ FILE: examples/freertos/boards/qemu_mps2_an386/memfault_platform_impl.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/components.h" #define SCB_AIRCR_ADDR (0xE000ED0C) #define SCB_AIRCR_VECTKEY_WRITE (0x5FAUL << 16U) #define SCB_AIRCR_SYSRESETREQ (1U << 2) #define SCB_AIRCR_RESET_SYSTEM (SCB_AIRCR_VECTKEY_WRITE | SCB_AIRCR_SYSRESETREQ) #define CMSDK_RSTINFO_ADDR (0x4001F010) #define CMSDK_RSTINFO_MASK (0x07) #define CMSDK_RSTINFO_SYSRESETREQ_MASK (1 << 0) #define CMSDK_RSTINFO_WATCHDOG_MASK (1 << 1) #define CMSDK_RSTINFO_LOCKUP_MASK (1 << 2) void memfault_platform_reboot(void) { volatile uint32_t *SCB_AIRCR = (uint32_t *)SCB_AIRCR_ADDR; *SCB_AIRCR = SCB_AIRCR_RESET_SYSTEM; while (1) { } // unreachable } size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { static const struct { uint32_t start_addr; size_t length; } s_mcu_mem_regions[] = { { .start_addr = 0x20000000, .length = 0x800000 }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_mcu_mem_regions); i++) { const uint32_t lower_addr = s_mcu_mem_regions[i].start_addr; const uint32_t upper_addr = lower_addr + s_mcu_mem_regions[i].length; if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); } } return 0; } static uint32_t prv_read_reboot_reg(void) { volatile uint32_t *cmsdk_rstinfo = (uint32_t *)CMSDK_RSTINFO_ADDR; return (*cmsdk_rstinfo & CMSDK_RSTINFO_MASK); } static void prv_clear_reboot_reg(void) { volatile uint32_t *cmsdk_rstinfo = (uint32_t *)CMSDK_RSTINFO_ADDR; // Write any bit to clear *cmsdk_rstinfo = 1; } //! Decode the reset info register //! //! NB: QEMU does not follow the behavior specified in AN386 and always returns 0 static eMemfaultRebootReason prv_decode_reboot_reg(uint32_t reboot_reg) { eMemfaultRebootReason result = kMfltRebootReason_Unknown; if (reboot_reg & CMSDK_RSTINFO_SYSRESETREQ_MASK) { result = kMfltRebootReason_SoftwareReset; } else if (reboot_reg & CMSDK_RSTINFO_WATCHDOG_MASK) { result = kMfltRebootReason_HardwareWatchdog; } else if (reboot_reg & CMSDK_RSTINFO_LOCKUP_MASK) { result = kMfltRebootReason_Lockup; } else { result = kMfltRebootReason_PowerOnReset; } prv_clear_reboot_reg(); return result; } void memfault_reboot_reason_get(sResetBootupInfo *reset_info) { uint32_t reboot_reg = prv_read_reboot_reg(); eMemfaultRebootReason reboot_reason = prv_decode_reboot_reg(reboot_reg); *reset_info = (sResetBootupInfo){ .reset_reason_reg = reboot_reg, .reset_reason = reboot_reason, }; } ================================================ FILE: examples/freertos/boards/qemu_mps2_an386/startup.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include "memfault/components.h" typedef struct UART_t { volatile uint32_t DATA; volatile uint32_t STATE; volatile uint32_t CTRL; volatile uint32_t INTSTATUS; volatile uint32_t BAUDDIV; } UART_t; #define UART0_ADDR ((UART_t *)(0x40004000)) #define UART_DR(baseaddr) (*(unsigned int *)(baseaddr)) #define UART_STATE_TXFULL (1 << 0) #define UART_CTRL_TX_EN (1 << 0) #define UART_CTRL_RX_EN (1 << 1) // snagged the below def from // https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_ARM_FM/TARGET_FVP_MPS2/serial_api.c#L356 #define UART_STATE_RXRDY (2 << 0) /** * @brief initializes the UART emulated hardware */ void uart_init(void) { UART0_ADDR->BAUDDIV = 16; UART0_ADDR->CTRL = UART_CTRL_TX_EN | UART_CTRL_RX_EN; } /** * @brief not used anywhere in the code * @todo implement if necessary * */ int _read(__attribute__((unused)) int file, __attribute__((unused)) char *buf, __attribute__((unused)) int len) { for (int i = 0; i < len; i++) { if (UART0_ADDR->STATE & UART_STATE_RXRDY) { char data = UART0_ADDR->DATA; // for some reason, BACKSPACE=0x7f, DEL=0x7e, so remap those data = (data == 0x7f) ? '\b' : data; data = (data == 0x7e) ? 0x7f : data; buf[i] = data; } else { return i; } } return len; } /** * @brief Write bytes to the UART channel to be displayed on the command line * with qemu * @param [in] file ignored * @param [in] buf buffer to send * @param [in] len length of the buffer * @returns the number of bytes written */ int _write(__attribute__((unused)) int file, __attribute__((unused)) char *buf, int len) { int todo; for (todo = 0; todo < len; todo++) { UART_DR(UART0_ADDR) = *buf++; } return len; } extern int main(void); // Following symbols are defined by the linker. // Start address for the initialization values of the .data section. extern uint32_t _sidata; // Start address for the .data section extern uint32_t _sdata; // End address for the .data section extern uint32_t _edata; // Start address for the .bss section extern uint32_t __bss_start__; // End address for the .bss section extern uint32_t __bss_end__; // End address for stack extern uint32_t __stack; // Prevent inlining to avoid persisting any stack allocations __attribute__((noinline)) static void prv_cinit(void) { // Initialize data and bss // Copy the data segment initializers from flash to SRAM for (uint32_t *dst = &_sdata, *src = &_sidata; dst < &_edata;) { *(dst++) = *(src++); } // Zero fill the bss segment. for (uint32_t *dst = &__bss_start__; (uintptr_t)dst < (uintptr_t)&__bss_end__;) { *(dst++) = 0; } } __attribute__((noreturn)) void Reset_Handler(void) { // enable mem/bus/usage faults #define SCBSHCSR_ (*(uint32_t *)0xE000ED24) SCBSHCSR_ |= (1UL << 16U) | (1UL << 17U) | (1UL << 18U); prv_cinit(); uart_init(); // Call the application's entry point. (void)main(); // shouldn't return while (1) { }; } // These are defined in the FreeRTOS port extern void vPortSVCHandler(void); extern void xPortPendSVHandler(void); extern void xPortSysTickHandler(void); extern void NMI_Handler(void); extern void HardFault_Handler(void); extern void MemoryManagement_Handler(void); extern void BusFault_Handler(void); 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 Reset_Handler, NMI_Handler, HardFault_Handler, MemoryManagement_Handler, BusFault_Handler, UsageFault_Handler, 0, 0, 0, 0, vPortSVCHandler, /* SVC_Handler -5 */ 0, /* DebugMon_Handler -4 */ 0, /* reserved */ xPortPendSVHandler, /* PendSV handler -2 */ xPortSysTickHandler, /* SysTick_Handler -1 */ 0, // 16 UART 0 0, // 17 UART 0 0, // 18 UART 1 0, // 19 UART 1 0, // 20 UART 2 0, // 21 UART 2 0, // 22 GPIO 0 0, // 23 GPIO 1 MEMFAULT_EXC_HANDLER_WATCHDOG, // 24 Timer 0 }; ================================================ FILE: examples/freertos/boards/qemu_mps2_an505/Makefile ================================================ # Board-specific build/debug properties FREERTOS_PORT = ARM_CM33_NTZ/non_secure MEMFAULT_ARCH = arm # doc for -mcmse # https://gcc.gnu.org/onlinedocs/gcc-14.1.0/gcc/ARM-Options.html#index-mcmse ARCH_CFLAGS += \ -mcpu=cortex-m33 \ -mthumb \ -mfloat-abi=hard \ -mfpu=fpv5-sp-d16 \ -DconfigENABLE_FPU=1 \ -DconfigENABLE_MPU=0 \ -DconfigENABLE_TRUSTZONE=0 \ -DconfigRUN_FREERTOS_SECURE_ONLY=1 \ FREERTOS_SRCS += \ $(FREERTOS_DIR)/portable/GCC/$(FREERTOS_PORT)/port.c \ $(FREERTOS_DIR)/portable/GCC/$(FREERTOS_PORT)/portasm.c \ ATTACH_DEBUGGER = -s -S # From: # https://github.com/Introduction-To-System-On-Chip/QEMU_an505/blob/master/Makefile MACHINE_NAME := mps2-an505 # RUN_COMMAND = \ # qemu-system-arm \ # -machine $(MACHINE_NAME) \ # -cpu cortex-m33 \ # -m 16M \ # -monitor null -semihosting \ # --semihosting-config enable=on,target=native \ # -kernel $(ELF) \ # -serial stdio -nographic \ RUN_COMMAND = \ qemu-system-arm \ -machine $(MACHINE_NAME) \ -cpu cortex-m33 \ -m 16M \ -monitor none \ -nographic \ --semihosting-config enable=on,target=native \ -d cpu_reset \ -kernel $(ELF) DEBUG_COMMAND = $(RUN_COMMAND) $(ATTACH_DEBUGGER) # Permit overriding the gdb executable GDB ?= gdb GDB_COMMAND = \ $(GDB) --ex 'target extended-remote :1234' $(ELF) ================================================ FILE: examples/freertos/boards/qemu_mps2_an505/README.md ================================================ # Cortex-M33 MPS2 AN505 Cortex-M33 as documented in Arm Application Note AN505 ================================================ FILE: examples/freertos/boards/qemu_mps2_an505/linker.ld ================================================ MEMORY { /* Flash is actually 224M */ FLASH (rx) : ORIGIN = 0x10000000, LENGTH = 512K FLASH_END (rx) : ORIGIN = 0x10020000, LENGTH = 32K RAM (rwx) : ORIGIN = 0x38000000, LENGTH = 64K } ENTRY(Reset_Handler) _Min_Heap_Size = 0x1000 ; /* Required amount of heap. */ _Min_Stack_Size = 0x1000 ; /* Required amount of stack. */ M_VECTOR_RAM_SIZE = (16 + 48) * 4; _estack = ORIGIN(RAM) + LENGTH(RAM); SECTIONS { .isr_vector : { __vector_table = .; KEEP(*(.isr_vector)) . = ALIGN(4); } > FLASH .text : { . = ALIGN(4); *(.text*) KEEP (*(.init)) KEEP (*(.fini)) KEEP(*(.eh_frame)) *(.rodata*) . = ALIGN(4); _etext = .; } > FLASH .note.gnu.build-id : { __start_gnu_build_id_start = .; KEEP(*(.note.gnu.build-id)) } > FLASH .ARM.extab : { . = ALIGN(4); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(4); } >FLASH .ARM : { . = ALIGN(4); __exidx_start = .; *(.ARM.exidx* .gnu.linkonce.armexidx.*) __exidx_end = .; . = ALIGN(4); } >FLASH /* * SG veneers: * All SG veneers are placed in the special output section .gnu.sgstubs. * Its start address must be set, either with the command line option * '--section-start' or in a linker script, to indicate where to place these * veneers in memory. */ .gnu.sgstubs : { _ld_veneer_base = .; *(.gnu.sgstubs*) . = ALIGN(32); _ld_veneer_limit = .; } > FLASH .interrupts_ram : { . = ALIGN(4); __VECTOR_RAM__ = .; __interrupts_ram_start__ = .; . += M_VECTOR_RAM_SIZE; . = ALIGN(4); __interrupts_ram_end = .; } > RAM _sidata = LOADADDR(.data); .data : /* AT ( _sidata ) */ { . = ALIGN(4); _sdata = .; *(.data*) . = ALIGN(4); _edata = .; } > RAM AT > FLASH .uninitialized (NOLOAD): { . = ALIGN(32); __uninitialized_start = .; *(.uninitialized) KEEP(*(.keep.uninitialized)) . = ALIGN(32); __uninitialized_end = .; } > RAM .bss : { . = ALIGN(4); _sbss = .; __bss_start__ = _sbss; __memfault_capture_bss_start = .; *tasks*.o(.bss COMMON .bss*) *timers*.o(.bss COMMON .bss*) __memfault_capture_bss_end = .; *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; __bss_end__ = _ebss; } >RAM .heap : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); _heap_bottom = .; . = . + _Min_Heap_Size; _heap_top = .; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM /* Set stack top to end of RAM, and stack limit move down by * size of stack_dummy section */ __StackTop = ORIGIN(RAM) + LENGTH(RAM); __StackLimit = __StackTop - _Min_Stack_Size; PROVIDE(__stack = __StackTop); log_fmt 0xF0000000 (INFO): { /* Old ld (GCC ARM 4.9.3 for example) doesn't emit this symbol by default */ __start_log_fmt = ABSOLUTE(.); KEEP(*(*.log_fmt_hdr)) KEEP(*(log_fmt)) } /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack") } ================================================ FILE: examples/freertos/boards/qemu_mps2_an505/memfault_platform_impl.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/components.h" #define SCB_AIRCR_ADDR (0xE000ED0C) #define SCB_AIRCR_VECTKEY_WRITE (0x5FAUL << 16U) #define SCB_AIRCR_SYSRESETREQ (1U << 2) #define SCB_AIRCR_RESET_SYSTEM (SCB_AIRCR_VECTKEY_WRITE | SCB_AIRCR_SYSRESETREQ) #define CMSDK_RSTINFO_ADDR (0x4001F010) #define CMSDK_RSTINFO_MASK (0x07) #define CMSDK_RSTINFO_SYSRESETREQ_MASK (1 << 0) #define CMSDK_RSTINFO_WATCHDOG_MASK (1 << 1) #define CMSDK_RSTINFO_LOCKUP_MASK (1 << 2) void memfault_platform_reboot(void) { // // just insert memfault breakpoint // __asm volatile("bkpt #0"); volatile uint32_t *SCB_AIRCR = (uint32_t *)SCB_AIRCR_ADDR; *SCB_AIRCR = SCB_AIRCR_RESET_SYSTEM; while (1) { } // unreachable } size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { static const struct { uint32_t start_addr; size_t length; } s_mcu_mem_regions[] = { { .start_addr = 0x38000000, .length = 64 * 1024 }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_mcu_mem_regions); i++) { const uint32_t lower_addr = s_mcu_mem_regions[i].start_addr; const uint32_t upper_addr = lower_addr + s_mcu_mem_regions[i].length; if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); } } return 0; } void memfault_reboot_reason_get(sResetBootupInfo *reset_info) { // an505 doesn't seem to provide a reset reason register (an385 does, RSTINFO // register in the CMSDK system control registers) uint32_t reboot_reg = 0x1234; eMemfaultRebootReason reboot_reason = kMfltRebootReason_Unknown; *reset_info = (sResetBootupInfo){ .reset_reason_reg = reboot_reg, .reset_reason = reboot_reason, }; } ================================================ FILE: examples/freertos/boards/qemu_mps2_an505/startup.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include "memfault/components.h" typedef struct UART_t { volatile uint32_t DATA; volatile uint32_t STATE; volatile uint32_t CTRL; volatile uint32_t INTSTATUS; volatile uint32_t BAUDDIV; } UART_t; // See application note AN505, and the CMSDK UART doc: // https://developer.arm.com/documentation/ddi0479/b/APB-Components/UART/Programmers-model #define UART0_ADDR ((UART_t *)(0x40200000)) #define UART_DR(baseaddr) (*(unsigned int *)(baseaddr)) #define UART_STATE_TXFULL (1 << 0) #define UART_CTRL_TX_EN (1 << 0) #define UART_CTRL_RX_EN (1 << 1) #define UART_STATE_RXRDY (2 << 0) void uart_init(void) { UART0_ADDR->CTRL |= UART_CTRL_TX_EN; UART0_ADDR->CTRL |= UART_CTRL_RX_EN; } int _read(__attribute__((unused)) int file, __attribute__((unused)) char *buf, __attribute__((unused)) int len) { for (int i = 0; i < len; i++) { if (UART0_ADDR->STATE & UART_STATE_RXRDY) { char data = UART0_ADDR->DATA; // for some reason, BACKSPACE=0x7f, DEL=0x7e, so remap those data = (data == 0x7f) ? '\b' : data; data = (data == 0x7e) ? 0x7f : data; buf[i] = data; } else { return i; } } return len; } /** * @brief Write bytes to the UART channel to be displayed on the command line * with qemu * @param [in] file ignored * @param [in] buf buffer to send * @param [in] len length of the buffer * @returns the number of bytes written */ int _write(__attribute__((unused)) int file, __attribute__((unused)) char *buf, int len) { int todo; for (todo = 0; todo < len; todo++) { UART_DR(UART0_ADDR) = *buf++; } return len; } extern int main(void); // Following symbols are defined by the linker. // Start address for the initialization values of the .data section. extern uint32_t _sidata; // Start address for the .data section extern uint32_t _sdata; // End address for the .data section extern uint32_t _edata; // Start address for the .bss section extern uint32_t __bss_start__; // End address for the .bss section extern uint32_t __bss_end__; // End address for stack extern uint32_t __stack; // Prevent inlining to avoid persisting any stack allocations __attribute__((noinline)) static void prv_cinit(void) { // Initialize data and bss // Copy the data segment initializers from flash to SRAM for (uint32_t *dst = &_sdata, *src = &_sidata; dst < &_edata;) { *(dst++) = *(src++); } // Zero fill the bss segment. for (uint32_t *dst = &__bss_start__; (uintptr_t)dst < (uintptr_t)&__bss_end__;) { *(dst++) = 0; } } __attribute__((noreturn)) void Reset_Handler(void) { // use assembly to set the msr msplim,__stack __asm volatile("msr msplim, %0" ::"r"(__stack)); // enable the FPU #define SCB_CPACR_ (*(volatile uint32_t *)0xE000ED88) SCB_CPACR_ |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // set CP10 and CP11 Full Access // enable mem/bus/usage faults #define SCBSHCSR_ (*(uint32_t *)0xE000ED24) SCBSHCSR_ |= (1UL << 16U) | (1UL << 17U) | (1UL << 18U); prv_cinit(); uart_init(); // Call the application's entry point. (void)main(); // shouldn't return while (1) { }; } // These are defined in the FreeRTOS port extern void SVC_Handler(void); extern void PendSV_Handler(void); extern void SysTick_Handler(void); extern void NMI_Handler(void); extern void HardFault_Handler(void); extern void MemManage_Handler(void); extern void BusFault_Handler(void); 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 Reset_Handler, /* Reset Handler */ NMI_Handler, /* -14 NMI Handler */ HardFault_Handler, /* -13 Hard Fault Handler */ MemManage_Handler, /* -12 MPU Fault Handler */ BusFault_Handler, /* -11 Bus Fault Handler */ UsageFault_Handler, /* -10 Usage Fault Handler */ 0, /* -9 Secure Fault Handler */ 0, /* Reserved */ 0, /* Reserved */ 0, /* Reserved */ SVC_Handler, /* -5 SVCall Handler */ 0 /*DebugMon_Handler*/, /* -4 Debug Monitor Handler */ 0, /* Reserved */ PendSV_Handler, /* -2 PendSV Handler */ SysTick_Handler, /* -1 SysTick Handler */ 0, // 16 UART 0 0, // 17 UART 0 0, // 18 UART 1 0, // 19 UART 1 0, // 20 UART 2 0, // 21 UART 2 0, // 22 GPIO 0 0, // 23 GPIO 1 MEMFAULT_EXC_HANDLER_WATCHDOG, // 24 Timer 0 }; ================================================ FILE: examples/freertos/src/FreeRTOSConfig.h ================================================ /* * FreeRTOS V202212.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H #if MEMFAULT_TEST_USE_PORT_TEMPLATE != 1 #include "memfault/ports/freertos_trace.h" #endif /*----------------------------------------------------------- * Application specific definitions. * * These definitions should be adjusted for your particular hardware and * application requirements. * * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. * * See https://www.freertos.org/a00110.html *----------------------------------------------------------*/ #if MEMFAULT_TEST_USE_PORT_TEMPLATE != 1 #define configASSERT_DEFINED 1 extern void vAssertCalled(const char *file, int line); #define configASSERT(x) \ if ((x) == 0) vAssertCalled(__FILE__, __LINE__) #else // Disable this check, to avoid an unused variable warning due to FreeRTOS // configASSERT() being compiled out by default. // https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/0adc196d4bd52a2d91102b525b0aafc1e14a2386/portable/GCC/ARM_CM3/port.c#L282-L302 #define configCHECK_HANDLER_INSTALLATION 0 // There's an unused variable warning here too, disable these stats. #define configGENERATE_RUN_TIME_STATS 0 #endif #define configQUEUE_REGISTRY_SIZE 20 #define configUSE_PREEMPTION 1 #define configUSE_TIME_SLICING 0 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 #define configCPU_CLOCK_HZ ((unsigned long)20000000) #define configTICK_RATE_HZ ((TickType_t)1000) #define configMINIMAL_STACK_SIZE ((unsigned short)1000) #define configMINIMAL_SECURE_STACK_SIZE (1024) #define configTOTAL_HEAP_SIZE ((size_t)(2048)) #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configMAX_PRIORITIES (10) #define configTIMER_QUEUE_LENGTH 20 #define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) #define configUSE_COUNTING_SEMAPHORES 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configSUPPORT_STATIC_ALLOCATION 1 #define configNUM_TX_DESCRIPTORS 15 #define configSTREAM_BUFFER_TRIGGER_LEVEL_TEST_MARGIN 2 /* Set the following definitions to 1 to include the API function, or zero * to exclude the API function. */ #if MEMFAULT_TEST_USE_PORT_TEMPLATE != 1 #define configCHECK_FOR_STACK_OVERFLOW 2 #else #define configCHECK_FOR_STACK_OVERFLOW 0 #endif #define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_TIMERS 1 #define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) #define configRECORD_STACK_HIGH_ADDRESS 1 #if !defined(configGENERATE_RUN_TIME_STATS) #define configGENERATE_RUN_TIME_STATS 1 #endif #define configUSE_TRACE_FACILITY 1 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() // unused #define portGET_RUN_TIME_COUNTER_VALUE() ulGetRunTimeCounterValue() #define configUSE_STATS_FORMATTING_FUNCTIONS 1 #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskCleanUpResources 0 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 #define INCLUDE_uxTaskGetStackHighWaterMark2 1 #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 #define INCLUDE_xTaskGetIdleTaskHandle 1 #define INCLUDE_xTaskGetHandle 1 #define INCLUDE_eTaskGetState 1 #define INCLUDE_xSemaphoreGetMutexHolder 1 #define INCLUDE_xTimerPendFunctionCall 1 #define INCLUDE_xTaskAbortDelay 1 unsigned long ulGetRunTimeCounterValue( void); /* Prototype of function that returns run time counter. */ #define projCOVERAGE_TEST 0 #define configKERNEL_INTERRUPT_PRIORITY 255 /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! * See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ // On a device implementing 8 priority bits (like our emulated QEMU hardware), // all of the bits are considered for priority, and the LSB MUST NOT be set, or // the kernel crashes on startup. Set it to 4. #define configMAX_SYSCALL_INTERRUPT_PRIORITY 4 // For the qemu_mps2_an505 target, configENABLE_PAC must be defined since it is referenced here: // https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/V11.2.0/portable/GCC/ARM_CM33/non_secure/portmacrocommon.h#L131 #define configENABLE_PAC 0 /* Prototype for the function used to print out. */ extern void vLoggingPrintf(const char *pcFormatString, ...); #ifdef HEAP3 #define xPortGetMinimumEverFreeHeapSize (x) #define xPortGetFreeHeapSize (x) #endif #endif /* FREERTOS_CONFIG_H */ ================================================ FILE: examples/freertos/src/compact_log.cpp ================================================ //! @file //! //! Small example showcasing the use of compact logs from C++. #include "compact_log.h" #include #include "memfault/components.h" void compact_log_cpp_example(void) { #if MEMFAULT_COMPACT_LOG_ENABLE MEMFAULT_COMPACT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, "This is a compact log example from c++ " // clang-format off "%d" " %" PRIu64 " %f" " %f" " %s" " %s" " %s" " %s" " %s", 1234, (uint64_t)0x7, 1.0f, 2.0, "1212", (uint8_t*)"1212", (int8_t*)"1212", (const uint8_t*)"1212", (const int8_t*)"1212" // ^int ^uint64_t ^float ^double ^char* ^unsigned char* ^signed char* ^const unsigned char* ^const signed char* // clang-format on ); #endif } ================================================ FILE: examples/freertos/src/compact_log.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #ifdef __cplusplus extern "C" { #endif void compact_log_cpp_example(void); #ifdef __cplusplus } #endif ================================================ FILE: examples/freertos/src/console.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include #include "FreeRTOS.h" #include "memfault/components.h" #include "task.h" #include "timers.h" #define CONSOLE_INPUT_STACK_SIZE 500 #define SIMULATED_WATCHDOG_INTERRUPT_NUM 8 /* Structure that will hold the TCB of the task being created. */ static StaticTask_t console_input_task; /* Buffer that the task being created will use as its stack. Note this is an array of StackType_t variables. The size of StackType_t is dependent on the RTOS port. */ static StackType_t console_input_task_stack[CONSOLE_INPUT_STACK_SIZE]; static int prv_send_char(char c) { int sent = putchar(c); fflush(stdout); return sent; } /* Function that implements the task being created. */ static void prv_console_input_task(MEMFAULT_UNUSED void *pvParameters) { while (true) { char c; if (read(0, &c, sizeof(c))) { memfault_demo_shell_receive_char((char)c); } else { // sleep a bit if we didn't just receive a character, to prevent 100% CPU // utilization in this thread. vTaskDelay((10) / portTICK_PERIOD_MS); } } } // shell extensions #if defined(MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS) static int prv_freertos_vassert_cmd(int argc, char *argv[]) { (void)argc, (void)argv; printf("Triggering vAssertCalled!\n"); configASSERT(0); return 0; } // print all task information static int prv_freertos_tasks_cmd(int argc, char *argv[]) { (void)argc, (void)argv; size_t task_count = uxTaskGetNumberOfTasks(); // 100 bytes per task should be enough™️ char *write_buf = pvPortMalloc(task_count * 100); vTaskList(write_buf); puts(write_buf); vPortFree(write_buf); return 0; } static int prv_fake_fw_update_error_assert_cmd(int argc, char *argv[]) { (void)argc, (void)argv; printf("Triggering fake firmware update error assert!\n"); MEMFAULT_ASSERT_WITH_REASON(0, kMfltRebootReason_FirmwareUpdateError); return 0; } MEMFAULT_WEAK int memfault_metrics_session_start(eMfltMetricsSessionIndex session_key) { (void)session_key; MEMFAULT_LOG_RAW("Disabled. metrics component integration required"); return 0; } MEMFAULT_WEAK int memfault_metrics_session_end(eMfltMetricsSessionIndex session_key) { (void)session_key; MEMFAULT_LOG_RAW("Disabled. metrics component integration required"); return 0; } MEMFAULT_WEAK int memfault_metrics_heartbeat_set_string(MemfaultMetricId key, const char *value) { (void)key; (void)value; MEMFAULT_LOG_RAW("Disabled. metrics component integration required"); return 0; } static int prv_session(int argc, char *argv[]) { const char *cmd_name_string = "UNKNOWN"; if (argc > 1) { cmd_name_string = argv[1]; } printf("Executing a test metrics session named 'cli'\n"); MEMFAULT_METRICS_SESSION_START(cli); // API v1 // MEMFAULT_METRIC_SET_STRING(cli_cmd_name, cmd_name_string); // API v2 MEMFAULT_METRIC_SESSION_SET_STRING(cmd_name, cli, cmd_name_string); MEMFAULT_METRICS_SESSION_END(cli); return 0; } static int prv_spin_cpu(int argc, char *argv[]) { uint32_t cycle_count = UINT32_MAX; if (argc > 1) { char *end = NULL; cycle_count = strtoul(argv[1], &end, 10); if (end == argv[1]) { MEMFAULT_LOG_RAW("Invalid cycle count"); cycle_count = UINT32_MAX; } } MEMFAULT_LOG_RAW("Spinning CPU for %lu cycles", cycle_count); for (uint32_t i = 0; i < cycle_count; i++) { } MEMFAULT_LOG_RAW("Spinning completed, check cpu_usage_pct"); return 0; } static int prv_session_crash(int argc, char *argv[]) { (void)argc, (void)argv; MEMFAULT_METRICS_SESSION_START(cli); printf("Triggering session start + crash!\n"); MEMFAULT_ASSERT(0); return 0; } static int prv_leak_memory(int argc, char *argv[]) { (void)argc, (void)argv; // Allocate memory and leak it if (argc > 1) { unsigned long size = strtoul(argv[1], NULL, 0); void *leaked_memory = malloc(size); if (leaked_memory == NULL) { MEMFAULT_LOG_RAW("Failed to allocate memory"); return -1; } } return 0; } static int prv_assert_with_reason(int argc, char *argv[]) { eMemfaultRebootReason reason = kMfltRebootReason_Assert; if (argc >= 2) { // integer argument that should be used for trace reason reason = (eMemfaultRebootReason)strtoul(argv[1], NULL, 0); } MEMFAULT_LOG_ERROR("Triggering assert with reason code %d", reason); MEMFAULT_ASSERT_WITH_REASON(0, reason); return 0; } static int prv_get_log_count(int argc, char *argv[]) { (void)argc, (void)argv; sMfltLogUnsentCount log_count = memfault_log_get_unsent_count(); printf("Number of logs: %u\nSize: %u bytes\n", (unsigned int)log_count.num_logs, (unsigned int)log_count.bytes); return 0; } static int prv_stack_overflow(int argc, char *argv[]) { (void)argc, (void)argv; TaskHandle_t task_handle = xTaskGetCurrentTaskHandle(); MEMFAULT_LOG_INFO("Triggering stack overflow test on task %p / %s", (void *)task_handle, pcTaskGetName(task_handle)); // get the current address- it should be ~ the address of the above variable, // offset it by 4 words to hopefully prevent clobbering our current variables. // we'll write from this address, so the stack watermark will be updated. volatile uint32_t *stack_start = ((volatile uint32_t *)&task_handle) - 4; volatile uint32_t *stack_current = stack_start; // starting at the stack start address, write decrementing values. this should crash! for (; stack_current > (stack_start - CONSOLE_INPUT_STACK_SIZE); stack_current--) { *stack_current = 0xDEADBEEF; // sleep a bit vTaskDelay((10) / portTICK_PERIOD_MS); } MEMFAULT_LOG_ERROR("Stack overflow test failed, stack overflow was not triggered!"); return 0; } // issue a compact log that exceeds the max MEMFAULT_LOG_MAX_LINE_SAVE_LEN static int prv_long_compact_log(int argc, char *argv[]) { (void)argc, (void)argv; #if MEMFAULT_COMPACT_LOG_ENABLE MEMFAULT_LOG_INFO("Issuing a long compact log (> %d bytes)", MEMFAULT_LOG_MAX_LINE_SAVE_LEN); char *long_string = pvPortMalloc(MEMFAULT_LOG_MAX_LINE_SAVE_LEN + 1); if (long_string == NULL) { MEMFAULT_LOG_ERROR("Failed to allocate memory for long compact log"); return -1; } for (size_t i = 0; i < MEMFAULT_LOG_MAX_LINE_SAVE_LEN; i++) { long_string[i] = 'A' + (i % 26); } long_string[MEMFAULT_LOG_MAX_LINE_SAVE_LEN] = '\0'; MEMFAULT_COMPACT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, "%s", long_string); vPortFree(long_string); #endif // MEMFAULT_COMPACT_LOG_ENABLE return 0; } // Note: this callback is invoked from exception context void memfault_platform_fault_handler(MEMFAULT_UNUSED const sMfltRegState *regs, eMemfaultRebootReason reason) { if (reason == kMfltRebootReason_SoftwareWatchdog) { printf("Entered Watchdog interrupt...\n"); // In a real platform implementation, clear any watchdog or timer interrupt // flags here to prevent the interrupt from immediately retriggering. For // this example, we also clear the NVIC pending bit for the simulated // external interrupt 8 used below in prv_watchdog_cmd(). volatile uint32_t *nvic_icpr0 = (volatile uint32_t *)0xE000E280; *nvic_icpr0 = (uint32_t)(1u << SIMULATED_WATCHDOG_INTERRUPT_NUM); } } // Timer callback to reboot the device static void prv_reboot_timer_callback(TimerHandle_t xTimer) { (void)xTimer; printf("⏰ Reboot timer expired, rebooting now...\n"); memfault_platform_reboot(); } static int prv_watchdog_cmd(int argc, char *argv[]) { (void)argc, (void)argv; printf("🐶 Triggering a simulated watchdog!\n"); // enable external interrupt 8 #define NVIC_ISER0 ((volatile uint32_t *)0xE000E100) #define NVIC_ISPR0 ((volatile uint32_t *)0xE000E200) *(uint32_t *)NVIC_ISER0 |= 1 << SIMULATED_WATCHDOG_INTERRUPT_NUM; // set the bit in the NVIC register to trigger external // interrupt 8 *(uint32_t *)NVIC_ISPR0 = 1 << SIMULATED_WATCHDOG_INTERRUPT_NUM; const unsigned int reboot_delay_ms = 10000; printf("✅ Returned from watchdog! Scheduling reboot in %u ms\n", reboot_delay_ms); // Set a one-shot timer to reboot the device in a bit, to allow time to see // the message above before the reboot happens. In a real platform // implementation, there would be some mechanism to reboot the system after // the watchdog cleanup operations are completed. TimerHandle_t reboot_timer = xTimerCreate("RebootTimer", /* Timer name */ pdMS_TO_TICKS(reboot_delay_ms), /* Timer period in ticks */ pdFALSE, /* Auto-reload (pdFALSE = one-shot) */ NULL, /* Timer ID (unused) */ prv_reboot_timer_callback /* Callback function */ ); if (reboot_timer != NULL) { xTimerStart(reboot_timer, 0); } return 0; } static const sMemfaultShellCommand s_freertos_example_shell_extension_list[] = { { .command = "freertos_vassert", .handler = prv_freertos_vassert_cmd, .help = "FreeRTOS vAssertCalled assert", }, { .command = "freertos_tasks", .handler = prv_freertos_tasks_cmd, .help = "Print all FreeRTOS tasks", }, { .command = "fwup_assert", .handler = prv_fake_fw_update_error_assert_cmd, .help = "Trigger fake firmware update error assert", }, { .command = "session", .handler = prv_session, .help = "Execute a test metrics session named 'cli'", }, { .command = "spin_cpu", .handler = prv_spin_cpu, .help = "Spin CPU to increase cpu_usage_pct metric", }, { .command = "session_crash", .handler = prv_session_crash, .help = "Trigger a crash during a session", }, { .command = "leak", .handler = prv_leak_memory, .help = "Allocate memory and leak it. Usage: leak ", }, { .command = "assert_with_reason", .handler = prv_assert_with_reason, .help = "Execute an assert with a custom reason code", }, { .command = "log_count", .handler = prv_get_log_count, .help = "Get the number and size of unsent logs", }, { .command = "stack_overflow", .handler = prv_stack_overflow, .help = "Trigger a stack overflow", }, { .command = "long_compact_log", .handler = prv_long_compact_log, .help = "Issue a very long compact log (> MEMFAULT_LOG_MAX_LINE_SAVE_LEN)", }, { .command = "watchdog", .handler = prv_watchdog_cmd, .help = "Trigger a simulated watchdog interrupt", }, }; #endif void console_task_init(void) { xTaskCreateStatic( prv_console_input_task, /* Function that implements the task. */ "Console Input", /* Text name for the task. */ MEMFAULT_ARRAY_SIZE(console_input_task_stack), /* Number of indexes in the xStack array. */ NULL, /* Parameter passed into the task. */ tskIDLE_PRIORITY, /* Priority at which the task is created. */ console_input_task_stack, /* Array to use as the task's stack. */ &console_input_task); //! We will use the Memfault CLI shell as a very basic debug interface #if defined(MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS) memfault_shell_command_set_extensions( s_freertos_example_shell_extension_list, MEMFAULT_ARRAY_SIZE(s_freertos_example_shell_extension_list)); #endif const sMemfaultShellImpl impl = { .send_char = prv_send_char, }; memfault_demo_shell_boot(&impl); } ================================================ FILE: examples/freertos/src/console.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once void console_task_init(void); ================================================ FILE: examples/freertos/src/heap_task.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @brief Example heap task #include "heap_task.h" #include #include "FreeRTOS.h" #include "memfault/components.h" #include "task.h" #define EXAMPLE_TASK_STACKS 300 // The FreeRTOS heap uint8_t ucHeap[configTOTAL_HEAP_SIZE]; static StackType_t heap_task_stack[EXAMPLE_TASK_STACKS]; static StaticTask_t heap_task_tcb; static void prv_run_heap_task(MEMFAULT_UNUSED void *pvParameters) { void *heap_pointers[16] = { 0 }; while (true) { for (uint8_t i = 0; i < MEMFAULT_ARRAY_SIZE(heap_pointers); i++) { heap_pointers[i] = pvPortMalloc((i + 1) * 64); vTaskDelay((1000 * 3) / portTICK_PERIOD_MS); MEMFAULT_LOG_DEBUG("Allocated %d @ %p\n", (i + 1) * 64, heap_pointers[i]); } for (uint8_t i = 0; i < MEMFAULT_ARRAY_SIZE(heap_pointers); i++) { MEMFAULT_LOG_DEBUG("Deallocating %p\n", heap_pointers[i]); vPortFree(heap_pointers[i]); vTaskDelay((1000 * 3) / portTICK_PERIOD_MS); } } } void heap_task_init(void) { xTaskCreateStatic( prv_run_heap_task, /* Function that implements the task. */ "Heap Task", /* Text name for the task. */ MEMFAULT_ARRAY_SIZE(heap_task_stack), /* Number of indexes in the xStack array. */ NULL, /* Parameter passed into the task. */ tskIDLE_PRIORITY, /* Priority at which the task is created. */ heap_task_stack, /* Array to use as the task's stack. */ &heap_task_tcb); } ================================================ FILE: examples/freertos/src/heap_task.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @brief Example heap task void heap_task_init(void); ================================================ FILE: examples/freertos/src/main.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #if defined(DEBUG_COMPACT_LOGS) #include #endif #include #include "FreeRTOS.h" #include "compact_log.h" #include "console.h" #include "heap_task.h" #include "memfault/components.h" #include "metrics.h" #include "mpu.h" #include "task.h" // Next two functions from: // https://github.com/FreeRTOS/FreeRTOS/blob/6f7f9fd9ed56fbb34d5314af21f17cfa571133a7/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/main.c#L156-L204 void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) { /* If the buffers to be provided to the Idle task are declared inside this * function then they must be declared static - otherwise they will be allocated on * the stack and so not exists after this function exits. */ static StaticTask_t xIdleTaskTCB; static StackType_t uxIdleTaskStack[configMINIMAL_STACK_SIZE]; /* Pass out a pointer to the StaticTask_t structure in which the Idle task's * state will be stored. */ *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; /* Pass out the array that will be used as the Idle task's stack. */ *ppxIdleTaskStackBuffer = uxIdleTaskStack; /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer. * Note that, as the array is necessarily of type StackType_t, * configMINIMAL_STACK_SIZE is specified in words, not bytes. */ *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; } StackType_t uxTimerTaskStack[configTIMER_TASK_STACK_DEPTH]; void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) { /* If the buffers to be provided to the Timer task are declared inside this * function then they must be declared static - otherwise they will be allocated on * the stack and so not exists after this function exits. */ static StaticTask_t xTimerTaskTCB; /* Pass out a pointer to the StaticTask_t structure in which the Timer * task's state will be stored. */ *ppxTimerTaskTCBBuffer = &xTimerTaskTCB; /* Pass out the array that will be used as the Timer task's stack. */ *ppxTimerTaskStackBuffer = uxTimerTaskStack; /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. * Note that, as the array is necessarily of type StackType_t, * configMINIMAL_STACK_SIZE is specified in words, not bytes. */ *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; } unsigned long ulGetRunTimeCounterValue(void) { struct tms xTimes; times(&xTimes); return (unsigned long)xTimes.tms_utime; } void compact_log_c_example(void) { #if MEMFAULT_COMPACT_LOG_ENABLE MEMFAULT_COMPACT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, "This is a compact log example from c " // clang-format off "%d" " %" PRIu64 " %f" " %f" " %s" " %s" " %s" " %s" " %s", 1234, (uint64_t)0x7, 1.0f, 2.0, "1212", (uint8_t*)"1212", (int8_t*)"1212", (const uint8_t*)"1212", (const int8_t*)"1212" // ^int ^uint64_t ^float ^double ^char* ^unsigned char* ^signed char* ^const unsigned char* ^const signed char* // clang-format on ); #endif // MEMFAULT_COMPACT_LOG_ENABLE } // Add a custom _sbrk() implementation. // The default implementation in newlib has a check to make sure the heap does not grow past the // current stack pointer (indicating it's overlapping the active stack). This check doesn't work // correctly in our FreeRTOS setup, where the thread stacks are allocated in .data, which is always // at a lower address than the heap. This implementation removes that check. // https://github.com/bminor/newlib/blob/1a0908203606527b6ac0ed438669b5bcd247a5f9/newlib/libc/sys/arm/syscalls.c#L524 // // This is our memory layout: // [.data/.bss] // [heap] // | (grows up) // ↓ // ↑ // | (grows down) // [stack] <- end of RAM void *_sbrk(ptrdiff_t incr) { static char *heap_end; char *prev_heap_end; if (heap_end == NULL) { extern uint32_t _heap_bottom; heap_end = (char *)&_heap_bottom; } prev_heap_end = heap_end; extern uint32_t _heap_top; if (heap_end + incr > (char *)&_heap_top) { return (void *)-1; } heap_end += incr; return (void *)prev_heap_end; } int main(void) { memfault_platform_boot(); mpu_init(); heap_task_init(); #if defined(MEMFAULT_COMPONENT_metrics_) metrics_task_init(); #endif console_task_init(); compact_log_c_example(); compact_log_cpp_example(); // Enable this to export compact log data to stdio. This is intended only for // testing deserialization of the compact log example above and is not // typically useful. #if defined(DEBUG_COMPACT_LOGS) printf(">> exporting logs...\n"); memfault_log_export_logs(); #endif // Initialize the FreeRTOS kernel vTaskStartScheduler(); // Should never reach here while (1) { }; return 0; } ================================================ FILE: examples/freertos/src/memfault/memfault_metrics_heartbeat_config.def ================================================ // Add application custom metrics MEMFAULT_METRICS_KEY_DEFINE(FreeRTOS_HeapFreeBytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(FreeRTOS_HeapMinFreeBytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(FreeRTOS_Heap_pct_max, kMemfaultMetricType_Unsigned, 100) // libc heap, more commonly used in our application MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_pct_max, kMemfaultMetricType_Unsigned, 100) // Thread stack usage metrics // clang-format off MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_console_input_pct_max, kMemfaultMetricType_Unsigned, 100) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_metrics_pct_max, kMemfaultMetricType_Unsigned, 100) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_heap_task_pct_max, kMemfaultMetricType_Unsigned, 100) // clang-format on // Example "cli" session MEMFAULT_METRICS_SESSION_KEY_DEFINE(cli) MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(cmd_name, 16, cli) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(time, kMemfaultMetricType_Unsigned, cli) // Pathological key name, to test that heartbeat key extraction handles it correctly MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(__cli__cli__, kMemfaultMetricType_Unsigned, cli) // Example "daily_heartbeat" session MEMFAULT_METRICS_SESSION_KEY_DEFINE(daily_heartbeat) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(uptime_s, kMemfaultMetricType_Unsigned, daily_heartbeat) #include "ports/freertos/config/memfault_metrics_heartbeat_freertos_config.def" ================================================ FILE: examples/freertos/src/memfault/memfault_platform_config.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Application platform config. Add Memfault configs here. #pragma once #define MEMFAULT_USE_GNU_BUILD_ID 1 #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE 8192 #define MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS 1 #define MEMFAULT_COREDUMP_COLLECT_HEAP_STATS 1 #define MEMFAULT_FREERTOS_PORT_HEAP_STATS_ENABLE 1 #define MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE 0 #define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS 60 #define MEMFAULT_COMPACT_LOG_ENABLE 1 #define MEMFAULT_COLLECT_MPU_STATE 1 #define MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE 1 // Enable adding custom demo shell commands #define MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS 1 #if !defined(MEMFAULT_PLATFORM_HAS_LOG_CONFIG) #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 #endif // 1=Info #if !defined(MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL) #define MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL 1 #endif #define MEMFAULT_FAULT_HANDLER_WATCHDOG_RETURN 1 ================================================ FILE: examples/freertos/src/memfault/memfault_platform_log_config.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Shim Memfault logs over to platform logging handler, inserting any //! additional metadata. #pragma once #include #include #include "memfault/config.h" #include "memfault/core/log.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/debug_log.h" #ifdef __cplusplus extern "C" { #endif #define _MEMFAULT_LOG_IMPL(_level, str_level, fmt, ...) \ do { \ MEMFAULT_SDK_LOG_SAVE(_level, fmt, ##__VA_ARGS__); \ /* Insert a timestamp and system uptime */ \ time_t time_now = time(NULL); \ struct tm *tm_time = gmtime(&time_now); \ char time_str[32]; \ strftime(time_str, sizeof(time_str), "%Y-%m-%dT%H:%M:%SZ", tm_time); \ uint32_t uptime = memfault_platform_get_time_since_boot_ms(); \ memfault_platform_log(_level, "%s|%lu " #str_level " " fmt, time_str, uptime, ##__VA_ARGS__); \ } while (0) #if MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL <= 0 #define MEMFAULT_LOG_DEBUG(fmt, ...) \ _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Debug, D, fmt, ##__VA_ARGS__) #else #define MEMFAULT_LOG_DEBUG(fmt, ...) #endif #if MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL <= 1 #define MEMFAULT_LOG_INFO(fmt, ...) \ _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Info, I, fmt, ##__VA_ARGS__) #else #define MEMFAULT_LOG_INFO(fmt, ...) #endif #if MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL <= 2 #define MEMFAULT_LOG_WARN(fmt, ...) \ _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Warning, W, fmt, ##__VA_ARGS__) #else #define MEMFAULT_LOG_WARN(fmt, ...) #endif #if MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL <= 3 #define MEMFAULT_LOG_ERROR(fmt, ...) \ _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Error, E, fmt, ##__VA_ARGS__) #else #define MEMFAULT_LOG_ERROR(fmt, ...) #endif //! Only needs to be implemented when using demo component #define MEMFAULT_LOG_RAW(...) memfault_platform_log_raw(__VA_ARGS__) #ifdef __cplusplus } #endif ================================================ FILE: examples/freertos/src/memfault/memfault_platform_port.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include "memfault/components.h" #include "memfault/panics/arch/arm/cortex_m.h" #include "memfault/ports/freertos.h" #include "memfault/ports/freertos_coredump.h" #include "memfault/ports/reboot_reason.h" #if !defined(MEMFAULT_EXAMPLE_USE_REAL_TIME) #define MEMFAULT_EXAMPLE_USE_REAL_TIME 1 #endif // Buffer used to store formatted string for output #define MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES \ (sizeof("2024-11-27T14:19:29Z|123456780 I ") + MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN) #define MEMFAULT_COREDUMP_MAX_TASK_REGIONS ((MEMFAULT_PLATFORM_MAX_TRACKED_TASKS) * 2) #define MEMFAULT_APP_DEBUG 0 #if MEMFAULT_APP_DEBUG #define MEMFAULT_APP_PRINTF(...) printf(__VA_ARGS__) #else #define MEMFAULT_APP_PRINTF(...) #endif // Reboot tracking storage, must be placed in no-init RAM to keep state after reboot MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_info") static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; // Memfault logging storage static uint8_t s_log_buf_storage[512]; // Use Memfault logging level to filter messages static eMemfaultPlatformLogLevel s_min_log_level = MEMFAULT_RAM_LOGGER_DEFAULT_MIN_LOG_LEVEL; void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { *info = (sMemfaultDeviceInfo){ .device_serial = "freertos-example", .hardware_version = BOARD, .software_type = "qemu-app", .software_version = "1.0.0", }; } void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; va_list args; va_start(args, fmt); if (level >= s_min_log_level) { vsnprintf(log_buf, sizeof(log_buf), fmt, args); // If needed, additional data could be emitted in the log line (timestamp, // etc). Here we'll insert ANSI color codes depending on log level. switch (level) { case kMemfaultPlatformLogLevel_Debug: printf("\033[0;32m"); break; case kMemfaultPlatformLogLevel_Info: printf("\033[0;37m"); break; case kMemfaultPlatformLogLevel_Warning: printf("\033[0;33m"); break; case kMemfaultPlatformLogLevel_Error: printf("\033[0;31m"); break; default: break; } printf("%s", log_buf); printf("\033[0m\n"); } va_end(args); } void memfault_platform_log_raw(const char *fmt, ...) { char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; va_list args; va_start(args, fmt); vsnprintf(log_buf, sizeof(log_buf), fmt, args); printf("%s\n", log_buf); va_end(args); } //! Route the 'export' command to output via printf, so we don't drop messages //! from logging in a big burst. void memfault_data_export_base64_encoded_chunk(const char *base64_chunk) { printf("%s\n", base64_chunk); } bool memfault_platform_time_get_current(sMemfaultCurrentTime *time_output) { #if MEMFAULT_EXAMPLE_USE_REAL_TIME // Get time from time.h // Debug: print time fields time_t time_now = time(NULL); struct tm *tm_time = gmtime(&time_now); MEMFAULT_APP_PRINTF("Time: %u-%u-%u %u:%u:%u", tm_time->tm_year + 1900, tm_time->tm_mon + 1, tm_time->tm_mday, tm_time->tm_hour, tm_time->tm_min, tm_time->tm_sec); // If pre-2023, something is wrong if ((tm_time->tm_year < 123) || (tm_time->tm_year > 200)) { MEMFAULT_APP_PRINTF("Time doesn't make sense: year %u", tm_time->tm_year + 1900); return false; } // load the timestamp and return true for a valid timestamp *time_output = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = (uint64_t)time_now, }, }; return true; #else (void)time_output; return false; #endif } void memfault_platform_reboot_tracking_boot(void) { sResetBootupInfo reset_info = { 0 }; memfault_reboot_reason_get(&reset_info); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP sMfltRebootReason stored_reboot_reason; int rv = memfault_reboot_tracking_get_reboot_reason(&stored_reboot_reason); if (!rv) { MEMFAULT_LOG_INFO("Reset Cause Stored: 0x%04x", stored_reboot_reason.prior_stored_reason); } #endif // MEMFAULT_ENABLE_REBOOT_DIAG_DUMP } int memfault_platform_boot(void) { puts(MEMFAULT_BANNER_COLORIZED); memfault_freertos_port_boot(); memfault_platform_reboot_tracking_boot(); static uint8_t s_event_storage[1024]; const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(evt_storage); memfault_reboot_tracking_collect_reset_info(evt_storage); #if defined(MEMFAULT_COMPONENT_metrics_) sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); #if MEMFAULT_METRICS_BATTERY_ENABLE memfault_metrics_battery_boot(); #endif #endif memfault_log_boot(s_log_buf_storage, MEMFAULT_ARRAY_SIZE(s_log_buf_storage)); memfault_build_info_dump(); memfault_device_info_dump(); MEMFAULT_LOG_INFO("Memfault Initialized!"); return 0; } static uint32_t prv_read_psp_reg(void) { uint32_t reg_val; __asm volatile("mrs %0, psp" : "=r"(reg_val)); return reg_val; } static sMfltCoredumpRegion s_coredump_regions[MEMFAULT_COREDUMP_MAX_TASK_REGIONS + 2 /* active stack(s) */ + 1 /* _kernel variable */ + 1 /* __memfault_capture_start */ + 2 /* s_task_tcbs + s_task_watermarks */ ]; extern uint32_t __memfault_capture_bss_end; extern uint32_t __memfault_capture_bss_start; const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { int region_idx = 0; const size_t active_stack_size_to_collect = 512; // first, capture the active stack (and ISR if applicable) const bool msp_was_active = (crash_info->exception_reg_state->exc_return & (1 << 2)) == 0; size_t stack_size_to_collect = memfault_platform_sanitize_address_range( crash_info->stack_address, MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT); s_coredump_regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(crash_info->stack_address, stack_size_to_collect); region_idx++; if (msp_was_active) { // System crashed in an ISR but the running task state is on PSP so grab that too void *psp = (void *)prv_read_psp_reg(); // Collect a little bit more stack for the PSP since there is an // exception frame that will have been stacked on it as well const uint32_t extra_stack_bytes = 128; stack_size_to_collect = memfault_platform_sanitize_address_range( psp, active_stack_size_to_collect + extra_stack_bytes); s_coredump_regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(psp, stack_size_to_collect); region_idx++; } // Scoop up memory regions necessary to perform unwinds of the FreeRTOS tasks const size_t memfault_region_size = (uint32_t)&__memfault_capture_bss_end - (uint32_t)&__memfault_capture_bss_start; s_coredump_regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&__memfault_capture_bss_start, memfault_region_size); region_idx++; #if MEMFAULT_TEST_USE_PORT_TEMPLATE != 1 region_idx += memfault_freertos_get_task_regions( &s_coredump_regions[region_idx], MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx); #endif *num_regions = region_idx; return &s_coredump_regions[0]; } ================================================ FILE: examples/freertos/src/memfault/memfault_trace_reason_user_config.def ================================================ // Application trace events here ================================================ FILE: examples/freertos/src/metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @brief Metrics for the application #include "metrics.h" #include #include "FreeRTOS.h" #include "memfault/components.h" #include "memfault/ports/freertos/metrics.h" #include "memfault/ports/freertos/thread_metrics.h" #include "task.h" #define EXAMPLE_TASK_STACKS 300 #define DEBUG_ 0 #if DEBUG_ #include #define DEBUG_PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define DEBUG_PRINTF(fmt, ...) #endif #if !defined(MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE) #define MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE 1 #endif static StackType_t metrics_task_stack[EXAMPLE_TASK_STACKS]; static StaticTask_t metrics_task_tcb; #if MEMFAULT_EVENT_STORAGE_RESTORE_STATE // Just a stub, not implemented in this example bool memfault_event_storage_restore_state(sMfltEventStorageSaveState *state) { (void)state; return false; } #endif #if MEMFAULT_METRICS_RESTORE_STATE // Just a stub, not implemented in this example bool memfault_metrics_restore_state(void *state) { (void)state; return false; } #endif #if MEMFAULT_LOG_RESTORE_STATE // Just a stub, not implemented in this example bool memfault_log_restore_state(sMfltLogSaveState *state) { (void)state; return false; } #endif // This is incompatible with cstd=gnu11 and gcc < 5 #if (__STDC_VERSION__ < 201100L) || (__GNUC__ >= 5) MEMFAULT_METRICS_DEFINE_THREAD_METRICS( { .thread_name = "IDLE", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle_pct_max), }, { .thread_name = "Tmr Svc", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_tmr_svc_pct_max), }, { .thread_name = "Console Input", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_console_input_pct_max), }, { .thread_name = "📊 Metrics", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_metrics_pct_max), }, { .thread_name = "Heap Task", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_heap_task_pct_max), }); #endif static void prv_run_metrics_task(MEMFAULT_UNUSED void *pvParameters) { while (true) { HeapStats_t stats = { 0 }; vPortGetHeapStats(&stats); MEMFAULT_METRIC_SET_UNSIGNED(FreeRTOS_HeapFreeBytes, stats.xAvailableHeapSpaceInBytes); MEMFAULT_METRIC_SET_UNSIGNED(FreeRTOS_HeapMinFreeBytes, stats.xMinimumEverFreeBytesRemaining); // per-mille (1/1000) percentage, scaled on ingestion const uint32_t heap_pct_max = (10000 * (configTOTAL_HEAP_SIZE - stats.xMinimumEverFreeBytesRemaining)) / configTOTAL_HEAP_SIZE; MEMFAULT_METRIC_SET_UNSIGNED(FreeRTOS_Heap_pct_max, heap_pct_max); vTaskDelay((1000 * 10) / portTICK_PERIOD_MS); } } void metrics_task_init(void) { xTaskCreateStatic( prv_run_metrics_task, /* Function that implements the task. */ "📊 Metrics", /* Text name for the task. */ MEMFAULT_ARRAY_SIZE(metrics_task_stack), /* Number of indexes in the xStack array. */ NULL, /* Parameter passed into the task. */ tskIDLE_PRIORITY, /* Priority at which the task is created. */ metrics_task_stack, /* Array to use as the task's stack. */ &metrics_task_tcb); } static void prv_collect_libc_heap_usage_metrics(void) { // use mallinfo to compute libc heap utilization percentage struct mallinfo info = mallinfo(); extern uint32_t _heap_bottom; extern uint32_t _heap_top; const uint32_t heap_size = (uint32_t)(&_heap_top - &_heap_bottom); const uint32_t heap_used = info.uordblks; const uint32_t heap_pct = (10000 * heap_used) / heap_size; DEBUG_PRINTF("Heap Usage: %lu/%lu (%lu.%02lu%%)\n", heap_used, heap_size, heap_pct / 100, heap_pct % 100); MEMFAULT_METRIC_SET_UNSIGNED(memory_pct_max, heap_pct); } #if MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE static void prv_daily_heartbeat(void) { // Generate a daily_heartbeat session report once a day, based on // MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS. This demonstrates a simple way to // track metrics over a 24 hour interval. MEMFAULT_STATIC_ASSERT((24 * 60 * 60) % MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS == 0, "Heartbeat interval must be an even divisor of a day"); // For testing, set MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS to a low value (1 second), and // uncomment the below line // #define DAILY_HEARTBEAT_INTERVAL_COUNT (24) #define DAILY_HEARTBEAT_INTERVAL_COUNT ((24 * 60 * 60) / MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) static uint64_t s_last_daily_heartbeat_interval = 0; if (++s_last_daily_heartbeat_interval % DAILY_HEARTBEAT_INTERVAL_COUNT == 0) { // The first time this is called on boot will be a no-op, since the session // has not been started yet uint32_t uptime_s = memfault_platform_get_time_since_boot_ms() / 1000; if (uptime_s > MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) { MEMFAULT_LOG_INFO("📆 Triggering daily heartbeat"); } // Record a sample metric into the daily session MEMFAULT_METRIC_SESSION_SET_UNSIGNED(uptime_s, daily_heartbeat, uptime_s); // End the session MEMFAULT_METRICS_SESSION_END(daily_heartbeat); // Start a new session for the next daily interval MEMFAULT_METRICS_SESSION_START(daily_heartbeat); } } #endif // MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE #if MEMFAULT_METRICS_BATTERY_ENABLE int memfault_platform_get_stateofcharge(sMfltPlatformBatterySoc *soc) { static uint32_t s_soc = 100 * MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE; // 100.0% *soc = (sMfltPlatformBatterySoc){ .soc = s_soc, .discharging = true, }; if (s_soc > 0) { s_soc -= 1 * MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE; // discharge 1% every heartbeat } return 0; } #endif void memfault_metrics_heartbeat_collect_data(void) { MEMFAULT_LOG_INFO("💓 Heartbeat callback triggered"); MEMFAULT_STATIC_ASSERT( MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS <= (60 * 60), "Heartbeat must be an hour or less for runtime metrics to mitigate counter overflow"); #if MEMFAULT_TEST_USE_PORT_TEMPLATE != 1 memfault_freertos_port_task_runtime_metrics(); #endif prv_collect_libc_heap_usage_metrics(); #if MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE prv_daily_heartbeat(); #endif #if MEMFAULT_METRICS_SYNC_SUCCESS memfault_metrics_connectivity_record_sync_success(); memfault_metrics_connectivity_record_sync_failure(); #endif #if MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS memfault_metrics_connectivity_record_memfault_sync_success(); memfault_metrics_connectivity_record_memfault_sync_failure(); #endif #if MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME static eMemfaultMetricsConnectivityState s_connectivity_state = kMemfaultMetricsConnectivityState_Stopped; // Increment the state on each call, for demonstration purposes memfault_metrics_connectivity_connected_state_change(s_connectivity_state++ % kMemfaultMetricsConnectivityState_NumStates); #endif // For demonstration purposes, print the current values. This is not // recommended for production. memfault_metrics_heartbeat_debug_print(); } ================================================ FILE: examples/freertos/src/metrics.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @brief Metrics for the application void metrics_task_init(void); ================================================ FILE: examples/freertos/src/mpu.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/components.h" void mpu_init(void) { // The below defines are usually provided by the CMSIS headers for the target. // They're defined in-line here to avoid needing to bring in a separate SDK. #define __IM volatile const /*! Defines 'read only' structure member permissions */ #define __IOM volatile /*! Defines 'read / write' structure member permissions */ typedef struct { __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ } MPU_Type; #define MPU ((MPU_Type *)0xe000ed90) /*!< Memory Protection Unit */ // clang-format off #define MPU_RBAR_ADDR_Pos 8U /*!< MPU RBAR: ADDR Position */ #define MPU_RBAR_ADDR_Msk (0xFFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ #define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ #define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ #define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ #define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ #define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ #define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ #define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ #define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ #define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ #define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ #define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ #define MPU_RASR_AP_RO (0b110 << 24) // read-only / read-only #define MPU_RASR_AP_NO_ACCESS (0b0 << 24) // no access / no access // clang-format on const uint32_t dregion = (MPU->TYPE >> 8) & 0xff; MEMFAULT_LOG_INFO("MPU_TYPE.DREGION: %lx\n", dregion); // If the MPU is not present, there is nothing to do if (dregion == 0) { MEMFAULT_LOG_INFO("MPU not available\n"); return; } // Enable 2 MPU regions, to demonstrate Memfault MPU decoding. // // The regions are applied to unused memory, to avoid affecting the application. // 1. Region 0: 0x20100000-0x20100000, read-only, no subregions, no execute never // 2. Region 1: 0x20100000-0x20100020, no access // // The region locations are hard-coded, but work for the an385 + an386 boards. // Other boards will need to adjust the region locations. // // NOTE: These regions are intentionally set up to overlap, to showcase Memfault's // MPU analyzer feature for coredumps. // Region 0 #define MPU_RASR_SIZE_128KB (16 << MPU_RASR_SIZE_Pos) // 2^(SIZE+1) = 2^(16+1) = 2^17 = 128KB MPU->RBAR = (0x20100000 & MPU_RBAR_ADDR_Msk) | MPU_RBAR_VALID_Msk | 0; MPU->RASR = (MPU_RASR_ENABLE_Msk | MPU_RASR_SIZE_128KB | MPU_RASR_AP_RO | MPU_RASR_XN_Msk); // Region 1 #define MPU_RASR_SIZE_32B (4 << MPU_RASR_SIZE_Pos) // 2^(SIZE+1) = 2^(4+1) = 2^5 = 32B MPU->RBAR = (0x20100000 & MPU_RBAR_ADDR_Msk) | MPU_RBAR_VALID_Msk | 1; MPU->RASR = (MPU_RASR_ENABLE_Msk | MPU_RASR_SIZE_32B | MPU_RASR_AP_NO_ACCESS); // Enable the MPU MPU->CTRL = MPU_CTRL_ENABLE_Msk | MPU_CTRL_PRIVDEFENA_Msk; } ================================================ FILE: examples/freertos/src/mpu.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once void mpu_init(void); ================================================ FILE: examples/libcurl/Makefile ================================================ # Use these environment variables to customize the build MEMFAULT_PROJECT_KEY ?= MEMFAULT_DEVICE_SERIAL ?= TESTSERIAL MEMFAULT_HARDWARE_VERSION ?= mp MEMFAULT_SOFTWARE_TYPE ?= test-software MEMFAULT_SOFTWARE_VERSION ?= 0.9.0 CFLAGS += \ -DMEMFAULT_PROJECT_KEY=\"$(MEMFAULT_PROJECT_KEY)\" # Enable sanitizers CFLAGS += \ -fsanitize=leak -fsanitize=address \ -fsanitize=undefined \ -fno-sanitize-recover=all # Common warnings CFLAGS += \ -Wall \ -Werror \ -Wformat-signedness \ -Wshadow \ # Static analysis CFLAGS += \ -fanalyzer # Need curl and openssl libs CFLAGS += \ -lcurl -lcrypto -lssl .PHONY: all all: build/post-chunks build/get-latest build: mkdir -p $@ build/post-chunks: post_chunks.c | build $(CC) $(CFLAGS) $^ -o $@ build/get-latest: get_latest.c | build $(CC) $(CFLAGS) $^ -o $@ .PHONY: test test: build/post-chunks build/get-latest ./build/post-chunks ./build/get-latest $(MEMFAULT_DEVICE_SERIAL) $(MEMFAULT_HARDWARE_VERSION) $(MEMFAULT_SOFTWARE_TYPE) $(MEMFAULT_SOFTWARE_VERSION) .PHONY: clean clean: rm -rf build ================================================ FILE: examples/libcurl/README.md ================================================ # Memfault `libcurl` Samples This folder contains sample applications demonstrating how to interact with the Memfault Device API using `libcurl`. See the individual files for usage instructions, or the [`Makefile`](Makefile) to see an example. ================================================ FILE: examples/libcurl/get_latest.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Example code that uses libcurl to fetch the latest release for an example //! device. //! //! Compiling: In Memfault UI, navigate to Settings->General Settings and //! Copy/Paste "Project API Key" into PROJECT_KEY below: //! //! $ gcc get_latest.c -lcurl -lssl -lcrypto -Wall -DMEMFAULT_PROJECT_KEY=\"PROJECT_KEY\" //! //! NOTE: Requires libcurl and libopenssl. On Debian, 'sudo apt install -y libcurl4-openssl-dev' //! //! Usage: //! $ ./a.out //! //! NOTE: The device_serial, hardware_version, software_type, and //! current_version are mandatory, and must be valid for the test device. When //! testing cohort release activation, make sure the device_serial is present in //! the test cohort. #define _GNU_SOURCE // for asprintf #include #include #include #include #include #include #include #include "../../components/include/memfault/http/root_certs.h" #ifndef MEMFAULT_PROJECT_KEY #error "MEMFAULT_PROJECT_KEY definition required" #endif //! Provide the CA cert from openssl directly as an in-memory cert, instead of //! retrieving from the default system certificate store. //! //! This strategy is adapted from the example here: //! https://github.com/curl/curl/blob/master/docs/examples/cacertinmem.c static CURLcode prv_install_root_certs(CURL *curl, void *sslctx, void *param) { CURLcode rv = CURLE_ABORTED_BY_CALLBACK; static const char mypem[] = MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2; BIO *cbio = BIO_new_mem_buf(mypem, sizeof(mypem)); X509_STORE *cts = SSL_CTX_get_cert_store((SSL_CTX *)sslctx); int i; STACK_OF(X509_INFO) * inf; (void)curl; (void)param; if (!cts || !cbio) { return rv; } inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL); if (!inf) { BIO_free(cbio); return rv; } for (i = 0; i < sk_X509_INFO_num(inf); i++) { X509_INFO *itmp = sk_X509_INFO_value(inf, i); if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); } if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); } } sk_X509_INFO_pop_free(inf, X509_INFO_free); BIO_free(cbio); rv = CURLE_OK; return rv; } static int prv_get_latest_release(const char *device_serial, const char *hardware_version, const char *software_type, const char *current_version, bool verbose) { CURLcode ret; CURL *hnd; struct curl_slist *slist1; slist1 = NULL; slist1 = curl_slist_append(slist1, "Memfault-Project-Key:" MEMFAULT_PROJECT_KEY); hnd = curl_easy_init(); char *device_serial_ = curl_easy_escape(hnd, device_serial, 0); char *hardware_version_ = curl_easy_escape(hnd, hardware_version, 0); char *software_type_ = curl_easy_escape(hnd, software_type, 0); char *current_version_ = curl_easy_escape(hnd, current_version, 0); char *url; int rv = asprintf(&url, "https://device.memfault.com/api/v0/releases/latest/url" "?device_serial=%s&hardware_version=%s&software_type=%s¤t_version=%s", device_serial_, hardware_version_, software_type_, current_version_); curl_free(device_serial_); curl_free(hardware_version_); curl_free(software_type_); curl_free(current_version_); assert(rv >= 0); curl_easy_setopt(hnd, CURLOPT_URL, url); free(url); curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/8.5.0"); curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1); curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET"); curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L); /* Turn off the default CA locations, otherwise libcurl loads CA * certificates from the locations that were detected/specified at * build-time */ curl_easy_setopt(hnd, CURLOPT_CAINFO, NULL); curl_easy_setopt(hnd, CURLOPT_CAPATH, NULL); curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 1L); curl_easy_setopt(hnd, CURLOPT_SSLCERTTYPE, "PEM"); curl_easy_setopt(hnd, CURLOPT_SSL_CTX_FUNCTION, prv_install_root_certs); if (verbose) { curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); } ret = curl_easy_perform(hnd); if (ret == 0) { long http_code = 0; curl_easy_getinfo(hnd, CURLINFO_RESPONSE_CODE, &http_code); if ((http_code != 200) && (http_code != 204)) { ret = http_code; } } curl_easy_cleanup(hnd); hnd = NULL; curl_slist_free_all(slist1); slist1 = NULL; return (int)ret; } int main(int argc, char *argv[]) { if (argc < 5) { printf("Usage: %s \n", argv[0]); return 1; } const char *device_serial = argv[1]; const char *hardware_version = argv[2]; const char *software_type = argv[3]; const char *current_version = argv[4]; int rv = prv_get_latest_release(device_serial, hardware_version, software_type, current_version, true /* verbose */); if (rv != 0) { printf("\n\nERROR: Get latest release failed, rv=%d\n", rv); } else { printf("\n\nSuccess!\n\n"); } return rv; } ================================================ FILE: examples/libcurl/post_chunks.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Example code that uses libcurl to post memfault the memfault test data from: //! https://mflt.io/chunks-api-integration //! //! Compiling: //! In Memfault UI, navigate to Settings->General Settings and Copy/Paste "Project API Key" //! into PROJECT_KEY below: //! //! $ gcc libcurl_example.c -lcurl -lssl -lcrypto -Wall -DMEMFAULT_PROJECT_KEY=\"PROJECT_KEY\" //! //! NOTE: Requires libcurl and libopenssl. On Debian, 'sudo apt install -y libcurl4-openssl-dev' //! //! Usage: //! $ ./a.out //! $ Chunk successfully sent! #include #include #include #include "../../components/include/memfault/http/root_certs.h" #ifndef MEMFAULT_PROJECT_KEY #error "MEMFAULT_PROJECT_KEY definition required" #endif #include #include #include #include //! Provide the CA cert from openssl directly as an in-memory cert, instead of //! retrieving from the default system certificate store. //! //! This strategy is adapted from the example here: //! https://github.com/curl/curl/blob/master/docs/examples/cacertinmem.c static CURLcode prv_install_root_certs(CURL *curl, void *sslctx, void *param) { CURLcode rv = CURLE_ABORTED_BY_CALLBACK; static const char mypem[] = MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2; BIO *cbio = BIO_new_mem_buf(mypem, sizeof(mypem)); X509_STORE *cts = SSL_CTX_get_cert_store((SSL_CTX *)sslctx); int i; STACK_OF(X509_INFO) * inf; (void)curl; (void)param; if (!cts || !cbio) { return rv; } inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL); if (!inf) { BIO_free(cbio); return rv; } for (i = 0; i < sk_X509_INFO_num(inf); i++) { X509_INFO *itmp = sk_X509_INFO_value(inf, i); if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); } if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); } } sk_X509_INFO_pop_free(inf, X509_INFO_free); BIO_free(cbio); rv = CURLE_OK; return rv; } static int prv_post_chunk(const char *device_serial, const void *chunk, size_t chunk_len, bool verbose) { CURLcode ret; CURL *hnd; struct curl_slist *slist1; slist1 = NULL; slist1 = curl_slist_append(slist1, "Memfault-Project-Key:" MEMFAULT_PROJECT_KEY); slist1 = curl_slist_append(slist1, "Content-Type: application/octet-stream"); hnd = curl_easy_init(); char dest_url[512]; snprintf(dest_url, sizeof(dest_url), "https://chunks.memfault.com/api/v0/chunks/%s", device_serial); curl_easy_setopt(hnd, CURLOPT_URL, dest_url); curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, chunk); curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)chunk_len); curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/8.5.0"); curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1); curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS); curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L); /* Turn off the default CA locations, otherwise libcurl loads CA * certificates from the locations that were detected/specified at * build-time */ curl_easy_setopt(hnd, CURLOPT_CAINFO, NULL); curl_easy_setopt(hnd, CURLOPT_CAPATH, NULL); curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 1L); curl_easy_setopt(hnd, CURLOPT_SSLCERTTYPE, "PEM"); curl_easy_setopt(hnd, CURLOPT_SSL_CTX_FUNCTION, prv_install_root_certs); if (verbose) { curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); } ret = curl_easy_perform(hnd); if (ret == 0) { long http_code = 0; curl_easy_getinfo(hnd, CURLINFO_RESPONSE_CODE, &http_code); ret = !(http_code == 202); } curl_easy_cleanup(hnd); hnd = NULL; curl_slist_free_all(slist1); slist1 = NULL; return (int)ret; } int main(int argc, char *argv[]) { // This is the example chunk from: // https://docs.memfault.com/docs/embedded/test-patterns-for-chunks-endpoint#event-message-encoded-in-a-single-chunk const uint8_t chunk[] = { 0x08, 0x02, 0xa7, 0x02, 0x01, 0x03, 0x01, 0x07, 0x6a, 0x54, 0x45, 0x53, 0x54, 0x53, 0x45, 0x52, 0x49, 0x41, 0x4c, 0x0a, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x2d, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x09, 0x6a, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x06, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x2d, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x04, 0xa1, 0x01, 0xa1, 0x72, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x01, 0x31, 0xe4 }; int rv = prv_post_chunk("TESTSERIAL", chunk, sizeof(chunk), true /* verbose */); if (rv != 0) { printf("\n\nERROR: Chunk post failed, rv=%d\n", rv); } else { printf("\n\nChunk successfully sent!\n\n"); } return rv; } ================================================ FILE: examples/nrf-connect-sdk/nrf5/.gitignore ================================================ /bootloader /build /mbedtls /modules /nrf /nrfxlib /sparc /test /tools /.west /zephyr ================================================ FILE: examples/nrf-connect-sdk/nrf5/README.md ================================================ # nRF-Connect Memfault Example This is an example application showing a Memfault integration running on a Nordic Semiconductor development board, using the nRF Connect SDK. Any nRF board supporting `CONFIG_BT` should work. The example has been tested on: - nRF52840-DK (`nrf52840dk/nrf52840`) - nRF5340-DK (`nrf5340dk/nrf5340/cpuapp`) - nRF54L15-DK (`nrf54l15dk/nrf54l15/cpuapp`) - nRF54LM20-DK (`nrf54lm20dk/nrf54lm20a/cpuapp`) You can follow a guided tutorial for this example here: ## Usage Make sure you have the Zephyr / nRF-Connect tools installed first: This application is tested on nRF Connect SDK v3.1.1. To build and flash this example to an nRF5340-DK (PCA10095), run the following commands: ```bash ❯ west init --local memfault_demo_app ❯ west update ❯ west build --sysbuild --pristine=always --board nrf5340dk/nrf5340/cpuapp memfault_demo_app -- \ -DCONFIG_MEMFAULT_NCS_PROJECT_KEY=\"YOUR_PROJECT_KEY\" ❯ west flash ``` Open a serial terminal to access the console: ```bash # for example, pypserial-miniterm ❯ pyserial-miniterm --raw /dev/ttyACM0 115200 ``` The console has several Memfault test commands available: ```bash mflt - Memfault Test Commands Subcommands: clear_core : clear coredump collected export : dump chunks collected by Memfault SDK using https://mflt.io/chunk-data-export get_core : check if coredump is stored and present get_device_info : display device information get_latest_url : gets latest release URL get_latest_release : performs an OTA update using Memfault client coredump_size : print coredump computed size and storage capacity metrics_dump : dump current heartbeat or session metrics test : commands to verify memfault data collection (https://mflt.io/mcu-test-commands) ``` The `mflt test` subgroup contains commands for testing Memfault functionality: ```bash uart:~$ mflt test help test - commands to verify memfault data collection (https://mflt.io/mcu-test-commands) Subcommands: busfault : trigger a busfault hardfault : trigger a hardfault memmanage : trigger a memory management fault usagefault : trigger a usage fault hang : trigger a hang zassert : trigger a zephyr assert stack_overflow : trigger a stack overflow assert : trigger memfault assert loadaddr : test a 32 bit load from an address double_free : trigger a double free error badptr : trigger fault via store to a bad address isr_badptr : trigger fault via store to a bad address from an ISR isr_hang : trigger a hang in an ISR reboot : trigger a reboot and record it using memfault heartbeat : trigger an immediate capture of all heartbeat metrics log_capture : trigger capture of current log buffer contents logs : writes test logs to log buffer trace : capture an example trace event ``` For example, to test the coredump functionality: 1. run `mflt test hardfault` and wait for the board to reset 2. run `mflt get_core` to confirm the coredump was saved 3. run `mflt export` to print out the base-64 chunks: ```plaintext uart:~$ mflt export : MC:SE4DpwIEAwEKbW5yZjUyX2V4YW1wbGUJZTAuMC4xBmFhC0Z5RE1gF8EEhgFpSW5mbyBsb2chAmxXYXJuaW5nIGxvZyEDakVycm9yIGxvZyE=: : MC:gE6A/A==: ``` 4. upload the chunks to Memfault. see here for details: ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/1mbaud_uart.overlay ================================================ /* gotta go fast 🐎 */ &uart0 { current-speed = <1000000>; }; ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/CMakeLists.txt ================================================ # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.20.0) # The default board for this example is the nrf52840dk/nrf52840, but can be # overridden by passing --board= to west or -DBOARD= to cmake set(BOARD nrf52840dk/nrf52840) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(fs_shell) FILE(GLOB app_sources src/*.c) target_sources(app PRIVATE src/main.c src/shell_commands.c ) zephyr_include_directories(config) # Normalize dbug + __FILE__ paths to relative locations: # 1. west workspace # 2. toolchain install location (applies to paths in cross tool library files) # # If debugging from location 1., the west-manifest files should already have the # correct relative path for GDB to locate them, otherwise the GDB "directory" # command can be used to tell GDB where to find the source files (also required # if the cross tool libraries/headers need to be located by GDB) get_filename_component(WEST_PROJECT_ROOT_PATH "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) zephyr_compile_options( -ffile-prefix-map=${WEST_PROJECT_ROOT_PATH}=. -ffile-prefix-map=${TOOLCHAIN_HOME}=. -Wa,--debug-prefix-map=${WEST_PROJECT_ROOT_PATH}=. ) # If building for nrf54l15dk, add a custom linker fragment for placing the # noinit section. if(CONFIG_BOARD_NRF54L15DK) zephyr_linker_sources(SECTIONS ${CMAKE_CURRENT_SOURCE_DIR}/boards/nrf54l15dk_nrf54l15_cpuapp_noinit.ld ) endif() # Same for nrf54lm20DK if(CONFIG_BOARD_NRF54LM20DK) zephyr_linker_sources(SECTIONS ${CMAKE_CURRENT_SOURCE_DIR}/boards/nrf54lm20dk_nrf54lm20a_cpuapp_noinit.ld ) endif() ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/Kconfig ================================================ menu "MEMFAULT_APP" # Set Memfault software_version from the VERSION file in the application. This # is also used by MCUBoot to set CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION, so the # MCUBoot image version will match the Memfault-reported version with this # setting. Note: the Kconfig flag MEMFAULT_NCS_FW_VERSION is defined in the NCS # repo, here it's just overriding the default string to be set to the desired value. config MEMFAULT_NCS_FW_VERSION default "$(APP_VERSION_TWEAK_STRING)" config MEMFAULT_APP_CAPTURE_ALL_RAM bool "Capture all RAM" help Capture all RAM regions. This is useful for debugging purposes, but will increase the amount of data captured by the Memfault SDK. # If MEMFAULT_APP_CAPTURE_ALL_RAM is enabled, increase the configured space # for the Memfault storage partition to accommodate the larger coredump. # # This overrides the default setting, which can be found here: # https://github.com/nrfconnect/sdk-nrf/blob/96f97684565fbf711f1ceb7da061fed0bfe7b60b/modules/memfault-firmware-sdk/Kconfig#L170-L172 # # Expanded into the template here: # https://github.com/nrfconnect/sdk-nrf/blob/96f97684565fbf711f1ceb7da061fed0bfe7b60b/subsys/partition_manager/Kconfig.template.partition_size # # Keep 'source "Kconfig.zephyr"' at the end of this file so this default takes # precedence. if MEMFAULT_NCS_INTERNAL_FLASH_BACKED_COREDUMP config PM_PARTITION_SIZE_MEMFAULT_STORAGE hex "Size of the Memfault storage partition" # The default size is set to one page above the total RAM size on the # NRF52840 SOC, which provides room for the coredump header and NVIC and CPU # registers. This size needs to be flash page-aligned ("minimum eraseable # unit"). default 0x41000 if MEMFAULT_APP_CAPTURE_ALL_RAM endif endmenu # # Copyright (c) 2024 Nordic Semiconductor # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # config SETTINGS default y config ZMS default y if (SOC_FLASH_NRF_RRAM || SOC_FLASH_NRF_MRAM) config NVS default y if !(SOC_FLASH_NRF_RRAM || SOC_FLASH_NRF_MRAM) ### Memfault Device Identification settings ### # # There are 3 places device identification is used: # # | Field | Memfault (NCS) | Bluetooth DIS | MCUboot | # |-------------------|-------------------------|--------------------------|------------------------------| # | .software_version | MEMFAULT_NCS_FW_VERSION | BT_DIS_FW_REV_STR | MCUBOOT_IMGTOOL_SIGN_VERSION | # | .software_type | MEMFAULT_NCS_FW_TYPE | BT_DIS_SW_REV_STR | N/A | # | .hardware_version | MEMFAULT_NCS_HW_VERSION | BT_DIS_HW_REV_STR | N/A | # | .device_serial | MEMFAULT_NCS_DEVICE_ID | BT_DIS_SERIAL_NUMBER_STR | N/A | # # How each is used: # - Memfault uses these values to tag telemetry data sent to the Memfault cloud. # - The Bluetooth Device Information Service (DIS) exposes these values over # BLE. These are used by the nRF Connect Device Manager app to execute FOTA # with nRF Cloud (Memfault). These values are required when performing the # FOTA requests. # - MCUboot contains the .software_version in the image header, mostly used for # informational purposes. # # We use this Kconfig override file to set all 3 places simultaneously to ensure # consistency. # .software_version config MEMFAULT_NCS_FW_VERSION # The MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_VERSION default uses the VERSION # file for the software version value default MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_VERSION config MCUBOOT_IMGTOOL_SIGN_VERSION default MEMFAULT_NCS_FW_VERSION config BT_DIS_FW_REV_STR default MEMFAULT_NCS_FW_VERSION # .software_type config MEMFAULT_NCS_FW_TYPE # Defaults to "app" default MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_TYPE config BT_DIS_SW_REV_STR default MEMFAULT_NCS_FW_TYPE # .hardware_version config BT_DIS_HW_REV_STR default MEMFAULT_NCS_HW_VERSION source "Kconfig.zephyr" ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/Kconfig.sysbuild ================================================ source "share/sysbuild/Kconfig" config NRF_DEFAULT_IPC_RADIO default y ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/VERSION ================================================ VERSION_MAJOR = 0 VERSION_MINOR = 0 PATCHLEVEL = 1 VERSION_TWEAK = 0 EXTRAVERSION = ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/boards/nrf54h20dk_nrf54h20_cpuapp.conf ================================================ # Use MRAM internal storage implementation instead of flash-based CONFIG_MEMFAULT_NCS_INTERNAL_FLASH_BACKED_COREDUMP=n CONFIG_MEMFAULT_COREDUMP_STORAGE_MRAM=y ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/boards/nrf54h20dk_nrf54h20_cpuapp.overlay ================================================ /* Adjust the fixed partitions in MRAM1 to add a 'memfault_coredump' partition for storing coredumps when using MRAM-backed coredump storage. */ &mram1x { partitions { storage_partition: partition@1a4000 { reg = <0x1a4000 DT_SIZE_K(20)>; }; memfault_coredump_partition: partition@1a9000 { reg = <0x1a9000 DT_SIZE_K(20)>; }; }; }; ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/boards/nrf54l15dk_nrf54l15_cpuapp.conf ================================================ # Use RRAM internal storage implementation instead of flash-based CONFIG_MEMFAULT_NCS_INTERNAL_FLASH_BACKED_COREDUMP=n CONFIG_MEMFAULT_COREDUMP_STORAGE_NRF_RRAM=y # Place the noinit objects into a custom section. CONFIG_MEMFAULT_REBOOT_TRACKING_REGION="memfault_noinit.memfault_reboot_info" ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/boards/nrf54l15dk_nrf54l15_cpuapp_noinit.ld ================================================ /* Using the Memfault RAM-backed coredump storage on the nRF54 series requires placing the noinit section at an arbitrarily higher location in RAM, to prevent the bootloader from overwriting it. */ SECTION_PROLOGUE (memfault_noinit, 0x20020000 (NOLOAD),) { KEEP(*(*.memfault_reboot_info)); KEEP(*(*.memfault_coredump)); } GROUP_DATA_LINK_IN(RAMABLE_REGION, RAMABLE_REGION) ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/boards/nrf54lm20dk_nrf54lm20a_cpuapp.conf ================================================ # Use RRAM internal storage implementation instead of flash-based CONFIG_MEMFAULT_NCS_INTERNAL_FLASH_BACKED_COREDUMP=n CONFIG_MEMFAULT_COREDUMP_STORAGE_NRF_RRAM=y # Place the noinit objects into a custom section. CONFIG_MEMFAULT_REBOOT_TRACKING_REGION="memfault_noinit.memfault_reboot_info" CONFIG_MEMFAULT_RAM_BACKED_COREDUMP_REGION="memfault_noinit.memfault_coredump" ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay ================================================ /* Reserve a fixed retained SRAM window for Memfault reboot tracking and * RAM-backed coredumps without shifting the main RAM placement. */ / { memfault_noinit_sram: memfault_noinit_sram@2007f000 { compatible = "zephyr,memory-region", "mmio-sram"; reg = <0x2007f000 0xc00>; zephyr,memory-region = "MemfaultNoInit"; status = "okay"; }; }; &cpuapp_sram { reg = <0x20000000 0x7f000>; ranges = <0x0 0x20000000 0x7f000>; }; ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay.example ================================================ /* Example device tree overlay for the nrf54lm20dk, showing how to set a fixed-partition region for RRAM-backed coredump storage. Pass it with '-DDTC_OVERLAY_FILE=boards/nrf54lm20pdk_nrf54lm20a_cpuapp.overlay.example' when building. */ &cpuapp_rram { partitions { storage_partition: partition@1d1000 { label = "storage"; reg = < 0x1d1000 0x4000 >; }; memfault_coredump_partition: partition@1d5000 { label = "memfault_coredump_partition"; reg = <0x1d5000 0x5000>; }; }; }; ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/boards/nrf54lm20dk_nrf54lm20a_cpuapp_noinit.ld ================================================ /* Demonstrate storing reboot event data in a fixed location of noninit. The nRF54LM20 has 0x7fc00 bytes of RAM starting at 0x20000000. Reserve the last 0xc00 bytes in devicetree and link this section into that dedicated memory region so .bss can still start at the base of normal SRAM. */ SECTION_PROLOGUE(memfault_noinit, (NOLOAD),) { KEEP(*(*.memfault_reboot_info)); KEEP(*(*.memfault_coredump)); } GROUP_DATA_LINK_IN(LINKER_DT_NODE_REGION_NAME(DT_NODELABEL(memfault_noinit_sram)), LINKER_DT_NODE_REGION_NAME(DT_NODELABEL(memfault_noinit_sram))) ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/config/memfault_metrics_heartbeat_config.def ================================================ //! Define custom system metrics to track. For example, // MEMFAULT_METRICS_KEY_DEFINE(main_task_stack_hwm, kMemfaultMetricType_Unsigned) // MEMFAULT_METRICS_KEY_DEFINE(ble_min_rssi, kMemfaultMetricType_Signed) // MEMFAULT_METRICS_KEY_DEFINE(mcu_sleep_time_ms, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(button_press_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(button_elapsed_time_ms, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(battery_soc_pct, kMemfaultMetricType_Unsigned) ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/config/memfault_trace_reason_user_config.def ================================================ // This is where project specific trace reasons should be defined using the // MEMFAULT_TRACE_REASON_DEFINE macro // // For example: MEMFAULT_TRACE_REASON_DEFINE(your_custom_error_reason) // // Refer to https://docs.memfault.com/docs/embedded/trace-events for more // details. MEMFAULT_TRACE_REASON_DEFINE(button_state_changed) ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/pm_static_nrf54l15dk_nrf54l15_cpuapp.yml ================================================ EMPTY_0: address: 0xd800 end_address: 0xe000 placement: after: - mcuboot region: flash_primary size: 0x800 EMPTY_1: address: 0x154000 end_address: 0x155000 placement: after: - mcuboot_secondary region: flash_primary size: 0x1000 app: address: 0xe800 end_address: 0xb1000 region: flash_primary size: 0xa2800 bootconf: address: 0xffd080 end_address: 0xffd084 region: bootconf size: 0x4 mcuboot: address: 0x0 end_address: 0xd800 placement: align: end: 0x1000 before: - mcuboot_primary region: flash_primary size: 0xd800 mcuboot_pad: address: 0xe000 end_address: 0xe800 placement: before: - mcuboot_primary_app region: flash_primary size: 0x800 mcuboot_primary: address: 0xe000 end_address: 0xb1000 orig_span: &id001 - mcuboot_pad - app region: flash_primary sharers: 0x1 size: 0xa3000 span: *id001 mcuboot_primary_app: address: 0xe800 end_address: 0xb1000 orig_span: &id002 - app region: flash_primary size: 0xa2800 span: *id002 mcuboot_secondary: address: 0xb1000 end_address: 0x154000 placement: after: - mcuboot_primary align: start: 0x1000 region: flash_primary share_size: - mcuboot_primary size: 0xa3000 memfault_coredump_partition: address: 0x155000 end_address: 0x165000 placement: align: start: 0x1000 before: - end region: flash_primary size: 0x10000 otp: address: 0xffd500 end_address: 0xffd9fc region: otp size: 0x4fc sram_primary: address: 0x20000000 end_address: 0x2002f000 region: sram_primary size: 0x2f000 ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/pm_static_nrf54lm20dk_nrf54lm20a_cpuapp.yml ================================================ EMPTY_0: address: 0xd800 end_address: 0xe000 placement: after: - mcuboot region: flash_primary size: 0x800 memfault_coredump_partition: address: 0x1fa000 end_address: 0x1fb000 placement: after: - mcuboot_secondary region: flash_primary size: 0x1000 app: address: 0xe800 end_address: 0x104000 region: flash_primary size: 0xf5800 bootconf: address: 0xffd080 end_address: 0xffd084 region: bootconf size: 0x4 mcuboot: address: 0x0 end_address: 0xd800 placement: align: end: 0x1000 before: - mcuboot_primary region: flash_primary size: 0xd800 mcuboot_pad: address: 0xe000 end_address: 0xe800 placement: before: - mcuboot_primary_app region: flash_primary size: 0x800 mcuboot_primary: address: 0xe000 end_address: 0x104000 orig_span: &id001 - app - mcuboot_pad region: flash_primary sharers: 0x1 size: 0xf6000 span: *id001 mcuboot_primary_app: address: 0xe800 end_address: 0x104000 orig_span: &id002 - app region: flash_primary size: 0xf5800 span: *id002 mcuboot_secondary: address: 0x104000 end_address: 0x1fa000 placement: after: - mcuboot_primary align: start: 0x1000 region: flash_primary share_size: - mcuboot_primary size: 0xf6000 otp: address: 0xffd500 end_address: 0xffd97c region: otp size: 0x47c settings_storage: address: 0x1fb000 end_address: 0x1fd000 placement: after: - app align: start: 0x1000 before: - end region: flash_primary size: 0x2000 sram_primary: address: 0x20000000 end_address: 0x2007fc00 region: sram_primary size: 0x7fc00 ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/prj.conf ================================================ # Heap memory is required for the memfault_demo_cli.c CONFIG_HEAP_MEM_POOL_SIZE=256 CONFIG_MAIN_STACK_SIZE=4096 CONFIG_BT=y CONFIG_BT_SMP=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_DEVICE_NAME="Nordic_Memfault" CONFIG_BT_PRIVACY=y CONFIG_BT_BAS=y CONFIG_BT_MDS=y CONFIG_BT_CTLR_PHY_2M=y CONFIG_BT_BUF_ACL_RX_SIZE=251 CONFIG_BT_L2CAP_TX_MTU=247 CONFIG_BT_BUF_ACL_TX_SIZE=251 CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 CONFIG_LOG=y CONFIG_LOG_PRINTK=n CONFIG_LOG_DEFAULT_LEVEL=2 CONFIG_LOG_MODE_DEFERRED=y CONFIG_LOG_MODE_OVERFLOW=y CONFIG_LOG_BACKEND_RTT=n CONFIG_SHELL=y CONFIG_SENSOR_SHELL=n CONFIG_DK_LIBRARY=y # Heap memory is required for the memfault_demo_cli.c CONFIG_MEMFAULT=y CONFIG_MEMFAULT_SHELL=y CONFIG_MEMFAULT_LOGGING_ENABLE=y CONFIG_MEMFAULT_LOG_LEVEL_INF=y CONFIG_MEMFAULT_NCS_BT_METRICS=y CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_CUSTOM=y # Enable bonding CONFIG_BT_SETTINGS=y CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_FLASH_MAP=y # Enable OTA updates CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=y # Device Information Service (required for OTA) CONFIG_BT_DIS=y CONFIG_BT_DIS_SERIAL_NUMBER=y CONFIG_BT_DIS_HW_REV=y CONFIG_BT_DIS_SW_REV=y CONFIG_BT_DIS_FW_REV=y # Fill in with a Memfault Project Key CONFIG_MEMFAULT_NCS_PROJECT_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Memfault firmware identification CONFIG_MEMFAULT_NCS_FW_VERSION_STATIC=y # Set the Memfault device serial at runtime (loaded from settings bt/dis/serial) CONFIG_MEMFAULT_NCS_DEVICE_ID_RUNTIME=y # Enable BT DIS (Device Information Service) settings storage CONFIG_BT_DIS_SETTINGS=y # Enable HW ID library to get device ID from UID CONFIG_HW_ID_LIBRARY=y CONFIG_HW_ID_LIBRARY_SOURCE_DEVICE_ID=y # Enable more fields in the nRF Connect Device Manager app view CONFIG_MCUMGR_GRP_OS_INFO=y # Setting shell, useful for testing CONFIG_SETTINGS_SHELL=y # Settings runtime enables applying settings immediately without reboot CONFIG_SETTINGS_RUNTIME=y # Enable Flash and Bootloader components for flash-backed coredump storage CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_MCUBOOT_IMG_MANAGER=y CONFIG_IMG_MANAGER=y CONFIG_STREAM_FLASH=y CONFIG_WATCHDOG=y # Enable Zephyr runtime asserts CONFIG_ASSERT=y # For testing poweroff mode, enable GPIO + poweroff subsystems CONFIG_GPIO=y CONFIG_POWEROFF=y ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/sample.yaml ================================================ sample: description: Memfault SDK example integration built on top of the Zephyr FS shell sample app, with the nRF Connect SDK name: Memfault Demo App ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/src/main.c ================================================ /* * Copyright (c) 2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME CONFIG_BT_DEVICE_NAME #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) #define RUN_STATUS_LED DK_LED1 #define CON_STATUS_LED DK_LED2 #define RUN_LED_BLINK_INTERVAL 1000 static const struct bt_data ad[] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_MDS_VAL), }; static const struct bt_data sd[] = { BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), }; static struct bt_conn *mds_conn; static struct k_work adv_work; static void bas_work_handler(struct k_work *work); static K_WORK_DELAYABLE_DEFINE(bas_work, bas_work_handler); static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); if (!err) { printk("Security changed: %s level %u\n", addr, level); } else { printk("Security failed: %s level %u err %d %s\n", addr, level, err, bt_security_err_to_str(err)); } if (level >= BT_SECURITY_L2) { if (!mds_conn) { mds_conn = conn; } } } static void adv_work_handler(struct k_work *work) { int err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_2, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); if (err) { printk("Advertising failed to start (err %d)\n", err); return; } printk("Advertising successfully started\n"); } static void advertising_start(void) { k_work_submit(&adv_work); } static void connected(struct bt_conn *conn, uint8_t conn_err) { char addr[BT_ADDR_LE_STR_LEN]; if (conn_err) { printk("Connection failed, err 0x%02x %s\n", conn_err, bt_hci_err_to_str(conn_err)); return; } bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); printk("Connected %s\n", addr); dk_set_led_on(CON_STATUS_LED); } static void disconnected(struct bt_conn *conn, uint8_t reason) { printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason)); dk_set_led_off(CON_STATUS_LED); if (conn == mds_conn) { mds_conn = NULL; } } static void recycled_cb(void) { printk("Connection object available from previous conn. Disconnect is complete!\n"); advertising_start(); } BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, .security_changed = security_changed, .recycled = recycled_cb, }; static void pairing_complete(struct bt_conn *conn, bool bonded) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); printk("Pairing completed: %s, bonded: %d\n", addr, bonded); } static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); printk("Pairing failed conn: %s, reason %d %s\n", addr, reason, bt_security_err_to_str(reason)); } static struct bt_conn_auth_info_cb conn_auth_info_callbacks = { .pairing_complete = pairing_complete, .pairing_failed = pairing_failed }; static void auth_cancel(struct bt_conn *conn) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); printk("Pairing cancelled: %s\n", addr); } static struct bt_conn_auth_cb conn_auth_callbacks = { .cancel = auth_cancel, }; #if defined(CONFIG_BT_MDS) static bool mds_access_enable(struct bt_conn *conn) { if (mds_conn && (conn == mds_conn)) { return true; } return false; } static const struct bt_mds_cb mds_cb = { .access_enable = mds_access_enable, }; #endif static void button_handler(uint32_t button_state, uint32_t has_changed) { static bool time_measure_start; int err; uint32_t buttons = button_state & has_changed; if (buttons & DK_BTN1_MSK) { time_measure_start = !time_measure_start; if (time_measure_start) { err = MEMFAULT_METRIC_TIMER_START(button_elapsed_time_ms); if (err) { printk("Failed to start memfault metrics timer: %d\n", err); } } else { err = MEMFAULT_METRIC_TIMER_STOP(button_elapsed_time_ms); if (err) { printk("Failed to stop memfault metrics: %d\n", err); } /* Trigger collection of heartbeat data. */ memfault_metrics_heartbeat_debug_trigger(); } } if (has_changed & DK_BTN2_MSK) { bool button_state = (buttons & DK_BTN2_MSK) ? 1 : 0; MEMFAULT_TRACE_EVENT_WITH_LOG(button_state_changed, "Button state: %u", button_state); printk("button_state_changed event has been tracked, button state: %u\n", button_state); } if (buttons & DK_BTN3_MSK) { err = MEMFAULT_METRIC_ADD(button_press_count, 1); if (err) { printk("Failed to increase button_press_count metric: %d\n", err); } else { printk("button_press_count metric increased\n"); } } if (buttons & DK_BTN4_MSK) { volatile uint32_t i; printk("Division by zero will now be triggered\n"); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdiv-by-zero" i = 1 / 0; #pragma GCC diagnostic pop ARG_UNUSED(i); } } static void bas_notify(void) { int err; uint8_t battery_level = bt_bas_get_battery_level(); __ASSERT_NO_MSG(battery_level > 0); battery_level--; if (battery_level == 0) { battery_level = 100U; } err = MEMFAULT_METRIC_SET_UNSIGNED(battery_soc_pct, battery_level); if (err) { printk("Failed to set battery_soc_pct memfault metrics (err %d)\n", err); } bt_bas_set_battery_level(battery_level); } static void bas_work_handler(struct k_work *work) { bas_notify(); k_work_reschedule((struct k_work_delayable *)work, K_SECONDS(1)); } #define SERIAL_NUMBER_SETTING_KEY "bt/dis/serial" static void serial_number_init(void) { int err = settings_get_val_len(SERIAL_NUMBER_SETTING_KEY); printk("Checked for existing serial number in settings, length %d\n", err); /* Check if the serial number is already set */ char serial_buf[64] = "unknown"; int serial_len = settings_load_one(SERIAL_NUMBER_SETTING_KEY, serial_buf, sizeof(serial_buf)); if (serial_len == 0) { printk("📥 Writing new device serial number to " SERIAL_NUMBER_SETTING_KEY " from HW info\n"); int ret = hw_id_get(serial_buf, sizeof(serial_buf)); if (ret) { printk("Failed to get device ID from HW ID (err %d)\n", ret); return; } serial_len = strlen(serial_buf); settings_save_one(SERIAL_NUMBER_SETTING_KEY, serial_buf, serial_len); ret = settings_runtime_set(SERIAL_NUMBER_SETTING_KEY, serial_buf, serial_len); if (ret) { printk("Failed to set device serial number in settings runtime (err %d)\n", ret); } } else { printk("📤 Loaded serial number from settings\n"); } printk("➡️ Device serial set to: %s\n", serial_buf); // Apply the serial number to Memfault (void)memfault_ncs_device_id_set(serial_buf, serial_len); } // Shell command to set Bluetooth DIS + Memfault serial number static int cmd_set_bt_serial(const struct shell *shell_ptr, size_t argc, char *argv[]) { if (argc != 2) { shell_print(shell_ptr, "Usage: set-bt-serial "); return -EINVAL; } const char *serial_number = argv[1]; int ret = settings_save_one(SERIAL_NUMBER_SETTING_KEY, serial_number, strlen(serial_number)); if (ret) { shell_print(shell_ptr, "Error: Failed to save serial number to settings (err %d)", ret); return ret; } ret = settings_runtime_set(SERIAL_NUMBER_SETTING_KEY, serial_number, strlen(serial_number)); if (ret) { shell_print(shell_ptr, "Error: Failed to set serial number in settings runtime (err %d)", ret); return ret; } (void)memfault_ncs_device_id_set(serial_number, strlen(serial_number)); shell_print(shell_ptr, "Bluetooth serial number saved to settings successfully"); return 0; } #if defined(CONFIG_MEMFAULT_MCUMGR_GRP) #include "memfault/ports/zephyr/memfault_mcumgr.h" //! The access-enabled variable is passed as user arg for demonstration purposes static bool s_memfault_mcumgr_access_allowed = true; static bool prv_memfault_mgmt_is_access_allowed(void *user_arg) { return *(bool *)user_arg; } static int cmd_memfault_mcumgr_access(const struct shell *shell_ptr, size_t argc, char **argv) { memfault_mcumgr_set_access_callback(prv_memfault_mgmt_is_access_allowed, &s_memfault_mcumgr_access_allowed); if (argc > 1) { if (strcmp(argv[1], "1") == 0) { s_memfault_mcumgr_access_allowed = true; } else if (strcmp(argv[1], "0") == 0) { s_memfault_mcumgr_access_allowed = false; } else { shell_print(shell_ptr, "Usage: memfault-mcumgr-access <1|0>"); return -EINVAL; } } shell_print(shell_ptr, "Memfault MCUmgr group access is now %s", s_memfault_mcumgr_access_allowed ? "ENABLED" : "DISABLED"); return 0; } #endif // defined(CONFIG_MEMFAULT_MCUMGR_GRP) SHELL_STATIC_SUBCMD_SET_CREATE( sub_config, SHELL_CMD_ARG(set_bt_serial, NULL, "Set Bluetooth serial number in settings", cmd_set_bt_serial, 2, 0), #if defined(CONFIG_MEMFAULT_MCUMGR_GRP) SHELL_CMD_ARG(memfault_mcumgr_access, NULL, "Enable or disable Memfault MCUmgr group access. Usage: " "memfault-mcumgr-access <1|0>", cmd_memfault_mcumgr_access, 1, 1), #endif // defined(CONFIG_MEMFAULT_MCUMGR_GRP) SHELL_SUBCMD_SET_END); SHELL_CMD_REGISTER(config, &sub_config, "Configure the example", NULL); #if CONFIG_MEMFAULT_APP_CAPTURE_ALL_RAM // capture *ALL* of ram const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { static sMfltCoredumpRegion s_regions[] = { MEMFAULT_COREDUMP_MEMORY_REGION_INIT(CONFIG_PM_SRAM_BASE, CONFIG_PM_SRAM_SIZE), }; *num_regions = ARRAY_SIZE(s_regions); return s_regions; } #endif // CONFIG_MEMFAULT_APP_CAPTURE_ALL_RAM #define WD_FEED_THREAD_STACK_SIZE 500 // set priority to lowest application thread; shell_uart, where the 'mflt test // hang' command runs from, uses the same priority by default, so this should // not preempt it and correctly trip the watchdog #if CONFIG_SHELL_THREAD_PRIORITY_OVERRIDE #error "Watchdog feed thread priority must be lower than shell thread priority" #endif #define WD_FEED_THREAD_PRIORITY K_LOWEST_APPLICATION_THREAD_PRIO static void prv_wd_feed_thread_function(void *arg0, void *arg1, void *arg2) { ARG_UNUSED(arg0); ARG_UNUSED(arg1); ARG_UNUSED(arg2); while (1) { memfault_software_watchdog_feed(); k_sleep(K_SECONDS(1)); } } K_THREAD_DEFINE(wd_feed_thread, WD_FEED_THREAD_STACK_SIZE, prv_wd_feed_thread_function, NULL, NULL, NULL, WD_FEED_THREAD_PRIORITY, 0, 0); static void prv_start_watchdog_feed_thread(void) { memfault_software_watchdog_enable(); k_thread_name_set(wd_feed_thread, "wd_feed_thread"); k_thread_start(wd_feed_thread); } int main(void) { uint32_t blink_status = 0; int err; printk(MEMFAULT_BANNER_COLORIZED "\n"); printk("Memfault Demo Application\n"); printk("Board: " CONFIG_BOARD "\n"); prv_start_watchdog_feed_thread(); err = dk_leds_init(); if (err) { printk("LEDs init failed (err %d)\n", err); return 0; } err = dk_buttons_init(button_handler); if (err) { printk("Failed to initialize buttons (err %d)\n", err); return 0; } #if defined(CONFIG_BT_MDS) err = bt_mds_cb_register(&mds_cb); if (err) { printk("Memfault Diagnostic service callback registration failed (err %d)\n", err); return 0; } #endif // defined(CONFIG_BT_MDS) err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); return 0; } err = bt_conn_auth_cb_register(&conn_auth_callbacks); if (err) { printk("Failed to register authorization callbacks (err %d)\n", err); return 0; } err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks); if (err) { printk("Failed to register authorization info callbacks (err %d)\n", err); return 0; } printk("Bluetooth initialized\n"); if (IS_ENABLED(CONFIG_SETTINGS)) { err = settings_load(); if (err) { printk("Failed to load settings (err %d)\n", err); return 0; } serial_number_init(); } k_work_init(&adv_work, adv_work_handler); advertising_start(); k_work_schedule(&bas_work, K_SECONDS(1)); for (;;) { dk_set_led(RUN_STATUS_LED, (++blink_status) % 2); k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL)); } } ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/src/shell_commands.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Shell commands #include #include // #include #include #include #include LOG_MODULE_REGISTER(appshell, LOG_LEVEL_INF); static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); // Define a simple shell command static int cmd_poweroff(const struct shell *shell, size_t argc, char **argv) { LOG_INF("Powering off device"); /* configure sw0 as input, interrupt as level active to allow wake-up */ int rc = gpio_pin_configure_dt(&sw0, GPIO_INPUT); if (rc < 0) { LOG_ERR("Could not configure sw0 GPIO (%d)\n", rc); return 0; } rc = gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_LEVEL_ACTIVE); if (rc < 0) { LOG_ERR("Could not configure sw0 GPIO interrupt (%d)\n", rc); return 0; } LOG_INF("Entering system off; press sw0 to restart\n"); // rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND); // if (rc < 0) { // printf("Could not suspend console (%d)\n", rc); // return 0; // } // if (IS_ENABLED(CONFIG_APP_RETENTION)) { // /* Update the retained state */ // retained.off_count += 1; // retained_update(); // } sys_poweroff(); return 0; } SHELL_CMD_REGISTER(poweroff, NULL, "Power off the device", cmd_poweroff); ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/sysbuild/ipc_radio/prj.conf ================================================ CONFIG_HEAP_MEM_POOL_SIZE=8192 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_MBOX=y CONFIG_IPC_SERVICE=y CONFIG_BT=y CONFIG_BT_HCI_RAW=y CONFIG_BT_CTLR_ASSERT_HANDLER=y #CONFIG_BT_MAX_CONN=16 # Enable and adjust the below value as necessary # CONFIG_BT_BUF_EVT_RX_COUNT=16 # CONFIG_BT_BUF_EVT_RX_SIZE=255 # CONFIG_BT_BUF_ACL_RX_SIZE=255 # CONFIG_BT_BUF_ACL_TX_SIZE=251 # CONFIG_BT_BUF_CMD_TX_SIZE=255 CONFIG_BT_CTLR_PHY_2M=y CONFIG_BT_BUF_ACL_TX_SIZE=251 CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 CONFIG_BT_BUF_ACL_RX_SIZE=251 CONFIG_BT_MAX_CONN=2 CONFIG_ASSERT=y CONFIG_EXCEPTION_STACK_TRACE=y CONFIG_IPC_RADIO_BT=y CONFIG_IPC_RADIO_BT_HCI_IPC=y ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/sysbuild.conf ================================================ SB_CONFIG_BOOTLOADER_MCUBOOT=y ================================================ FILE: examples/nrf-connect-sdk/nrf5/memfault_demo_app/west.yml ================================================ manifest: remotes: - name: nrf-connect-sdk url-base: https://github.com/nrfconnect projects: - name: nrf url: https://github.com/nrfconnect/sdk-nrf revision: v3.2.0 import: true # Adding the memfault-firmware-sdk module here overrides the version the nRF # Connect SDK would normally import above - name: memfault-firmware-sdk url: https://github.com/memfault/memfault-firmware-sdk path: modules/lib/memfault-firmware-sdk revision: master self: path: memfault_demo_app ================================================ FILE: examples/nrf-connect-sdk/nrf9160/.gitignore ================================================ /bootloader /build /mbedtls /modules /nrf /nrfxlib /sparc /test /tools /.west /zephyr ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/CMakeLists.txt ================================================ # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.13.1) if(DEFINED ENV{MEMFAULT_ZEPHYR_EXTRA_MODULE_INCLUDE}) list(APPEND ZEPHYR_EXTRA_MODULES $ENV{ZEPHYR_BASE}/../modules/memfault-firmware-sdk/ports) endif() # Starting in nRF Connect SDK >= 1.3, versions are exposed in # a CMake package: # https://github.com/nrfconnect/sdk-nrf/blob/master/share/ncs-package/cmake/NcsConfig.cmake # # We can pre-load the package before Zephyr to dynamically change Kconfig options based on version # which is not something that can be achieved with Zephyr today find_package(Ncs HINTS $ENV{ZEPHYR_BASE}/../nrf) # 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 via overlays based on nRF Connect SDK version # (since setting non-existent variables result in a build time error which # would break compiling older builds) if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_GREATER_EQUAL 2.9.99) set(OVERLAY_CONFIG "${OVERLAY_CONFIG};overlays/ncs-main.conf") endif() if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 2.9.99) set(OVERLAY_CONFIG "${OVERLAY_CONFIG};overlays/ncs-pre-v2.9.99.conf") endif() if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 2.8.0) set(OVERLAY_CONFIG "${OVERLAY_CONFIG};overlays/ncs-pre-v2.8.0.conf") endif() if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 2.2.99) set(OVERLAY_CONFIG "${OVERLAY_CONFIG};overlays/ncs-pre-v2.2.99.conf") endif() if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 2.4.0) set(OVERLAY_CONFIG "${OVERLAY_CONFIG};overlays/ncs-pre-v2.4.0.conf") endif() if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 2.0.0) set(OVERLAY_CONFIG "${OVERLAY_CONFIG};overlays/ncs-pre-v2.0.0.conf") endif() find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) project(hello_world) target_sources(app PRIVATE src/main.c) target_sources(app PRIVATE src/watchdog.c) zephyr_include_directories(config) ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/README.md ================================================ # Memfault Demo App A modified version of the the `samples/nrf9160/https_client` in the nRF Connect SDK which includes a Memfault Integration! ## Setup An API key will need to be baked into the demo app to enable it to communicate with Memfault's web services. nRF91 users can provision a project and key from [here](https://goto.memfault.com/create-key/nrf91). ## Compiling You can compile for any board supported by the nRF Connect SDK. For example, targeting the nRF91 PDK would look like: ```bash $ west init -l memfault_demo_app $ west update # Replace ${YOUR_PROJECT_KEY} with the Project Key from https://mflt.io/project-key $ west build -b nrf9160dk/nrf9160/ns memfault_demo_app -- -DCONFIG_MEMFAULT_NCS_PROJECT_KEY=\"${YOUR_PROJECT_KEY}\" ... [181/181] Linking C executable zephyr/zephyr.elf ``` Note: you may want to target a version of the DK by passing `-b @/`, for example `nrf9160dk@1.0.0/nrf9160/ns`, in order to use [specific hardware](https://docs.nordicsemi.com/bundle/ncs-2.9.2/page/zephyr/boards/nordic/nrf9160dk/doc/index.html#additional_hardware_in_v0140). For NCS < 2.7.0, use `@`, for example `nrf9160dk_nrf9160@1.0.0`. ## Overlays Support for uploading chunks via a CoAP connection with nRF Cloud instead over HTTPS is available via an overlay. To leverage this support, add it to your build with `-DEXTRA_CONF_FILE="overlay-coap.conf"`. Note, uploading over CoAP requires your device to be provisioned in nRF Cloud. See [Nordic's getting started documentation](https://docs.nordicsemi.com/bundle/nrf-cloud/page/GettingStarted.html) for more details. > [!NOTE] > This overlay is currently only supported on NCS >= v2.8.0. ## Testing the Integration Commands to test the integration are exposed under the `mflt` submenu in the CLI ```bash uart:~$ mflt help mflt - Memfault Test Commands Subcommands: clear_core :clear coredump collected export :dump chunks collected by Memfault SDK using https://mflt.io/chunk-data-export get_core :check if coredump is stored and present get_device_info :display device information get_latest_release :checks to see if new ota payload is available post_chunks :Post Memfault data to cloud test :commands to verify memfault data collection (https://mflt.io/mcu-test-commands) ``` ## Adding the Memfault SDK to your Project For more details on how to add the memfault-firmware-sdk to your own nRF Connect SDK based project, follow our step-by-step guide available [here](https://mflt.io/nrf-connect-sdk-integration-guide). ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/boards/nrf9160dk_nrf9160_ns_0_14_0.overlay ================================================ /* Device Tree overlay to enable auto-mounting littlefs in an internal partition, via the Nordic Partition Manager system */ / { fstab { compatible = "zephyr,fstab"; lfs1: lfs1 { compatible = "zephyr,fstab,littlefs"; mount-point = "/lfs1"; partition = <&storage_partition>; automount; read-size = <16>; prog-size = <16>; cache-size = <64>; lookahead-size = <32>; block-cycles = <512>; }; }; }; ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/config/memfault_metrics_heartbeat_config.def ================================================ // Thread stack usage metrics MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_wdt_pct_max, kMemfaultMetricType_Unsigned, 100) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_shell_uart_pct_max, kMemfaultMetricType_Unsigned, 100) ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/config/memfault_reboot_reason_user_config.def ================================================ MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(CustomExpectedReboot) MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(CustomUnexpectedReboot) ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/config/memfault_trace_reason_user_config.def ================================================ // This is where project specific trace reasons should be defined // using the MEMFAULT_TRACE_REASON_DEFINE macro // // For example: // MEMFAULT_TRACE_REASON_DEFINE(your_custom_error_reason) ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/overlay-coap.conf ================================================ # Overlay for selecting CoAP for connecting to Memfault. # Requires NCS >=2.8.0. # Enable CoAP, disable HTTP CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP=y CONFIG_MEMFAULT_HTTP_ENABLE=n # nRF Cloud CoAP (required for Memfault CoAP uploads) CONFIG_NRF_CLOUD=y CONFIG_NRF_CLOUD_COAP=y # Use the modem's internal UUID as the nRF Cloud client ID (for JWT auth) CONFIG_NRF_CLOUD_CLIENT_ID_SRC_INTERNAL_UUID=y CONFIG_MODEM_JWT=y # Required to be able to use the nRF Cloud CoAP connection for FOTA image downloads CONFIG_NRF_CLOUD_FOTA=y CONFIG_DOWNLOADER_TRANSPORT_COAP=y # Ensure Memfault device id matches nRF Cloud client id CONFIG_MEMFAULT_NCS_DEVICE_ID_HW_ID=y CONFIG_HW_ID_LIBRARY_SOURCE_UUID=y # CoAP client (needs more stack and heap than HTTP) CONFIG_COAP_CLIENT_BLOCK_SIZE=1024 CONFIG_COAP_CLIENT_STACK_SIZE=6144 CONFIG_COAP_CLIENT_THREAD_PRIORITY=0 CONFIG_AT_MONITOR_HEAP_SIZE=512 # Required for streaming large FOTA images from nRF Cloud over CoAP CONFIG_HEAP_MEM_POOL_SIZE=12288 CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE=192 CONFIG_NRF_CLOUD_COAP_MAX_USER_OPTIONS=2 CONFIG_COAP_CLIENT_MAX_EXTRA_OPTIONS=3 ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/overlays/ncs-main.conf ================================================ # In 2.9.99, the download_client was refactored as the downloader library CONFIG_DOWNLOADER=y CONFIG_DOWNLOADER_MAX_FILENAME_SIZE=400 CONFIG_DOWNLOADER_STACK_SIZE=1600 ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/overlays/ncs-pre-v2.0.0.conf ================================================ # NRF_MODEM_LIB_ON_INIT() was added in NCS v2.0.0 CONFIG_MEMFAULT_ROOT_CERT_INSTALL_ON_MODEM_LIB_INIT=n ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/overlays/ncs-pre-v2.2.99.conf ================================================ # 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. # Use SPM instead of TFM for Secure Firmware as TFM does not (yet) support forwarding of fault handlers CONFIG_BUILD_WITH_TFM=n # Explicitly set SPM=y because as of nRF Connect >= 2.1.0 it is marked as deprecated and no longer enabled by default CONFIG_SPM=y ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/overlays/ncs-pre-v2.4.0.conf ================================================ CONFIG_LTE_AUTO_INIT_AND_CONNECT=n ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/overlays/ncs-pre-v2.8.0.conf ================================================ # Pre-2.8.0, the AT_SHELL library requires new libc CONFIG_NEWLIB_LIBC=y ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/overlays/ncs-pre-v2.9.99.conf ================================================ # In 2.9.99, the download_client was refactored as the downloader library CONFIG_DOWNLOAD_CLIENT=y CONFIG_DOWNLOAD_CLIENT_STACK_SIZE=1600 CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE=400 CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_1024=y ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/prj.conf ================================================ # project specific configuration settings # Note: See the overlays directory for additional Kconfig variables settings as not all variables can be # set in a single prj.conf for backward compatibility with older versions of the nRF Connect SDK / # Zephyr CONFIG_NETWORKING=y CONFIG_NET_SOCKETS=y CONFIG_NET_NATIVE=n CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_MAIN_STACK_SIZE=4096 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_MODEM_KEY_MGMT=y CONFIG_LTE_LINK_CONTROL=y CONFIG_NRF_MODEM_LIB=y CONFIG_MODEM_INFO=y CONFIG_AT_SHELL=y CONFIG_SHELL=y CONFIG_SHELL_LOG_BACKEND=n # Make it possible to send AT commands ending with a "?" # https://developer.nordicsemi.com/nRF_Connect_SDK/doc/2.4.2/nrf/libraries/modem/at_shell.html CONFIG_SHELL_WILDCARD=y CONFIG_NET_LOG=y CONFIG_NET_SOCKETS_OFFLOAD=y CONFIG_MEMFAULT=y CONFIG_MEMFAULT_NCS_DEVICE_ID_RUNTIME=y # Note: Can optionally be changed to =y to implement # a custom event handler for FOTA events # CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM=n # Enable to demo custom reboot reason capture CONFIG_MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE=y # Required for logging backends to work as expected CONFIG_LOG=y CONFIG_LOG_BACKEND_UART=y # Enable capture of recent logs as part of a coredump CONFIG_MEMFAULT_LOGGING_ENABLE=y # Disable logging of printk, to improve 'mflt export' performance under deferred # logging mode. CONFIG_LOG_PRINTK=n # Enable Bootloader so OTA updates can be performed CONFIG_BOOTLOADER_MCUBOOT=y CONFIG_MCUBOOT_IMG_MANAGER=y # The subsystems we need so OTA payloads can be written to # flash and updated by MCUBoot CONFIG_DFU_TARGET=y CONFIG_DFU_TARGET_MCUBOOT=y CONFIG_IMG_MANAGER=y CONFIG_FLASH=y CONFIG_FLASH_MAP=y CONFIG_STREAM_FLASH=y CONFIG_IMG_ERASE_PROGRESSIVELY=y # For Memfault FOTA, we will use the FOTA_DOWNLOAD API's # from the nRF Connect SDK which depends on the DOWNLOAD_CLIENT CONFIG_FOTA_DOWNLOAD=y # Enable printing of file download progress to console CONFIG_FOTA_DOWNLOAD_PROGRESS_EVT=y CONFIG_WATCHDOG=y # Enable Zephyr runtime asserts CONFIG_ASSERT=y # Enable littlefs on internal flash, to demo the file system utilization metric CONFIG_FILE_SYSTEM=y CONFIG_FILE_SYSTEM_LITTLEFS=y CONFIG_FILE_SYSTEM_SHELL=y # Enable Nordic datetime library, which will get network time from the modem CONFIG_DATE_TIME=y ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/sample.yaml ================================================ sample: description: Memfault SDK example integration built on top of Hello World Sample App with the nRF Connect SDK name: Memfault Demo App ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/main.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(device.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(sys/printk.h) #include MEMFAULT_ZEPHYR_INCLUDE(shell/shell.h) #include #include "memfault/components.h" #include "memfault/ports/zephyr/thread_metrics.h" #include "memfault/ports/zephyr/http.h" #include "memfault/ports/ncs/version.h" #include "memfault/ports/ncs/date_time_callback.h" #include "memfault_demo_app.h" #include #include #if defined(CONFIG_NRF_CLOUD_COAP) #include #include static K_SEM_DEFINE(s_date_time_ready, 0, 1); static void prv_date_time_evt_handler(const struct date_time_evt *evt) { #if defined(CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_DATETIME) // Forward event to Memfault's date/time callback handler, which will update // the system time used for timestamping events and coredumps memfault_zephyr_date_time_evt_handler(evt); #endif // Only signal that date/time is ready on successful time obtain events if ((evt != NULL) && (evt->type != DATE_TIME_NOT_OBTAINED)) { k_sem_give(&s_date_time_ready); } } #endif MEMFAULT_METRICS_DEFINE_THREAD_METRICS( { .thread_name = "idle", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle_pct_max), }, { .thread_name = "sysworkq", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_sysworkq_pct_max), }, { .thread_name = "mflt_upload", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_mflt_upload_pct_max), }, { .thread_name = "🐶 wdt", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_wdt_pct_max), }, { .thread_name = "shell_uart", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_shell_uart_pct_max), } ); #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) { return nrf_modem_lib_init(NORMAL_MODE); } #else /* nRF Connect SDK >= 2.4 */ #include "memfault_ncs.h" #include static int prv_init_modem_lib(void) { return nrf_modem_lib_init(); } #endif #if CONFIG_DFU_TARGET_MCUBOOT #include MEMFAULT_ZEPHYR_INCLUDE(dfu/mcuboot.h) #endif // clang-format on #if defined(CONFIG_MEMFAULT_NCS_DEVICE_ID_RUNTIME) // Since the example app manages when the modem starts/stops, we manually configure the device // serial even when using nRF Connect SDKs including the Memfault integration (by using // CONFIG_MEMFAULT_NCS_DEVICE_ID_RUNTIME) #define IMEI_LEN 15 static char s_device_serial[IMEI_LEN + 1 /* '\0' */] = "unknown"; static int prv_get_imei(char *buf, size_t buf_len) { // use the cached modem info to fetch the IMEI int err = modem_info_init(); if (err != 0) { printk("Modem info Init error: %d\n", err); } else { modem_info_string_get(MODEM_INFO_IMEI, buf, buf_len); } return err; } static void prv_init_device_info(void) { // we'll use the IMEI as the device serial char modem_info[MODEM_INFO_MAX_RESPONSE_SIZE]; int ret = prv_get_imei(modem_info, sizeof(modem_info)); if (ret != 0) { printk("Failed to get IMEI\n\r"); } else { modem_info[IMEI_LEN] = '\0'; strcpy(s_device_serial, modem_info); printk("IMEI: %s\n", s_device_serial); } // register the device id with memfault port so it is used for reporting memfault_ncs_device_id_set(s_device_serial, IMEI_LEN); } #endif int main(void) { printk("Memfault Demo App Started!\n"); memfault_demo_app_watchdog_boot(); #if CONFIG_DFU_TARGET_MCUBOOT if (!boot_is_img_confirmed()) { // Mark the ota image as installed so we don't revert printk("Confirming OTA update\n"); boot_write_img_confirmed(); } #endif int err = prv_init_modem_lib(); if (err) { printk("Failed to initialize modem!"); goto cleanup; } #if defined(CONFIG_MEMFAULT_NCS_DEVICE_ID_RUNTIME) // requires AT modem interface to be up prv_init_device_info(); #endif #if defined(CONFIG_MEMFAULT_HTTP_ENABLE) && \ !defined(CONFIG_MEMFAULT_ROOT_CERT_INSTALL_ON_MODEM_LIB_INIT) // Note: We must provision certs prior to connecting to the LTE network // in order to use them err = memfault_zephyr_port_install_root_certs(); if (err) { goto cleanup; } #endif #if defined(CONFIG_MEMFAULT_HTTP_SOCKET_DISPATCH) // Set the network interface name to use for Memfault HTTP uploads err = memfault_zephyr_port_http_set_interface_name("net0"); if (err) { printk("Failed to set Memfault HTTP network interface name, err %d\n", err); goto cleanup; } #endif #if defined(CONFIG_NRF_CLOUD_COAP) // Register early so we don't miss the time-update event after LTE connects. // JWT authentication (used by nRF Cloud CoAP) requires a valid timestamp. date_time_register_handler(prv_date_time_evt_handler); #endif printk("Waiting for network...\n"); // lte_lc_init_and_connect is deprecated in NCS 2.6 err = #if MEMFAULT_NCS_VERSION_GT(2, 5) lte_lc_connect(); #else lte_lc_init_and_connect(); #endif if (err) { printk("Failed to connect to the LTE network, err %d\n", err); goto cleanup; } printk("OK\n"); #if defined(CONFIG_NRF_CLOUD_COAP) // Wait for the date/time library to sync from the network (up to 10 minutes). // nrf_cloud_coap_connect() uses a JWT signed with the current time; connecting // before the clock is set will result in an authentication failure. printk("Waiting for date/time sync...\n"); k_sem_take(&s_date_time_ready, K_MINUTES(10)); if (!date_time_is_valid()) { printk("Warning: date/time not yet valid, CoAP connect may fail\n"); } err = nrf_cloud_coap_init(); if (err) { printk("Failed to initialize nRF Cloud CoAP client, err %d\n", err); goto cleanup; } err = nrf_cloud_coap_connect(NULL); if (err) { printk("Failed to connect to nRF Cloud via CoAP, err %d\n", err); goto cleanup; } printk("Connected to nRF Cloud via CoAP\n"); #endif cleanup: return err; } static int prv_reboot_custom(const struct shell *shell, size_t argc, char **argv) { if (argc != 2) { shell_print(shell, "Usage: app reboot_custom "); return -1; } if (strcmp(argv[1], "expected") == 0) { MEMFAULT_REBOOT_MARK_RESET_IMMINENT_CUSTOM(CustomExpectedReboot); } else if (strcmp(argv[1], "unexpected") == 0) { MEMFAULT_REBOOT_MARK_RESET_IMMINENT_CUSTOM(CustomUnexpectedReboot); } else { shell_print(shell, "Invalid argument: %s", argv[1]); return -1; } shell_print(shell, "Rebooting with custom reason..."); // Allow the shell output buffer to be flushed before the reboot k_sleep(K_MSEC(100)); memfault_platform_reboot(); return 0; // should be unreachable } #if defined(CONFIG_MEMFAULT_HTTP_SOCKET_DISPATCH) static int prv_set_net_iface(const struct shell *shell, size_t argc, char **argv) { if (argc != 2) { shell_print(shell, "Usage: app set_net_iface "); return -1; } const char *if_name = argv[1]; int err = memfault_zephyr_port_http_set_interface_name(if_name); if (err) { shell_print(shell, "Failed to set Memfault HTTP network interface name, err %d", err); return err; } shell_print(shell, "Set Memfault HTTP network interface name to: %s", if_name); return 0; } #endif SHELL_STATIC_SUBCMD_SET_CREATE( sub_app_cmds, SHELL_CMD(reboot_custom, NULL, "test custom reboot", prv_reboot_custom), #if defined(CONFIG_MEMFAULT_HTTP_SOCKET_DISPATCH) SHELL_CMD(set_net_iface, NULL, "set net iface name", prv_set_net_iface), #endif SHELL_SUBCMD_SET_END /* Array terminated. */ ); SHELL_CMD_REGISTER(app, &sub_app_cmds, "Memfault Demo App Test Commands", NULL); ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/memfault_demo_app.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! Start watchdog subsystem for demo application void memfault_demo_app_watchdog_boot(void); //! Reset watchdog timeout for application void memfault_demo_app_watchdog_feed(void); ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/src/watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Example configuration of Zephyr hardware watchdog with Memfault software watchdog //! port such that a coredump is captured ahead of the hardware watchdog firing // clang-format off #include "memfault/ports/watchdog.h" #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(device.h) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/watchdog.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include "memfault/components.h" #include "memfault/ports/zephyr/version.h" // clang-format on //! Note: The timeout must be large enough to give us enough time to capture a coredump //! before the system resets #define MEMFAULT_WATCHDOG_HW_TIMEOUT_SECS (MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS + 10) #define WDT_MAX_WINDOW (MEMFAULT_WATCHDOG_HW_TIMEOUT_SECS * 1000) #define WATCHDOG_TASK_STACK_SIZE 512 K_THREAD_STACK_DEFINE(s_wdt_task_stack_area, WATCHDOG_TASK_STACK_SIZE); struct k_thread wdt_thread; #if MEMFAULT_ZEPHYR_VERSION_GT(3, 1) // Between Zephyr 3.1 and 3.2, "label" properties in DTS started to get phased out: // https://github.com/zephyrproject-rtos/zephyr/pull/48360 // // This breaks support for accessing device tree information via device_get_binding / DT_LABEL // 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)); #else static const struct device *s_wdt = NULL; #endif //! 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) static int s_wdt_channel_id = -1; void memfault_demo_app_watchdog_feed(void) { if ((s_wdt == NULL) || (s_wdt_channel_id < 0)) { return; } wdt_feed(s_wdt, s_wdt_channel_id); memfault_software_watchdog_feed(); } //! A basic watchdog implementation //! //! Once Zephyr has a Software & Task watchdog in place, the example will be updated to make use of //! that For more info about watchdog setup in general, see https://mflt.io/root-cause-watchdogs static void prv_wdt_task(void *arg1, void *arg2, void *arg3) { while (1) { k_sleep(K_SECONDS(1)); memfault_demo_app_watchdog_feed(); } } void memfault_demo_app_watchdog_boot(void) { MEMFAULT_LOG_DEBUG("Initializing WDT Subsystem"); #if MEMFAULT_ZEPHYR_VERSION_GT(3, 1) if (!device_is_ready(s_wdt)) { printk("Watchdog device not ready"); return; } #else // Zephyr <= 3.1 s_wdt = device_get_binding(WDT_DEV_NAME); if (s_wdt == NULL) { printk("No WDT device available\n"); return; } #endif struct wdt_timeout_cfg wdt_config = { /* Reset SoC when watchdog timer expires. */ .flags = WDT_FLAG_RESET_SOC, /* Expire watchdog after max window */ .window.min = 0U, .window.max = WDT_MAX_WINDOW, }; s_wdt_channel_id = wdt_install_timeout(s_wdt, &wdt_config); if (s_wdt_channel_id < 0) { MEMFAULT_LOG_ERROR("Watchdog install error, rv=%d", s_wdt_channel_id); return; } const uint8_t options = WDT_OPT_PAUSE_HALTED_BY_DBG; int err = wdt_setup(s_wdt, options); if (err < 0) { MEMFAULT_LOG_ERROR("Watchdog setup error, rv=%d", err); return; } // start a low priority task that feeds the watchdog. // We make it the lowest priority so a hot loop in any task above will // cause the watchdog to not be fed memfault_software_watchdog_enable(); k_thread_create(&wdt_thread, s_wdt_task_stack_area, K_THREAD_STACK_SIZEOF(s_wdt_task_stack_area), prv_wdt_task, NULL, NULL, NULL, K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT); k_thread_name_set(&wdt_thread, "🐶 wdt"); } ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/submanifests/.gitkeep ================================================ ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/sysbuild.conf ================================================ SB_CONFIG_BOOTLOADER_MCUBOOT=y ================================================ FILE: examples/nrf-connect-sdk/nrf9160/memfault_demo_app/west.yml ================================================ manifest: projects: - name: nrf url: https://github.com/nrfconnect/sdk-nrf revision: f6b4550c9c384c93c390c33cad5edd3f4431d949 # v3.2.4 + nrf_cloud_coap_get_user_options() import: true import: path-blocklist: modules/lib/memfault-firmware-sdk name-blocklist: # unused, save the download - matter - name: memfault-firmware-sdk url: https://github.com/memfault/memfault-firmware-sdk path: modules/lib/memfault-firmware-sdk revision: master self: path: memfault_demo_app import: submanifests ================================================ FILE: examples/nrf5/README.md ================================================ # Memfault for nRF5_SDK This folder contains support files to make it easy to integrate Memfault into a nRF5 SDK based project. The demo app is tested on the [nRF52840 DK] -- PCA10056 evaluation board. The instructions below assume this development board is being used and that the [nRF Command Line Tools] have been installed. The demo app is tested with v15.2.0 of the [nRF52 SDK], but should integrate in a similar manner with other versions as well. ## Getting started Make sure you have read the instructions in the `README.md` in the root of the SDK and performed the installation steps that are mentioned there. ### Adding Memfault to the nRF5 SDK 1. Download the [v15.2.0 nRF5 SDK] 2. Delete the nrf5_sdk directory (if present) and create a symlink between that and the nRF5 SDK. For example ```bash $ rm -rf nrf5_sdk $ ln -s path/to/nrf5_sdk/ nrf5_sdk ``` ### Modifications to the nRF5 SDK The nRF5 SDK defines a couple assert macros that Memfault needs to override in order to capture a coredump at the exact location an assert is hit. To do this, the `app_error.h` is overridden at `libraries/memfault/platform_reference_impl/sdk_overrides/app_error.h`. You simply need to move the original file from the sdk to pick up this definition. Otherwise, you will see an error like: ``` ../../libraries/memfault/platform_reference_impl/sdk_overrides/app_error.h:50:2: error: #error "Please rename or remove components/libraries/util/app_error.h from the NRF SDK, as the Memfault SDK needs to override this header file." ``` Example fix: ```bash $ mv nrf5_sdk/components/libraries/util/app_error.h nrf5_sdk/components/libraries/util/app_error.h.overriden ``` ### Update Demo App with your Memfault Project Key A Project Key will need to be baked into the demo app to enable it to communicate with Memfault's web services. Go to https://app.memfault.com/, navigate to the project you want to use and select 'Settings'. Copy the 'Project Key' and paste it into `apps/memfault_demo_app/src/cli.c`, replacing `` with your Project Key. ### Building the demo app You should now be able to compile the demo app with the Memfault components included! To build: using pyinvoke: ```bash $ invoke nrf.build ``` or ```bash $ cd /path/to/examples/nrf5/apps/memfault_demo_app/ $ export GNU_INSTALL_ROOT=/path/to/arm_toolchain_dir && make ``` ### Flashing the demo app _NOTE_: If you were previously using the dev board for something else, it's recommended to put it into a clean state by performing a full erase using pyinvoke: ```bash $ invoke nrf.eraseflash ``` or ```bash nrfutil device erase --all ``` Once an erase has been performed you can flash the compiled demo app: using pyinvoke: ```bash # In one terminal start a gdbserver $ invoke nrf.gdbserver # In another terminal flash the binary $ invoke nrf.flash ``` or ```bash # In one terminal start a gdb server JLinkGDBServer -if swd -device nRF52840_xxAA -speed auto -port 2331 # In another terminal flash the binary $ cd /path/to/examples/nrf5/apps/memfault_demo_app/ $ make flash_softdevice $ /path/to/arm-none-eabi-gdb[-py] --eval-command="target remote localhost:2331" --ex="mon reset" --ex="load" --ex="mon reset" --se=/path/to/examples/nrf5/apps/memfault_demo_app/build/memfault_demo_app_nrf52840_s140.out ``` once the flash has completed you can start the application by typing `continue` in the gdb console ### Attaching the debug console (via SEGGER RTT) You need to have started the GDBServer as described in the Flashing the demo steps. Then from another terminal: using pyinvoke: ```bash $ invoke nrf.console ``` or ```bash JLinkRTTClient -LocalEcho Off ``` You can now type commands from the [Memfault demo CLI](https://mflt.io/demo-cli). To get started, run `help` to see short descriptions of each command. ## Using the demo app The demo app is a simple console based app that has commands to cause a crash in several ways. Upon crashing, the `memfault/panics` component of the Memfault SDK will capture a coredump and save it to the internal device flash. For the purposes of this demo, once a coredump is captured, it can be dumped out over the console and posted to the Memfault web services. In a real world environment, the data can be sent over BLE using a Gatt Service to a gateway device (i.e a mobile phone) and posted to the Memfault web services in the same way. Let's walk through the coredump process step by step: ### Checking the device info As a sanity check, let's request the device info from the debug console, enter `get_device_info` and press enter: ``` > get_device_info app: MFLT: S/N: C9DB1227DEAE6A52 app: MFLT: SW type: nrf-main app: MFLT: SW version: 1.0.0 app: MFLT: HW version: nrf-proto ``` In the platform reference implementation for nRF, the hardware version is hard-coded to `nrf-proto`, software type is hard-coded to `nrf-main` and the NRF5 `DEVICEID` is used as serial number. You can change this to match your requirements (see `libraries/memfault/platform_reference_impl/memfault_platform_device_info.c`). ### Causing a crash Command `test_hardfault` will trigger a hard fault due to a bad instruction fetch at a non-existing address, `0xbadcafe`. ``` > test_hardfault ... etc ... ``` Upon crashing, the coredump will be written to internal NOR flash. Note this can take a up to 15 seconds. Once done, you should see gdb hit a breakpoint and then you can `next` through it to reboot: ``` Program received signal SIGTRAP, Trace/breakpoint trap. memfault_platform_halt_if_debugging () at ../../libraries/memfault/platform_reference_impl/memfault_platform_core.c:13 13 NRF_BREAKPOINT_COND; (gdb) next memfault_platform_reboot () at ../../libraries/memfault/platform_reference_impl/memfault_platform_core.c:19 19 NVIC_SystemReset(); (gdb) continue Continuing. ``` To check whether the coredump has been captured, try running the `get_core` command: ``` rtt_cli:~$ get_core app: MFLT: Has coredump with size: 8448 ``` This confirms that a coredump of 8448 bytes (the entire space allocated for the stack) has been captured to internal flash. ### Posting the coredump to Memfault for analysis #### Uploading Symbols Memfault needs the symbols for the firmware in order to analyze the coredump. The nRF SDK demo app symbol file can be found at: `/path/to/examples/nrf5/apps/memfault_demo_app/build/memfault_demo_app_nrf52840_s140.out` This ELF file contains the symbols (debug information) amongst other things. [More information on Build Ids and uploading Symbol Files can be found here](https://mflt.io/symbol-file-build-ids). #### Post coredump In a production environment the collected coredump can be posted to Memfault's services via a gateway device with a connection to the internet. For example, the data can be sent over BLE to a mobile application which will do the post. Memfault has SDKs for different types of gateways (i.e iOS, Android) to re-assemble data sent over a BLE link and post. If you are interested in these implementations, please don't hesitate to reach out! For the purposes of this demo, we will just grab the core information from the cli using the `export` command. It should look something like: ``` rtt_cli:~$ export app: MFLT: MC:CAKmAgIDAQpobnJmLW1haW4JbDEuMC4wKzU1ZmMxMwZocGNhMTAwNTYEowEIBAQFAH/0: ``` You can copy & paste this output into the "Chunks Debug" view in the Memfault UI or upload using the [desktop CLI tool](https://mflt.io/chunk-data-export). The coredump is now being processed by Memfault's services and will show up shortly under Issues. If it does not, take a look at the FAQ in the `README.md` in the root of the SDK. ### Clearing a coredump New coredumps are only written if the coredump storage area is not already occupied. Typically coredumps are cleared once they have been posted to the memfault cloud by using the `memfault_platform_coredump_storage_clear` function. You can invoke this command from the cli by running `clear_core`. For more details on how to use the CLI to explore each of Memfault's subsystems, see the [Memfault docs](https://mflt.io/demo-cli). # Integrating into existing NRF52 projects Adding Memfault to existing NRF52 projects is relatively simple. The Memfault SDK comes with an example of what adding Memfault to an nRF SDK Makefile would look like and has been tested with nRF SDK v15.2.0. ## Basic integration steps - Add Memfault component dependencies to your project. An example of how you can add Memfault to a typical nRF52 SDK based project can be found in the top of the `apps/memfault_demo_app/Makefile` in the section titled "Memfault SDK integration". The Makefile commands: - Collect the Memfault SDK components under `components`. The Memfault SDK has libraries for the features being used under `components/*`. The `demo` component used in the example app is not strictly necessary and should only be added if you are interested in adding the same cli commands to your project - Picks up the include paths for each component and adds them to the nRF5 build system - Picks up platform specific reference implementations needed for the Memfault SDK - Add configuration and initialization code: ``` // main.c int main(void) { [... other initialization code ...] memfault_platform_boot(); [... application code ...] ``` - If your project doesn't already include them you may need to add a few files from the nRF SDK to your project such as `modules/nrfx/hal/nrf_nvmc.c` which is used by the reference platform implementation - NOTE: The component `libraries/memfault/platform_reference_impl` contain implementations of platform dependencies that the Memfault SDK relies upon. They serve as a good example and starting point, but it's very likely you will need to customize some of the files to your use case. For example, by the software type defaults to `"nrf-main"`, the software version defaults to `"1.0.0"` and the hardware version defaults to `"nrf-proto"`. This needs to be changed to use the mechanisms you already have in place in your project to get the software type, software version and hardware version. Please refer to the `memfault_sdk/components/*/README.md` files to learn more about the customization options for each component. - To save coredumps in internal flash, you will need to budget some space to store them. You can find more details in the top of `libraries/memfault/platform_reference_impl/memfault_platform_coredump.c` but basically it just involves adding a new section to your .ld script. You can also configure how much of ram you want to capture in this file. The demo app is only collecting the active stack using the `MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY` define but if space is available its great to capture all of RAM for the best amount of detail when doing post-mortem analysis [v15.2.0 nrf5 sdk]: https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v15.x.x/nRF5_SDK_15.2.0_9412b96.zip [nrf52840 dk]: https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK [nrf52 sdk]: https://developer.nordicsemi.com/nRF5_SDK/ [http api to upload symbols files]: https://docs.memfault.com/?version=latest#ca2a72d2-69ef-4703-98bf-60621738091a [nrf command line tools]: https://www.nordicsemi.com/Software-and-tools/Development-Tools/nRF-Command-Line-Tools ================================================ FILE: examples/nrf5/apps/memfault_demo_app/.gitignore ================================================ /build /fw_client_build ================================================ FILE: examples/nrf5/apps/memfault_demo_app/Makefile ================================================ PROJECT_NAME := memfault_demo_app APP_TARGET := memfault_demo_app_nrf52840_s140 TARGETS := $(APP_TARGET) OUTPUT_DIRECTORY := build SDK_ROOT := ../../nrf5_sdk PROJ_SRC_DIR := src/ # # Memfault SDK integration # # Pick up the MEMFAULT_PORT_ROOT := $(abspath third_party/memfault) # Note: Would typically be $(MEMFAULT_PORT_ROOT)/memfault-firmware-sdk MEMFAULT_SDK_ROOT := $(abspath ../../../../) MEMFAULT_COMPONENTS := core demo panics http util metrics include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk MEMFAULT_NRF5_SDK_PORT_ROOT := $(MEMFAULT_SDK_ROOT)/ports/nrf5_sdk SRC_FILES += \ $(MEMFAULT_COMPONENTS_SRCS) \ $(MEMFAULT_NRF5_SDK_PORT_ROOT)/memfault_platform_metrics.c \ $(MEMFAULT_NRF5_SDK_PORT_ROOT)/nrf5_coredump_regions.c \ $(MEMFAULT_NRF5_SDK_PORT_ROOT)/nrf5_coredump_storage.c \ $(MEMFAULT_NRF5_SDK_PORT_ROOT)/resetreas_reboot_tracking.c \ $(MEMFAULT_NRF5_SDK_PORT_ROOT)/software_watchdog.c \ $(MEMFAULT_PORT_ROOT)/memfault_platform_port.c INC_FOLDERS += \ $(MEMFAULT_COMPONENTS_INC_FOLDERS) \ $(MEMFAULT_SDK_ROOT)/ports/include \ $(MEMFAULT_PORT_ROOT) \ $(MEMFAULT_PORT_ROOT)/sdk_overrides # Add the gnu build id to the elf for better tracking between elf and binary # https://interrupt.memfault.com/blog/gnu-build-id-for-firmware LDFLAGS += -Wl,--build-id # # Typical look of # $(OUTPUT_DIRECTORY)/$(APP_TARGET).out: \ LINKER_SCRIPT := memfault_demo_app_nrf52.ld # Source files common to all targets SRC_FILES += \ $(PROJ_SRC_DIR)/cli.c \ $(PROJ_SRC_DIR)/main.c \ $(SDK_ROOT)/components/boards/boards.c \ $(SDK_ROOT)/components/libraries/atomic/nrf_atomic.c \ $(SDK_ROOT)/components/libraries/atomic_flags/nrf_atflags.c \ $(SDK_ROOT)/components/libraries/balloc/nrf_balloc.c \ $(SDK_ROOT)/components/libraries/bsp/bsp.c \ $(SDK_ROOT)/components/libraries/cli/nrf_cli.c \ $(SDK_ROOT)/components/libraries/cli/rtt/nrf_cli_rtt.c \ $(SDK_ROOT)/components/libraries/experimental_section_vars/nrf_section_iter.c \ $(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_rtt.c \ $(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_serial.c \ $(SDK_ROOT)/components/libraries/log/src/nrf_log_default_backends.c \ $(SDK_ROOT)/components/libraries/log/src/nrf_log_frontend.c \ $(SDK_ROOT)/components/libraries/log/src/nrf_log_str_formatter.c \ $(SDK_ROOT)/components/libraries/memobj/nrf_memobj.c \ $(SDK_ROOT)/components/libraries/pwr_mgmt/nrf_pwr_mgmt.c \ $(SDK_ROOT)/components/libraries/queue/nrf_queue.c \ $(SDK_ROOT)/components/libraries/ringbuf/nrf_ringbuf.c \ $(SDK_ROOT)/components/libraries/scheduler/app_scheduler.c \ $(SDK_ROOT)/components/libraries/timer/app_timer.c \ $(SDK_ROOT)/components/libraries/util/app_util_platform.c \ $(SDK_ROOT)/components/libraries/util/nrf_assert.c \ $(SDK_ROOT)/components/softdevice/common/nrf_sdh.c \ $(SDK_ROOT)/components/softdevice/common/nrf_sdh_soc.c \ $(SDK_ROOT)/external/fprintf/nrf_fprintf.c \ $(SDK_ROOT)/external/fprintf/nrf_fprintf_format.c \ $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT.c \ $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_Syscalls_GCC.c \ $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_printf.c \ $(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_clock.c \ $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_clock.c \ $(SDK_ROOT)/modules/nrfx/drivers/src/prs/nrfx_prs.c \ $(SDK_ROOT)/modules/nrfx/hal/nrf_nvmc.c \ $(SDK_ROOT)/modules/nrfx/mdk/gcc_startup_nrf52840.S \ $(SDK_ROOT)/modules/nrfx/mdk/system_nrf52840.c \ $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_wdt.c \ $(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_rtc.c \ # Include folders common to all targets INC_FOLDERS += \ config \ $(SDK_ROOT)/components \ $(SDK_ROOT)/components/boards \ $(SDK_ROOT)/components/libraries/atomic \ $(SDK_ROOT)/components/libraries/atomic_fifo \ $(SDK_ROOT)/components/libraries/atomic_flags \ $(SDK_ROOT)/components/libraries/balloc \ $(SDK_ROOT)/components/libraries/bsp \ $(SDK_ROOT)/components/libraries/button \ $(SDK_ROOT)/components/libraries/cli \ $(SDK_ROOT)/components/libraries/cli/rtt \ $(SDK_ROOT)/components/libraries/delay \ $(SDK_ROOT)/components/libraries/experimental_section_vars \ $(SDK_ROOT)/components/libraries/log \ $(SDK_ROOT)/components/libraries/log/src \ $(SDK_ROOT)/components/libraries/memobj \ $(SDK_ROOT)/components/libraries/mpu \ $(SDK_ROOT)/components/libraries/mutex \ $(SDK_ROOT)/components/libraries/pwr_mgmt \ $(SDK_ROOT)/components/libraries/queue \ $(SDK_ROOT)/components/libraries/ringbuf \ $(SDK_ROOT)/components/libraries/scheduler \ $(SDK_ROOT)/components/libraries/stack_guard \ $(SDK_ROOT)/components/libraries/strerror \ $(SDK_ROOT)/components/libraries/svc \ $(SDK_ROOT)/components/libraries/timer \ $(SDK_ROOT)/components/libraries/util \ $(SDK_ROOT)/components/softdevice/common \ $(SDK_ROOT)/components/softdevice/s140/headers \ $(SDK_ROOT)/components/softdevice/s140/headers/nrf52 \ $(SDK_ROOT)/components/toolchain/cmsis/include \ $(SDK_ROOT)/external/fprintf \ $(SDK_ROOT)/external/segger_rtt \ $(SDK_ROOT)/integration/nrfx \ $(SDK_ROOT)/integration/nrfx/legacy \ $(SDK_ROOT)/modules/nrfx \ $(SDK_ROOT)/modules/nrfx/drivers/include \ $(SDK_ROOT)/modules/nrfx/hal \ $(SDK_ROOT)/modules/nrfx/mdk \ # Libraries common to all targets LIB_FILES += \ # Optimization flags OPT = -O3 -g3 # NB: Compact logging utilizes GNU extension for token paste operator CFLAGS += -std=gnu11 # C flags common to all targets CFLAGS += $(OPT) CFLAGS += -DBOARD_PCA10056 CFLAGS += -DCONFIG_GPIO_AS_PINRESET CFLAGS += -DFLOAT_ABI_HARD CFLAGS += -DNRF52840_XXAA CFLAGS += -DNRF_SD_BLE_API_VERSION=6 CFLAGS += -DS140 CFLAGS += -DSOFTDEVICE_PRESENT CFLAGS += -DSWI_DISABLE0 CFLAGS += -mcpu=cortex-m4 CFLAGS += -mthumb -mabi=aapcs CFLAGS += -Wall -Werror -Wno-error=array-bounds CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 # keep every function in a separate section, this allows linker to discard unused ones CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing CFLAGS += -fno-builtin -fshort-enums # C++ flags common to all targets CXXFLAGS += $(OPT) # Assembler flags common to all targets ASMFLAGS += -g3 ASMFLAGS += -mcpu=cortex-m4 ASMFLAGS += -mthumb -mabi=aapcs ASMFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 ASMFLAGS += -DBOARD_PCA10056 ASMFLAGS += -DCONFIG_GPIO_AS_PINRESET ASMFLAGS += -DFLOAT_ABI_HARD ASMFLAGS += -DNRF52840_XXAA ASMFLAGS += -DNRF_SD_BLE_API_VERSION=6 ASMFLAGS += -DS140 ASMFLAGS += -DSOFTDEVICE_PRESENT ASMFLAGS += -DSWI_DISABLE0 # Linker flags LDFLAGS += $(OPT) LDFLAGS += -mthumb -mabi=aapcs -L$(SDK_ROOT)/modules/nrfx/mdk -T$(LINKER_SCRIPT) LDFLAGS += -mcpu=cortex-m4 LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 # let linker dump unused sections LDFLAGS += -Wl,--gc-sections # use newlib in nano version LDFLAGS += --specs=nano.specs $(APP_TARGET): CFLAGS += -D__HEAP_SIZE=8192 $(APP_TARGET): CFLAGS += -D__STACK_SIZE=8192 $(APP_TARGET): ASMFLAGS += -D__HEAP_SIZE=8192 $(APP_TARGET): ASMFLAGS += -D__STACK_SIZE=8192 # Add standard libraries at the very end of the linker input, after all objects # that may need symbols provided by these libraries. LIB_FILES += -lc -lnosys -lm .PHONY: default help # Default target - first one defined default: $(APP_TARGET) # Print all targets that can be built help: @echo following targets are available: @echo $(APP_TARGET) @echo flash_softdevice @echo flash - flashing binary TEMPLATE_PATH := $(SDK_ROOT)/components/toolchain/gcc include $(TEMPLATE_PATH)/Makefile.common $(foreach target, $(TARGETS), $(call define_target, $(target))) .PHONY: flash flash_softdevice erase # Flash the program flash: default @echo Flashing: $(OUTPUT_DIRECTORY)/$(APP_TARGET).hex nrfutil device program --options chip_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE --firmware $(OUTPUT_DIRECTORY)/$(APP_TARGET).hex nrfutil device reset --reset-kind RESET_PIN # Flash softdevice flash_softdevice: @echo Flashing: s140_nrf52_6.1.0_softdevice.hex nrfutil device program --options chip_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE --firmware $(SDK_ROOT)/components/softdevice/s140/hex/s140_nrf52_6.1.0_softdevice.hex nrfutil device reset --reset-kind RESET_PIN erase: nrfutil device erase --all ================================================ FILE: examples/nrf5/apps/memfault_demo_app/config/sdk_config.h ================================================ /** * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef SDK_CONFIG_H #define SDK_CONFIG_H // <<< Use Configuration Wizard in Context Menu >>>\n #ifdef USE_APP_CONFIG #include "app_config.h" #endif // A tag identifying the SoftDevice BLE configuration // NB: I'm still not entirely certain what this controls exactly but many of the soft device APIs // need a reference to the config ID #define APP_BLE_CONN_CFG_TAG 1 // Central Support #define NRF_SDH_BLE_CENTRAL_LINK_COUNT 1 #define PM_CENTRAL_ENABLED 1 #define BLE_DB_DISCOVERY_ENABLED 1 // LE Scanning #define NRF_BLE_SCAN_BUFFER 31 #define NRF_BLE_SCAN_OBSERVER_PRIO 1 #define NRF_BLE_SCAN_ENABLED 1 #define NRF_BLE_SCAN_SCAN_INTERVAL 160 #define NRF_BLE_SCAN_SCAN_WINDOW 80 #define NRF_BLE_SCAN_SCAN_DURATION 0 #define NRF_BLE_SCAN_SUPERVISION_TIMEOUT 4000 #define NRF_BLE_SCAN_MIN_CONNECTION_INTERVAL 7.5 #define NRF_BLE_SCAN_MAX_CONNECTION_INTERVAL 30 #define NRF_BLE_SCAN_SLAVE_LATENCY 0 // Overrides required for enabling RTT CLI #define NRF_CLI_RTT_ENABLED 1 #define NRF_CLI_RTT_TERMINAL_ID 0 #define NRF_CLI_LOG_BACKEND 1 #define NRF_CLI_ENABLED 1 #define NRF_QUEUE_ENABLED 1 #define NRF_CLI_RTT_TX_RETRY_CNT 5 #define NRF_CLI_RTT_TX_RETRY_DELAY_MS 1000 #define NRF_CLI_VT100_COLORS_ENABLED 0 // // Hardware & Software Watchdog configuration // #define RTC_ENABLED 1 #define RTC2_ENABLED 1 #define WDT_ENABLED 1 // Note We've provisioned 256kB (64 pages) for coredumps and per Section "4.3.10 Electrical // specification" of the "nRF52840 Product Specification" manual, the maximum erase time of a page // is 85ms. This means we need 64 * 85ms = 5440ms to erase all flash pages. Let's use a watchdog // timeout above that so we do not have to feed the watchdog from the fault handlers. #define WDT_CONFIG_RELOAD_VALUE 10000 /* ms */ // Board Support //========================================================== // BSP_BTN_BLE_ENABLED - bsp_btn_ble - Button Control for BLE #ifndef BSP_BTN_BLE_ENABLED #define BSP_BTN_BLE_ENABLED 1 #endif // //========================================================== // nRF_BLE //========================================================== // BLE_ADVERTISING_ENABLED - ble_advertising - Advertising module #ifndef BLE_ADVERTISING_ENABLED #define BLE_ADVERTISING_ENABLED 1 #endif // BLE_DTM_ENABLED - ble_dtm - Module for testing RF/PHY using DTM commands #ifndef BLE_DTM_ENABLED #define BLE_DTM_ENABLED 0 #endif // BLE_RACP_ENABLED - ble_racp - Record Access Control Point library #ifndef BLE_RACP_ENABLED #define BLE_RACP_ENABLED 0 #endif // NRF_BLE_CONN_PARAMS_ENABLED - ble_conn_params - Initiating and executing a connection // parameters negotiation procedure //========================================================== #ifndef NRF_BLE_CONN_PARAMS_ENABLED #define NRF_BLE_CONN_PARAMS_ENABLED 1 #endif // NRF_BLE_CONN_PARAMS_MAX_SLAVE_LATENCY_DEVIATION - The largest acceptable deviation in slave // latency. The largest deviation (+ or -) from the requested slave latency that will not be // renegotiated. #ifndef NRF_BLE_CONN_PARAMS_MAX_SLAVE_LATENCY_DEVIATION #define NRF_BLE_CONN_PARAMS_MAX_SLAVE_LATENCY_DEVIATION 499 #endif // NRF_BLE_CONN_PARAMS_MAX_SUPERVISION_TIMEOUT_DEVIATION - The largest acceptable deviation (in // 10 ms units) in supervision timeout. The largest deviation (+ or -, in 10 ms units) from the // requested supervision timeout that will not be renegotiated. #ifndef NRF_BLE_CONN_PARAMS_MAX_SUPERVISION_TIMEOUT_DEVIATION #define NRF_BLE_CONN_PARAMS_MAX_SUPERVISION_TIMEOUT_DEVIATION 65535 #endif // // NRF_BLE_GATT_ENABLED - nrf_ble_gatt - GATT module #ifndef NRF_BLE_GATT_ENABLED #define NRF_BLE_GATT_ENABLED 1 #endif // NRF_BLE_QWR_ENABLED - nrf_ble_qwr - Queued writes support module (prepare/execute write) //========================================================== #ifndef NRF_BLE_QWR_ENABLED #define NRF_BLE_QWR_ENABLED 1 #endif // NRF_BLE_QWR_MAX_ATTR - Maximum number of attribute handles that can be registered. This // number must be adjusted according to the number of attributes for which Queued Writes will be // enabled. If it is zero, the module will reject all Queued Write requests. #ifndef NRF_BLE_QWR_MAX_ATTR #define NRF_BLE_QWR_MAX_ATTR 0 #endif // // PEER_MANAGER_ENABLED - peer_manager - Peer Manager //========================================================== #ifndef PEER_MANAGER_ENABLED #define PEER_MANAGER_ENABLED 1 #endif // PM_MAX_REGISTRANTS - Number of event handlers that can be registered. #ifndef PM_MAX_REGISTRANTS #define PM_MAX_REGISTRANTS 3 #endif // PM_FLASH_BUFFERS - Number of internal buffers for flash operations. // Decrease this value to lower RAM usage. #ifndef PM_FLASH_BUFFERS #define PM_FLASH_BUFFERS 4 #endif // PM_CENTRAL_ENABLED - Enable/disable central-specific Peer Manager functionality. // Enable/disable central-specific Peer Manager functionality. #ifndef PM_CENTRAL_ENABLED #define PM_CENTRAL_ENABLED 0 #endif // PM_SERVICE_CHANGED_ENABLED - Enable/disable the service changed management for GATT server // in Peer Manager. // If not using a GATT server, or using a server wihout a service changed characteristic, // disable this to save code space. #ifndef PM_SERVICE_CHANGED_ENABLED #define PM_SERVICE_CHANGED_ENABLED 1 #endif // PM_PEER_RANKS_ENABLED - Enable/disable the peer rank management in Peer Manager. // Set this to false to save code space if not using the peer rank API. #ifndef PM_PEER_RANKS_ENABLED #define PM_PEER_RANKS_ENABLED 1 #endif // PM_LESC_ENABLED - Enable/disable LESC support in Peer Manager. // If set to true, you need to call nrf_ble_lesc_request_handler() in the main loop to respond // to LESC-related BLE events. If LESC support is not required, set this to false to save code // space. #ifndef PM_LESC_ENABLED #define PM_LESC_ENABLED 0 #endif // PM_RA_PROTECTION_ENABLED - Enable/disable protection against repeated pairing attempts in // Peer Manager. //========================================================== #ifndef PM_RA_PROTECTION_ENABLED #define PM_RA_PROTECTION_ENABLED 0 #endif // PM_RA_PROTECTION_TRACKED_PEERS_NUM - Maximum number of peers whose authorization status can // be tracked. #ifndef PM_RA_PROTECTION_TRACKED_PEERS_NUM #define PM_RA_PROTECTION_TRACKED_PEERS_NUM 8 #endif // PM_RA_PROTECTION_MIN_WAIT_INTERVAL - Minimum waiting interval (in ms) before a new pairing // attempt can be initiated. #ifndef PM_RA_PROTECTION_MIN_WAIT_INTERVAL #define PM_RA_PROTECTION_MIN_WAIT_INTERVAL 4000 #endif // PM_RA_PROTECTION_MAX_WAIT_INTERVAL - Maximum waiting interval (in ms) before a new pairing // attempt can be initiated. #ifndef PM_RA_PROTECTION_MAX_WAIT_INTERVAL #define PM_RA_PROTECTION_MAX_WAIT_INTERVAL 64000 #endif // PM_RA_PROTECTION_REWARD_PERIOD - Reward period (in ms). // The waiting interval is gradually decreased when no new failed pairing attempts are made // during reward period. #ifndef PM_RA_PROTECTION_REWARD_PERIOD #define PM_RA_PROTECTION_REWARD_PERIOD 10000 #endif // // PM_HANDLER_SEC_DELAY_MS - Delay before starting security. // This might be necessary for interoperability reasons, especially as peripheral. #ifndef PM_HANDLER_SEC_DELAY_MS #define PM_HANDLER_SEC_DELAY_MS 0 #endif // // //========================================================== // nRF_BLE_Services //========================================================== // BLE_ANCS_C_ENABLED - ble_ancs_c - Apple Notification Service Client #ifndef BLE_ANCS_C_ENABLED #define BLE_ANCS_C_ENABLED 0 #endif // BLE_ANS_C_ENABLED - ble_ans_c - Alert Notification Service Client #ifndef BLE_ANS_C_ENABLED #define BLE_ANS_C_ENABLED 0 #endif // BLE_BAS_C_ENABLED - ble_bas_c - Battery Service Client #ifndef BLE_BAS_C_ENABLED #define BLE_BAS_C_ENABLED 0 #endif // BLE_BAS_ENABLED - ble_bas - Battery Service //========================================================== #ifndef BLE_BAS_ENABLED #define BLE_BAS_ENABLED 1 #endif // BLE_BAS_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef BLE_BAS_CONFIG_LOG_ENABLED #define BLE_BAS_CONFIG_LOG_ENABLED 0 #endif // BLE_BAS_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef BLE_BAS_CONFIG_LOG_LEVEL #define BLE_BAS_CONFIG_LOG_LEVEL 3 #endif // BLE_BAS_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef BLE_BAS_CONFIG_INFO_COLOR #define BLE_BAS_CONFIG_INFO_COLOR 0 #endif // BLE_BAS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef BLE_BAS_CONFIG_DEBUG_COLOR #define BLE_BAS_CONFIG_DEBUG_COLOR 0 #endif // // // BLE_CSCS_ENABLED - ble_cscs - Cycling Speed and Cadence Service #ifndef BLE_CSCS_ENABLED #define BLE_CSCS_ENABLED 0 #endif // BLE_CTS_C_ENABLED - ble_cts_c - Current Time Service Client #ifndef BLE_CTS_C_ENABLED #define BLE_CTS_C_ENABLED 0 #endif // BLE_DIS_ENABLED - ble_dis - Device Information Service #ifndef BLE_DIS_ENABLED #define BLE_DIS_ENABLED 1 #endif // BLE_GLS_ENABLED - ble_gls - Glucose Service #ifndef BLE_GLS_ENABLED #define BLE_GLS_ENABLED 0 #endif // BLE_HIDS_ENABLED - ble_hids - Human Interface Device Service #ifndef BLE_HIDS_ENABLED #define BLE_HIDS_ENABLED 0 #endif // BLE_HRS_C_ENABLED - ble_hrs_c - Heart Rate Service Client #ifndef BLE_HRS_C_ENABLED #define BLE_HRS_C_ENABLED 0 #endif // BLE_HRS_ENABLED - ble_hrs - Heart Rate Service #ifndef BLE_HRS_ENABLED #define BLE_HRS_ENABLED 0 #endif // BLE_HTS_ENABLED - ble_hts - Health Thermometer Service #ifndef BLE_HTS_ENABLED #define BLE_HTS_ENABLED 1 #endif // BLE_IAS_C_ENABLED - ble_ias_c - Immediate Alert Service Client #ifndef BLE_IAS_C_ENABLED #define BLE_IAS_C_ENABLED 0 #endif // BLE_IAS_ENABLED - ble_ias - Immediate Alert Service //========================================================== #ifndef BLE_IAS_ENABLED #define BLE_IAS_ENABLED 0 #endif // BLE_IAS_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef BLE_IAS_CONFIG_LOG_ENABLED #define BLE_IAS_CONFIG_LOG_ENABLED 0 #endif // BLE_IAS_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef BLE_IAS_CONFIG_LOG_LEVEL #define BLE_IAS_CONFIG_LOG_LEVEL 3 #endif // BLE_IAS_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef BLE_IAS_CONFIG_INFO_COLOR #define BLE_IAS_CONFIG_INFO_COLOR 0 #endif // BLE_IAS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef BLE_IAS_CONFIG_DEBUG_COLOR #define BLE_IAS_CONFIG_DEBUG_COLOR 0 #endif // // // BLE_LBS_C_ENABLED - ble_lbs_c - Nordic LED Button Service Client #ifndef BLE_LBS_C_ENABLED #define BLE_LBS_C_ENABLED 0 #endif // BLE_LBS_ENABLED - ble_lbs - LED Button Service #ifndef BLE_LBS_ENABLED #define BLE_LBS_ENABLED 0 #endif // BLE_LLS_ENABLED - ble_lls - Link Loss Service #ifndef BLE_LLS_ENABLED #define BLE_LLS_ENABLED 0 #endif // BLE_NUS_C_ENABLED - ble_nus_c - Nordic UART Central Service #ifndef BLE_NUS_C_ENABLED #define BLE_NUS_C_ENABLED 0 #endif // BLE_NUS_ENABLED - ble_nus - Nordic UART Service //========================================================== #ifndef BLE_NUS_ENABLED #define BLE_NUS_ENABLED 0 #endif // BLE_NUS_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef BLE_NUS_CONFIG_LOG_ENABLED #define BLE_NUS_CONFIG_LOG_ENABLED 0 #endif // BLE_NUS_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef BLE_NUS_CONFIG_LOG_LEVEL #define BLE_NUS_CONFIG_LOG_LEVEL 3 #endif // BLE_NUS_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef BLE_NUS_CONFIG_INFO_COLOR #define BLE_NUS_CONFIG_INFO_COLOR 0 #endif // BLE_NUS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef BLE_NUS_CONFIG_DEBUG_COLOR #define BLE_NUS_CONFIG_DEBUG_COLOR 0 #endif // // // BLE_RSCS_C_ENABLED - ble_rscs_c - Running Speed and Cadence Client #ifndef BLE_RSCS_C_ENABLED #define BLE_RSCS_C_ENABLED 0 #endif // BLE_RSCS_ENABLED - ble_rscs - Running Speed and Cadence Service #ifndef BLE_RSCS_ENABLED #define BLE_RSCS_ENABLED 0 #endif // BLE_TPS_ENABLED - ble_tps - TX Power Service #ifndef BLE_TPS_ENABLED #define BLE_TPS_ENABLED 0 #endif // //========================================================== // nRF_Core //========================================================== // NRF_MPU_ENABLED - nrf_mpu - Module for MPU //========================================================== #ifndef NRF_MPU_ENABLED #define NRF_MPU_ENABLED 0 #endif // NRF_MPU_CLI_CMDS - Enable CLI commands specific to the module. #ifndef NRF_MPU_CLI_CMDS #define NRF_MPU_CLI_CMDS 0 #endif // // NRF_STACK_GUARD_ENABLED - nrf_stack_guard - Stack guard //========================================================== #ifndef NRF_STACK_GUARD_ENABLED #define NRF_STACK_GUARD_ENABLED 0 #endif // NRF_STACK_GUARD_CONFIG_SIZE - Size of the stack guard. // <5=> 32 bytes // <6=> 64 bytes // <7=> 128 bytes // <8=> 256 bytes // <9=> 512 bytes // <10=> 1024 bytes // <11=> 2048 bytes // <12=> 4096 bytes #ifndef NRF_STACK_GUARD_CONFIG_SIZE #define NRF_STACK_GUARD_CONFIG_SIZE 7 #endif // // //========================================================== // nRF_Crypto //========================================================== // NRF_CRYPTO_ENABLED - nrf_crypto - Cryptography library. //========================================================== #ifndef NRF_CRYPTO_ENABLED #define NRF_CRYPTO_ENABLED 1 #endif // NRF_CRYPTO_ALLOCATOR - Memory allocator // Choose memory allocator used by nrf_crypto. Default is alloca if possible or nrf_malloc // otherwise. If 'User macros' are selected, the user has to create 'nrf_crypto_allocator.h' file // that contains NRF_CRYPTO_ALLOC, NRF_CRYPTO_FREE, and NRF_CRYPTO_ALLOC_ON_STACK. <0=> Default <1=> // User macros <2=> On stack (alloca) <3=> C dynamic memory (malloc) <4=> SDK Memory Manager // (nrf_malloc) #ifndef NRF_CRYPTO_ALLOCATOR #define NRF_CRYPTO_ALLOCATOR 0 #endif // NRF_CRYPTO_BACKEND_CC310_BL_ENABLED - Enable the ARM Cryptocell CC310 reduced backend. // The CC310 hardware-accelerated cryptography backend with reduced functionality and footprint // (only available on nRF52840). //========================================================== #ifndef NRF_CRYPTO_BACKEND_CC310_BL_ENABLED #define NRF_CRYPTO_BACKEND_CC310_BL_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP224R1_ENABLED - Enable the secp224r1 elliptic curve // support using CC310_BL. #ifndef NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP224R1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP224R1_ENABLED 0 #endif // NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP256R1_ENABLED - Enable the secp256r1 elliptic curve // support using CC310_BL. #ifndef NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP256R1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_BL_ECC_SECP256R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_BL_HASH_SHA256_ENABLED - CC310_BL SHA-256 hash functionality. // CC310_BL backend implementation for hardware-accelerated SHA-256. #ifndef NRF_CRYPTO_BACKEND_CC310_BL_HASH_SHA256_ENABLED #define NRF_CRYPTO_BACKEND_CC310_BL_HASH_SHA256_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED - nrf_cc310_bl buffers to RAM // before running hash operation // Enabling this makes hashing of addresses in FLASH range possible. Size of buffer allocated // for hashing is set by NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE #ifndef NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED #define NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_ENABLED 0 #endif // NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE - nrf_cc310_bl hash outputs // digests in little endian Makes the nrf_cc310_bl hash functions output digests in little // endian format. Only for use in nRF SDK DFU! #ifndef NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE #define NRF_CRYPTO_BACKEND_CC310_BL_HASH_AUTOMATIC_RAM_BUFFER_SIZE 4096 #endif // // NRF_CRYPTO_BACKEND_CC310_ENABLED - Enable the ARM Cryptocell CC310 backend. // The CC310 hardware-accelerated cryptography backend (only available on nRF52840). //========================================================== #ifndef NRF_CRYPTO_BACKEND_CC310_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ENABLED 0 #endif // NRF_CRYPTO_BACKEND_CC310_AES_CBC_ENABLED - Enable the AES CBC mode using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_AES_CBC_ENABLED #define NRF_CRYPTO_BACKEND_CC310_AES_CBC_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_AES_CTR_ENABLED - Enable the AES CTR mode using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_AES_CTR_ENABLED #define NRF_CRYPTO_BACKEND_CC310_AES_CTR_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_AES_ECB_ENABLED - Enable the AES ECB mode using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_AES_ECB_ENABLED #define NRF_CRYPTO_BACKEND_CC310_AES_ECB_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_AES_CBC_MAC_ENABLED - Enable the AES CBC_MAC mode using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_AES_CBC_MAC_ENABLED #define NRF_CRYPTO_BACKEND_CC310_AES_CBC_MAC_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_AES_CMAC_ENABLED - Enable the AES CMAC mode using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_AES_CMAC_ENABLED #define NRF_CRYPTO_BACKEND_CC310_AES_CMAC_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_AES_CCM_ENABLED - Enable the AES CCM mode using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_AES_CCM_ENABLED #define NRF_CRYPTO_BACKEND_CC310_AES_CCM_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_AES_CCM_STAR_ENABLED - Enable the AES CCM* mode using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_AES_CCM_STAR_ENABLED #define NRF_CRYPTO_BACKEND_CC310_AES_CCM_STAR_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_CHACHA_POLY_ENABLED - Enable the CHACHA-POLY mode using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_CHACHA_POLY_ENABLED #define NRF_CRYPTO_BACKEND_CC310_CHACHA_POLY_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R1_ENABLED - Enable the secp160r1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R2_ENABLED - Enable the secp160r2 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R2_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP160R2_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP192R1_ENABLED - Enable the secp192r1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP192R1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP192R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP224R1_ENABLED - Enable the secp224r1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP224R1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP224R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP256R1_ENABLED - Enable the secp256r1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP256R1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP256R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP384R1_ENABLED - Enable the secp384r1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP384R1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP384R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP521R1_ENABLED - Enable the secp521r1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP521R1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP521R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP160K1_ENABLED - Enable the secp160k1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP160K1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP160K1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP192K1_ENABLED - Enable the secp192k1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP192K1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP192K1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP224K1_ENABLED - Enable the secp224k1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP224K1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP224K1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_SECP256K1_ENABLED - Enable the secp256k1 elliptic curve support // using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_SECP256K1_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_SECP256K1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_CURVE25519_ENABLED - Enable the Curve25519 curve support using // CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_CURVE25519_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_CURVE25519_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_ECC_ED25519_ENABLED - Enable the Ed25519 curve support using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_ECC_ED25519_ENABLED #define NRF_CRYPTO_BACKEND_CC310_ECC_ED25519_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_HASH_SHA256_ENABLED - CC310 SHA-256 hash functionality. // CC310 backend implementation for hardware-accelerated SHA-256. #ifndef NRF_CRYPTO_BACKEND_CC310_HASH_SHA256_ENABLED #define NRF_CRYPTO_BACKEND_CC310_HASH_SHA256_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_HASH_SHA512_ENABLED - CC310 SHA-512 hash functionality // CC310 backend implementation for SHA-512 (in software). #ifndef NRF_CRYPTO_BACKEND_CC310_HASH_SHA512_ENABLED #define NRF_CRYPTO_BACKEND_CC310_HASH_SHA512_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_HMAC_SHA256_ENABLED - CC310 HMAC using SHA-256 // CC310 backend implementation for HMAC using hardware-accelerated SHA-256. #ifndef NRF_CRYPTO_BACKEND_CC310_HMAC_SHA256_ENABLED #define NRF_CRYPTO_BACKEND_CC310_HMAC_SHA256_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_HMAC_SHA512_ENABLED - CC310 HMAC using SHA-512 // CC310 backend implementation for HMAC using SHA-512 (in software). #ifndef NRF_CRYPTO_BACKEND_CC310_HMAC_SHA512_ENABLED #define NRF_CRYPTO_BACKEND_CC310_HMAC_SHA512_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_CC310_RNG_ENABLED - Enable RNG support using CC310. #ifndef NRF_CRYPTO_BACKEND_CC310_RNG_ENABLED #define NRF_CRYPTO_BACKEND_CC310_RNG_ENABLED 1 #endif // // NRF_CRYPTO_BACKEND_CIFRA_ENABLED - Enable the Cifra backend. //========================================================== #ifndef NRF_CRYPTO_BACKEND_CIFRA_ENABLED #define NRF_CRYPTO_BACKEND_CIFRA_ENABLED 0 #endif // NRF_CRYPTO_BACKEND_CIFRA_AES_EAX_ENABLED - Enable the AES EAX mode using Cifra. #ifndef NRF_CRYPTO_BACKEND_CIFRA_AES_EAX_ENABLED #define NRF_CRYPTO_BACKEND_CIFRA_AES_EAX_ENABLED 1 #endif // // NRF_CRYPTO_BACKEND_MBEDTLS_ENABLED - Enable the mbed TLS backend. //========================================================== #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ENABLED 0 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_ENABLED - Enable the AES CBC mode mbed TLS. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_AES_CTR_ENABLED - Enable the AES CTR mode using mbed TLS. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CTR_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CTR_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_AES_CFB_ENABLED - Enable the AES CFB mode using mbed TLS. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CFB_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CFB_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_AES_ECB_ENABLED - Enable the AES ECB mode using mbed TLS. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_ECB_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_AES_ECB_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_MAC_ENABLED - Enable the AES CBC MAC mode using mbed TLS. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_MAC_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CBC_MAC_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_AES_CMAC_ENABLED - Enable the AES CMAC mode using mbed TLS. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CMAC_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CMAC_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_AES_CCM_ENABLED - Enable the AES CCM mode using mbed TLS. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_CCM_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_AES_CCM_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_AES_GCM_ENABLED - Enable the AES GCM mode using mbed TLS. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_AES_GCM_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_AES_GCM_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192R1_ENABLED - Enable secp192r1 (NIST 192-bit) curve // Enable this setting if you need secp192r1 (NIST 192-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192R1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224R1_ENABLED - Enable secp224r1 (NIST 224-bit) curve // Enable this setting if you need secp224r1 (NIST 224-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224R1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256R1_ENABLED - Enable secp256r1 (NIST 256-bit) curve // Enable this setting if you need secp256r1 (NIST 256-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256R1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP384R1_ENABLED - Enable secp384r1 (NIST 384-bit) curve // Enable this setting if you need secp384r1 (NIST 384-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP384R1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP384R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP521R1_ENABLED - Enable secp521r1 (NIST 521-bit) curve // Enable this setting if you need secp521r1 (NIST 521-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP521R1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP521R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192K1_ENABLED - Enable secp192k1 (Koblitz 192-bit) curve // Enable this setting if you need secp192k1 (Koblitz 192-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192K1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP192K1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224K1_ENABLED - Enable secp224k1 (Koblitz 224-bit) curve // Enable this setting if you need secp224k1 (Koblitz 224-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224K1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP224K1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256K1_ENABLED - Enable secp256k1 (Koblitz 256-bit) curve // Enable this setting if you need secp256k1 (Koblitz 256-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256K1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_SECP256K1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP256R1_ENABLED - Enable bp256r1 (Brainpool 256-bit) curve // Enable this setting if you need bp256r1 (Brainpool 256-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP256R1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP256R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP384R1_ENABLED - Enable bp384r1 (Brainpool 384-bit) curve // Enable this setting if you need bp384r1 (Brainpool 384-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP384R1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP384R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP512R1_ENABLED - Enable bp512r1 (Brainpool 512-bit) curve // Enable this setting if you need bp512r1 (Brainpool 512-bit) support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP512R1_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_BP512R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_ECC_CURVE25519_ENABLED - Enable Curve25519 curve // Enable this setting if you need Curve25519 support using MBEDTLS #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_ECC_CURVE25519_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_ECC_CURVE25519_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA256_ENABLED - Enable mbed TLS SHA-256 hash functionality. // mbed TLS backend implementation for SHA-256. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA256_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA256_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA512_ENABLED - Enable mbed TLS SHA-512 hash functionality. // mbed TLS backend implementation for SHA-512. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA512_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_HASH_SHA512_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA256_ENABLED - Enable mbed TLS HMAC using SHA-256. // mbed TLS backend implementation for HMAC using SHA-256. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA256_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA256_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA512_ENABLED - Enable mbed TLS HMAC using SHA-512. // mbed TLS backend implementation for HMAC using SHA-512. #ifndef NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA512_ENABLED #define NRF_CRYPTO_BACKEND_MBEDTLS_HMAC_SHA512_ENABLED 1 #endif // // NRF_CRYPTO_BACKEND_MICRO_ECC_ENABLED - Enable the micro-ecc backend. //========================================================== #ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ENABLED #define NRF_CRYPTO_BACKEND_MICRO_ECC_ENABLED 0 #endif // NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP192R1_ENABLED - Enable secp192r1 (NIST 192-bit) curve // Enable this setting if you need secp192r1 (NIST 192-bit) support using micro-ecc #ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP192R1_ENABLED #define NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP192R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP224R1_ENABLED - Enable secp224r1 (NIST 224-bit) curve // Enable this setting if you need secp224r1 (NIST 224-bit) support using micro-ecc #ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP224R1_ENABLED #define NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP224R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256R1_ENABLED - Enable secp256r1 (NIST 256-bit) curve // Enable this setting if you need secp256r1 (NIST 256-bit) support using micro-ecc #ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256R1_ENABLED #define NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256K1_ENABLED - Enable secp256k1 (Koblitz 256-bit) // curve // Enable this setting if you need secp256k1 (Koblitz 256-bit) support using micro-ecc #ifndef NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256K1_ENABLED #define NRF_CRYPTO_BACKEND_MICRO_ECC_ECC_SECP256K1_ENABLED 1 #endif // // NRF_CRYPTO_BACKEND_NRF_HW_RNG_ENABLED - Enable the nRF HW RNG backend. // The nRF HW backend provide access to RNG peripheral in nRF5x devices. //========================================================== #ifndef NRF_CRYPTO_BACKEND_NRF_HW_RNG_ENABLED #define NRF_CRYPTO_BACKEND_NRF_HW_RNG_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_NRF_HW_RNG_MBEDTLS_CTR_DRBG_ENABLED - Enable mbed TLS CTR-DRBG algorithm. // Enable mbed TLS CTR-DRBG standardized by NIST (NIST SP 800-90A Rev. 1). The nRF HW RNG is // used as an entropy source for seeding. #ifndef NRF_CRYPTO_BACKEND_NRF_HW_RNG_MBEDTLS_CTR_DRBG_ENABLED #define NRF_CRYPTO_BACKEND_NRF_HW_RNG_MBEDTLS_CTR_DRBG_ENABLED 0 #endif // // NRF_CRYPTO_BACKEND_NRF_SW_ENABLED - Enable the legacy nRFx sw for crypto. // The nRF SW cryptography backend (only used in bootloader context). //========================================================== #ifndef NRF_CRYPTO_BACKEND_NRF_SW_ENABLED #define NRF_CRYPTO_BACKEND_NRF_SW_ENABLED 0 #endif // NRF_CRYPTO_BACKEND_NRF_SW_HASH_SHA256_ENABLED - nRF SW hash backend support for SHA-256 // The nRF SW backend provide access to nRF SDK legacy hash implementation of SHA-256. #ifndef NRF_CRYPTO_BACKEND_NRF_SW_HASH_SHA256_ENABLED #define NRF_CRYPTO_BACKEND_NRF_SW_HASH_SHA256_ENABLED 1 #endif // // NRF_CRYPTO_BACKEND_OBERON_ENABLED - Enable the Oberon backend // The Oberon backend //========================================================== #ifndef NRF_CRYPTO_BACKEND_OBERON_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_ENABLED 0 #endif // NRF_CRYPTO_BACKEND_OBERON_CHACHA_POLY_ENABLED - Enable the CHACHA-POLY mode using Oberon. #ifndef NRF_CRYPTO_BACKEND_OBERON_CHACHA_POLY_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_CHACHA_POLY_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_OBERON_ECC_SECP256R1_ENABLED - Enable secp256r1 curve // Enable this setting if you need secp256r1 curve support using Oberon library #ifndef NRF_CRYPTO_BACKEND_OBERON_ECC_SECP256R1_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_ECC_SECP256R1_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_OBERON_ECC_CURVE25519_ENABLED - Enable Curve25519 ECDH // Enable this setting if you need Curve25519 ECDH support using Oberon library #ifndef NRF_CRYPTO_BACKEND_OBERON_ECC_CURVE25519_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_ECC_CURVE25519_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_OBERON_ECC_ED25519_ENABLED - Enable Ed25519 signature scheme // Enable this setting if you need Ed25519 support using Oberon library #ifndef NRF_CRYPTO_BACKEND_OBERON_ECC_ED25519_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_ECC_ED25519_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_OBERON_HASH_SHA256_ENABLED - Oberon SHA-256 hash functionality // Oberon backend implementation for SHA-256. #ifndef NRF_CRYPTO_BACKEND_OBERON_HASH_SHA256_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_HASH_SHA256_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_OBERON_HASH_SHA512_ENABLED - Oberon SHA-512 hash functionality // Oberon backend implementation for SHA-512. #ifndef NRF_CRYPTO_BACKEND_OBERON_HASH_SHA512_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_HASH_SHA512_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA256_ENABLED - Oberon HMAC using SHA-256 // Oberon backend implementation for HMAC using SHA-256. #ifndef NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA256_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA256_ENABLED 1 #endif // NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA512_ENABLED - Oberon HMAC using SHA-512 // Oberon backend implementation for HMAC using SHA-512. #ifndef NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA512_ENABLED #define NRF_CRYPTO_BACKEND_OBERON_HMAC_SHA512_ENABLED 1 #endif // // NRF_CRYPTO_CURVE25519_BIG_ENDIAN_ENABLED - Big-endian byte order in raw Curve25519 data // Enable big-endian byte order in Curve25519 API, if set to 1. Use little-endian, if set to 0. #ifndef NRF_CRYPTO_CURVE25519_BIG_ENDIAN_ENABLED #define NRF_CRYPTO_CURVE25519_BIG_ENDIAN_ENABLED 0 #endif // nrf_crypto_rng - RNG Configuration //========================================================== // NRF_CRYPTO_RNG_STATIC_MEMORY_BUFFERS_ENABLED - Use static memory buffers for context and // temporary init buffer. // Always recommended when using the nRF HW RNG as the context and temporary buffers are small. // Consider disabling if using the CC310 RNG in a RAM constrained application. In this case, memory // must be provided to nrf_crypto_rng_init, or it can be allocated internally provided that // NRF_CRYPTO_ALLOCATOR does not allocate memory on the stack. #ifndef NRF_CRYPTO_RNG_STATIC_MEMORY_BUFFERS_ENABLED #define NRF_CRYPTO_RNG_STATIC_MEMORY_BUFFERS_ENABLED 1 #endif // NRF_CRYPTO_RNG_AUTO_INIT_ENABLED - Initialize the RNG module automatically when nrf_crypto // is initialized. // Automatic initialization is only supported with static or internally allocated context and // temporary memory. #ifndef NRF_CRYPTO_RNG_AUTO_INIT_ENABLED #define NRF_CRYPTO_RNG_AUTO_INIT_ENABLED 1 #endif // // //========================================================== // nRF_DFU //========================================================== // ble_dfu - Device Firmware Update //========================================================== // BLE_DFU_ENABLED - Enable DFU Service. #ifndef BLE_DFU_ENABLED #define BLE_DFU_ENABLED 0 #endif // NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS - Buttonless DFU supports bonds. #ifndef NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS #define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 0 #endif // //========================================================== // //========================================================== // nRF_Drivers //========================================================== // COMP_ENABLED - nrf_drv_comp - COMP peripheral driver - legacy layer //========================================================== #ifndef COMP_ENABLED #define COMP_ENABLED 0 #endif // COMP_CONFIG_REF - Reference voltage // <0=> Internal 1.2V // <1=> Internal 1.8V // <2=> Internal 2.4V // <4=> VDD // <7=> ARef #ifndef COMP_CONFIG_REF #define COMP_CONFIG_REF 1 #endif // COMP_CONFIG_MAIN_MODE - Main mode // <0=> Single ended // <1=> Differential #ifndef COMP_CONFIG_MAIN_MODE #define COMP_CONFIG_MAIN_MODE 0 #endif // COMP_CONFIG_SPEED_MODE - Speed mode // <0=> Low power // <1=> Normal // <2=> High speed #ifndef COMP_CONFIG_SPEED_MODE #define COMP_CONFIG_SPEED_MODE 2 #endif // COMP_CONFIG_HYST - Hystheresis // <0=> No // <1=> 50mV #ifndef COMP_CONFIG_HYST #define COMP_CONFIG_HYST 0 #endif // COMP_CONFIG_ISOURCE - Current Source // <0=> Off // <1=> 2.5 uA // <2=> 5 uA // <3=> 10 uA #ifndef COMP_CONFIG_ISOURCE #define COMP_CONFIG_ISOURCE 0 #endif // COMP_CONFIG_INPUT - Analog input // <0=> 0 // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef COMP_CONFIG_INPUT #define COMP_CONFIG_INPUT 0 #endif // COMP_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef COMP_CONFIG_IRQ_PRIORITY #define COMP_CONFIG_IRQ_PRIORITY 6 #endif // // EGU_ENABLED - nrf_drv_swi - SWI(EGU) peripheral driver - legacy layer #ifndef EGU_ENABLED #define EGU_ENABLED 0 #endif // GPIOTE_ENABLED - nrf_drv_gpiote - GPIOTE peripheral driver - legacy layer //========================================================== #ifndef GPIOTE_ENABLED #define GPIOTE_ENABLED 1 #endif // GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS - Number of lower power input pins #ifndef GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS #define GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 4 #endif // GPIOTE_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef GPIOTE_CONFIG_IRQ_PRIORITY #define GPIOTE_CONFIG_IRQ_PRIORITY 6 #endif // // I2S_ENABLED - nrf_drv_i2s - I2S peripheral driver - legacy layer //========================================================== #ifndef I2S_ENABLED #define I2S_ENABLED 0 #endif // I2S_CONFIG_SCK_PIN - SCK pin <0-31> #ifndef I2S_CONFIG_SCK_PIN #define I2S_CONFIG_SCK_PIN 31 #endif // I2S_CONFIG_LRCK_PIN - LRCK pin <1-31> #ifndef I2S_CONFIG_LRCK_PIN #define I2S_CONFIG_LRCK_PIN 30 #endif // I2S_CONFIG_MCK_PIN - MCK pin #ifndef I2S_CONFIG_MCK_PIN #define I2S_CONFIG_MCK_PIN 255 #endif // I2S_CONFIG_SDOUT_PIN - SDOUT pin <0-31> #ifndef I2S_CONFIG_SDOUT_PIN #define I2S_CONFIG_SDOUT_PIN 29 #endif // I2S_CONFIG_SDIN_PIN - SDIN pin <0-31> #ifndef I2S_CONFIG_SDIN_PIN #define I2S_CONFIG_SDIN_PIN 28 #endif // I2S_CONFIG_MASTER - Mode // <0=> Master // <1=> Slave #ifndef I2S_CONFIG_MASTER #define I2S_CONFIG_MASTER 0 #endif // I2S_CONFIG_FORMAT - Format // <0=> I2S // <1=> Aligned #ifndef I2S_CONFIG_FORMAT #define I2S_CONFIG_FORMAT 0 #endif // I2S_CONFIG_ALIGN - Alignment // <0=> Left // <1=> Right #ifndef I2S_CONFIG_ALIGN #define I2S_CONFIG_ALIGN 0 #endif // I2S_CONFIG_SWIDTH - Sample width (bits) // <0=> 8 // <1=> 16 // <2=> 24 #ifndef I2S_CONFIG_SWIDTH #define I2S_CONFIG_SWIDTH 1 #endif // I2S_CONFIG_CHANNELS - Channels // <0=> Stereo // <1=> Left // <2=> Right #ifndef I2S_CONFIG_CHANNELS #define I2S_CONFIG_CHANNELS 1 #endif // I2S_CONFIG_MCK_SETUP - MCK behavior // <0=> Disabled // <2147483648=> 32MHz/2 // <1342177280=> 32MHz/3 // <1073741824=> 32MHz/4 // <805306368=> 32MHz/5 // <671088640=> 32MHz/6 // <536870912=> 32MHz/8 // <402653184=> 32MHz/10 // <369098752=> 32MHz/11 // <285212672=> 32MHz/15 // <268435456=> 32MHz/16 // <201326592=> 32MHz/21 // <184549376=> 32MHz/23 // <142606336=> 32MHz/30 // <138412032=> 32MHz/31 // <134217728=> 32MHz/32 // <100663296=> 32MHz/42 // <68157440=> 32MHz/63 // <34340864=> 32MHz/125 #ifndef I2S_CONFIG_MCK_SETUP #define I2S_CONFIG_MCK_SETUP 536870912 #endif // I2S_CONFIG_RATIO - MCK/LRCK ratio // <0=> 32x // <1=> 48x // <2=> 64x // <3=> 96x // <4=> 128x // <5=> 192x // <6=> 256x // <7=> 384x // <8=> 512x #ifndef I2S_CONFIG_RATIO #define I2S_CONFIG_RATIO 2000 #endif // I2S_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef I2S_CONFIG_IRQ_PRIORITY #define I2S_CONFIG_IRQ_PRIORITY 6 #endif // I2S_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef I2S_CONFIG_LOG_ENABLED #define I2S_CONFIG_LOG_ENABLED 0 #endif // I2S_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef I2S_CONFIG_LOG_LEVEL #define I2S_CONFIG_LOG_LEVEL 3 #endif // I2S_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef I2S_CONFIG_INFO_COLOR #define I2S_CONFIG_INFO_COLOR 0 #endif // I2S_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef I2S_CONFIG_DEBUG_COLOR #define I2S_CONFIG_DEBUG_COLOR 0 #endif // // // LPCOMP_ENABLED - nrf_drv_lpcomp - LPCOMP peripheral driver - legacy layer //========================================================== #ifndef LPCOMP_ENABLED #define LPCOMP_ENABLED 0 #endif // LPCOMP_CONFIG_REFERENCE - Reference voltage // <0=> Supply 1/8 // <1=> Supply 2/8 // <2=> Supply 3/8 // <3=> Supply 4/8 // <4=> Supply 5/8 // <5=> Supply 6/8 // <6=> Supply 7/8 // <8=> Supply 1/16 (nRF52) // <9=> Supply 3/16 (nRF52) // <10=> Supply 5/16 (nRF52) // <11=> Supply 7/16 (nRF52) // <12=> Supply 9/16 (nRF52) // <13=> Supply 11/16 (nRF52) // <14=> Supply 13/16 (nRF52) // <15=> Supply 15/16 (nRF52) // <7=> External Ref 0 // <65543=> External Ref 1 #ifndef LPCOMP_CONFIG_REFERENCE #define LPCOMP_CONFIG_REFERENCE 3 #endif // LPCOMP_CONFIG_DETECTION - Detection // <0=> Crossing // <1=> Up // <2=> Down #ifndef LPCOMP_CONFIG_DETECTION #define LPCOMP_CONFIG_DETECTION 2 #endif // LPCOMP_CONFIG_INPUT - Analog input // <0=> 0 // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef LPCOMP_CONFIG_INPUT #define LPCOMP_CONFIG_INPUT 0 #endif // LPCOMP_CONFIG_HYST - Hysteresis #ifndef LPCOMP_CONFIG_HYST #define LPCOMP_CONFIG_HYST 0 #endif // LPCOMP_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef LPCOMP_CONFIG_IRQ_PRIORITY #define LPCOMP_CONFIG_IRQ_PRIORITY 6 #endif // // NRFX_CLOCK_ENABLED - nrfx_clock - CLOCK peripheral driver //========================================================== #ifndef NRFX_CLOCK_ENABLED #define NRFX_CLOCK_ENABLED 1 #endif // NRFX_CLOCK_CONFIG_LF_SRC - LF Clock Source // <0=> RC // <1=> XTAL // <2=> Synth // <131073=> External Low Swing // <196609=> External Full Swing #ifndef NRFX_CLOCK_CONFIG_LF_SRC #define NRFX_CLOCK_CONFIG_LF_SRC 1 #endif // NRFX_CLOCK_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_CLOCK_CONFIG_IRQ_PRIORITY #define NRFX_CLOCK_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_CLOCK_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_CLOCK_CONFIG_LOG_ENABLED #define NRFX_CLOCK_CONFIG_LOG_ENABLED 0 #endif // NRFX_CLOCK_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_CLOCK_CONFIG_LOG_LEVEL #define NRFX_CLOCK_CONFIG_LOG_LEVEL 3 #endif // NRFX_CLOCK_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_CLOCK_CONFIG_INFO_COLOR #define NRFX_CLOCK_CONFIG_INFO_COLOR 0 #endif // NRFX_CLOCK_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_CLOCK_CONFIG_DEBUG_COLOR #define NRFX_CLOCK_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_COMP_ENABLED - nrfx_comp - COMP peripheral driver //========================================================== #ifndef NRFX_COMP_ENABLED #define NRFX_COMP_ENABLED 0 #endif // NRFX_COMP_CONFIG_REF - Reference voltage // <0=> Internal 1.2V // <1=> Internal 1.8V // <2=> Internal 2.4V // <4=> VDD // <7=> ARef #ifndef NRFX_COMP_CONFIG_REF #define NRFX_COMP_CONFIG_REF 1 #endif // NRFX_COMP_CONFIG_MAIN_MODE - Main mode // <0=> Single ended // <1=> Differential #ifndef NRFX_COMP_CONFIG_MAIN_MODE #define NRFX_COMP_CONFIG_MAIN_MODE 0 #endif // NRFX_COMP_CONFIG_SPEED_MODE - Speed mode // <0=> Low power // <1=> Normal // <2=> High speed #ifndef NRFX_COMP_CONFIG_SPEED_MODE #define NRFX_COMP_CONFIG_SPEED_MODE 2 #endif // NRFX_COMP_CONFIG_HYST - Hystheresis // <0=> No // <1=> 50mV #ifndef NRFX_COMP_CONFIG_HYST #define NRFX_COMP_CONFIG_HYST 0 #endif // NRFX_COMP_CONFIG_ISOURCE - Current Source // <0=> Off // <1=> 2.5 uA // <2=> 5 uA // <3=> 10 uA #ifndef NRFX_COMP_CONFIG_ISOURCE #define NRFX_COMP_CONFIG_ISOURCE 0 #endif // NRFX_COMP_CONFIG_INPUT - Analog input // <0=> 0 // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_COMP_CONFIG_INPUT #define NRFX_COMP_CONFIG_INPUT 0 #endif // NRFX_COMP_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_COMP_CONFIG_IRQ_PRIORITY #define NRFX_COMP_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_COMP_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_COMP_CONFIG_LOG_ENABLED #define NRFX_COMP_CONFIG_LOG_ENABLED 0 #endif // NRFX_COMP_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_COMP_CONFIG_LOG_LEVEL #define NRFX_COMP_CONFIG_LOG_LEVEL 3 #endif // NRFX_COMP_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_COMP_CONFIG_INFO_COLOR #define NRFX_COMP_CONFIG_INFO_COLOR 0 #endif // NRFX_COMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_COMP_CONFIG_DEBUG_COLOR #define NRFX_COMP_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_GPIOTE_ENABLED - nrfx_gpiote - GPIOTE peripheral driver //========================================================== #ifndef NRFX_GPIOTE_ENABLED #define NRFX_GPIOTE_ENABLED 1 #endif // NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS - Number of lower power input pins #ifndef NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS #define NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 1 #endif // NRFX_GPIOTE_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_GPIOTE_CONFIG_IRQ_PRIORITY #define NRFX_GPIOTE_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_GPIOTE_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_GPIOTE_CONFIG_LOG_ENABLED #define NRFX_GPIOTE_CONFIG_LOG_ENABLED 0 #endif // NRFX_GPIOTE_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_GPIOTE_CONFIG_LOG_LEVEL #define NRFX_GPIOTE_CONFIG_LOG_LEVEL 3 #endif // NRFX_GPIOTE_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_GPIOTE_CONFIG_INFO_COLOR #define NRFX_GPIOTE_CONFIG_INFO_COLOR 0 #endif // NRFX_GPIOTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_GPIOTE_CONFIG_DEBUG_COLOR #define NRFX_GPIOTE_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_I2S_ENABLED - nrfx_i2s - I2S peripheral driver //========================================================== #ifndef NRFX_I2S_ENABLED #define NRFX_I2S_ENABLED 0 #endif // NRFX_I2S_CONFIG_SCK_PIN - SCK pin <0-31> #ifndef NRFX_I2S_CONFIG_SCK_PIN #define NRFX_I2S_CONFIG_SCK_PIN 31 #endif // NRFX_I2S_CONFIG_LRCK_PIN - LRCK pin <1-31> #ifndef NRFX_I2S_CONFIG_LRCK_PIN #define NRFX_I2S_CONFIG_LRCK_PIN 30 #endif // NRFX_I2S_CONFIG_MCK_PIN - MCK pin #ifndef NRFX_I2S_CONFIG_MCK_PIN #define NRFX_I2S_CONFIG_MCK_PIN 255 #endif // NRFX_I2S_CONFIG_SDOUT_PIN - SDOUT pin <0-31> #ifndef NRFX_I2S_CONFIG_SDOUT_PIN #define NRFX_I2S_CONFIG_SDOUT_PIN 29 #endif // NRFX_I2S_CONFIG_SDIN_PIN - SDIN pin <0-31> #ifndef NRFX_I2S_CONFIG_SDIN_PIN #define NRFX_I2S_CONFIG_SDIN_PIN 28 #endif // NRFX_I2S_CONFIG_MASTER - Mode // <0=> Master // <1=> Slave #ifndef NRFX_I2S_CONFIG_MASTER #define NRFX_I2S_CONFIG_MASTER 0 #endif // NRFX_I2S_CONFIG_FORMAT - Format // <0=> I2S // <1=> Aligned #ifndef NRFX_I2S_CONFIG_FORMAT #define NRFX_I2S_CONFIG_FORMAT 0 #endif // NRFX_I2S_CONFIG_ALIGN - Alignment // <0=> Left // <1=> Right #ifndef NRFX_I2S_CONFIG_ALIGN #define NRFX_I2S_CONFIG_ALIGN 0 #endif // NRFX_I2S_CONFIG_SWIDTH - Sample width (bits) // <0=> 8 // <1=> 16 // <2=> 24 #ifndef NRFX_I2S_CONFIG_SWIDTH #define NRFX_I2S_CONFIG_SWIDTH 1 #endif // NRFX_I2S_CONFIG_CHANNELS - Channels // <0=> Stereo // <1=> Left // <2=> Right #ifndef NRFX_I2S_CONFIG_CHANNELS #define NRFX_I2S_CONFIG_CHANNELS 1 #endif // NRFX_I2S_CONFIG_MCK_SETUP - MCK behavior // <0=> Disabled // <2147483648=> 32MHz/2 // <1342177280=> 32MHz/3 // <1073741824=> 32MHz/4 // <805306368=> 32MHz/5 // <671088640=> 32MHz/6 // <536870912=> 32MHz/8 // <402653184=> 32MHz/10 // <369098752=> 32MHz/11 // <285212672=> 32MHz/15 // <268435456=> 32MHz/16 // <201326592=> 32MHz/21 // <184549376=> 32MHz/23 // <142606336=> 32MHz/30 // <138412032=> 32MHz/31 // <134217728=> 32MHz/32 // <100663296=> 32MHz/42 // <68157440=> 32MHz/63 // <34340864=> 32MHz/125 #ifndef NRFX_I2S_CONFIG_MCK_SETUP #define NRFX_I2S_CONFIG_MCK_SETUP 536870912 #endif // NRFX_I2S_CONFIG_RATIO - MCK/LRCK ratio // <0=> 32x // <1=> 48x // <2=> 64x // <3=> 96x // <4=> 128x // <5=> 192x // <6=> 256x // <7=> 384x // <8=> 512x #ifndef NRFX_I2S_CONFIG_RATIO #define NRFX_I2S_CONFIG_RATIO 2000 #endif // NRFX_I2S_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_I2S_CONFIG_IRQ_PRIORITY #define NRFX_I2S_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_I2S_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_I2S_CONFIG_LOG_ENABLED #define NRFX_I2S_CONFIG_LOG_ENABLED 0 #endif // NRFX_I2S_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_I2S_CONFIG_LOG_LEVEL #define NRFX_I2S_CONFIG_LOG_LEVEL 3 #endif // NRFX_I2S_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_I2S_CONFIG_INFO_COLOR #define NRFX_I2S_CONFIG_INFO_COLOR 0 #endif // NRFX_I2S_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_I2S_CONFIG_DEBUG_COLOR #define NRFX_I2S_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_LPCOMP_ENABLED - nrfx_lpcomp - LPCOMP peripheral driver //========================================================== #ifndef NRFX_LPCOMP_ENABLED #define NRFX_LPCOMP_ENABLED 0 #endif // NRFX_LPCOMP_CONFIG_REFERENCE - Reference voltage // <0=> Supply 1/8 // <1=> Supply 2/8 // <2=> Supply 3/8 // <3=> Supply 4/8 // <4=> Supply 5/8 // <5=> Supply 6/8 // <6=> Supply 7/8 // <8=> Supply 1/16 (nRF52) // <9=> Supply 3/16 (nRF52) // <10=> Supply 5/16 (nRF52) // <11=> Supply 7/16 (nRF52) // <12=> Supply 9/16 (nRF52) // <13=> Supply 11/16 (nRF52) // <14=> Supply 13/16 (nRF52) // <15=> Supply 15/16 (nRF52) // <7=> External Ref 0 // <65543=> External Ref 1 #ifndef NRFX_LPCOMP_CONFIG_REFERENCE #define NRFX_LPCOMP_CONFIG_REFERENCE 3 #endif // NRFX_LPCOMP_CONFIG_DETECTION - Detection // <0=> Crossing // <1=> Up // <2=> Down #ifndef NRFX_LPCOMP_CONFIG_DETECTION #define NRFX_LPCOMP_CONFIG_DETECTION 2 #endif // NRFX_LPCOMP_CONFIG_INPUT - Analog input // <0=> 0 // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_LPCOMP_CONFIG_INPUT #define NRFX_LPCOMP_CONFIG_INPUT 0 #endif // NRFX_LPCOMP_CONFIG_HYST - Hysteresis #ifndef NRFX_LPCOMP_CONFIG_HYST #define NRFX_LPCOMP_CONFIG_HYST 0 #endif // NRFX_LPCOMP_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_LPCOMP_CONFIG_IRQ_PRIORITY #define NRFX_LPCOMP_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_LPCOMP_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_LPCOMP_CONFIG_LOG_ENABLED #define NRFX_LPCOMP_CONFIG_LOG_ENABLED 0 #endif // NRFX_LPCOMP_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_LPCOMP_CONFIG_LOG_LEVEL #define NRFX_LPCOMP_CONFIG_LOG_LEVEL 3 #endif // NRFX_LPCOMP_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_LPCOMP_CONFIG_INFO_COLOR #define NRFX_LPCOMP_CONFIG_INFO_COLOR 0 #endif // NRFX_LPCOMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_LPCOMP_CONFIG_DEBUG_COLOR #define NRFX_LPCOMP_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_PDM_ENABLED - nrfx_pdm - PDM peripheral driver //========================================================== #ifndef NRFX_PDM_ENABLED #define NRFX_PDM_ENABLED 0 #endif // NRFX_PDM_CONFIG_MODE - Mode // <0=> Stereo // <1=> Mono #ifndef NRFX_PDM_CONFIG_MODE #define NRFX_PDM_CONFIG_MODE 1 #endif // NRFX_PDM_CONFIG_EDGE - Edge // <0=> Left falling // <1=> Left rising #ifndef NRFX_PDM_CONFIG_EDGE #define NRFX_PDM_CONFIG_EDGE 0 #endif // NRFX_PDM_CONFIG_CLOCK_FREQ - Clock frequency // <134217728=> 1000k // <138412032=> 1032k (default) // <142606336=> 1067k #ifndef NRFX_PDM_CONFIG_CLOCK_FREQ #define NRFX_PDM_CONFIG_CLOCK_FREQ 138412032 #endif // NRFX_PDM_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_PDM_CONFIG_IRQ_PRIORITY #define NRFX_PDM_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_PDM_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_PDM_CONFIG_LOG_ENABLED #define NRFX_PDM_CONFIG_LOG_ENABLED 0 #endif // NRFX_PDM_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_PDM_CONFIG_LOG_LEVEL #define NRFX_PDM_CONFIG_LOG_LEVEL 3 #endif // NRFX_PDM_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_PDM_CONFIG_INFO_COLOR #define NRFX_PDM_CONFIG_INFO_COLOR 0 #endif // NRFX_PDM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_PDM_CONFIG_DEBUG_COLOR #define NRFX_PDM_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_POWER_ENABLED - nrfx_power - POWER peripheral driver //========================================================== #ifndef NRFX_POWER_ENABLED #define NRFX_POWER_ENABLED 0 #endif // NRFX_POWER_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_POWER_CONFIG_IRQ_PRIORITY #define NRFX_POWER_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_POWER_CONFIG_DEFAULT_DCDCEN - The default configuration of main DCDC regulator // This settings means only that components for DCDC regulator are installed and it can be // enabled. #ifndef NRFX_POWER_CONFIG_DEFAULT_DCDCEN #define NRFX_POWER_CONFIG_DEFAULT_DCDCEN 0 #endif // NRFX_POWER_CONFIG_DEFAULT_DCDCENHV - The default configuration of High Voltage DCDC // regulator // This settings means only that components for DCDC regulator are installed and it can be // enabled. #ifndef NRFX_POWER_CONFIG_DEFAULT_DCDCENHV #define NRFX_POWER_CONFIG_DEFAULT_DCDCENHV 0 #endif // // NRFX_PPI_ENABLED - nrfx_ppi - PPI peripheral allocator //========================================================== #ifndef NRFX_PPI_ENABLED #define NRFX_PPI_ENABLED 0 #endif // NRFX_PPI_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_PPI_CONFIG_LOG_ENABLED #define NRFX_PPI_CONFIG_LOG_ENABLED 0 #endif // NRFX_PPI_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_PPI_CONFIG_LOG_LEVEL #define NRFX_PPI_CONFIG_LOG_LEVEL 3 #endif // NRFX_PPI_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_PPI_CONFIG_INFO_COLOR #define NRFX_PPI_CONFIG_INFO_COLOR 0 #endif // NRFX_PPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_PPI_CONFIG_DEBUG_COLOR #define NRFX_PPI_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_PRS_ENABLED - nrfx_prs - Peripheral Resource Sharing module //========================================================== #ifndef NRFX_PRS_ENABLED #define NRFX_PRS_ENABLED 1 #endif // NRFX_PRS_BOX_0_ENABLED - Enables box 0 in the module. #ifndef NRFX_PRS_BOX_0_ENABLED #define NRFX_PRS_BOX_0_ENABLED 0 #endif // NRFX_PRS_BOX_1_ENABLED - Enables box 1 in the module. #ifndef NRFX_PRS_BOX_1_ENABLED #define NRFX_PRS_BOX_1_ENABLED 0 #endif // NRFX_PRS_BOX_2_ENABLED - Enables box 2 in the module. #ifndef NRFX_PRS_BOX_2_ENABLED #define NRFX_PRS_BOX_2_ENABLED 0 #endif // NRFX_PRS_BOX_3_ENABLED - Enables box 3 in the module. #ifndef NRFX_PRS_BOX_3_ENABLED #define NRFX_PRS_BOX_3_ENABLED 0 #endif // NRFX_PRS_BOX_4_ENABLED - Enables box 4 in the module. #ifndef NRFX_PRS_BOX_4_ENABLED #define NRFX_PRS_BOX_4_ENABLED 1 #endif // NRFX_PRS_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_PRS_CONFIG_LOG_ENABLED #define NRFX_PRS_CONFIG_LOG_ENABLED 0 #endif // NRFX_PRS_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_PRS_CONFIG_LOG_LEVEL #define NRFX_PRS_CONFIG_LOG_LEVEL 3 #endif // NRFX_PRS_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_PRS_CONFIG_INFO_COLOR #define NRFX_PRS_CONFIG_INFO_COLOR 0 #endif // NRFX_PRS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_PRS_CONFIG_DEBUG_COLOR #define NRFX_PRS_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_PWM_ENABLED - nrfx_pwm - PWM peripheral driver //========================================================== #ifndef NRFX_PWM_ENABLED #define NRFX_PWM_ENABLED 0 #endif // NRFX_PWM0_ENABLED - Enable PWM0 instance #ifndef NRFX_PWM0_ENABLED #define NRFX_PWM0_ENABLED 0 #endif // NRFX_PWM1_ENABLED - Enable PWM1 instance #ifndef NRFX_PWM1_ENABLED #define NRFX_PWM1_ENABLED 0 #endif // NRFX_PWM2_ENABLED - Enable PWM2 instance #ifndef NRFX_PWM2_ENABLED #define NRFX_PWM2_ENABLED 0 #endif // NRFX_PWM3_ENABLED - Enable PWM3 instance #ifndef NRFX_PWM3_ENABLED #define NRFX_PWM3_ENABLED 0 #endif // NRFX_PWM_DEFAULT_CONFIG_OUT0_PIN - Out0 pin <0-31> #ifndef NRFX_PWM_DEFAULT_CONFIG_OUT0_PIN #define NRFX_PWM_DEFAULT_CONFIG_OUT0_PIN 31 #endif // NRFX_PWM_DEFAULT_CONFIG_OUT1_PIN - Out1 pin <0-31> #ifndef NRFX_PWM_DEFAULT_CONFIG_OUT1_PIN #define NRFX_PWM_DEFAULT_CONFIG_OUT1_PIN 31 #endif // NRFX_PWM_DEFAULT_CONFIG_OUT2_PIN - Out2 pin <0-31> #ifndef NRFX_PWM_DEFAULT_CONFIG_OUT2_PIN #define NRFX_PWM_DEFAULT_CONFIG_OUT2_PIN 31 #endif // NRFX_PWM_DEFAULT_CONFIG_OUT3_PIN - Out3 pin <0-31> #ifndef NRFX_PWM_DEFAULT_CONFIG_OUT3_PIN #define NRFX_PWM_DEFAULT_CONFIG_OUT3_PIN 31 #endif // NRFX_PWM_DEFAULT_CONFIG_BASE_CLOCK - Base clock // <0=> 16 MHz // <1=> 8 MHz // <2=> 4 MHz // <3=> 2 MHz // <4=> 1 MHz // <5=> 500 kHz // <6=> 250 kHz // <7=> 125 kHz #ifndef NRFX_PWM_DEFAULT_CONFIG_BASE_CLOCK #define NRFX_PWM_DEFAULT_CONFIG_BASE_CLOCK 4 #endif // NRFX_PWM_DEFAULT_CONFIG_COUNT_MODE - Count mode // <0=> Up // <1=> Up and Down #ifndef NRFX_PWM_DEFAULT_CONFIG_COUNT_MODE #define NRFX_PWM_DEFAULT_CONFIG_COUNT_MODE 0 #endif // NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE - Top value #ifndef NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE #define NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE 1000 #endif // NRFX_PWM_DEFAULT_CONFIG_LOAD_MODE - Load mode // <0=> Common // <1=> Grouped // <2=> Individual // <3=> Waveform #ifndef NRFX_PWM_DEFAULT_CONFIG_LOAD_MODE #define NRFX_PWM_DEFAULT_CONFIG_LOAD_MODE 0 #endif // NRFX_PWM_DEFAULT_CONFIG_STEP_MODE - Step mode // <0=> Auto // <1=> Triggered #ifndef NRFX_PWM_DEFAULT_CONFIG_STEP_MODE #define NRFX_PWM_DEFAULT_CONFIG_STEP_MODE 0 #endif // NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_PWM_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_PWM_CONFIG_LOG_ENABLED #define NRFX_PWM_CONFIG_LOG_ENABLED 0 #endif // NRFX_PWM_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_PWM_CONFIG_LOG_LEVEL #define NRFX_PWM_CONFIG_LOG_LEVEL 3 #endif // NRFX_PWM_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_PWM_CONFIG_INFO_COLOR #define NRFX_PWM_CONFIG_INFO_COLOR 0 #endif // NRFX_PWM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_PWM_CONFIG_DEBUG_COLOR #define NRFX_PWM_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_QDEC_ENABLED - nrfx_qdec - QDEC peripheral driver //========================================================== #ifndef NRFX_QDEC_ENABLED #define NRFX_QDEC_ENABLED 0 #endif // NRFX_QDEC_CONFIG_REPORTPER - Report period // <0=> 10 Samples // <1=> 40 Samples // <2=> 80 Samples // <3=> 120 Samples // <4=> 160 Samples // <5=> 200 Samples // <6=> 240 Samples // <7=> 280 Samples #ifndef NRFX_QDEC_CONFIG_REPORTPER #define NRFX_QDEC_CONFIG_REPORTPER 0 #endif // NRFX_QDEC_CONFIG_SAMPLEPER - Sample period // <0=> 128 us // <1=> 256 us // <2=> 512 us // <3=> 1024 us // <4=> 2048 us // <5=> 4096 us // <6=> 8192 us // <7=> 16384 us #ifndef NRFX_QDEC_CONFIG_SAMPLEPER #define NRFX_QDEC_CONFIG_SAMPLEPER 7 #endif // NRFX_QDEC_CONFIG_PIO_A - A pin <0-31> #ifndef NRFX_QDEC_CONFIG_PIO_A #define NRFX_QDEC_CONFIG_PIO_A 31 #endif // NRFX_QDEC_CONFIG_PIO_B - B pin <0-31> #ifndef NRFX_QDEC_CONFIG_PIO_B #define NRFX_QDEC_CONFIG_PIO_B 31 #endif // NRFX_QDEC_CONFIG_PIO_LED - LED pin <0-31> #ifndef NRFX_QDEC_CONFIG_PIO_LED #define NRFX_QDEC_CONFIG_PIO_LED 31 #endif // NRFX_QDEC_CONFIG_LEDPRE - LED pre #ifndef NRFX_QDEC_CONFIG_LEDPRE #define NRFX_QDEC_CONFIG_LEDPRE 511 #endif // NRFX_QDEC_CONFIG_LEDPOL - LED polarity // <0=> Active low // <1=> Active high #ifndef NRFX_QDEC_CONFIG_LEDPOL #define NRFX_QDEC_CONFIG_LEDPOL 1 #endif // NRFX_QDEC_CONFIG_DBFEN - Debouncing enable #ifndef NRFX_QDEC_CONFIG_DBFEN #define NRFX_QDEC_CONFIG_DBFEN 0 #endif // NRFX_QDEC_CONFIG_SAMPLE_INTEN - Sample ready interrupt enable #ifndef NRFX_QDEC_CONFIG_SAMPLE_INTEN #define NRFX_QDEC_CONFIG_SAMPLE_INTEN 0 #endif // NRFX_QDEC_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_QDEC_CONFIG_IRQ_PRIORITY #define NRFX_QDEC_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_QDEC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_QDEC_CONFIG_LOG_ENABLED #define NRFX_QDEC_CONFIG_LOG_ENABLED 0 #endif // NRFX_QDEC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_QDEC_CONFIG_LOG_LEVEL #define NRFX_QDEC_CONFIG_LOG_LEVEL 3 #endif // NRFX_QDEC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_QDEC_CONFIG_INFO_COLOR #define NRFX_QDEC_CONFIG_INFO_COLOR 0 #endif // NRFX_QDEC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_QDEC_CONFIG_DEBUG_COLOR #define NRFX_QDEC_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_QSPI_ENABLED - nrfx_qspi - QSPI peripheral driver //========================================================== #ifndef NRFX_QSPI_ENABLED #define NRFX_QSPI_ENABLED 0 #endif // NRFX_QSPI_CONFIG_SCK_DELAY - tSHSL, tWHSL and tSHWL in number of 16 MHz periods (62.5 ns). // <0-255> #ifndef NRFX_QSPI_CONFIG_SCK_DELAY #define NRFX_QSPI_CONFIG_SCK_DELAY 1 #endif // NRFX_QSPI_CONFIG_XIP_OFFSET - Address offset in the external memory for Execute in Place // operation. #ifndef NRFX_QSPI_CONFIG_XIP_OFFSET #define NRFX_QSPI_CONFIG_XIP_OFFSET 0 #endif // NRFX_QSPI_CONFIG_READOC - Number of data lines and opcode used for reading. // <0=> FastRead // <1=> Read2O // <2=> Read2IO // <3=> Read4O // <4=> Read4IO #ifndef NRFX_QSPI_CONFIG_READOC #define NRFX_QSPI_CONFIG_READOC 0 #endif // NRFX_QSPI_CONFIG_WRITEOC - Number of data lines and opcode used for writing. // <0=> PP // <1=> PP2O // <2=> PP4O // <3=> PP4IO #ifndef NRFX_QSPI_CONFIG_WRITEOC #define NRFX_QSPI_CONFIG_WRITEOC 0 #endif // NRFX_QSPI_CONFIG_ADDRMODE - Addressing mode. // <0=> 24bit // <1=> 32bit #ifndef NRFX_QSPI_CONFIG_ADDRMODE #define NRFX_QSPI_CONFIG_ADDRMODE 0 #endif // NRFX_QSPI_CONFIG_MODE - SPI mode. // <0=> Mode 0 // <1=> Mode 1 #ifndef NRFX_QSPI_CONFIG_MODE #define NRFX_QSPI_CONFIG_MODE 0 #endif // NRFX_QSPI_CONFIG_FREQUENCY - Frequency divider. // <0=> 32MHz/1 // <1=> 32MHz/2 // <2=> 32MHz/3 // <3=> 32MHz/4 // <4=> 32MHz/5 // <5=> 32MHz/6 // <6=> 32MHz/7 // <7=> 32MHz/8 // <8=> 32MHz/9 // <9=> 32MHz/10 // <10=> 32MHz/11 // <11=> 32MHz/12 // <12=> 32MHz/13 // <13=> 32MHz/14 // <14=> 32MHz/15 // <15=> 32MHz/16 #ifndef NRFX_QSPI_CONFIG_FREQUENCY #define NRFX_QSPI_CONFIG_FREQUENCY 15 #endif // NRFX_QSPI_PIN_SCK - SCK pin value. #ifndef NRFX_QSPI_PIN_SCK #define NRFX_QSPI_PIN_SCK NRF_QSPI_PIN_NOT_CONNECTED #endif // NRFX_QSPI_PIN_CSN - CSN pin value. #ifndef NRFX_QSPI_PIN_CSN #define NRFX_QSPI_PIN_CSN NRF_QSPI_PIN_NOT_CONNECTED #endif // NRFX_QSPI_PIN_IO0 - IO0 pin value. #ifndef NRFX_QSPI_PIN_IO0 #define NRFX_QSPI_PIN_IO0 NRF_QSPI_PIN_NOT_CONNECTED #endif // NRFX_QSPI_PIN_IO1 - IO1 pin value. #ifndef NRFX_QSPI_PIN_IO1 #define NRFX_QSPI_PIN_IO1 NRF_QSPI_PIN_NOT_CONNECTED #endif // NRFX_QSPI_PIN_IO2 - IO2 pin value. #ifndef NRFX_QSPI_PIN_IO2 #define NRFX_QSPI_PIN_IO2 NRF_QSPI_PIN_NOT_CONNECTED #endif // NRFX_QSPI_PIN_IO3 - IO3 pin value. #ifndef NRFX_QSPI_PIN_IO3 #define NRFX_QSPI_PIN_IO3 NRF_QSPI_PIN_NOT_CONNECTED #endif // NRFX_QSPI_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_QSPI_CONFIG_IRQ_PRIORITY #define NRFX_QSPI_CONFIG_IRQ_PRIORITY 6 #endif // // NRFX_RNG_ENABLED - nrfx_rng - RNG peripheral driver //========================================================== #ifndef NRFX_RNG_ENABLED #define NRFX_RNG_ENABLED 0 #endif // NRFX_RNG_CONFIG_ERROR_CORRECTION - Error correction #ifndef NRFX_RNG_CONFIG_ERROR_CORRECTION #define NRFX_RNG_CONFIG_ERROR_CORRECTION 1 #endif // NRFX_RNG_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_RNG_CONFIG_IRQ_PRIORITY #define NRFX_RNG_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_RNG_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_RNG_CONFIG_LOG_ENABLED #define NRFX_RNG_CONFIG_LOG_ENABLED 0 #endif // NRFX_RNG_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_RNG_CONFIG_LOG_LEVEL #define NRFX_RNG_CONFIG_LOG_LEVEL 3 #endif // NRFX_RNG_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_RNG_CONFIG_INFO_COLOR #define NRFX_RNG_CONFIG_INFO_COLOR 0 #endif // NRFX_RNG_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_RNG_CONFIG_DEBUG_COLOR #define NRFX_RNG_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_RTC_ENABLED - nrfx_rtc - RTC peripheral driver //========================================================== #ifndef NRFX_RTC_ENABLED #define NRFX_RTC_ENABLED 0 #endif // NRFX_RTC0_ENABLED - Enable RTC0 instance #ifndef NRFX_RTC0_ENABLED #define NRFX_RTC0_ENABLED 0 #endif // NRFX_RTC1_ENABLED - Enable RTC1 instance #ifndef NRFX_RTC1_ENABLED #define NRFX_RTC1_ENABLED 0 #endif // NRFX_RTC2_ENABLED - Enable RTC2 instance #ifndef NRFX_RTC2_ENABLED #define NRFX_RTC2_ENABLED 0 #endif // NRFX_RTC_MAXIMUM_LATENCY_US - Maximum possible time[us] in highest priority interrupt #ifndef NRFX_RTC_MAXIMUM_LATENCY_US #define NRFX_RTC_MAXIMUM_LATENCY_US 2000 #endif // NRFX_RTC_DEFAULT_CONFIG_FREQUENCY - Frequency <16-32768> #ifndef NRFX_RTC_DEFAULT_CONFIG_FREQUENCY #define NRFX_RTC_DEFAULT_CONFIG_FREQUENCY 32768 #endif // NRFX_RTC_DEFAULT_CONFIG_RELIABLE - Ensures safe compare event triggering #ifndef NRFX_RTC_DEFAULT_CONFIG_RELIABLE #define NRFX_RTC_DEFAULT_CONFIG_RELIABLE 0 #endif // NRFX_RTC_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_RTC_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_RTC_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_RTC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_RTC_CONFIG_LOG_ENABLED #define NRFX_RTC_CONFIG_LOG_ENABLED 0 #endif // NRFX_RTC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_RTC_CONFIG_LOG_LEVEL #define NRFX_RTC_CONFIG_LOG_LEVEL 3 #endif // NRFX_RTC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_RTC_CONFIG_INFO_COLOR #define NRFX_RTC_CONFIG_INFO_COLOR 0 #endif // NRFX_RTC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_RTC_CONFIG_DEBUG_COLOR #define NRFX_RTC_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_SAADC_ENABLED - nrfx_saadc - SAADC peripheral driver //========================================================== #ifndef NRFX_SAADC_ENABLED #define NRFX_SAADC_ENABLED 0 #endif // NRFX_SAADC_CONFIG_RESOLUTION - Resolution // <0=> 8 bit // <1=> 10 bit // <2=> 12 bit // <3=> 14 bit #ifndef NRFX_SAADC_CONFIG_RESOLUTION #define NRFX_SAADC_CONFIG_RESOLUTION 1 #endif // NRFX_SAADC_CONFIG_OVERSAMPLE - Sample period // <0=> Disabled // <1=> 2x // <2=> 4x // <3=> 8x // <4=> 16x // <5=> 32x // <6=> 64x // <7=> 128x // <8=> 256x #ifndef NRFX_SAADC_CONFIG_OVERSAMPLE #define NRFX_SAADC_CONFIG_OVERSAMPLE 0 #endif // NRFX_SAADC_CONFIG_LP_MODE - Enabling low power mode #ifndef NRFX_SAADC_CONFIG_LP_MODE #define NRFX_SAADC_CONFIG_LP_MODE 0 #endif // NRFX_SAADC_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_SAADC_CONFIG_IRQ_PRIORITY #define NRFX_SAADC_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_SAADC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_SAADC_CONFIG_LOG_ENABLED #define NRFX_SAADC_CONFIG_LOG_ENABLED 0 #endif // NRFX_SAADC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_SAADC_CONFIG_LOG_LEVEL #define NRFX_SAADC_CONFIG_LOG_LEVEL 3 #endif // NRFX_SAADC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SAADC_CONFIG_INFO_COLOR #define NRFX_SAADC_CONFIG_INFO_COLOR 0 #endif // NRFX_SAADC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SAADC_CONFIG_DEBUG_COLOR #define NRFX_SAADC_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_SPIM_ENABLED - nrfx_spim - SPIM peripheral driver //========================================================== #ifndef NRFX_SPIM_ENABLED #define NRFX_SPIM_ENABLED 0 #endif // NRFX_SPIM0_ENABLED - Enable SPIM0 instance #ifndef NRFX_SPIM0_ENABLED #define NRFX_SPIM0_ENABLED 0 #endif // NRFX_SPIM1_ENABLED - Enable SPIM1 instance #ifndef NRFX_SPIM1_ENABLED #define NRFX_SPIM1_ENABLED 0 #endif // NRFX_SPIM2_ENABLED - Enable SPIM2 instance #ifndef NRFX_SPIM2_ENABLED #define NRFX_SPIM2_ENABLED 0 #endif // NRFX_SPIM3_ENABLED - Enable SPIM3 instance #ifndef NRFX_SPIM3_ENABLED #define NRFX_SPIM3_ENABLED 0 #endif // NRFX_SPIM_EXTENDED_ENABLED - Enable extended SPIM features #ifndef NRFX_SPIM_EXTENDED_ENABLED #define NRFX_SPIM_EXTENDED_ENABLED 0 #endif // NRFX_SPIM_MISO_PULL_CFG - MISO pin pull configuration. // <0=> NRF_GPIO_PIN_NOPULL // <1=> NRF_GPIO_PIN_PULLDOWN // <3=> NRF_GPIO_PIN_PULLUP #ifndef NRFX_SPIM_MISO_PULL_CFG #define NRFX_SPIM_MISO_PULL_CFG 1 #endif // NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_SPIM_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_SPIM_CONFIG_LOG_ENABLED #define NRFX_SPIM_CONFIG_LOG_ENABLED 0 #endif // NRFX_SPIM_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_SPIM_CONFIG_LOG_LEVEL #define NRFX_SPIM_CONFIG_LOG_LEVEL 3 #endif // NRFX_SPIM_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SPIM_CONFIG_INFO_COLOR #define NRFX_SPIM_CONFIG_INFO_COLOR 0 #endif // NRFX_SPIM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SPIM_CONFIG_DEBUG_COLOR #define NRFX_SPIM_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_SPIS_ENABLED - nrfx_spis - SPIS peripheral driver //========================================================== #ifndef NRFX_SPIS_ENABLED #define NRFX_SPIS_ENABLED 0 #endif // NRFX_SPIS0_ENABLED - Enable SPIS0 instance #ifndef NRFX_SPIS0_ENABLED #define NRFX_SPIS0_ENABLED 0 #endif // NRFX_SPIS1_ENABLED - Enable SPIS1 instance #ifndef NRFX_SPIS1_ENABLED #define NRFX_SPIS1_ENABLED 0 #endif // NRFX_SPIS2_ENABLED - Enable SPIS2 instance #ifndef NRFX_SPIS2_ENABLED #define NRFX_SPIS2_ENABLED 0 #endif // NRFX_SPIS_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_SPIS_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_SPIS_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_SPIS_DEFAULT_DEF - SPIS default DEF character <0-255> #ifndef NRFX_SPIS_DEFAULT_DEF #define NRFX_SPIS_DEFAULT_DEF 255 #endif // NRFX_SPIS_DEFAULT_ORC - SPIS default ORC character <0-255> #ifndef NRFX_SPIS_DEFAULT_ORC #define NRFX_SPIS_DEFAULT_ORC 255 #endif // NRFX_SPIS_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_SPIS_CONFIG_LOG_ENABLED #define NRFX_SPIS_CONFIG_LOG_ENABLED 0 #endif // NRFX_SPIS_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_SPIS_CONFIG_LOG_LEVEL #define NRFX_SPIS_CONFIG_LOG_LEVEL 3 #endif // NRFX_SPIS_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SPIS_CONFIG_INFO_COLOR #define NRFX_SPIS_CONFIG_INFO_COLOR 0 #endif // NRFX_SPIS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SPIS_CONFIG_DEBUG_COLOR #define NRFX_SPIS_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_SPI_ENABLED - nrfx_spi - SPI peripheral driver //========================================================== #ifndef NRFX_SPI_ENABLED #define NRFX_SPI_ENABLED 0 #endif // NRFX_SPI0_ENABLED - Enable SPI0 instance #ifndef NRFX_SPI0_ENABLED #define NRFX_SPI0_ENABLED 0 #endif // NRFX_SPI1_ENABLED - Enable SPI1 instance #ifndef NRFX_SPI1_ENABLED #define NRFX_SPI1_ENABLED 0 #endif // NRFX_SPI2_ENABLED - Enable SPI2 instance #ifndef NRFX_SPI2_ENABLED #define NRFX_SPI2_ENABLED 0 #endif // NRFX_SPI_MISO_PULL_CFG - MISO pin pull configuration. // <0=> NRF_GPIO_PIN_NOPULL // <1=> NRF_GPIO_PIN_PULLDOWN // <3=> NRF_GPIO_PIN_PULLUP #ifndef NRFX_SPI_MISO_PULL_CFG #define NRFX_SPI_MISO_PULL_CFG 1 #endif // NRFX_SPI_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_SPI_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_SPI_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_SPI_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_SPI_CONFIG_LOG_ENABLED #define NRFX_SPI_CONFIG_LOG_ENABLED 0 #endif // NRFX_SPI_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_SPI_CONFIG_LOG_LEVEL #define NRFX_SPI_CONFIG_LOG_LEVEL 3 #endif // NRFX_SPI_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SPI_CONFIG_INFO_COLOR #define NRFX_SPI_CONFIG_INFO_COLOR 0 #endif // NRFX_SPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SPI_CONFIG_DEBUG_COLOR #define NRFX_SPI_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_SWI_ENABLED - nrfx_swi - SWI/EGU peripheral allocator //========================================================== #ifndef NRFX_SWI_ENABLED #define NRFX_SWI_ENABLED 0 #endif // NRFX_EGU_ENABLED - Enable EGU support #ifndef NRFX_EGU_ENABLED #define NRFX_EGU_ENABLED 0 #endif // NRFX_SWI0_DISABLED - Exclude SWI0 from being utilized by the driver #ifndef NRFX_SWI0_DISABLED #define NRFX_SWI0_DISABLED 0 #endif // NRFX_SWI1_DISABLED - Exclude SWI1 from being utilized by the driver #ifndef NRFX_SWI1_DISABLED #define NRFX_SWI1_DISABLED 0 #endif // NRFX_SWI2_DISABLED - Exclude SWI2 from being utilized by the driver #ifndef NRFX_SWI2_DISABLED #define NRFX_SWI2_DISABLED 0 #endif // NRFX_SWI3_DISABLED - Exclude SWI3 from being utilized by the driver #ifndef NRFX_SWI3_DISABLED #define NRFX_SWI3_DISABLED 0 #endif // NRFX_SWI4_DISABLED - Exclude SWI4 from being utilized by the driver #ifndef NRFX_SWI4_DISABLED #define NRFX_SWI4_DISABLED 0 #endif // NRFX_SWI5_DISABLED - Exclude SWI5 from being utilized by the driver #ifndef NRFX_SWI5_DISABLED #define NRFX_SWI5_DISABLED 0 #endif // NRFX_SWI_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_SWI_CONFIG_LOG_ENABLED #define NRFX_SWI_CONFIG_LOG_ENABLED 0 #endif // NRFX_SWI_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_SWI_CONFIG_LOG_LEVEL #define NRFX_SWI_CONFIG_LOG_LEVEL 3 #endif // NRFX_SWI_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SWI_CONFIG_INFO_COLOR #define NRFX_SWI_CONFIG_INFO_COLOR 0 #endif // NRFX_SWI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_SWI_CONFIG_DEBUG_COLOR #define NRFX_SWI_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_TIMER_ENABLED - nrfx_timer - TIMER periperal driver //========================================================== #ifndef NRFX_TIMER_ENABLED #define NRFX_TIMER_ENABLED 0 #endif // NRFX_TIMER0_ENABLED - Enable TIMER0 instance #ifndef NRFX_TIMER0_ENABLED #define NRFX_TIMER0_ENABLED 0 #endif // NRFX_TIMER1_ENABLED - Enable TIMER1 instance #ifndef NRFX_TIMER1_ENABLED #define NRFX_TIMER1_ENABLED 0 #endif // NRFX_TIMER2_ENABLED - Enable TIMER2 instance #ifndef NRFX_TIMER2_ENABLED #define NRFX_TIMER2_ENABLED 0 #endif // NRFX_TIMER3_ENABLED - Enable TIMER3 instance #ifndef NRFX_TIMER3_ENABLED #define NRFX_TIMER3_ENABLED 0 #endif // NRFX_TIMER4_ENABLED - Enable TIMER4 instance #ifndef NRFX_TIMER4_ENABLED #define NRFX_TIMER4_ENABLED 0 #endif // NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY - Timer frequency if in Timer mode // <0=> 16 MHz // <1=> 8 MHz // <2=> 4 MHz // <3=> 2 MHz // <4=> 1 MHz // <5=> 500 kHz // <6=> 250 kHz // <7=> 125 kHz // <8=> 62.5 kHz // <9=> 31.25 kHz #ifndef NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY #define NRFX_TIMER_DEFAULT_CONFIG_FREQUENCY 0 #endif // NRFX_TIMER_DEFAULT_CONFIG_MODE - Timer mode or operation // <0=> Timer // <1=> Counter #ifndef NRFX_TIMER_DEFAULT_CONFIG_MODE #define NRFX_TIMER_DEFAULT_CONFIG_MODE 0 #endif // NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH - Timer counter bit width // <0=> 16 bit // <1=> 8 bit // <2=> 24 bit // <3=> 32 bit #ifndef NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH #define NRFX_TIMER_DEFAULT_CONFIG_BIT_WIDTH 0 #endif // NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_TIMER_CONFIG_LOG_ENABLED #define NRFX_TIMER_CONFIG_LOG_ENABLED 0 #endif // NRFX_TIMER_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_TIMER_CONFIG_LOG_LEVEL #define NRFX_TIMER_CONFIG_LOG_LEVEL 3 #endif // NRFX_TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_TIMER_CONFIG_INFO_COLOR #define NRFX_TIMER_CONFIG_INFO_COLOR 0 #endif // NRFX_TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_TIMER_CONFIG_DEBUG_COLOR #define NRFX_TIMER_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_TWIM_ENABLED - nrfx_twim - TWIM peripheral driver //========================================================== #ifndef NRFX_TWIM_ENABLED #define NRFX_TWIM_ENABLED 0 #endif // NRFX_TWIM0_ENABLED - Enable TWIM0 instance #ifndef NRFX_TWIM0_ENABLED #define NRFX_TWIM0_ENABLED 0 #endif // NRFX_TWIM1_ENABLED - Enable TWIM1 instance #ifndef NRFX_TWIM1_ENABLED #define NRFX_TWIM1_ENABLED 0 #endif // NRFX_TWIM_DEFAULT_CONFIG_FREQUENCY - Frequency // <26738688=> 100k // <67108864=> 250k // <104857600=> 400k #ifndef NRFX_TWIM_DEFAULT_CONFIG_FREQUENCY #define NRFX_TWIM_DEFAULT_CONFIG_FREQUENCY 26738688 #endif // NRFX_TWIM_DEFAULT_CONFIG_HOLD_BUS_UNINIT - Enables bus holding after uninit #ifndef NRFX_TWIM_DEFAULT_CONFIG_HOLD_BUS_UNINIT #define NRFX_TWIM_DEFAULT_CONFIG_HOLD_BUS_UNINIT 0 #endif // NRFX_TWIM_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_TWIM_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_TWIM_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_TWIM_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_TWIM_CONFIG_LOG_ENABLED #define NRFX_TWIM_CONFIG_LOG_ENABLED 0 #endif // NRFX_TWIM_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_TWIM_CONFIG_LOG_LEVEL #define NRFX_TWIM_CONFIG_LOG_LEVEL 3 #endif // NRFX_TWIM_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_TWIM_CONFIG_INFO_COLOR #define NRFX_TWIM_CONFIG_INFO_COLOR 0 #endif // NRFX_TWIM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_TWIM_CONFIG_DEBUG_COLOR #define NRFX_TWIM_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_TWIS_ENABLED - nrfx_twis - TWIS peripheral driver //========================================================== #ifndef NRFX_TWIS_ENABLED #define NRFX_TWIS_ENABLED 0 #endif // NRFX_TWIS0_ENABLED - Enable TWIS0 instance #ifndef NRFX_TWIS0_ENABLED #define NRFX_TWIS0_ENABLED 0 #endif // NRFX_TWIS1_ENABLED - Enable TWIS1 instance #ifndef NRFX_TWIS1_ENABLED #define NRFX_TWIS1_ENABLED 0 #endif // NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY - Assume that any instance would be initialized only // once // Optimization flag. Registers used by TWIS are shared by other peripherals. Normally, during // initialization driver tries to clear all registers to known state before doing the initialization // itself. This gives initialization safe procedure, no matter when it would be called. If you // activate TWIS only once and do never uninitialize it - set this flag to 1 what gives more optimal // code. #ifndef NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY #define NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY 0 #endif // NRFX_TWIS_NO_SYNC_MODE - Remove support for synchronous mode // Synchronous mode would be used in specific situations. And it uses some additional code and // data memory to safely process state machine by polling it in status functions. If this // functionality is not required it may be disabled to free some resources. #ifndef NRFX_TWIS_NO_SYNC_MODE #define NRFX_TWIS_NO_SYNC_MODE 0 #endif // NRFX_TWIS_DEFAULT_CONFIG_ADDR0 - Address0 #ifndef NRFX_TWIS_DEFAULT_CONFIG_ADDR0 #define NRFX_TWIS_DEFAULT_CONFIG_ADDR0 0 #endif // NRFX_TWIS_DEFAULT_CONFIG_ADDR1 - Address1 #ifndef NRFX_TWIS_DEFAULT_CONFIG_ADDR1 #define NRFX_TWIS_DEFAULT_CONFIG_ADDR1 0 #endif // NRFX_TWIS_DEFAULT_CONFIG_SCL_PULL - SCL pin pull configuration // <0=> Disabled // <1=> Pull down // <3=> Pull up #ifndef NRFX_TWIS_DEFAULT_CONFIG_SCL_PULL #define NRFX_TWIS_DEFAULT_CONFIG_SCL_PULL 0 #endif // NRFX_TWIS_DEFAULT_CONFIG_SDA_PULL - SDA pin pull configuration // <0=> Disabled // <1=> Pull down // <3=> Pull up #ifndef NRFX_TWIS_DEFAULT_CONFIG_SDA_PULL #define NRFX_TWIS_DEFAULT_CONFIG_SDA_PULL 0 #endif // NRFX_TWIS_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_TWIS_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_TWIS_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_TWIS_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_TWIS_CONFIG_LOG_ENABLED #define NRFX_TWIS_CONFIG_LOG_ENABLED 0 #endif // NRFX_TWIS_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_TWIS_CONFIG_LOG_LEVEL #define NRFX_TWIS_CONFIG_LOG_LEVEL 3 #endif // NRFX_TWIS_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_TWIS_CONFIG_INFO_COLOR #define NRFX_TWIS_CONFIG_INFO_COLOR 0 #endif // NRFX_TWIS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_TWIS_CONFIG_DEBUG_COLOR #define NRFX_TWIS_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_TWI_ENABLED - nrfx_twi - TWI peripheral driver //========================================================== #ifndef NRFX_TWI_ENABLED #define NRFX_TWI_ENABLED 0 #endif // NRFX_TWI0_ENABLED - Enable TWI0 instance #ifndef NRFX_TWI0_ENABLED #define NRFX_TWI0_ENABLED 0 #endif // NRFX_TWI1_ENABLED - Enable TWI1 instance #ifndef NRFX_TWI1_ENABLED #define NRFX_TWI1_ENABLED 0 #endif // NRFX_TWI_DEFAULT_CONFIG_FREQUENCY - Frequency // <26738688=> 100k // <67108864=> 250k // <104857600=> 400k #ifndef NRFX_TWI_DEFAULT_CONFIG_FREQUENCY #define NRFX_TWI_DEFAULT_CONFIG_FREQUENCY 26738688 #endif // NRFX_TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT - Enables bus holding after uninit #ifndef NRFX_TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT #define NRFX_TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT 0 #endif // NRFX_TWI_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_TWI_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_TWI_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_TWI_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_TWI_CONFIG_LOG_ENABLED #define NRFX_TWI_CONFIG_LOG_ENABLED 0 #endif // NRFX_TWI_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_TWI_CONFIG_LOG_LEVEL #define NRFX_TWI_CONFIG_LOG_LEVEL 3 #endif // NRFX_TWI_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_TWI_CONFIG_INFO_COLOR #define NRFX_TWI_CONFIG_INFO_COLOR 0 #endif // NRFX_TWI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_TWI_CONFIG_DEBUG_COLOR #define NRFX_TWI_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_UARTE_ENABLED - nrfx_uarte - UARTE peripheral driver //========================================================== #ifndef NRFX_UARTE_ENABLED #define NRFX_UARTE_ENABLED 1 #endif // NRFX_UARTE0_ENABLED - Enable UARTE0 instance #ifndef NRFX_UARTE0_ENABLED #define NRFX_UARTE0_ENABLED 0 #endif // NRFX_UARTE1_ENABLED - Enable UARTE1 instance #ifndef NRFX_UARTE1_ENABLED #define NRFX_UARTE1_ENABLED 0 #endif // NRFX_UARTE_DEFAULT_CONFIG_HWFC - Hardware Flow Control // <0=> Disabled // <1=> Enabled #ifndef NRFX_UARTE_DEFAULT_CONFIG_HWFC #define NRFX_UARTE_DEFAULT_CONFIG_HWFC 0 #endif // NRFX_UARTE_DEFAULT_CONFIG_PARITY - Parity // <0=> Excluded // <14=> Included #ifndef NRFX_UARTE_DEFAULT_CONFIG_PARITY #define NRFX_UARTE_DEFAULT_CONFIG_PARITY 0 #endif // NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE - Default Baudrate // <323584=> 1200 baud // <643072=> 2400 baud // <1290240=> 4800 baud // <2576384=> 9600 baud // <3862528=> 14400 baud // <5152768=> 19200 baud // <7716864=> 28800 baud // <8388608=> 31250 baud // <10289152=> 38400 baud // <15007744=> 56000 baud // <15400960=> 57600 baud // <20615168=> 76800 baud // <30801920=> 115200 baud // <61865984=> 230400 baud // <67108864=> 250000 baud // <121634816=> 460800 baud // <251658240=> 921600 baud // <268435456=> 1000000 baud #ifndef NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE #define NRFX_UARTE_DEFAULT_CONFIG_BAUDRATE 30801920 #endif // NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_UARTE_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_UARTE_CONFIG_LOG_ENABLED #define NRFX_UARTE_CONFIG_LOG_ENABLED 0 #endif // NRFX_UARTE_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_UARTE_CONFIG_LOG_LEVEL #define NRFX_UARTE_CONFIG_LOG_LEVEL 3 #endif // NRFX_UARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_UARTE_CONFIG_INFO_COLOR #define NRFX_UARTE_CONFIG_INFO_COLOR 0 #endif // NRFX_UARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_UARTE_CONFIG_DEBUG_COLOR #define NRFX_UARTE_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_UART_ENABLED - nrfx_uart - UART peripheral driver //========================================================== #ifndef NRFX_UART_ENABLED #define NRFX_UART_ENABLED 1 #endif // NRFX_UART0_ENABLED - Enable UART0 instance #ifndef NRFX_UART0_ENABLED #define NRFX_UART0_ENABLED 0 #endif // NRFX_UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control // <0=> Disabled // <1=> Enabled #ifndef NRFX_UART_DEFAULT_CONFIG_HWFC #define NRFX_UART_DEFAULT_CONFIG_HWFC 0 #endif // NRFX_UART_DEFAULT_CONFIG_PARITY - Parity // <0=> Excluded // <14=> Included #ifndef NRFX_UART_DEFAULT_CONFIG_PARITY #define NRFX_UART_DEFAULT_CONFIG_PARITY 0 #endif // NRFX_UART_DEFAULT_CONFIG_BAUDRATE - Default Baudrate // <323584=> 1200 baud // <643072=> 2400 baud // <1290240=> 4800 baud // <2576384=> 9600 baud // <3866624=> 14400 baud // <5152768=> 19200 baud // <7729152=> 28800 baud // <8388608=> 31250 baud // <10309632=> 38400 baud // <15007744=> 56000 baud // <15462400=> 57600 baud // <20615168=> 76800 baud // <30924800=> 115200 baud // <61845504=> 230400 baud // <67108864=> 250000 baud // <123695104=> 460800 baud // <247386112=> 921600 baud // <268435456=> 1000000 baud #ifndef NRFX_UART_DEFAULT_CONFIG_BAUDRATE #define NRFX_UART_DEFAULT_CONFIG_BAUDRATE 30924800 #endif // NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY #define NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_UART_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_UART_CONFIG_LOG_ENABLED #define NRFX_UART_CONFIG_LOG_ENABLED 0 #endif // NRFX_UART_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_UART_CONFIG_LOG_LEVEL #define NRFX_UART_CONFIG_LOG_LEVEL 3 #endif // NRFX_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_UART_CONFIG_INFO_COLOR #define NRFX_UART_CONFIG_INFO_COLOR 0 #endif // NRFX_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_UART_CONFIG_DEBUG_COLOR #define NRFX_UART_CONFIG_DEBUG_COLOR 0 #endif // // // NRFX_WDT_ENABLED - nrfx_wdt - WDT peripheral driver //========================================================== #ifndef NRFX_WDT_ENABLED #define NRFX_WDT_ENABLED 0 #endif // NRFX_WDT_CONFIG_BEHAVIOUR - WDT behavior in CPU SLEEP or HALT mode // <1=> Run in SLEEP, Pause in HALT // <8=> Pause in SLEEP, Run in HALT // <9=> Run in SLEEP and HALT // <0=> Pause in SLEEP and HALT #ifndef NRFX_WDT_CONFIG_BEHAVIOUR #define NRFX_WDT_CONFIG_BEHAVIOUR 1 #endif // NRFX_WDT_CONFIG_RELOAD_VALUE - Reload value <15-4294967295> #ifndef NRFX_WDT_CONFIG_RELOAD_VALUE #define NRFX_WDT_CONFIG_RELOAD_VALUE 2000 #endif // NRFX_WDT_CONFIG_IRQ_PRIORITY - Interrupt priority // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NRFX_WDT_CONFIG_IRQ_PRIORITY #define NRFX_WDT_CONFIG_IRQ_PRIORITY 6 #endif // NRFX_WDT_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRFX_WDT_CONFIG_LOG_ENABLED #define NRFX_WDT_CONFIG_LOG_ENABLED 0 #endif // NRFX_WDT_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRFX_WDT_CONFIG_LOG_LEVEL #define NRFX_WDT_CONFIG_LOG_LEVEL 3 #endif // NRFX_WDT_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_WDT_CONFIG_INFO_COLOR #define NRFX_WDT_CONFIG_INFO_COLOR 0 #endif // NRFX_WDT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRFX_WDT_CONFIG_DEBUG_COLOR #define NRFX_WDT_CONFIG_DEBUG_COLOR 0 #endif // // // NRF_CLOCK_ENABLED - nrf_drv_clock - CLOCK peripheral driver - legacy layer //========================================================== #ifndef NRF_CLOCK_ENABLED #define NRF_CLOCK_ENABLED 1 #endif // CLOCK_CONFIG_LF_SRC - LF Clock Source // <0=> RC // <1=> XTAL // <2=> Synth // <131073=> External Low Swing // <196609=> External Full Swing #ifndef CLOCK_CONFIG_LF_SRC #define CLOCK_CONFIG_LF_SRC 1 #endif // CLOCK_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef CLOCK_CONFIG_IRQ_PRIORITY #define CLOCK_CONFIG_IRQ_PRIORITY 6 #endif // // PDM_ENABLED - nrf_drv_pdm - PDM peripheral driver - legacy layer //========================================================== #ifndef PDM_ENABLED #define PDM_ENABLED 0 #endif // PDM_CONFIG_MODE - Mode // <0=> Stereo // <1=> Mono #ifndef PDM_CONFIG_MODE #define PDM_CONFIG_MODE 1 #endif // PDM_CONFIG_EDGE - Edge // <0=> Left falling // <1=> Left rising #ifndef PDM_CONFIG_EDGE #define PDM_CONFIG_EDGE 0 #endif // PDM_CONFIG_CLOCK_FREQ - Clock frequency // <134217728=> 1000k // <138412032=> 1032k (default) // <142606336=> 1067k #ifndef PDM_CONFIG_CLOCK_FREQ #define PDM_CONFIG_CLOCK_FREQ 138412032 #endif // PDM_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef PDM_CONFIG_IRQ_PRIORITY #define PDM_CONFIG_IRQ_PRIORITY 6 #endif // // POWER_ENABLED - nrf_drv_power - POWER peripheral driver - legacy layer //========================================================== #ifndef POWER_ENABLED #define POWER_ENABLED 0 #endif // POWER_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef POWER_CONFIG_IRQ_PRIORITY #define POWER_CONFIG_IRQ_PRIORITY 6 #endif // POWER_CONFIG_DEFAULT_DCDCEN - The default configuration of main DCDC regulator // This settings means only that components for DCDC regulator are installed and it can be // enabled. #ifndef POWER_CONFIG_DEFAULT_DCDCEN #define POWER_CONFIG_DEFAULT_DCDCEN 0 #endif // POWER_CONFIG_DEFAULT_DCDCENHV - The default configuration of High Voltage DCDC regulator // This settings means only that components for DCDC regulator are installed and it can be // enabled. #ifndef POWER_CONFIG_DEFAULT_DCDCENHV #define POWER_CONFIG_DEFAULT_DCDCENHV 0 #endif // // PPI_ENABLED - nrf_drv_ppi - PPI peripheral driver - legacy layer #ifndef PPI_ENABLED #define PPI_ENABLED 0 #endif // PWM_ENABLED - nrf_drv_pwm - PWM peripheral driver - legacy layer //========================================================== #ifndef PWM_ENABLED #define PWM_ENABLED 0 #endif // PWM_DEFAULT_CONFIG_OUT0_PIN - Out0 pin <0-31> #ifndef PWM_DEFAULT_CONFIG_OUT0_PIN #define PWM_DEFAULT_CONFIG_OUT0_PIN 31 #endif // PWM_DEFAULT_CONFIG_OUT1_PIN - Out1 pin <0-31> #ifndef PWM_DEFAULT_CONFIG_OUT1_PIN #define PWM_DEFAULT_CONFIG_OUT1_PIN 31 #endif // PWM_DEFAULT_CONFIG_OUT2_PIN - Out2 pin <0-31> #ifndef PWM_DEFAULT_CONFIG_OUT2_PIN #define PWM_DEFAULT_CONFIG_OUT2_PIN 31 #endif // PWM_DEFAULT_CONFIG_OUT3_PIN - Out3 pin <0-31> #ifndef PWM_DEFAULT_CONFIG_OUT3_PIN #define PWM_DEFAULT_CONFIG_OUT3_PIN 31 #endif // PWM_DEFAULT_CONFIG_BASE_CLOCK - Base clock // <0=> 16 MHz // <1=> 8 MHz // <2=> 4 MHz // <3=> 2 MHz // <4=> 1 MHz // <5=> 500 kHz // <6=> 250 kHz // <7=> 125 kHz #ifndef PWM_DEFAULT_CONFIG_BASE_CLOCK #define PWM_DEFAULT_CONFIG_BASE_CLOCK 4 #endif // PWM_DEFAULT_CONFIG_COUNT_MODE - Count mode // <0=> Up // <1=> Up and Down #ifndef PWM_DEFAULT_CONFIG_COUNT_MODE #define PWM_DEFAULT_CONFIG_COUNT_MODE 0 #endif // PWM_DEFAULT_CONFIG_TOP_VALUE - Top value #ifndef PWM_DEFAULT_CONFIG_TOP_VALUE #define PWM_DEFAULT_CONFIG_TOP_VALUE 1000 #endif // PWM_DEFAULT_CONFIG_LOAD_MODE - Load mode // <0=> Common // <1=> Grouped // <2=> Individual // <3=> Waveform #ifndef PWM_DEFAULT_CONFIG_LOAD_MODE #define PWM_DEFAULT_CONFIG_LOAD_MODE 0 #endif // PWM_DEFAULT_CONFIG_STEP_MODE - Step mode // <0=> Auto // <1=> Triggered #ifndef PWM_DEFAULT_CONFIG_STEP_MODE #define PWM_DEFAULT_CONFIG_STEP_MODE 0 #endif // PWM_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef PWM_DEFAULT_CONFIG_IRQ_PRIORITY #define PWM_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // PWM0_ENABLED - Enable PWM0 instance #ifndef PWM0_ENABLED #define PWM0_ENABLED 0 #endif // PWM1_ENABLED - Enable PWM1 instance #ifndef PWM1_ENABLED #define PWM1_ENABLED 0 #endif // PWM2_ENABLED - Enable PWM2 instance #ifndef PWM2_ENABLED #define PWM2_ENABLED 0 #endif // PWM3_ENABLED - Enable PWM3 instance #ifndef PWM3_ENABLED #define PWM3_ENABLED 0 #endif // // QDEC_ENABLED - nrf_drv_qdec - QDEC peripheral driver - legacy layer //========================================================== #ifndef QDEC_ENABLED #define QDEC_ENABLED 0 #endif // QDEC_CONFIG_REPORTPER - Report period // <0=> 10 Samples // <1=> 40 Samples // <2=> 80 Samples // <3=> 120 Samples // <4=> 160 Samples // <5=> 200 Samples // <6=> 240 Samples // <7=> 280 Samples #ifndef QDEC_CONFIG_REPORTPER #define QDEC_CONFIG_REPORTPER 0 #endif // QDEC_CONFIG_SAMPLEPER - Sample period // <0=> 128 us // <1=> 256 us // <2=> 512 us // <3=> 1024 us // <4=> 2048 us // <5=> 4096 us // <6=> 8192 us // <7=> 16384 us #ifndef QDEC_CONFIG_SAMPLEPER #define QDEC_CONFIG_SAMPLEPER 7 #endif // QDEC_CONFIG_PIO_A - A pin <0-31> #ifndef QDEC_CONFIG_PIO_A #define QDEC_CONFIG_PIO_A 31 #endif // QDEC_CONFIG_PIO_B - B pin <0-31> #ifndef QDEC_CONFIG_PIO_B #define QDEC_CONFIG_PIO_B 31 #endif // QDEC_CONFIG_PIO_LED - LED pin <0-31> #ifndef QDEC_CONFIG_PIO_LED #define QDEC_CONFIG_PIO_LED 31 #endif // QDEC_CONFIG_LEDPRE - LED pre #ifndef QDEC_CONFIG_LEDPRE #define QDEC_CONFIG_LEDPRE 511 #endif // QDEC_CONFIG_LEDPOL - LED polarity // <0=> Active low // <1=> Active high #ifndef QDEC_CONFIG_LEDPOL #define QDEC_CONFIG_LEDPOL 1 #endif // QDEC_CONFIG_DBFEN - Debouncing enable #ifndef QDEC_CONFIG_DBFEN #define QDEC_CONFIG_DBFEN 0 #endif // QDEC_CONFIG_SAMPLE_INTEN - Sample ready interrupt enable #ifndef QDEC_CONFIG_SAMPLE_INTEN #define QDEC_CONFIG_SAMPLE_INTEN 0 #endif // QDEC_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef QDEC_CONFIG_IRQ_PRIORITY #define QDEC_CONFIG_IRQ_PRIORITY 6 #endif // // QSPI_ENABLED - nrf_drv_qspi - QSPI peripheral driver - legacy layer //========================================================== #ifndef QSPI_ENABLED #define QSPI_ENABLED 0 #endif // QSPI_CONFIG_SCK_DELAY - tSHSL, tWHSL and tSHWL in number of 16 MHz periods (62.5 ns). <0-255> #ifndef QSPI_CONFIG_SCK_DELAY #define QSPI_CONFIG_SCK_DELAY 1 #endif // QSPI_CONFIG_XIP_OFFSET - Address offset in the external memory for Execute in Place // operation. #ifndef QSPI_CONFIG_XIP_OFFSET #define QSPI_CONFIG_XIP_OFFSET 0 #endif // QSPI_CONFIG_READOC - Number of data lines and opcode used for reading. // <0=> FastRead // <1=> Read2O // <2=> Read2IO // <3=> Read4O // <4=> Read4IO #ifndef QSPI_CONFIG_READOC #define QSPI_CONFIG_READOC 0 #endif // QSPI_CONFIG_WRITEOC - Number of data lines and opcode used for writing. // <0=> PP // <1=> PP2O // <2=> PP4O // <3=> PP4IO #ifndef QSPI_CONFIG_WRITEOC #define QSPI_CONFIG_WRITEOC 0 #endif // QSPI_CONFIG_ADDRMODE - Addressing mode. // <0=> 24bit // <1=> 32bit #ifndef QSPI_CONFIG_ADDRMODE #define QSPI_CONFIG_ADDRMODE 0 #endif // QSPI_CONFIG_MODE - SPI mode. // <0=> Mode 0 // <1=> Mode 1 #ifndef QSPI_CONFIG_MODE #define QSPI_CONFIG_MODE 0 #endif // QSPI_CONFIG_FREQUENCY - Frequency divider. // <0=> 32MHz/1 // <1=> 32MHz/2 // <2=> 32MHz/3 // <3=> 32MHz/4 // <4=> 32MHz/5 // <5=> 32MHz/6 // <6=> 32MHz/7 // <7=> 32MHz/8 // <8=> 32MHz/9 // <9=> 32MHz/10 // <10=> 32MHz/11 // <11=> 32MHz/12 // <12=> 32MHz/13 // <13=> 32MHz/14 // <14=> 32MHz/15 // <15=> 32MHz/16 #ifndef QSPI_CONFIG_FREQUENCY #define QSPI_CONFIG_FREQUENCY 15 #endif // QSPI_PIN_SCK - SCK pin value. #ifndef QSPI_PIN_SCK #define QSPI_PIN_SCK NRF_QSPI_PIN_NOT_CONNECTED #endif // QSPI_PIN_CSN - CSN pin value. #ifndef QSPI_PIN_CSN #define QSPI_PIN_CSN NRF_QSPI_PIN_NOT_CONNECTED #endif // QSPI_PIN_IO0 - IO0 pin value. #ifndef QSPI_PIN_IO0 #define QSPI_PIN_IO0 NRF_QSPI_PIN_NOT_CONNECTED #endif // QSPI_PIN_IO1 - IO1 pin value. #ifndef QSPI_PIN_IO1 #define QSPI_PIN_IO1 NRF_QSPI_PIN_NOT_CONNECTED #endif // QSPI_PIN_IO2 - IO2 pin value. #ifndef QSPI_PIN_IO2 #define QSPI_PIN_IO2 NRF_QSPI_PIN_NOT_CONNECTED #endif // QSPI_PIN_IO3 - IO3 pin value. #ifndef QSPI_PIN_IO3 #define QSPI_PIN_IO3 NRF_QSPI_PIN_NOT_CONNECTED #endif // QSPI_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef QSPI_CONFIG_IRQ_PRIORITY #define QSPI_CONFIG_IRQ_PRIORITY 6 #endif // // RNG_ENABLED - nrf_drv_rng - RNG peripheral driver - legacy layer //========================================================== #ifndef RNG_ENABLED #define RNG_ENABLED 1 #endif // RNG_CONFIG_ERROR_CORRECTION - Error correction #ifndef RNG_CONFIG_ERROR_CORRECTION #define RNG_CONFIG_ERROR_CORRECTION 1 #endif // RNG_CONFIG_POOL_SIZE - Pool size #ifndef RNG_CONFIG_POOL_SIZE #define RNG_CONFIG_POOL_SIZE 64 #endif // RNG_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef RNG_CONFIG_IRQ_PRIORITY #define RNG_CONFIG_IRQ_PRIORITY 6 #endif // // RTC_ENABLED - nrf_drv_rtc - RTC peripheral driver - legacy layer //========================================================== #ifndef RTC_ENABLED #define RTC_ENABLED 0 #endif // RTC_DEFAULT_CONFIG_FREQUENCY - Frequency <16-32768> #ifndef RTC_DEFAULT_CONFIG_FREQUENCY #define RTC_DEFAULT_CONFIG_FREQUENCY 32768 #endif // RTC_DEFAULT_CONFIG_RELIABLE - Ensures safe compare event triggering #ifndef RTC_DEFAULT_CONFIG_RELIABLE #define RTC_DEFAULT_CONFIG_RELIABLE 0 #endif // RTC_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef RTC_DEFAULT_CONFIG_IRQ_PRIORITY #define RTC_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // RTC0_ENABLED - Enable RTC0 instance #ifndef RTC0_ENABLED #define RTC0_ENABLED 0 #endif // RTC1_ENABLED - Enable RTC1 instance #ifndef RTC1_ENABLED #define RTC1_ENABLED 0 #endif // RTC2_ENABLED - Enable RTC2 instance #ifndef RTC2_ENABLED #define RTC2_ENABLED 0 #endif // NRF_MAXIMUM_LATENCY_US - Maximum possible time[us] in highest priority interrupt #ifndef NRF_MAXIMUM_LATENCY_US #define NRF_MAXIMUM_LATENCY_US 2000 #endif // // SAADC_ENABLED - nrf_drv_saadc - SAADC peripheral driver - legacy layer //========================================================== #ifndef SAADC_ENABLED #define SAADC_ENABLED 0 #endif // SAADC_CONFIG_RESOLUTION - Resolution // <0=> 8 bit // <1=> 10 bit // <2=> 12 bit // <3=> 14 bit #ifndef SAADC_CONFIG_RESOLUTION #define SAADC_CONFIG_RESOLUTION 1 #endif // SAADC_CONFIG_OVERSAMPLE - Sample period // <0=> Disabled // <1=> 2x // <2=> 4x // <3=> 8x // <4=> 16x // <5=> 32x // <6=> 64x // <7=> 128x // <8=> 256x #ifndef SAADC_CONFIG_OVERSAMPLE #define SAADC_CONFIG_OVERSAMPLE 0 #endif // SAADC_CONFIG_LP_MODE - Enabling low power mode #ifndef SAADC_CONFIG_LP_MODE #define SAADC_CONFIG_LP_MODE 0 #endif // SAADC_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef SAADC_CONFIG_IRQ_PRIORITY #define SAADC_CONFIG_IRQ_PRIORITY 6 #endif // // SPIS_ENABLED - nrf_drv_spis - SPIS peripheral driver - legacy layer //========================================================== #ifndef SPIS_ENABLED #define SPIS_ENABLED 0 #endif // SPIS_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef SPIS_DEFAULT_CONFIG_IRQ_PRIORITY #define SPIS_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // SPIS_DEFAULT_MODE - Mode // <0=> MODE_0 // <1=> MODE_1 // <2=> MODE_2 // <3=> MODE_3 #ifndef SPIS_DEFAULT_MODE #define SPIS_DEFAULT_MODE 0 #endif // SPIS_DEFAULT_BIT_ORDER - SPIS default bit order // <0=> MSB first // <1=> LSB first #ifndef SPIS_DEFAULT_BIT_ORDER #define SPIS_DEFAULT_BIT_ORDER 0 #endif // SPIS_DEFAULT_DEF - SPIS default DEF character <0-255> #ifndef SPIS_DEFAULT_DEF #define SPIS_DEFAULT_DEF 255 #endif // SPIS_DEFAULT_ORC - SPIS default ORC character <0-255> #ifndef SPIS_DEFAULT_ORC #define SPIS_DEFAULT_ORC 255 #endif // SPIS0_ENABLED - Enable SPIS0 instance #ifndef SPIS0_ENABLED #define SPIS0_ENABLED 0 #endif // SPIS1_ENABLED - Enable SPIS1 instance #ifndef SPIS1_ENABLED #define SPIS1_ENABLED 0 #endif // SPIS2_ENABLED - Enable SPIS2 instance #ifndef SPIS2_ENABLED #define SPIS2_ENABLED 0 #endif // // SPI_ENABLED - nrf_drv_spi - SPI/SPIM peripheral driver - legacy layer //========================================================== #ifndef SPI_ENABLED #define SPI_ENABLED 0 #endif // SPI_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef SPI_DEFAULT_CONFIG_IRQ_PRIORITY #define SPI_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // NRF_SPI_DRV_MISO_PULLUP_CFG - MISO PIN pull-up configuration. // <0=> NRF_GPIO_PIN_NOPULL // <1=> NRF_GPIO_PIN_PULLDOWN // <3=> NRF_GPIO_PIN_PULLUP #ifndef NRF_SPI_DRV_MISO_PULLUP_CFG #define NRF_SPI_DRV_MISO_PULLUP_CFG 1 #endif // SPI0_ENABLED - Enable SPI0 instance //========================================================== #ifndef SPI0_ENABLED #define SPI0_ENABLED 0 #endif // SPI0_USE_EASY_DMA - Use EasyDMA #ifndef SPI0_USE_EASY_DMA #define SPI0_USE_EASY_DMA 1 #endif // // SPI1_ENABLED - Enable SPI1 instance //========================================================== #ifndef SPI1_ENABLED #define SPI1_ENABLED 0 #endif // SPI1_USE_EASY_DMA - Use EasyDMA #ifndef SPI1_USE_EASY_DMA #define SPI1_USE_EASY_DMA 1 #endif // // SPI2_ENABLED - Enable SPI2 instance //========================================================== #ifndef SPI2_ENABLED #define SPI2_ENABLED 0 #endif // SPI2_USE_EASY_DMA - Use EasyDMA #ifndef SPI2_USE_EASY_DMA #define SPI2_USE_EASY_DMA 1 #endif // // // TIMER_ENABLED - nrf_drv_timer - TIMER periperal driver - legacy layer //========================================================== #ifndef TIMER_ENABLED #define TIMER_ENABLED 0 #endif // TIMER_DEFAULT_CONFIG_FREQUENCY - Timer frequency if in Timer mode // <0=> 16 MHz // <1=> 8 MHz // <2=> 4 MHz // <3=> 2 MHz // <4=> 1 MHz // <5=> 500 kHz // <6=> 250 kHz // <7=> 125 kHz // <8=> 62.5 kHz // <9=> 31.25 kHz #ifndef TIMER_DEFAULT_CONFIG_FREQUENCY #define TIMER_DEFAULT_CONFIG_FREQUENCY 0 #endif // TIMER_DEFAULT_CONFIG_MODE - Timer mode or operation // <0=> Timer // <1=> Counter #ifndef TIMER_DEFAULT_CONFIG_MODE #define TIMER_DEFAULT_CONFIG_MODE 0 #endif // TIMER_DEFAULT_CONFIG_BIT_WIDTH - Timer counter bit width // <0=> 16 bit // <1=> 8 bit // <2=> 24 bit // <3=> 32 bit #ifndef TIMER_DEFAULT_CONFIG_BIT_WIDTH #define TIMER_DEFAULT_CONFIG_BIT_WIDTH 0 #endif // TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef TIMER_DEFAULT_CONFIG_IRQ_PRIORITY #define TIMER_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // TIMER0_ENABLED - Enable TIMER0 instance #ifndef TIMER0_ENABLED #define TIMER0_ENABLED 0 #endif // TIMER1_ENABLED - Enable TIMER1 instance #ifndef TIMER1_ENABLED #define TIMER1_ENABLED 0 #endif // TIMER2_ENABLED - Enable TIMER2 instance #ifndef TIMER2_ENABLED #define TIMER2_ENABLED 0 #endif // TIMER3_ENABLED - Enable TIMER3 instance #ifndef TIMER3_ENABLED #define TIMER3_ENABLED 0 #endif // TIMER4_ENABLED - Enable TIMER4 instance #ifndef TIMER4_ENABLED #define TIMER4_ENABLED 0 #endif // // TWIS_ENABLED - nrf_drv_twis - TWIS peripheral driver - legacy layer //========================================================== #ifndef TWIS_ENABLED #define TWIS_ENABLED 0 #endif // TWIS0_ENABLED - Enable TWIS0 instance #ifndef TWIS0_ENABLED #define TWIS0_ENABLED 0 #endif // TWIS1_ENABLED - Enable TWIS1 instance #ifndef TWIS1_ENABLED #define TWIS1_ENABLED 0 #endif // TWIS_ASSUME_INIT_AFTER_RESET_ONLY - Assume that any instance would be initialized only once // Optimization flag. Registers used by TWIS are shared by other peripherals. Normally, during // initialization driver tries to clear all registers to known state before doing the initialization // itself. This gives initialization safe procedure, no matter when it would be called. If you // activate TWIS only once and do never uninitialize it - set this flag to 1 what gives more optimal // code. #ifndef TWIS_ASSUME_INIT_AFTER_RESET_ONLY #define TWIS_ASSUME_INIT_AFTER_RESET_ONLY 0 #endif // TWIS_NO_SYNC_MODE - Remove support for synchronous mode // Synchronous mode would be used in specific situations. And it uses some additional code and // data memory to safely process state machine by polling it in status functions. If this // functionality is not required it may be disabled to free some resources. #ifndef TWIS_NO_SYNC_MODE #define TWIS_NO_SYNC_MODE 0 #endif // TWIS_DEFAULT_CONFIG_ADDR0 - Address0 #ifndef TWIS_DEFAULT_CONFIG_ADDR0 #define TWIS_DEFAULT_CONFIG_ADDR0 0 #endif // TWIS_DEFAULT_CONFIG_ADDR1 - Address1 #ifndef TWIS_DEFAULT_CONFIG_ADDR1 #define TWIS_DEFAULT_CONFIG_ADDR1 0 #endif // TWIS_DEFAULT_CONFIG_SCL_PULL - SCL pin pull configuration // <0=> Disabled // <1=> Pull down // <3=> Pull up #ifndef TWIS_DEFAULT_CONFIG_SCL_PULL #define TWIS_DEFAULT_CONFIG_SCL_PULL 0 #endif // TWIS_DEFAULT_CONFIG_SDA_PULL - SDA pin pull configuration // <0=> Disabled // <1=> Pull down // <3=> Pull up #ifndef TWIS_DEFAULT_CONFIG_SDA_PULL #define TWIS_DEFAULT_CONFIG_SDA_PULL 0 #endif // TWIS_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef TWIS_DEFAULT_CONFIG_IRQ_PRIORITY #define TWIS_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // // TWI_ENABLED - nrf_drv_twi - TWI/TWIM peripheral driver - legacy layer //========================================================== #ifndef TWI_ENABLED #define TWI_ENABLED 0 #endif // TWI_DEFAULT_CONFIG_FREQUENCY - Frequency // <26738688=> 100k // <67108864=> 250k // <104857600=> 400k #ifndef TWI_DEFAULT_CONFIG_FREQUENCY #define TWI_DEFAULT_CONFIG_FREQUENCY 26738688 #endif // TWI_DEFAULT_CONFIG_CLR_BUS_INIT - Enables bus clearing procedure during init #ifndef TWI_DEFAULT_CONFIG_CLR_BUS_INIT #define TWI_DEFAULT_CONFIG_CLR_BUS_INIT 0 #endif // TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT - Enables bus holding after uninit #ifndef TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT #define TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT 0 #endif // TWI_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef TWI_DEFAULT_CONFIG_IRQ_PRIORITY #define TWI_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // TWI0_ENABLED - Enable TWI0 instance //========================================================== #ifndef TWI0_ENABLED #define TWI0_ENABLED 0 #endif // TWI0_USE_EASY_DMA - Use EasyDMA (if present) #ifndef TWI0_USE_EASY_DMA #define TWI0_USE_EASY_DMA 0 #endif // // TWI1_ENABLED - Enable TWI1 instance //========================================================== #ifndef TWI1_ENABLED #define TWI1_ENABLED 0 #endif // TWI1_USE_EASY_DMA - Use EasyDMA (if present) #ifndef TWI1_USE_EASY_DMA #define TWI1_USE_EASY_DMA 0 #endif // // // UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver - legacy layer //========================================================== #ifndef UART_ENABLED #define UART_ENABLED 1 #endif // UART_DEFAULT_CONFIG_HWFC - Hardware Flow Control // <0=> Disabled // <1=> Enabled #ifndef UART_DEFAULT_CONFIG_HWFC #define UART_DEFAULT_CONFIG_HWFC 0 #endif // UART_DEFAULT_CONFIG_PARITY - Parity // <0=> Excluded // <14=> Included #ifndef UART_DEFAULT_CONFIG_PARITY #define UART_DEFAULT_CONFIG_PARITY 0 #endif // UART_DEFAULT_CONFIG_BAUDRATE - Default Baudrate // <323584=> 1200 baud // <643072=> 2400 baud // <1290240=> 4800 baud // <2576384=> 9600 baud // <3862528=> 14400 baud // <5152768=> 19200 baud // <7716864=> 28800 baud // <10289152=> 38400 baud // <15400960=> 57600 baud // <20615168=> 76800 baud // <30801920=> 115200 baud // <61865984=> 230400 baud // <67108864=> 250000 baud // <121634816=> 460800 baud // <251658240=> 921600 baud // <268435456=> 1000000 baud #ifndef UART_DEFAULT_CONFIG_BAUDRATE #define UART_DEFAULT_CONFIG_BAUDRATE 30801920 #endif // UART_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef UART_DEFAULT_CONFIG_IRQ_PRIORITY #define UART_DEFAULT_CONFIG_IRQ_PRIORITY 6 #endif // UART_EASY_DMA_SUPPORT - Driver supporting EasyDMA #ifndef UART_EASY_DMA_SUPPORT #define UART_EASY_DMA_SUPPORT 1 #endif // UART_LEGACY_SUPPORT - Driver supporting Legacy mode #ifndef UART_LEGACY_SUPPORT #define UART_LEGACY_SUPPORT 1 #endif // UART0_ENABLED - Enable UART0 instance //========================================================== #ifndef UART0_ENABLED #define UART0_ENABLED 1 #endif // UART0_CONFIG_USE_EASY_DMA - Default setting for using EasyDMA #ifndef UART0_CONFIG_USE_EASY_DMA #define UART0_CONFIG_USE_EASY_DMA 1 #endif // // UART1_ENABLED - Enable UART1 instance //========================================================== #ifndef UART1_ENABLED #define UART1_ENABLED 0 #endif // // // USBD_ENABLED - nrf_drv_usbd - USB driver //========================================================== #ifndef USBD_ENABLED #define USBD_ENABLED 0 #endif // USBD_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef USBD_CONFIG_IRQ_PRIORITY #define USBD_CONFIG_IRQ_PRIORITY 6 #endif // USBD_CONFIG_DMASCHEDULER_MODE - USBD SMA scheduler working scheme // <0=> Prioritized access // <1=> Round Robin #ifndef USBD_CONFIG_DMASCHEDULER_MODE #define USBD_CONFIG_DMASCHEDULER_MODE 0 #endif // USBD_CONFIG_DMASCHEDULER_ISO_BOOST - Give priority to isochronous transfers // This option gives priority to isochronous transfers. // Enabling it assures that isochronous transfers are always processed, // even if multiple other transfers are pending. // Isochronous endpoints are prioritized before the usbd_dma_scheduler_algorithm // function is called, so the option is independent of the algorithm chosen. #ifndef USBD_CONFIG_DMASCHEDULER_ISO_BOOST #define USBD_CONFIG_DMASCHEDULER_ISO_BOOST 1 #endif // USBD_CONFIG_ISO_IN_ZLP - Respond to an IN token on ISO IN endpoint with ZLP when no data is // ready // If set, ISO IN endpoint will respond to an IN token with ZLP when no data is ready to be // sent. Else, there will be no response. #ifndef USBD_CONFIG_ISO_IN_ZLP #define USBD_CONFIG_ISO_IN_ZLP 0 #endif // // WDT_ENABLED - nrf_drv_wdt - WDT peripheral driver - legacy layer //========================================================== #ifndef WDT_ENABLED #define WDT_ENABLED 0 #endif // WDT_CONFIG_BEHAVIOUR - WDT behavior in CPU SLEEP or HALT mode // <1=> Run in SLEEP, Pause in HALT // <8=> Pause in SLEEP, Run in HALT // <9=> Run in SLEEP and HALT // <0=> Pause in SLEEP and HALT #ifndef WDT_CONFIG_BEHAVIOUR #define WDT_CONFIG_BEHAVIOUR 1 #endif // WDT_CONFIG_RELOAD_VALUE - Reload value <15-4294967295> #ifndef WDT_CONFIG_RELOAD_VALUE #define WDT_CONFIG_RELOAD_VALUE 2000 #endif // WDT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef WDT_CONFIG_IRQ_PRIORITY #define WDT_CONFIG_IRQ_PRIORITY 6 #endif // // //========================================================== // nRF_Drivers_External //========================================================== // NRF_TWI_SENSOR_ENABLED - nrf_twi_sensor - nRF TWI Sensor module #ifndef NRF_TWI_SENSOR_ENABLED #define NRF_TWI_SENSOR_ENABLED 0 #endif // //========================================================== // nRF_Libraries //========================================================== // APP_GPIOTE_ENABLED - app_gpiote - GPIOTE events dispatcher #ifndef APP_GPIOTE_ENABLED #define APP_GPIOTE_ENABLED 0 #endif // APP_PWM_ENABLED - app_pwm - PWM functionality #ifndef APP_PWM_ENABLED #define APP_PWM_ENABLED 0 #endif // APP_SCHEDULER_ENABLED - app_scheduler - Events scheduler //========================================================== #ifndef APP_SCHEDULER_ENABLED #define APP_SCHEDULER_ENABLED 1 #endif // APP_SCHEDULER_WITH_PAUSE - Enabling pause feature #ifndef APP_SCHEDULER_WITH_PAUSE #define APP_SCHEDULER_WITH_PAUSE 0 #endif // APP_SCHEDULER_WITH_PROFILER - Enabling scheduler profiling #ifndef APP_SCHEDULER_WITH_PROFILER #define APP_SCHEDULER_WITH_PROFILER 0 #endif // // APP_SDCARD_ENABLED - app_sdcard - SD/MMC card support using SPI //========================================================== #ifndef APP_SDCARD_ENABLED #define APP_SDCARD_ENABLED 0 #endif // APP_SDCARD_SPI_INSTANCE - SPI instance used // <0=> 0 // <1=> 1 // <2=> 2 #ifndef APP_SDCARD_SPI_INSTANCE #define APP_SDCARD_SPI_INSTANCE 0 #endif // APP_SDCARD_FREQ_INIT - SPI frequency // <33554432=> 125 kHz // <67108864=> 250 kHz // <134217728=> 500 kHz // <268435456=> 1 MHz // <536870912=> 2 MHz // <1073741824=> 4 MHz // <2147483648=> 8 MHz #ifndef APP_SDCARD_FREQ_INIT #define APP_SDCARD_FREQ_INIT 67108864 #endif // APP_SDCARD_FREQ_DATA - SPI frequency // <33554432=> 125 kHz // <67108864=> 250 kHz // <134217728=> 500 kHz // <268435456=> 1 MHz // <536870912=> 2 MHz // <1073741824=> 4 MHz // <2147483648=> 8 MHz #ifndef APP_SDCARD_FREQ_DATA #define APP_SDCARD_FREQ_DATA 1073741824 #endif // // APP_TIMER_ENABLED - app_timer - Application timer functionality //========================================================== #ifndef APP_TIMER_ENABLED #define APP_TIMER_ENABLED 1 #endif // APP_TIMER_CONFIG_RTC_FREQUENCY - Configure RTC prescaler. // <0=> 32768 Hz // <1=> 16384 Hz // <3=> 8192 Hz // <7=> 4096 Hz // <15=> 2048 Hz // <31=> 1024 Hz #ifndef APP_TIMER_CONFIG_RTC_FREQUENCY #define APP_TIMER_CONFIG_RTC_FREQUENCY 0 #endif // APP_TIMER_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef APP_TIMER_CONFIG_IRQ_PRIORITY #define APP_TIMER_CONFIG_IRQ_PRIORITY 6 #endif // APP_TIMER_CONFIG_OP_QUEUE_SIZE - Capacity of timer requests queue. // Size of the queue depends on how many timers are used // in the system, how often timers are started and overall // system latency. If queue size is too small app_timer calls // will fail. #ifndef APP_TIMER_CONFIG_OP_QUEUE_SIZE #define APP_TIMER_CONFIG_OP_QUEUE_SIZE 10 #endif // APP_TIMER_CONFIG_USE_SCHEDULER - Enable scheduling app_timer events to app_scheduler #ifndef APP_TIMER_CONFIG_USE_SCHEDULER #define APP_TIMER_CONFIG_USE_SCHEDULER 0 #endif // APP_TIMER_KEEPS_RTC_ACTIVE - Enable RTC always on // If option is enabled RTC is kept running even if there is no active timers. // This option can be used when app_timer is used for timestamping. #ifndef APP_TIMER_KEEPS_RTC_ACTIVE #define APP_TIMER_KEEPS_RTC_ACTIVE 0 #endif // APP_TIMER_SAFE_WINDOW_MS - Maximum possible latency (in milliseconds) of handling app_timer // event. Maximum possible timeout that can be set is reduced by safe window. Example: RTC // frequency 16384 Hz, maximum possible timeout 1024 seconds - APP_TIMER_SAFE_WINDOW_MS. Since // RTC is not stopped when processor is halted in debugging session, this value must cover it if // debugging is needed. It is possible to halt processor for APP_TIMER_SAFE_WINDOW_MS without // corrupting app_timer behavior. #ifndef APP_TIMER_SAFE_WINDOW_MS #define APP_TIMER_SAFE_WINDOW_MS 300000 #endif // App Timer Legacy configuration - Legacy configuration. //========================================================== // APP_TIMER_WITH_PROFILER - Enable app_timer profiling #ifndef APP_TIMER_WITH_PROFILER #define APP_TIMER_WITH_PROFILER 0 #endif // APP_TIMER_CONFIG_SWI_NUMBER - Configure SWI instance used. #ifndef APP_TIMER_CONFIG_SWI_NUMBER #define APP_TIMER_CONFIG_SWI_NUMBER 0 #endif // //========================================================== // // APP_USBD_AUDIO_ENABLED - app_usbd_audio - USB AUDIO class #ifndef APP_USBD_AUDIO_ENABLED #define APP_USBD_AUDIO_ENABLED 0 #endif // APP_USBD_ENABLED - app_usbd - USB Device library //========================================================== #ifndef APP_USBD_ENABLED #define APP_USBD_ENABLED 0 #endif // APP_USBD_VID - Vendor ID. <0x0000-0xFFFF> // Note: This value is not editable in Configuration Wizard. // Vendor ID ordered from USB IF: http://www.usb.org/developers/vendor/ #ifndef APP_USBD_VID #define APP_USBD_VID 0 #endif // APP_USBD_PID - Product ID. <0x0000-0xFFFF> // Note: This value is not editable in Configuration Wizard. // Selected Product ID #ifndef APP_USBD_PID #define APP_USBD_PID 0 #endif // APP_USBD_DEVICE_VER_MAJOR - Device version, major part. <0-99> // Device version, will be converted automatically to BCD notation. Use just decimal values. #ifndef APP_USBD_DEVICE_VER_MAJOR #define APP_USBD_DEVICE_VER_MAJOR 1 #endif // APP_USBD_DEVICE_VER_MINOR - Device version, minor part. <0-99> // Device version, will be converted automatically to BCD notation. Use just decimal values. #ifndef APP_USBD_DEVICE_VER_MINOR #define APP_USBD_DEVICE_VER_MINOR 0 #endif // APP_USBD_CONFIG_SELF_POWERED - Self-powered device, as opposed to bus-powered. #ifndef APP_USBD_CONFIG_SELF_POWERED #define APP_USBD_CONFIG_SELF_POWERED 1 #endif // APP_USBD_CONFIG_MAX_POWER - MaxPower field in configuration descriptor in milliamps. <0-500> #ifndef APP_USBD_CONFIG_MAX_POWER #define APP_USBD_CONFIG_MAX_POWER 500 #endif // APP_USBD_CONFIG_POWER_EVENTS_PROCESS - Process power events. // Enable processing power events in USB event handler. #ifndef APP_USBD_CONFIG_POWER_EVENTS_PROCESS #define APP_USBD_CONFIG_POWER_EVENTS_PROCESS 1 #endif // APP_USBD_CONFIG_EVENT_QUEUE_ENABLE - Enable event queue. // This is the default configuration when all the events are placed into internal queue. // Disable it when an external queue is used like app_scheduler or if you wish to process all // events inside interrupts. Processing all events from the interrupt level adds requirement not // to call any functions that modifies the USBD library state from the context higher than USB // interrupt context. Functions that modify USBD state are functions for sleep, wakeup, start, // stop, enable, and disable. //========================================================== #ifndef APP_USBD_CONFIG_EVENT_QUEUE_ENABLE #define APP_USBD_CONFIG_EVENT_QUEUE_ENABLE 1 #endif // APP_USBD_CONFIG_EVENT_QUEUE_SIZE - The size of the event queue. <16-64> // The size of the queue for the events that would be processed in the main loop. #ifndef APP_USBD_CONFIG_EVENT_QUEUE_SIZE #define APP_USBD_CONFIG_EVENT_QUEUE_SIZE 32 #endif // APP_USBD_CONFIG_SOF_HANDLING_MODE - Change SOF events handling mode. // Normal queue - SOF events are pushed normally into the event queue. // Compress queue - SOF events are counted and binded with other events or executed when the // queue is empty. This prevents the queue from filling up with SOF events. // Interrupt - SOF events are processed in interrupt. <0=> Normal queue <1=> Compress queue // <2=> Interrupt #ifndef APP_USBD_CONFIG_SOF_HANDLING_MODE #define APP_USBD_CONFIG_SOF_HANDLING_MODE 1 #endif // // APP_USBD_CONFIG_SOF_TIMESTAMP_PROVIDE - Provide a function that generates timestamps for // logs based on the current SOF. // The function app_usbd_sof_timestamp_get is implemented if the logger is enabled. // Use it when initializing the logger. // SOF processing is always enabled when this configuration parameter is active. // Note: This option is configured outside of APP_USBD_CONFIG_LOG_ENABLED. // This means that it works even if the logging in this very module is disabled. #ifndef APP_USBD_CONFIG_SOF_TIMESTAMP_PROVIDE #define APP_USBD_CONFIG_SOF_TIMESTAMP_PROVIDE 0 #endif // APP_USBD_CONFIG_DESC_STRING_SIZE - Maximum size of the NULL-terminated string of the string // descriptor. <31-254> // 31 characters can be stored in the internal USB buffer used for transfers. // Any value higher than 31 creates an additional buffer just for descriptor strings. #ifndef APP_USBD_CONFIG_DESC_STRING_SIZE #define APP_USBD_CONFIG_DESC_STRING_SIZE 31 #endif // APP_USBD_CONFIG_DESC_STRING_UTF_ENABLED - Enable UTF8 conversion. // Enable UTF8-encoded characters. In normal processing, only ASCII characters are available. #ifndef APP_USBD_CONFIG_DESC_STRING_UTF_ENABLED #define APP_USBD_CONFIG_DESC_STRING_UTF_ENABLED 0 #endif // APP_USBD_STRINGS_LANGIDS - Supported languages identifiers. // Note: This value is not editable in Configuration Wizard. // Comma-separated list of supported languages. #ifndef APP_USBD_STRINGS_LANGIDS #define APP_USBD_STRINGS_LANGIDS \ APP_USBD_LANG_AND_SUBLANG(APP_USBD_LANG_ENGLISH, APP_USBD_SUBLANG_ENGLISH_US) #endif // APP_USBD_STRING_ID_MANUFACTURER - Define manufacturer string ID. // Setting ID to 0 disables the string. //========================================================== #ifndef APP_USBD_STRING_ID_MANUFACTURER #define APP_USBD_STRING_ID_MANUFACTURER 1 #endif // APP_USBD_STRINGS_MANUFACTURER_EXTERN - Define whether @ref APP_USBD_STRINGS_MANUFACTURER is // created by macro or declared as a global variable. #ifndef APP_USBD_STRINGS_MANUFACTURER_EXTERN #define APP_USBD_STRINGS_MANUFACTURER_EXTERN 0 #endif // APP_USBD_STRINGS_MANUFACTURER - String descriptor for the manufacturer name. // Note: This value is not editable in Configuration Wizard. // Comma-separated list of manufacturer names for each defined language. // Use @ref APP_USBD_STRING_DESC macro to create string descriptor from a NULL-terminated // string. Use @ref APP_USBD_STRING_RAW8_DESC macro to create string descriptor from // comma-separated uint8_t values. Use @ref APP_USBD_STRING_RAW16_DESC macro to create string // descriptor from comma-separated uint16_t values. Alternatively, configure the macro to point // to any internal variable pointer that already contains the descriptor. Setting string to NULL // disables that string. The order of manufacturer names must be the same like in @ref // APP_USBD_STRINGS_LANGIDS. #ifndef APP_USBD_STRINGS_MANUFACTURER #define APP_USBD_STRINGS_MANUFACTURER APP_USBD_STRING_DESC("Nordic Semiconductor") #endif // // APP_USBD_STRING_ID_PRODUCT - Define product string ID. // Setting ID to 0 disables the string. //========================================================== #ifndef APP_USBD_STRING_ID_PRODUCT #define APP_USBD_STRING_ID_PRODUCT 2 #endif // APP_USBD_STRINGS_PRODUCT_EXTERN - Define whether @ref APP_USBD_STRINGS_PRODUCT is created by // macro or declared as a global variable. #ifndef APP_USBD_STRINGS_PRODUCT_EXTERN #define APP_USBD_STRINGS_PRODUCT_EXTERN 0 #endif // APP_USBD_STRINGS_PRODUCT - String descriptor for the product name. // Note: This value is not editable in Configuration Wizard. // List of product names that is defined the same way like in @ref // APP_USBD_STRINGS_MANUFACTURER. #ifndef APP_USBD_STRINGS_PRODUCT #define APP_USBD_STRINGS_PRODUCT APP_USBD_STRING_DESC("nRF52 USB Product") #endif // // APP_USBD_STRING_ID_SERIAL - Define serial number string ID. // Setting ID to 0 disables the string. //========================================================== #ifndef APP_USBD_STRING_ID_SERIAL #define APP_USBD_STRING_ID_SERIAL 3 #endif // APP_USBD_STRING_SERIAL_EXTERN - Define whether @ref APP_USBD_STRING_SERIAL is created by // macro or declared as a global variable. #ifndef APP_USBD_STRING_SERIAL_EXTERN #define APP_USBD_STRING_SERIAL_EXTERN 0 #endif // APP_USBD_STRING_SERIAL - String descriptor for the serial number. // Note: This value is not editable in Configuration Wizard. // Serial number that is defined the same way like in @ref APP_USBD_STRINGS_MANUFACTURER. #ifndef APP_USBD_STRING_SERIAL #define APP_USBD_STRING_SERIAL APP_USBD_STRING_DESC("000000000000") #endif // // APP_USBD_STRING_ID_CONFIGURATION - Define configuration string ID. // Setting ID to 0 disables the string. //========================================================== #ifndef APP_USBD_STRING_ID_CONFIGURATION #define APP_USBD_STRING_ID_CONFIGURATION 4 #endif // APP_USBD_STRING_CONFIGURATION_EXTERN - Define whether @ref APP_USBD_STRINGS_CONFIGURATION is // created by macro or declared as global variable. #ifndef APP_USBD_STRING_CONFIGURATION_EXTERN #define APP_USBD_STRING_CONFIGURATION_EXTERN 0 #endif // APP_USBD_STRINGS_CONFIGURATION - String descriptor for the device configuration. // Note: This value is not editable in Configuration Wizard. // Configuration string that is defined the same way like in @ref APP_USBD_STRINGS_MANUFACTURER. #ifndef APP_USBD_STRINGS_CONFIGURATION #define APP_USBD_STRINGS_CONFIGURATION APP_USBD_STRING_DESC("Default configuration") #endif // // APP_USBD_STRINGS_USER - Default values for user strings. // Note: This value is not editable in Configuration Wizard. // This value stores all application specific user strings with the default initialization. // The setup is done by X-macros. // Expected macro parameters: // @code // X(mnemonic, [=str_idx], ...) // @endcode // - @c mnemonic: Mnemonic of the string descriptor that would be added to // @ref app_usbd_string_desc_idx_t enumerator. // - @c str_idx : String index value, can be set or left empty. // For example, WinUSB driver requires descriptor to be present on 0xEE index. // Then use X(USBD_STRING_WINUSB, =0xEE, (APP_USBD_STRING_DESC(...))) // - @c ... : List of string descriptors for each defined language. #ifndef APP_USBD_STRINGS_USER #define APP_USBD_STRINGS_USER X(APP_USER_1, , APP_USBD_STRING_DESC("User 1")) #endif // // APP_USBD_HID_ENABLED - app_usbd_hid - USB HID class //========================================================== #ifndef APP_USBD_HID_ENABLED #define APP_USBD_HID_ENABLED 0 #endif // APP_USBD_HID_DEFAULT_IDLE_RATE - Default idle rate for HID class. <0-255> // 0 means indefinite duration, any other value is multiplied by 4 milliseconds. Refer to // Chapter 7.2.4 of HID 1.11 Specification. #ifndef APP_USBD_HID_DEFAULT_IDLE_RATE #define APP_USBD_HID_DEFAULT_IDLE_RATE 0 #endif // APP_USBD_HID_REPORT_IDLE_TABLE_SIZE - Size of idle rate table. <1-255> // Must be higher than the highest report ID used. #ifndef APP_USBD_HID_REPORT_IDLE_TABLE_SIZE #define APP_USBD_HID_REPORT_IDLE_TABLE_SIZE 4 #endif // // APP_USBD_HID_GENERIC_ENABLED - app_usbd_hid_generic - USB HID generic #ifndef APP_USBD_HID_GENERIC_ENABLED #define APP_USBD_HID_GENERIC_ENABLED 0 #endif // APP_USBD_HID_KBD_ENABLED - app_usbd_hid_kbd - USB HID keyboard #ifndef APP_USBD_HID_KBD_ENABLED #define APP_USBD_HID_KBD_ENABLED 0 #endif // APP_USBD_HID_MOUSE_ENABLED - app_usbd_hid_mouse - USB HID mouse #ifndef APP_USBD_HID_MOUSE_ENABLED #define APP_USBD_HID_MOUSE_ENABLED 0 #endif // APP_USBD_MSC_ENABLED - app_usbd_msc - USB MSC class #ifndef APP_USBD_MSC_ENABLED #define APP_USBD_MSC_ENABLED 0 #endif // CRC16_ENABLED - crc16 - CRC16 calculation routines #ifndef CRC16_ENABLED #define CRC16_ENABLED 1 #endif // CRC32_ENABLED - crc32 - CRC32 calculation routines #ifndef CRC32_ENABLED #define CRC32_ENABLED 0 #endif // ECC_ENABLED - ecc - Elliptic Curve Cryptography Library #ifndef ECC_ENABLED #define ECC_ENABLED 0 #endif // FDS_ENABLED - fds - Flash data storage module //========================================================== #ifndef FDS_ENABLED #define FDS_ENABLED 1 #endif // Pages - Virtual page settings // Configure the number of virtual pages to use and their size. //========================================================== // FDS_VIRTUAL_PAGES - Number of virtual flash pages to use. // One of the virtual pages is reserved by the system for garbage collection. // Therefore, the minimum is two virtual pages: one page to store data and one page to be used // by the system for garbage collection. The total amount of flash memory that is used by FDS // amounts to @ref FDS_VIRTUAL_PAGES * @ref FDS_VIRTUAL_PAGE_SIZE * 4 bytes. #ifndef FDS_VIRTUAL_PAGES #define FDS_VIRTUAL_PAGES 3 #endif // FDS_VIRTUAL_PAGE_SIZE - The size of a virtual flash page. // Expressed in number of 4-byte words. // By default, a virtual page is the same size as a physical page. // The size of a virtual page must be a multiple of the size of a physical page. // <1024=> 1024 // <2048=> 2048 #ifndef FDS_VIRTUAL_PAGE_SIZE #define FDS_VIRTUAL_PAGE_SIZE 1024 #endif // //========================================================== // Backend - Backend configuration // Configure which nrf_fstorage backend is used by FDS to write to flash. //========================================================== // FDS_BACKEND - FDS flash backend. // NRF_FSTORAGE_SD uses the nrf_fstorage_sd backend implementation using the SoftDevice API. Use // this if you have a SoftDevice present. NRF_FSTORAGE_NVMC uses the nrf_fstorage_nvmc // implementation. Use this setting if you don't use the SoftDevice. <1=> NRF_FSTORAGE_NVMC <2=> // NRF_FSTORAGE_SD #ifndef FDS_BACKEND #define FDS_BACKEND 2 #endif // //========================================================== // Queue - Queue settings //========================================================== // FDS_OP_QUEUE_SIZE - Size of the internal queue. // Increase this value if you frequently get synchronous FDS_ERR_NO_SPACE_IN_QUEUES errors. #ifndef FDS_OP_QUEUE_SIZE #define FDS_OP_QUEUE_SIZE 4 #endif // //========================================================== // CRC - CRC functionality //========================================================== // FDS_CRC_CHECK_ON_READ - Enable CRC checks. // Save a record's CRC when it is written to flash and check it when the record is opened. // Records with an incorrect CRC can still be 'seen' by the user using FDS functions, but they // cannot be opened. Additionally, they will not be garbage collected until they are deleted. //========================================================== #ifndef FDS_CRC_CHECK_ON_READ #define FDS_CRC_CHECK_ON_READ 0 #endif // FDS_CRC_CHECK_ON_WRITE - Perform a CRC check on newly written records. // Perform a CRC check on newly written records. // This setting can be used to make sure that the record data was not altered while being // written to flash. <1=> Enabled <0=> Disabled #ifndef FDS_CRC_CHECK_ON_WRITE #define FDS_CRC_CHECK_ON_WRITE 0 #endif // // //========================================================== // Users - Number of users //========================================================== // FDS_MAX_USERS - Maximum number of callbacks that can be registered. #ifndef FDS_MAX_USERS #define FDS_MAX_USERS 4 #endif // //========================================================== // // HARDFAULT_HANDLER_ENABLED - hardfault_default - HardFault default handler for debugging and // release #ifndef HARDFAULT_HANDLER_ENABLED #define HARDFAULT_HANDLER_ENABLED 0 #endif // HCI_MEM_POOL_ENABLED - hci_mem_pool - memory pool implementation used by HCI //========================================================== #ifndef HCI_MEM_POOL_ENABLED #define HCI_MEM_POOL_ENABLED 0 #endif // HCI_TX_BUF_SIZE - TX buffer size in bytes. #ifndef HCI_TX_BUF_SIZE #define HCI_TX_BUF_SIZE 600 #endif // HCI_RX_BUF_SIZE - RX buffer size in bytes. #ifndef HCI_RX_BUF_SIZE #define HCI_RX_BUF_SIZE 600 #endif // HCI_RX_BUF_QUEUE_SIZE - RX buffer queue size. #ifndef HCI_RX_BUF_QUEUE_SIZE #define HCI_RX_BUF_QUEUE_SIZE 4 #endif // // HCI_SLIP_ENABLED - hci_slip - SLIP protocol implementation used by HCI //========================================================== #ifndef HCI_SLIP_ENABLED #define HCI_SLIP_ENABLED 0 #endif // HCI_UART_BAUDRATE - Default Baudrate // <323584=> 1200 baud // <643072=> 2400 baud // <1290240=> 4800 baud // <2576384=> 9600 baud // <3862528=> 14400 baud // <5152768=> 19200 baud // <7716864=> 28800 baud // <10289152=> 38400 baud // <15400960=> 57600 baud // <20615168=> 76800 baud // <30801920=> 115200 baud // <61865984=> 230400 baud // <67108864=> 250000 baud // <121634816=> 460800 baud // <251658240=> 921600 baud // <268435456=> 1000000 baud #ifndef HCI_UART_BAUDRATE #define HCI_UART_BAUDRATE 30801920 #endif // HCI_UART_FLOW_CONTROL - Hardware Flow Control // <0=> Disabled // <1=> Enabled #ifndef HCI_UART_FLOW_CONTROL #define HCI_UART_FLOW_CONTROL 0 #endif // HCI_UART_RX_PIN - UART RX pin #ifndef HCI_UART_RX_PIN #define HCI_UART_RX_PIN 8 #endif // HCI_UART_TX_PIN - UART TX pin #ifndef HCI_UART_TX_PIN #define HCI_UART_TX_PIN 6 #endif // HCI_UART_RTS_PIN - UART RTS pin #ifndef HCI_UART_RTS_PIN #define HCI_UART_RTS_PIN 5 #endif // HCI_UART_CTS_PIN - UART CTS pin #ifndef HCI_UART_CTS_PIN #define HCI_UART_CTS_PIN 7 #endif // // HCI_TRANSPORT_ENABLED - hci_transport - HCI transport //========================================================== #ifndef HCI_TRANSPORT_ENABLED #define HCI_TRANSPORT_ENABLED 0 #endif // HCI_MAX_PACKET_SIZE_IN_BITS - Maximum size of a single application packet in bits. #ifndef HCI_MAX_PACKET_SIZE_IN_BITS #define HCI_MAX_PACKET_SIZE_IN_BITS 8000 #endif // // LED_SOFTBLINK_ENABLED - led_softblink - led_softblink module #ifndef LED_SOFTBLINK_ENABLED #define LED_SOFTBLINK_ENABLED 0 #endif // LOW_POWER_PWM_ENABLED - low_power_pwm - low_power_pwm module #ifndef LOW_POWER_PWM_ENABLED #define LOW_POWER_PWM_ENABLED 0 #endif // MEM_MANAGER_ENABLED - mem_manager - Dynamic memory allocator //========================================================== #ifndef MEM_MANAGER_ENABLED #define MEM_MANAGER_ENABLED 0 #endif // MEMORY_MANAGER_SMALL_BLOCK_COUNT - Size of each memory blocks identified as 'small' block. // <0-255> #ifndef MEMORY_MANAGER_SMALL_BLOCK_COUNT #define MEMORY_MANAGER_SMALL_BLOCK_COUNT 1 #endif // MEMORY_MANAGER_SMALL_BLOCK_SIZE - Size of each memory blocks identified as 'small' block. // Size of each memory blocks identified as 'small' block. Memory block are recommended to be // word-sized. #ifndef MEMORY_MANAGER_SMALL_BLOCK_SIZE #define MEMORY_MANAGER_SMALL_BLOCK_SIZE 32 #endif // MEMORY_MANAGER_MEDIUM_BLOCK_COUNT - Size of each memory blocks identified as 'medium' block. // <0-255> #ifndef MEMORY_MANAGER_MEDIUM_BLOCK_COUNT #define MEMORY_MANAGER_MEDIUM_BLOCK_COUNT 0 #endif // MEMORY_MANAGER_MEDIUM_BLOCK_SIZE - Size of each memory blocks identified as 'medium' block. // Size of each memory blocks identified as 'medium' block. Memory block are recommended to be // word-sized. #ifndef MEMORY_MANAGER_MEDIUM_BLOCK_SIZE #define MEMORY_MANAGER_MEDIUM_BLOCK_SIZE 256 #endif // MEMORY_MANAGER_LARGE_BLOCK_COUNT - Size of each memory blocks identified as 'large' block. // <0-255> #ifndef MEMORY_MANAGER_LARGE_BLOCK_COUNT #define MEMORY_MANAGER_LARGE_BLOCK_COUNT 0 #endif // MEMORY_MANAGER_LARGE_BLOCK_SIZE - Size of each memory blocks identified as 'large' block. // Size of each memory blocks identified as 'large' block. Memory block are recommended to be // word-sized. #ifndef MEMORY_MANAGER_LARGE_BLOCK_SIZE #define MEMORY_MANAGER_LARGE_BLOCK_SIZE 256 #endif // MEMORY_MANAGER_XLARGE_BLOCK_COUNT - Size of each memory blocks identified as 'extra large' // block. <0-255> #ifndef MEMORY_MANAGER_XLARGE_BLOCK_COUNT #define MEMORY_MANAGER_XLARGE_BLOCK_COUNT 0 #endif // MEMORY_MANAGER_XLARGE_BLOCK_SIZE - Size of each memory blocks identified as 'extra large' // block. Size of each memory blocks identified as 'extra large' block. Memory block are // recommended to be word-sized. #ifndef MEMORY_MANAGER_XLARGE_BLOCK_SIZE #define MEMORY_MANAGER_XLARGE_BLOCK_SIZE 1320 #endif // MEMORY_MANAGER_XXLARGE_BLOCK_COUNT - Size of each memory blocks identified as 'extra extra // large' block. <0-255> #ifndef MEMORY_MANAGER_XXLARGE_BLOCK_COUNT #define MEMORY_MANAGER_XXLARGE_BLOCK_COUNT 0 #endif // MEMORY_MANAGER_XXLARGE_BLOCK_SIZE - Size of each memory blocks identified as 'extra extra // large' block. Size of each memory blocks identified as 'extra extra large' block. Memory // block are recommended to be word-sized. #ifndef MEMORY_MANAGER_XXLARGE_BLOCK_SIZE #define MEMORY_MANAGER_XXLARGE_BLOCK_SIZE 3444 #endif // MEMORY_MANAGER_XSMALL_BLOCK_COUNT - Size of each memory blocks identified as 'extra small' // block. <0-255> #ifndef MEMORY_MANAGER_XSMALL_BLOCK_COUNT #define MEMORY_MANAGER_XSMALL_BLOCK_COUNT 0 #endif // MEMORY_MANAGER_XSMALL_BLOCK_SIZE - Size of each memory blocks identified as 'extra small' // block. Size of each memory blocks identified as 'extra large' block. Memory block are // recommended to be word-sized. #ifndef MEMORY_MANAGER_XSMALL_BLOCK_SIZE #define MEMORY_MANAGER_XSMALL_BLOCK_SIZE 64 #endif // MEMORY_MANAGER_XXSMALL_BLOCK_COUNT - Size of each memory blocks identified as 'extra extra // small' block. <0-255> #ifndef MEMORY_MANAGER_XXSMALL_BLOCK_COUNT #define MEMORY_MANAGER_XXSMALL_BLOCK_COUNT 0 #endif // MEMORY_MANAGER_XXSMALL_BLOCK_SIZE - Size of each memory blocks identified as 'extra extra // small' block. Size of each memory blocks identified as 'extra extra small' block. Memory // block are recommended to be word-sized. #ifndef MEMORY_MANAGER_XXSMALL_BLOCK_SIZE #define MEMORY_MANAGER_XXSMALL_BLOCK_SIZE 32 #endif // MEM_MANAGER_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef MEM_MANAGER_CONFIG_LOG_ENABLED #define MEM_MANAGER_CONFIG_LOG_ENABLED 0 #endif // MEM_MANAGER_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef MEM_MANAGER_CONFIG_LOG_LEVEL #define MEM_MANAGER_CONFIG_LOG_LEVEL 3 #endif // MEM_MANAGER_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef MEM_MANAGER_CONFIG_INFO_COLOR #define MEM_MANAGER_CONFIG_INFO_COLOR 0 #endif // MEM_MANAGER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef MEM_MANAGER_CONFIG_DEBUG_COLOR #define MEM_MANAGER_CONFIG_DEBUG_COLOR 0 #endif // // MEM_MANAGER_DISABLE_API_PARAM_CHECK - Disable API parameter checks in the module. #ifndef MEM_MANAGER_DISABLE_API_PARAM_CHECK #define MEM_MANAGER_DISABLE_API_PARAM_CHECK 0 #endif // // NRF_BALLOC_ENABLED - nrf_balloc - Block allocator module //========================================================== #ifndef NRF_BALLOC_ENABLED #define NRF_BALLOC_ENABLED 1 #endif // NRF_BALLOC_CONFIG_DEBUG_ENABLED - Enables debug mode in the module. //========================================================== #ifndef NRF_BALLOC_CONFIG_DEBUG_ENABLED #define NRF_BALLOC_CONFIG_DEBUG_ENABLED 0 #endif // NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS - Number of words used as head guard. <0-255> #ifndef NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS #define NRF_BALLOC_CONFIG_HEAD_GUARD_WORDS 1 #endif // NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS - Number of words used as tail guard. <0-255> #ifndef NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS #define NRF_BALLOC_CONFIG_TAIL_GUARD_WORDS 1 #endif // NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED - Enables basic checks in this module. #ifndef NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED #define NRF_BALLOC_CONFIG_BASIC_CHECKS_ENABLED 0 #endif // NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED - Enables double memory free check in this // module. #ifndef NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED #define NRF_BALLOC_CONFIG_DOUBLE_FREE_CHECK_ENABLED 0 #endif // NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED - Enables free memory corruption check in this // module. #ifndef NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED #define NRF_BALLOC_CONFIG_DATA_TRASHING_CHECK_ENABLED 0 #endif // NRF_BALLOC_CLI_CMDS - Enable CLI commands specific to the module #ifndef NRF_BALLOC_CLI_CMDS #define NRF_BALLOC_CLI_CMDS 0 #endif // // // NRF_CSENSE_ENABLED - nrf_csense - Capacitive sensor module //========================================================== #ifndef NRF_CSENSE_ENABLED #define NRF_CSENSE_ENABLED 0 #endif // NRF_CSENSE_PAD_HYSTERESIS - Minimum value of change required to determine that a pad was // touched. #ifndef NRF_CSENSE_PAD_HYSTERESIS #define NRF_CSENSE_PAD_HYSTERESIS 15 #endif // NRF_CSENSE_PAD_DEVIATION - Minimum value measured on a pad required to take it into account // while calculating the step. #ifndef NRF_CSENSE_PAD_DEVIATION #define NRF_CSENSE_PAD_DEVIATION 70 #endif // NRF_CSENSE_MIN_PAD_VALUE - Minimum normalized value on a pad required to take its value into // account. #ifndef NRF_CSENSE_MIN_PAD_VALUE #define NRF_CSENSE_MIN_PAD_VALUE 20 #endif // NRF_CSENSE_MAX_PADS_NUMBER - Maximum number of pads used for one instance. #ifndef NRF_CSENSE_MAX_PADS_NUMBER #define NRF_CSENSE_MAX_PADS_NUMBER 20 #endif // NRF_CSENSE_MAX_VALUE - Maximum normalized value obtained from measurement. #ifndef NRF_CSENSE_MAX_VALUE #define NRF_CSENSE_MAX_VALUE 1000 #endif // NRF_CSENSE_OUTPUT_PIN - Output pin used by the low-level module. // This is used when capacitive sensor does not use COMP. #ifndef NRF_CSENSE_OUTPUT_PIN #define NRF_CSENSE_OUTPUT_PIN 26 #endif // // NRF_DRV_CSENSE_ENABLED - nrf_drv_csense - Capacitive sensor low-level module //========================================================== #ifndef NRF_DRV_CSENSE_ENABLED #define NRF_DRV_CSENSE_ENABLED 0 #endif // USE_COMP - Use the comparator to implement the capacitive sensor driver. // Due to Anomaly 84, COMP I_SOURCE is not functional. It has too high a varation. //========================================================== #ifndef USE_COMP #define USE_COMP 0 #endif // TIMER0_FOR_CSENSE - First TIMER instance used by the driver (not used on nRF51). #ifndef TIMER0_FOR_CSENSE #define TIMER0_FOR_CSENSE 1 #endif // TIMER1_FOR_CSENSE - Second TIMER instance used by the driver (not used on nRF51). #ifndef TIMER1_FOR_CSENSE #define TIMER1_FOR_CSENSE 2 #endif // MEASUREMENT_PERIOD - Single measurement period. // Time of a single measurement can be calculated as // T = (1/2)*MEASUREMENT_PERIOD*(1/f_OSC) where f_OSC = I_SOURCE / (2C*(VUP-VDOWN) ). // I_SOURCE, VUP, and VDOWN are values used to initialize COMP and C is the capacitance of the // used pad. #ifndef MEASUREMENT_PERIOD #define MEASUREMENT_PERIOD 20 #endif // // // NRF_FPRINTF_ENABLED - nrf_fprintf - fprintf function. #ifndef NRF_FPRINTF_ENABLED #define NRF_FPRINTF_ENABLED 1 #endif // NRF_FSTORAGE_ENABLED - nrf_fstorage - Flash abstraction library //========================================================== #ifndef NRF_FSTORAGE_ENABLED #define NRF_FSTORAGE_ENABLED 1 #endif // nrf_fstorage - Common settings // Common settings to all fstorage implementations //========================================================== // NRF_FSTORAGE_PARAM_CHECK_DISABLED - Disable user input validation // If selected, use ASSERT to validate user input. // This effectively removes user input validation in production code. // Recommended setting: OFF, only enable this setting if size is a major concern. #ifndef NRF_FSTORAGE_PARAM_CHECK_DISABLED #define NRF_FSTORAGE_PARAM_CHECK_DISABLED 0 #endif // //========================================================== // nrf_fstorage_sd - Implementation using the SoftDevice // Configuration options for the fstorage implementation using the SoftDevice //========================================================== // NRF_FSTORAGE_SD_QUEUE_SIZE - Size of the internal queue of operations // Increase this value if API calls frequently return the error @ref NRF_ERROR_NO_MEM. #ifndef NRF_FSTORAGE_SD_QUEUE_SIZE #define NRF_FSTORAGE_SD_QUEUE_SIZE 4 #endif // NRF_FSTORAGE_SD_MAX_RETRIES - Maximum number of attempts at executing an operation when the // SoftDevice is busy Increase this value if events frequently return the @ref NRF_ERROR_TIMEOUT // error. The SoftDevice might fail to schedule flash access due to high BLE activity. #ifndef NRF_FSTORAGE_SD_MAX_RETRIES #define NRF_FSTORAGE_SD_MAX_RETRIES 8 #endif // NRF_FSTORAGE_SD_MAX_WRITE_SIZE - Maximum number of bytes to be written to flash in a single // operation This value must be a multiple of four. Lowering this value can increase the // chances of the SoftDevice being able to execute flash operations in between radio activity. // This value is bound by the maximum number of bytes that can be written to flash in a single call // to @ref sd_flash_write. That is 1024 bytes for nRF51 ICs and 4096 bytes for nRF52 ICs. #ifndef NRF_FSTORAGE_SD_MAX_WRITE_SIZE #define NRF_FSTORAGE_SD_MAX_WRITE_SIZE 4096 #endif // //========================================================== // // NRF_GFX_ENABLED - nrf_gfx - GFX module #ifndef NRF_GFX_ENABLED #define NRF_GFX_ENABLED 0 #endif // NRF_MEMOBJ_ENABLED - nrf_memobj - Linked memory allocator module #ifndef NRF_MEMOBJ_ENABLED #define NRF_MEMOBJ_ENABLED 1 #endif // NRF_PWR_MGMT_ENABLED - nrf_pwr_mgmt - Power management module //========================================================== #ifndef NRF_PWR_MGMT_ENABLED #define NRF_PWR_MGMT_ENABLED 1 #endif // NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED - Enables pin debug in the module. // Selected pin will be set when CPU is in sleep mode. //========================================================== #ifndef NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED #define NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED 0 #endif // NRF_PWR_MGMT_SLEEP_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef NRF_PWR_MGMT_SLEEP_DEBUG_PIN #define NRF_PWR_MGMT_SLEEP_DEBUG_PIN 31 #endif // // NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED - Enables CPU usage monitor. // Module will trace percentage of CPU usage in one second intervals. #ifndef NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED #define NRF_PWR_MGMT_CONFIG_CPU_USAGE_MONITOR_ENABLED 0 #endif // NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED - Enable standby timeout. //========================================================== #ifndef NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED #define NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_ENABLED 0 #endif // NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S - Standby timeout (in seconds). // Shutdown procedure will begin no earlier than after this number of seconds. #ifndef NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S #define NRF_PWR_MGMT_CONFIG_STANDBY_TIMEOUT_S 3 #endif // // NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED - Enables FPU event cleaning. #ifndef NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED #define NRF_PWR_MGMT_CONFIG_FPU_SUPPORT_ENABLED 1 #endif // NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY - Blocked shutdown procedure will be retried every // second. #ifndef NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY #define NRF_PWR_MGMT_CONFIG_AUTO_SHUTDOWN_RETRY 0 #endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER - Module will use @ref app_scheduler. #ifndef NRF_PWR_MGMT_CONFIG_USE_SCHEDULER #define NRF_PWR_MGMT_CONFIG_USE_SCHEDULER 0 #endif // NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT - The number of priorities for module handlers. // The number of stages of the shutdown process. #ifndef NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT #define NRF_PWR_MGMT_CONFIG_HANDLER_PRIORITY_COUNT 3 #endif // // NRF_QUEUE_ENABLED - nrf_queue - Queue module //========================================================== #ifndef NRF_QUEUE_ENABLED #define NRF_QUEUE_ENABLED 1 #endif // NRF_QUEUE_CLI_CMDS - Enable CLI commands specific to the module #ifndef NRF_QUEUE_CLI_CMDS #define NRF_QUEUE_CLI_CMDS 0 #endif // // NRF_SECTION_ITER_ENABLED - nrf_section_iter - Section iterator #ifndef NRF_SECTION_ITER_ENABLED #define NRF_SECTION_ITER_ENABLED 1 #endif // NRF_SORTLIST_ENABLED - nrf_sortlist - Sorted list #ifndef NRF_SORTLIST_ENABLED #define NRF_SORTLIST_ENABLED 0 #endif // NRF_SPI_MNGR_ENABLED - nrf_spi_mngr - SPI transaction manager #ifndef NRF_SPI_MNGR_ENABLED #define NRF_SPI_MNGR_ENABLED 0 #endif // NRF_STRERROR_ENABLED - nrf_strerror - Library for converting error code to string. #ifndef NRF_STRERROR_ENABLED #define NRF_STRERROR_ENABLED 1 #endif // NRF_TWI_MNGR_ENABLED - nrf_twi_mngr - TWI transaction manager #ifndef NRF_TWI_MNGR_ENABLED #define NRF_TWI_MNGR_ENABLED 0 #endif // SLIP_ENABLED - slip - SLIP encoding and decoding #ifndef SLIP_ENABLED #define SLIP_ENABLED 0 #endif // TASK_MANAGER_ENABLED - task_manager - Task manager. //========================================================== #ifndef TASK_MANAGER_ENABLED #define TASK_MANAGER_ENABLED 0 #endif // TASK_MANAGER_CLI_CMDS - Enable CLI commands specific to the module #ifndef TASK_MANAGER_CLI_CMDS #define TASK_MANAGER_CLI_CMDS 0 #endif // TASK_MANAGER_CONFIG_MAX_TASKS - Maximum number of tasks which can be created #ifndef TASK_MANAGER_CONFIG_MAX_TASKS #define TASK_MANAGER_CONFIG_MAX_TASKS 2 #endif // TASK_MANAGER_CONFIG_STACK_SIZE - Stack size for every task (power of 2) #ifndef TASK_MANAGER_CONFIG_STACK_SIZE #define TASK_MANAGER_CONFIG_STACK_SIZE 1024 #endif // TASK_MANAGER_CONFIG_STACK_PROFILER_ENABLED - Enable stack profiling. #ifndef TASK_MANAGER_CONFIG_STACK_PROFILER_ENABLED #define TASK_MANAGER_CONFIG_STACK_PROFILER_ENABLED 1 #endif // TASK_MANAGER_CONFIG_STACK_GUARD - Configures stack guard. // <0=> Disabled // <4=> 32 bytes // <5=> 64 bytes // <6=> 128 bytes // <7=> 256 bytes // <8=> 512 bytes #ifndef TASK_MANAGER_CONFIG_STACK_GUARD #define TASK_MANAGER_CONFIG_STACK_GUARD 7 #endif // // app_button - buttons handling module //========================================================== // BUTTON_ENABLED - Enables Button module #ifndef BUTTON_ENABLED #define BUTTON_ENABLED 1 #endif // BUTTON_HIGH_ACCURACY_ENABLED - Enables GPIOTE high accuracy for buttons #ifndef BUTTON_HIGH_ACCURACY_ENABLED #define BUTTON_HIGH_ACCURACY_ENABLED 0 #endif // //========================================================== // app_usbd_cdc_acm - USB CDC ACM class //========================================================== // APP_USBD_CDC_ACM_ENABLED - Enabling USBD CDC ACM Class library #ifndef APP_USBD_CDC_ACM_ENABLED #define APP_USBD_CDC_ACM_ENABLED 0 #endif // APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE - Send ZLP on write with same size as endpoint // If enabled, CDC ACM class will automatically send a zero length packet after transfer which // has the same size as endpoint. This may limit throughput if a lot of binary data is sent, but // in terminal mode operation it makes sure that the data is always displayed right after it is // sent. #ifndef APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE #define APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE 1 #endif // //========================================================== // nrf_cli - Command line interface //========================================================== // NRF_CLI_ENABLED - Enable/disable the CLI module. #ifndef NRF_CLI_ENABLED #define NRF_CLI_ENABLED 0 #endif // NRF_CLI_ARGC_MAX - Maximum number of parameters passed to the command handler. #ifndef NRF_CLI_ARGC_MAX #define NRF_CLI_ARGC_MAX 12 #endif // NRF_CLI_BUILD_IN_CMDS_ENABLED - CLI built-in commands. #ifndef NRF_CLI_BUILD_IN_CMDS_ENABLED #define NRF_CLI_BUILD_IN_CMDS_ENABLED 1 #endif // NRF_CLI_CMD_BUFF_SIZE - Maximum buffer size for a single command. #ifndef NRF_CLI_CMD_BUFF_SIZE #define NRF_CLI_CMD_BUFF_SIZE 128 #endif // NRF_CLI_ECHO_STATUS - CLI echo status. If set, echo is ON. #ifndef NRF_CLI_ECHO_STATUS #define NRF_CLI_ECHO_STATUS 1 #endif // NRF_CLI_WILDCARD_ENABLED - Enable wildcard functionality for CLI commands. #ifndef NRF_CLI_WILDCARD_ENABLED #define NRF_CLI_WILDCARD_ENABLED 0 #endif // NRF_CLI_METAKEYS_ENABLED - Enable additional control keys for CLI commands like ctrl+a, // ctrl+e, ctrl+w, ctrl+u #ifndef NRF_CLI_METAKEYS_ENABLED #define NRF_CLI_METAKEYS_ENABLED 0 #endif // NRF_CLI_PRINTF_BUFF_SIZE - Maximum print buffer size. #ifndef NRF_CLI_PRINTF_BUFF_SIZE #define NRF_CLI_PRINTF_BUFF_SIZE 23 #endif // NRF_CLI_HISTORY_ENABLED - Enable CLI history mode. //========================================================== #ifndef NRF_CLI_HISTORY_ENABLED #define NRF_CLI_HISTORY_ENABLED 1 #endif // NRF_CLI_HISTORY_ELEMENT_SIZE - Size of one memory object reserved for CLI history. #ifndef NRF_CLI_HISTORY_ELEMENT_SIZE #define NRF_CLI_HISTORY_ELEMENT_SIZE 32 #endif // NRF_CLI_HISTORY_ELEMENT_COUNT - Number of history memory objects. #ifndef NRF_CLI_HISTORY_ELEMENT_COUNT #define NRF_CLI_HISTORY_ELEMENT_COUNT 8 #endif // // NRF_CLI_VT100_COLORS_ENABLED - CLI VT100 colors. #ifndef NRF_CLI_VT100_COLORS_ENABLED #define NRF_CLI_VT100_COLORS_ENABLED 1 #endif // NRF_CLI_STATISTICS_ENABLED - Enable CLI statistics. #ifndef NRF_CLI_STATISTICS_ENABLED #define NRF_CLI_STATISTICS_ENABLED 1 #endif // NRF_CLI_LOG_BACKEND - Enable logger backend interface. #ifndef NRF_CLI_LOG_BACKEND #define NRF_CLI_LOG_BACKEND 1 #endif // NRF_CLI_USES_TASK_MANAGER_ENABLED - Enable CLI to use task_manager #ifndef NRF_CLI_USES_TASK_MANAGER_ENABLED #define NRF_CLI_USES_TASK_MANAGER_ENABLED 0 #endif // //========================================================== // //========================================================== // nRF_Log //========================================================== // NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend //========================================================== #ifndef NRF_LOG_BACKEND_RTT_ENABLED #define NRF_LOG_BACKEND_RTT_ENABLED 1 #endif // NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE - Size of buffer for partially processed strings. // Size of the buffer is a trade-off between RAM usage and processing. // if buffer is smaller then strings will often be fragmented. // It is recommended to use size which will fit typical log and only the // longer one will be fragmented. #ifndef NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE #define NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE 64 #endif // NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS - Period before retrying writing to RTT #ifndef NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS #define NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS 1 #endif // NRF_LOG_BACKEND_RTT_TX_RETRY_CNT - Writing to RTT retries. // If RTT fails to accept any new data after retries // module assumes that host is not active and on next // request it will perform only one write attempt. // On successful writing, module assumes that host is active // and scheme with retry is applied again. #ifndef NRF_LOG_BACKEND_RTT_TX_RETRY_CNT #define NRF_LOG_BACKEND_RTT_TX_RETRY_CNT 3 #endif // // NRF_LOG_BACKEND_UART_ENABLED - nrf_log_backend_uart - Log UART backend //========================================================== #ifndef NRF_LOG_BACKEND_UART_ENABLED #define NRF_LOG_BACKEND_UART_ENABLED 0 #endif // NRF_LOG_BACKEND_UART_TX_PIN - UART TX pin #ifndef NRF_LOG_BACKEND_UART_TX_PIN #define NRF_LOG_BACKEND_UART_TX_PIN 6 #endif // NRF_LOG_BACKEND_UART_BAUDRATE - Default Baudrate // <323584=> 1200 baud // <643072=> 2400 baud // <1290240=> 4800 baud // <2576384=> 9600 baud // <3862528=> 14400 baud // <5152768=> 19200 baud // <7716864=> 28800 baud // <10289152=> 38400 baud // <15400960=> 57600 baud // <20615168=> 76800 baud // <30801920=> 115200 baud // <61865984=> 230400 baud // <67108864=> 250000 baud // <121634816=> 460800 baud // <251658240=> 921600 baud // <268435456=> 1000000 baud #ifndef NRF_LOG_BACKEND_UART_BAUDRATE #define NRF_LOG_BACKEND_UART_BAUDRATE 30801920 #endif // NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE - Size of buffer for partially processed strings. // Size of the buffer is a trade-off between RAM usage and processing. // if buffer is smaller then strings will often be fragmented. // It is recommended to use size which will fit typical log and only the // longer one will be fragmented. #ifndef NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE #define NRF_LOG_BACKEND_UART_TEMP_BUFFER_SIZE 64 #endif // // NRF_LOG_ENABLED - nrf_log - Logger //========================================================== #ifndef NRF_LOG_ENABLED #define NRF_LOG_ENABLED 1 #endif // Log message pool - Configuration of log message pool //========================================================== // NRF_LOG_MSGPOOL_ELEMENT_SIZE - Size of a single element in the pool of memory objects. // If a small value is set, then performance of logs processing // is degraded because data is fragmented. Bigger value impacts // RAM memory utilization. The size is set to fit a message with // a timestamp and up to 2 arguments in a single memory object. #ifndef NRF_LOG_MSGPOOL_ELEMENT_SIZE #define NRF_LOG_MSGPOOL_ELEMENT_SIZE 20 #endif // NRF_LOG_MSGPOOL_ELEMENT_COUNT - Number of elements in the pool of memory objects // If a small value is set, then it may lead to a deadlock // in certain cases if backend has high latency and holds // multiple messages for long time. Bigger value impacts // RAM memory usage. #ifndef NRF_LOG_MSGPOOL_ELEMENT_COUNT #define NRF_LOG_MSGPOOL_ELEMENT_COUNT 8 #endif // //========================================================== // NRF_LOG_ALLOW_OVERFLOW - Configures behavior when circular buffer is full. // If set then oldest logs are overwritten. Otherwise a // marker is injected informing about overflow. #ifndef NRF_LOG_ALLOW_OVERFLOW #define NRF_LOG_ALLOW_OVERFLOW 1 #endif // NRF_LOG_BUFSIZE - Size of the buffer for storing logs (in bytes). // Must be power of 2 and multiple of 4. // If NRF_LOG_DEFERRED = 0 then buffer size can be reduced to minimum. // <128=> 128 // <256=> 256 // <512=> 512 // <1024=> 1024 // <2048=> 2048 // <4096=> 4096 // <8192=> 8192 // <16384=> 16384 #ifndef NRF_LOG_BUFSIZE #define NRF_LOG_BUFSIZE 4096 #endif // NRF_LOG_CLI_CMDS - Enable CLI commands for the module. #ifndef NRF_LOG_CLI_CMDS #define NRF_LOG_CLI_CMDS 0 #endif // NRF_LOG_DEFAULT_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_LOG_DEFAULT_LEVEL #define NRF_LOG_DEFAULT_LEVEL 4 #endif // NRF_LOG_DEFERRED - Enable deffered logger. // Log data is buffered and can be processed in idle. #ifndef NRF_LOG_DEFERRED #define NRF_LOG_DEFERRED 0 #endif // NRF_LOG_FILTERS_ENABLED - Enable dynamic filtering of logs. #ifndef NRF_LOG_FILTERS_ENABLED #define NRF_LOG_FILTERS_ENABLED 0 #endif // NRF_LOG_STR_PUSH_BUFFER_SIZE - Size of the buffer dedicated for strings stored using @ref // NRF_LOG_PUSH. // <16=> 16 // <32=> 32 // <64=> 64 // <128=> 128 // <256=> 256 // <512=> 512 // <1024=> 1024 #ifndef NRF_LOG_STR_PUSH_BUFFER_SIZE #define NRF_LOG_STR_PUSH_BUFFER_SIZE 128 #endif // NRF_LOG_STR_PUSH_BUFFER_SIZE - Size of the buffer dedicated for strings stored using @ref // NRF_LOG_PUSH. // <16=> 16 // <32=> 32 // <64=> 64 // <128=> 128 // <256=> 256 // <512=> 512 // <1024=> 1024 #ifndef NRF_LOG_STR_PUSH_BUFFER_SIZE #define NRF_LOG_STR_PUSH_BUFFER_SIZE 128 #endif // NRF_LOG_USES_COLORS - If enabled then ANSI escape code for colors is prefixed to every string //========================================================== #ifndef NRF_LOG_USES_COLORS #define NRF_LOG_USES_COLORS 0 #endif // NRF_LOG_COLOR_DEFAULT - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_LOG_COLOR_DEFAULT #define NRF_LOG_COLOR_DEFAULT 0 #endif // NRF_LOG_ERROR_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_LOG_ERROR_COLOR #define NRF_LOG_ERROR_COLOR 2 #endif // NRF_LOG_WARNING_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_LOG_WARNING_COLOR #define NRF_LOG_WARNING_COLOR 4 #endif // // NRF_LOG_USES_TIMESTAMP - Enable timestamping // Function for getting the timestamp is provided by the user //========================================================== #ifndef NRF_LOG_USES_TIMESTAMP #define NRF_LOG_USES_TIMESTAMP 0 #endif // NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY - Default frequency of the timestamp (in Hz) or 0 to use // app_timer frequency. #ifndef NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY #define NRF_LOG_TIMESTAMP_DEFAULT_FREQUENCY 0 #endif // // nrf_log module configuration //========================================================== // nrf_log in nRF_Core //========================================================== // NRF_MPU_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_MPU_CONFIG_LOG_ENABLED #define NRF_MPU_CONFIG_LOG_ENABLED 0 #endif // NRF_MPU_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_MPU_CONFIG_LOG_LEVEL #define NRF_MPU_CONFIG_LOG_LEVEL 3 #endif // NRF_MPU_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_MPU_CONFIG_INFO_COLOR #define NRF_MPU_CONFIG_INFO_COLOR 0 #endif // NRF_MPU_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_MPU_CONFIG_DEBUG_COLOR #define NRF_MPU_CONFIG_DEBUG_COLOR 0 #endif // // NRF_STACK_GUARD_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_STACK_GUARD_CONFIG_LOG_ENABLED #define NRF_STACK_GUARD_CONFIG_LOG_ENABLED 0 #endif // NRF_STACK_GUARD_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_STACK_GUARD_CONFIG_LOG_LEVEL #define NRF_STACK_GUARD_CONFIG_LOG_LEVEL 3 #endif // NRF_STACK_GUARD_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_STACK_GUARD_CONFIG_INFO_COLOR #define NRF_STACK_GUARD_CONFIG_INFO_COLOR 0 #endif // NRF_STACK_GUARD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_STACK_GUARD_CONFIG_DEBUG_COLOR #define NRF_STACK_GUARD_CONFIG_DEBUG_COLOR 0 #endif // // TASK_MANAGER_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef TASK_MANAGER_CONFIG_LOG_ENABLED #define TASK_MANAGER_CONFIG_LOG_ENABLED 0 #endif // TASK_MANAGER_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef TASK_MANAGER_CONFIG_LOG_LEVEL #define TASK_MANAGER_CONFIG_LOG_LEVEL 3 #endif // TASK_MANAGER_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef TASK_MANAGER_CONFIG_INFO_COLOR #define TASK_MANAGER_CONFIG_INFO_COLOR 0 #endif // TASK_MANAGER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef TASK_MANAGER_CONFIG_DEBUG_COLOR #define TASK_MANAGER_CONFIG_DEBUG_COLOR 0 #endif // // //========================================================== // nrf_log in nRF_Drivers //========================================================== // CLOCK_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef CLOCK_CONFIG_LOG_ENABLED #define CLOCK_CONFIG_LOG_ENABLED 0 #endif // CLOCK_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef CLOCK_CONFIG_LOG_LEVEL #define CLOCK_CONFIG_LOG_LEVEL 3 #endif // CLOCK_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef CLOCK_CONFIG_INFO_COLOR #define CLOCK_CONFIG_INFO_COLOR 0 #endif // CLOCK_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef CLOCK_CONFIG_DEBUG_COLOR #define CLOCK_CONFIG_DEBUG_COLOR 0 #endif // // COMP_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef COMP_CONFIG_LOG_ENABLED #define COMP_CONFIG_LOG_ENABLED 0 #endif // COMP_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef COMP_CONFIG_LOG_LEVEL #define COMP_CONFIG_LOG_LEVEL 3 #endif // COMP_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef COMP_CONFIG_INFO_COLOR #define COMP_CONFIG_INFO_COLOR 0 #endif // COMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef COMP_CONFIG_DEBUG_COLOR #define COMP_CONFIG_DEBUG_COLOR 0 #endif // // GPIOTE_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef GPIOTE_CONFIG_LOG_ENABLED #define GPIOTE_CONFIG_LOG_ENABLED 0 #endif // GPIOTE_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef GPIOTE_CONFIG_LOG_LEVEL #define GPIOTE_CONFIG_LOG_LEVEL 3 #endif // GPIOTE_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef GPIOTE_CONFIG_INFO_COLOR #define GPIOTE_CONFIG_INFO_COLOR 0 #endif // GPIOTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef GPIOTE_CONFIG_DEBUG_COLOR #define GPIOTE_CONFIG_DEBUG_COLOR 0 #endif // // LPCOMP_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef LPCOMP_CONFIG_LOG_ENABLED #define LPCOMP_CONFIG_LOG_ENABLED 0 #endif // LPCOMP_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef LPCOMP_CONFIG_LOG_LEVEL #define LPCOMP_CONFIG_LOG_LEVEL 3 #endif // LPCOMP_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef LPCOMP_CONFIG_INFO_COLOR #define LPCOMP_CONFIG_INFO_COLOR 0 #endif // LPCOMP_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef LPCOMP_CONFIG_DEBUG_COLOR #define LPCOMP_CONFIG_DEBUG_COLOR 0 #endif // // MAX3421E_HOST_CONFIG_LOG_ENABLED - Enable logging in the module //========================================================== #ifndef MAX3421E_HOST_CONFIG_LOG_ENABLED #define MAX3421E_HOST_CONFIG_LOG_ENABLED 0 #endif // MAX3421E_HOST_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef MAX3421E_HOST_CONFIG_LOG_LEVEL #define MAX3421E_HOST_CONFIG_LOG_LEVEL 3 #endif // MAX3421E_HOST_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef MAX3421E_HOST_CONFIG_INFO_COLOR #define MAX3421E_HOST_CONFIG_INFO_COLOR 0 #endif // MAX3421E_HOST_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef MAX3421E_HOST_CONFIG_DEBUG_COLOR #define MAX3421E_HOST_CONFIG_DEBUG_COLOR 0 #endif // // PDM_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef PDM_CONFIG_LOG_ENABLED #define PDM_CONFIG_LOG_ENABLED 0 #endif // PDM_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef PDM_CONFIG_LOG_LEVEL #define PDM_CONFIG_LOG_LEVEL 3 #endif // PDM_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef PDM_CONFIG_INFO_COLOR #define PDM_CONFIG_INFO_COLOR 0 #endif // PDM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef PDM_CONFIG_DEBUG_COLOR #define PDM_CONFIG_DEBUG_COLOR 0 #endif // // PPI_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef PPI_CONFIG_LOG_ENABLED #define PPI_CONFIG_LOG_ENABLED 0 #endif // PPI_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef PPI_CONFIG_LOG_LEVEL #define PPI_CONFIG_LOG_LEVEL 3 #endif // PPI_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef PPI_CONFIG_INFO_COLOR #define PPI_CONFIG_INFO_COLOR 0 #endif // PPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef PPI_CONFIG_DEBUG_COLOR #define PPI_CONFIG_DEBUG_COLOR 0 #endif // // PWM_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef PWM_CONFIG_LOG_ENABLED #define PWM_CONFIG_LOG_ENABLED 0 #endif // PWM_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef PWM_CONFIG_LOG_LEVEL #define PWM_CONFIG_LOG_LEVEL 3 #endif // PWM_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef PWM_CONFIG_INFO_COLOR #define PWM_CONFIG_INFO_COLOR 0 #endif // PWM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef PWM_CONFIG_DEBUG_COLOR #define PWM_CONFIG_DEBUG_COLOR 0 #endif // // QDEC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef QDEC_CONFIG_LOG_ENABLED #define QDEC_CONFIG_LOG_ENABLED 0 #endif // QDEC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef QDEC_CONFIG_LOG_LEVEL #define QDEC_CONFIG_LOG_LEVEL 3 #endif // QDEC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef QDEC_CONFIG_INFO_COLOR #define QDEC_CONFIG_INFO_COLOR 0 #endif // QDEC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef QDEC_CONFIG_DEBUG_COLOR #define QDEC_CONFIG_DEBUG_COLOR 0 #endif // // RNG_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef RNG_CONFIG_LOG_ENABLED #define RNG_CONFIG_LOG_ENABLED 0 #endif // RNG_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef RNG_CONFIG_LOG_LEVEL #define RNG_CONFIG_LOG_LEVEL 3 #endif // RNG_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef RNG_CONFIG_INFO_COLOR #define RNG_CONFIG_INFO_COLOR 0 #endif // RNG_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef RNG_CONFIG_DEBUG_COLOR #define RNG_CONFIG_DEBUG_COLOR 0 #endif // RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED - Enables logging of random numbers. #ifndef RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED #define RNG_CONFIG_RANDOM_NUMBER_LOG_ENABLED 0 #endif // // RTC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef RTC_CONFIG_LOG_ENABLED #define RTC_CONFIG_LOG_ENABLED 0 #endif // RTC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef RTC_CONFIG_LOG_LEVEL #define RTC_CONFIG_LOG_LEVEL 3 #endif // RTC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef RTC_CONFIG_INFO_COLOR #define RTC_CONFIG_INFO_COLOR 0 #endif // RTC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef RTC_CONFIG_DEBUG_COLOR #define RTC_CONFIG_DEBUG_COLOR 0 #endif // // SAADC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef SAADC_CONFIG_LOG_ENABLED #define SAADC_CONFIG_LOG_ENABLED 0 #endif // SAADC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef SAADC_CONFIG_LOG_LEVEL #define SAADC_CONFIG_LOG_LEVEL 3 #endif // SAADC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef SAADC_CONFIG_INFO_COLOR #define SAADC_CONFIG_INFO_COLOR 0 #endif // SAADC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef SAADC_CONFIG_DEBUG_COLOR #define SAADC_CONFIG_DEBUG_COLOR 0 #endif // // SPIS_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef SPIS_CONFIG_LOG_ENABLED #define SPIS_CONFIG_LOG_ENABLED 0 #endif // SPIS_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef SPIS_CONFIG_LOG_LEVEL #define SPIS_CONFIG_LOG_LEVEL 3 #endif // SPIS_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef SPIS_CONFIG_INFO_COLOR #define SPIS_CONFIG_INFO_COLOR 0 #endif // SPIS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef SPIS_CONFIG_DEBUG_COLOR #define SPIS_CONFIG_DEBUG_COLOR 0 #endif // // SPI_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef SPI_CONFIG_LOG_ENABLED #define SPI_CONFIG_LOG_ENABLED 0 #endif // SPI_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef SPI_CONFIG_LOG_LEVEL #define SPI_CONFIG_LOG_LEVEL 3 #endif // SPI_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef SPI_CONFIG_INFO_COLOR #define SPI_CONFIG_INFO_COLOR 0 #endif // SPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef SPI_CONFIG_DEBUG_COLOR #define SPI_CONFIG_DEBUG_COLOR 0 #endif // // TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef TIMER_CONFIG_LOG_ENABLED #define TIMER_CONFIG_LOG_ENABLED 0 #endif // TIMER_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef TIMER_CONFIG_LOG_LEVEL #define TIMER_CONFIG_LOG_LEVEL 3 #endif // TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef TIMER_CONFIG_INFO_COLOR #define TIMER_CONFIG_INFO_COLOR 0 #endif // TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef TIMER_CONFIG_DEBUG_COLOR #define TIMER_CONFIG_DEBUG_COLOR 0 #endif // // TWIS_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef TWIS_CONFIG_LOG_ENABLED #define TWIS_CONFIG_LOG_ENABLED 0 #endif // TWIS_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef TWIS_CONFIG_LOG_LEVEL #define TWIS_CONFIG_LOG_LEVEL 3 #endif // TWIS_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef TWIS_CONFIG_INFO_COLOR #define TWIS_CONFIG_INFO_COLOR 0 #endif // TWIS_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef TWIS_CONFIG_DEBUG_COLOR #define TWIS_CONFIG_DEBUG_COLOR 0 #endif // // TWI_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef TWI_CONFIG_LOG_ENABLED #define TWI_CONFIG_LOG_ENABLED 0 #endif // TWI_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef TWI_CONFIG_LOG_LEVEL #define TWI_CONFIG_LOG_LEVEL 3 #endif // TWI_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef TWI_CONFIG_INFO_COLOR #define TWI_CONFIG_INFO_COLOR 0 #endif // TWI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef TWI_CONFIG_DEBUG_COLOR #define TWI_CONFIG_DEBUG_COLOR 0 #endif // // UART_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef UART_CONFIG_LOG_ENABLED #define UART_CONFIG_LOG_ENABLED 0 #endif // UART_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef UART_CONFIG_LOG_LEVEL #define UART_CONFIG_LOG_LEVEL 3 #endif // UART_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef UART_CONFIG_INFO_COLOR #define UART_CONFIG_INFO_COLOR 0 #endif // UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef UART_CONFIG_DEBUG_COLOR #define UART_CONFIG_DEBUG_COLOR 0 #endif // // USBD_CONFIG_LOG_ENABLED - Enable logging in the module //========================================================== #ifndef USBD_CONFIG_LOG_ENABLED #define USBD_CONFIG_LOG_ENABLED 0 #endif // USBD_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef USBD_CONFIG_LOG_LEVEL #define USBD_CONFIG_LOG_LEVEL 3 #endif // USBD_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef USBD_CONFIG_INFO_COLOR #define USBD_CONFIG_INFO_COLOR 0 #endif // USBD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef USBD_CONFIG_DEBUG_COLOR #define USBD_CONFIG_DEBUG_COLOR 0 #endif // // WDT_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef WDT_CONFIG_LOG_ENABLED #define WDT_CONFIG_LOG_ENABLED 0 #endif // WDT_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef WDT_CONFIG_LOG_LEVEL #define WDT_CONFIG_LOG_LEVEL 3 #endif // WDT_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef WDT_CONFIG_INFO_COLOR #define WDT_CONFIG_INFO_COLOR 0 #endif // WDT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef WDT_CONFIG_DEBUG_COLOR #define WDT_CONFIG_DEBUG_COLOR 0 #endif // // //========================================================== // nrf_log in nRF_Libraries //========================================================== // APP_TIMER_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef APP_TIMER_CONFIG_LOG_ENABLED #define APP_TIMER_CONFIG_LOG_ENABLED 0 #endif // APP_TIMER_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef APP_TIMER_CONFIG_LOG_LEVEL #define APP_TIMER_CONFIG_LOG_LEVEL 3 #endif // APP_TIMER_CONFIG_INITIAL_LOG_LEVEL - Initial severity level if dynamic filtering is enabled. // If module generates a lot of logs, initial log level can // be decreased to prevent flooding. Severity level can be // increased on instance basis. // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef APP_TIMER_CONFIG_INITIAL_LOG_LEVEL #define APP_TIMER_CONFIG_INITIAL_LOG_LEVEL 3 #endif // APP_TIMER_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_TIMER_CONFIG_INFO_COLOR #define APP_TIMER_CONFIG_INFO_COLOR 0 #endif // APP_TIMER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_TIMER_CONFIG_DEBUG_COLOR #define APP_TIMER_CONFIG_DEBUG_COLOR 0 #endif // // APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED #define APP_USBD_CDC_ACM_CONFIG_LOG_ENABLED 0 #endif // APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL #define APP_USBD_CDC_ACM_CONFIG_LOG_LEVEL 3 #endif // APP_USBD_CDC_ACM_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_CDC_ACM_CONFIG_INFO_COLOR #define APP_USBD_CDC_ACM_CONFIG_INFO_COLOR 0 #endif // APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR #define APP_USBD_CDC_ACM_CONFIG_DEBUG_COLOR 0 #endif // // APP_USBD_CONFIG_LOG_ENABLED - Enable logging in the module. //========================================================== #ifndef APP_USBD_CONFIG_LOG_ENABLED #define APP_USBD_CONFIG_LOG_ENABLED 0 #endif // APP_USBD_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef APP_USBD_CONFIG_LOG_LEVEL #define APP_USBD_CONFIG_LOG_LEVEL 3 #endif // APP_USBD_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_CONFIG_INFO_COLOR #define APP_USBD_CONFIG_INFO_COLOR 0 #endif // APP_USBD_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_CONFIG_DEBUG_COLOR #define APP_USBD_CONFIG_DEBUG_COLOR 0 #endif // // APP_USBD_DUMMY_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef APP_USBD_DUMMY_CONFIG_LOG_ENABLED #define APP_USBD_DUMMY_CONFIG_LOG_ENABLED 0 #endif // APP_USBD_DUMMY_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef APP_USBD_DUMMY_CONFIG_LOG_LEVEL #define APP_USBD_DUMMY_CONFIG_LOG_LEVEL 3 #endif // APP_USBD_DUMMY_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_DUMMY_CONFIG_INFO_COLOR #define APP_USBD_DUMMY_CONFIG_INFO_COLOR 0 #endif // APP_USBD_DUMMY_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_DUMMY_CONFIG_DEBUG_COLOR #define APP_USBD_DUMMY_CONFIG_DEBUG_COLOR 0 #endif // // APP_USBD_MSC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef APP_USBD_MSC_CONFIG_LOG_ENABLED #define APP_USBD_MSC_CONFIG_LOG_ENABLED 0 #endif // APP_USBD_MSC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef APP_USBD_MSC_CONFIG_LOG_LEVEL #define APP_USBD_MSC_CONFIG_LOG_LEVEL 3 #endif // APP_USBD_MSC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_MSC_CONFIG_INFO_COLOR #define APP_USBD_MSC_CONFIG_INFO_COLOR 0 #endif // APP_USBD_MSC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_MSC_CONFIG_DEBUG_COLOR #define APP_USBD_MSC_CONFIG_DEBUG_COLOR 0 #endif // // APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED #define APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_ENABLED 0 #endif // APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL #define APP_USBD_NRF_DFU_TRIGGER_CONFIG_LOG_LEVEL 3 #endif // APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR #define APP_USBD_NRF_DFU_TRIGGER_CONFIG_INFO_COLOR 0 #endif // APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR #define APP_USBD_NRF_DFU_TRIGGER_CONFIG_DEBUG_COLOR 0 #endif // // NRF_ATFIFO_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_ATFIFO_CONFIG_LOG_ENABLED #define NRF_ATFIFO_CONFIG_LOG_ENABLED 0 #endif // NRF_ATFIFO_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_ATFIFO_CONFIG_LOG_LEVEL #define NRF_ATFIFO_CONFIG_LOG_LEVEL 3 #endif // NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic filtering is // enabled // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL #define NRF_ATFIFO_CONFIG_LOG_INIT_FILTER_LEVEL 3 #endif // NRF_ATFIFO_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_ATFIFO_CONFIG_INFO_COLOR #define NRF_ATFIFO_CONFIG_INFO_COLOR 0 #endif // NRF_ATFIFO_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_ATFIFO_CONFIG_DEBUG_COLOR #define NRF_ATFIFO_CONFIG_DEBUG_COLOR 0 #endif // // NRF_BALLOC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_BALLOC_CONFIG_LOG_ENABLED #define NRF_BALLOC_CONFIG_LOG_ENABLED 0 #endif // NRF_BALLOC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_BALLOC_CONFIG_LOG_LEVEL #define NRF_BALLOC_CONFIG_LOG_LEVEL 3 #endif // NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL - Initial severity level if dynamic filtering is // enabled. // If module generates a lot of logs, initial log level can // be decreased to prevent flooding. Severity level can be // increased on instance basis. // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL #define NRF_BALLOC_CONFIG_INITIAL_LOG_LEVEL 3 #endif // NRF_BALLOC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_BALLOC_CONFIG_INFO_COLOR #define NRF_BALLOC_CONFIG_INFO_COLOR 0 #endif // NRF_BALLOC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_BALLOC_CONFIG_DEBUG_COLOR #define NRF_BALLOC_CONFIG_DEBUG_COLOR 0 #endif // // NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED #define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_ENABLED 0 #endif // NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL #define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_LEVEL 3 #endif // NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic // filtering is enabled // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL #define NRF_BLOCK_DEV_EMPTY_CONFIG_LOG_INIT_FILTER_LEVEL 3 #endif // NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR #define NRF_BLOCK_DEV_EMPTY_CONFIG_INFO_COLOR 0 #endif // NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR #define NRF_BLOCK_DEV_EMPTY_CONFIG_DEBUG_COLOR 0 #endif // // NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED #define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_ENABLED 0 #endif // NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL #define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_LEVEL 3 #endif // NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic // filtering is enabled // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL #define NRF_BLOCK_DEV_QSPI_CONFIG_LOG_INIT_FILTER_LEVEL 3 #endif // NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR #define NRF_BLOCK_DEV_QSPI_CONFIG_INFO_COLOR 0 #endif // NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR #define NRF_BLOCK_DEV_QSPI_CONFIG_DEBUG_COLOR 0 #endif // // NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED #define NRF_BLOCK_DEV_RAM_CONFIG_LOG_ENABLED 0 #endif // NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL #define NRF_BLOCK_DEV_RAM_CONFIG_LOG_LEVEL 3 #endif // NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic filtering // is enabled // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL #define NRF_BLOCK_DEV_RAM_CONFIG_LOG_INIT_FILTER_LEVEL 3 #endif // NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR #define NRF_BLOCK_DEV_RAM_CONFIG_INFO_COLOR 0 #endif // NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR #define NRF_BLOCK_DEV_RAM_CONFIG_DEBUG_COLOR 0 #endif // // NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED #define NRF_CLI_BLE_UART_CONFIG_LOG_ENABLED 0 #endif // NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL #define NRF_CLI_BLE_UART_CONFIG_LOG_LEVEL 3 #endif // NRF_CLI_BLE_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_CLI_BLE_UART_CONFIG_INFO_COLOR #define NRF_CLI_BLE_UART_CONFIG_INFO_COLOR 0 #endif // NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR #define NRF_CLI_BLE_UART_CONFIG_DEBUG_COLOR 0 #endif // // NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED #define NRF_CLI_LIBUARTE_CONFIG_LOG_ENABLED 0 #endif // NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL #define NRF_CLI_LIBUARTE_CONFIG_LOG_LEVEL 3 #endif // NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR #define NRF_CLI_LIBUARTE_CONFIG_INFO_COLOR 0 #endif // NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR #define NRF_CLI_LIBUARTE_CONFIG_DEBUG_COLOR 0 #endif // // NRF_CLI_UART_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_CLI_UART_CONFIG_LOG_ENABLED #define NRF_CLI_UART_CONFIG_LOG_ENABLED 0 #endif // NRF_CLI_UART_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_CLI_UART_CONFIG_LOG_LEVEL #define NRF_CLI_UART_CONFIG_LOG_LEVEL 3 #endif // NRF_CLI_UART_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_CLI_UART_CONFIG_INFO_COLOR #define NRF_CLI_UART_CONFIG_INFO_COLOR 0 #endif // NRF_CLI_UART_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_CLI_UART_CONFIG_DEBUG_COLOR #define NRF_CLI_UART_CONFIG_DEBUG_COLOR 0 #endif // // NRF_LIBUARTE_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_LIBUARTE_CONFIG_LOG_ENABLED #define NRF_LIBUARTE_CONFIG_LOG_ENABLED 0 #endif // NRF_LIBUARTE_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_LIBUARTE_CONFIG_LOG_LEVEL #define NRF_LIBUARTE_CONFIG_LOG_LEVEL 3 #endif // NRF_LIBUARTE_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_LIBUARTE_CONFIG_INFO_COLOR #define NRF_LIBUARTE_CONFIG_INFO_COLOR 0 #endif // NRF_LIBUARTE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_LIBUARTE_CONFIG_DEBUG_COLOR #define NRF_LIBUARTE_CONFIG_DEBUG_COLOR 0 #endif // // NRF_MEMOBJ_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_MEMOBJ_CONFIG_LOG_ENABLED #define NRF_MEMOBJ_CONFIG_LOG_ENABLED 0 #endif // NRF_MEMOBJ_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_MEMOBJ_CONFIG_LOG_LEVEL #define NRF_MEMOBJ_CONFIG_LOG_LEVEL 3 #endif // NRF_MEMOBJ_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_MEMOBJ_CONFIG_INFO_COLOR #define NRF_MEMOBJ_CONFIG_INFO_COLOR 0 #endif // NRF_MEMOBJ_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_MEMOBJ_CONFIG_DEBUG_COLOR #define NRF_MEMOBJ_CONFIG_DEBUG_COLOR 0 #endif // // NRF_PWR_MGMT_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_PWR_MGMT_CONFIG_LOG_ENABLED #define NRF_PWR_MGMT_CONFIG_LOG_ENABLED 0 #endif // NRF_PWR_MGMT_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_PWR_MGMT_CONFIG_LOG_LEVEL #define NRF_PWR_MGMT_CONFIG_LOG_LEVEL 3 #endif // NRF_PWR_MGMT_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_PWR_MGMT_CONFIG_INFO_COLOR #define NRF_PWR_MGMT_CONFIG_INFO_COLOR 0 #endif // NRF_PWR_MGMT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_PWR_MGMT_CONFIG_DEBUG_COLOR #define NRF_PWR_MGMT_CONFIG_DEBUG_COLOR 0 #endif // // NRF_QUEUE_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_QUEUE_CONFIG_LOG_ENABLED #define NRF_QUEUE_CONFIG_LOG_ENABLED 0 #endif // NRF_QUEUE_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_QUEUE_CONFIG_LOG_LEVEL #define NRF_QUEUE_CONFIG_LOG_LEVEL 3 #endif // NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL - Initial severity level if dynamic filtering is // enabled // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL #define NRF_QUEUE_CONFIG_LOG_INIT_FILTER_LEVEL 3 #endif // NRF_QUEUE_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_QUEUE_CONFIG_INFO_COLOR #define NRF_QUEUE_CONFIG_INFO_COLOR 0 #endif // NRF_QUEUE_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_QUEUE_CONFIG_DEBUG_COLOR #define NRF_QUEUE_CONFIG_DEBUG_COLOR 0 #endif // // NRF_SDH_ANT_LOG_ENABLED - Enable logging in SoftDevice handler (ANT) module. //========================================================== #ifndef NRF_SDH_ANT_LOG_ENABLED #define NRF_SDH_ANT_LOG_ENABLED 0 #endif // NRF_SDH_ANT_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_SDH_ANT_LOG_LEVEL #define NRF_SDH_ANT_LOG_LEVEL 3 #endif // NRF_SDH_ANT_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SDH_ANT_INFO_COLOR #define NRF_SDH_ANT_INFO_COLOR 0 #endif // NRF_SDH_ANT_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SDH_ANT_DEBUG_COLOR #define NRF_SDH_ANT_DEBUG_COLOR 0 #endif // // NRF_SDH_BLE_LOG_ENABLED - Enable logging in SoftDevice handler (BLE) module. //========================================================== #ifndef NRF_SDH_BLE_LOG_ENABLED #define NRF_SDH_BLE_LOG_ENABLED 1 #endif // NRF_SDH_BLE_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_SDH_BLE_LOG_LEVEL #define NRF_SDH_BLE_LOG_LEVEL 3 #endif // NRF_SDH_BLE_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SDH_BLE_INFO_COLOR #define NRF_SDH_BLE_INFO_COLOR 0 #endif // NRF_SDH_BLE_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SDH_BLE_DEBUG_COLOR #define NRF_SDH_BLE_DEBUG_COLOR 0 #endif // // NRF_SDH_LOG_ENABLED - Enable logging in SoftDevice handler module. //========================================================== #ifndef NRF_SDH_LOG_ENABLED #define NRF_SDH_LOG_ENABLED 1 #endif // NRF_SDH_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_SDH_LOG_LEVEL #define NRF_SDH_LOG_LEVEL 3 #endif // NRF_SDH_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SDH_INFO_COLOR #define NRF_SDH_INFO_COLOR 0 #endif // NRF_SDH_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SDH_DEBUG_COLOR #define NRF_SDH_DEBUG_COLOR 0 #endif // // NRF_SDH_SOC_LOG_ENABLED - Enable logging in SoftDevice handler (SoC) module. //========================================================== #ifndef NRF_SDH_SOC_LOG_ENABLED #define NRF_SDH_SOC_LOG_ENABLED 1 #endif // NRF_SDH_SOC_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_SDH_SOC_LOG_LEVEL #define NRF_SDH_SOC_LOG_LEVEL 3 #endif // NRF_SDH_SOC_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SDH_SOC_INFO_COLOR #define NRF_SDH_SOC_INFO_COLOR 0 #endif // NRF_SDH_SOC_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SDH_SOC_DEBUG_COLOR #define NRF_SDH_SOC_DEBUG_COLOR 0 #endif // // NRF_SORTLIST_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_SORTLIST_CONFIG_LOG_ENABLED #define NRF_SORTLIST_CONFIG_LOG_ENABLED 0 #endif // NRF_SORTLIST_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_SORTLIST_CONFIG_LOG_LEVEL #define NRF_SORTLIST_CONFIG_LOG_LEVEL 3 #endif // NRF_SORTLIST_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SORTLIST_CONFIG_INFO_COLOR #define NRF_SORTLIST_CONFIG_INFO_COLOR 0 #endif // NRF_SORTLIST_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_SORTLIST_CONFIG_DEBUG_COLOR #define NRF_SORTLIST_CONFIG_DEBUG_COLOR 0 #endif // // NRF_TWI_SENSOR_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NRF_TWI_SENSOR_CONFIG_LOG_ENABLED #define NRF_TWI_SENSOR_CONFIG_LOG_ENABLED 0 #endif // NRF_TWI_SENSOR_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NRF_TWI_SENSOR_CONFIG_LOG_LEVEL #define NRF_TWI_SENSOR_CONFIG_LOG_LEVEL 3 #endif // NRF_TWI_SENSOR_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_TWI_SENSOR_CONFIG_INFO_COLOR #define NRF_TWI_SENSOR_CONFIG_INFO_COLOR 0 #endif // NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR #define NRF_TWI_SENSOR_CONFIG_DEBUG_COLOR 0 #endif // // PM_LOG_ENABLED - Enable logging in Peer Manager and its submodules. //========================================================== #ifndef PM_LOG_ENABLED #define PM_LOG_ENABLED 1 #endif // PM_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef PM_LOG_LEVEL #define PM_LOG_LEVEL 3 #endif // PM_LOG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef PM_LOG_INFO_COLOR #define PM_LOG_INFO_COLOR 0 #endif // PM_LOG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef PM_LOG_DEBUG_COLOR #define PM_LOG_DEBUG_COLOR 0 #endif // // //========================================================== // nrf_log in nRF_Serialization //========================================================== // SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED #define SER_HAL_TRANSPORT_CONFIG_LOG_ENABLED 0 #endif // SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL #define SER_HAL_TRANSPORT_CONFIG_LOG_LEVEL 3 #endif // SER_HAL_TRANSPORT_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef SER_HAL_TRANSPORT_CONFIG_INFO_COLOR #define SER_HAL_TRANSPORT_CONFIG_INFO_COLOR 0 #endif // SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR #define SER_HAL_TRANSPORT_CONFIG_DEBUG_COLOR 0 #endif // // //========================================================== // //========================================================== // // NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED - nrf_log_str_formatter - Log string // formatter #ifndef NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED #define NRF_LOG_STR_FORMATTER_TIMESTAMP_FORMAT_ENABLED 1 #endif // //========================================================== // nRF_NFC //========================================================== // NFC_AC_REC_ENABLED - nfc_ac_rec - NFC NDEF Alternative Carrier record encoder #ifndef NFC_AC_REC_ENABLED #define NFC_AC_REC_ENABLED 0 #endif // NFC_AC_REC_PARSER_ENABLED - nfc_ac_rec_parser - Alternative Carrier record parser #ifndef NFC_AC_REC_PARSER_ENABLED #define NFC_AC_REC_PARSER_ENABLED 0 #endif // NFC_BLE_OOB_ADVDATA_ENABLED - nfc_ble_oob_advdata - AD data for OOB pairing encoder //========================================================== #ifndef NFC_BLE_OOB_ADVDATA_ENABLED #define NFC_BLE_OOB_ADVDATA_ENABLED 0 #endif // ADVANCED_ADVDATA_SUPPORT - Non-mandatory AD types for BLE OOB pairing are encoded inside the // NDEF message (e.g. service UUIDs) // <1=> Enabled // <0=> Disabled #ifndef ADVANCED_ADVDATA_SUPPORT #define ADVANCED_ADVDATA_SUPPORT 0 #endif // // NFC_BLE_OOB_ADVDATA_PARSER_ENABLED - nfc_ble_oob_advdata_parser - BLE OOB pairing AD data // parser #ifndef NFC_BLE_OOB_ADVDATA_PARSER_ENABLED #define NFC_BLE_OOB_ADVDATA_PARSER_ENABLED 0 #endif // NFC_BLE_PAIR_LIB_ENABLED - nfc_ble_pair_lib - Library parameters //========================================================== #ifndef NFC_BLE_PAIR_LIB_ENABLED #define NFC_BLE_PAIR_LIB_ENABLED 0 #endif // NFC_BLE_PAIR_LIB_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NFC_BLE_PAIR_LIB_LOG_ENABLED #define NFC_BLE_PAIR_LIB_LOG_ENABLED 0 #endif // NFC_BLE_PAIR_LIB_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NFC_BLE_PAIR_LIB_LOG_LEVEL #define NFC_BLE_PAIR_LIB_LOG_LEVEL 3 #endif // NFC_BLE_PAIR_LIB_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_BLE_PAIR_LIB_INFO_COLOR #define NFC_BLE_PAIR_LIB_INFO_COLOR 0 #endif // NFC_BLE_PAIR_LIB_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_BLE_PAIR_LIB_DEBUG_COLOR #define NFC_BLE_PAIR_LIB_DEBUG_COLOR 0 #endif // // NFC_BLE_PAIR_LIB_SECURITY_PARAMETERS - Common Peer Manager security parameters. //========================================================== // BLE_NFC_SEC_PARAM_BOND - Enables device bonding. // If bonding is enabled at least one of the BLE_NFC_SEC_PARAM_KDIST options must be enabled. //========================================================== #ifndef BLE_NFC_SEC_PARAM_BOND #define BLE_NFC_SEC_PARAM_BOND 1 #endif // BLE_NFC_SEC_PARAM_KDIST_OWN_ENC - Enables Long Term Key and Master Identification // distribution by device. #ifndef BLE_NFC_SEC_PARAM_KDIST_OWN_ENC #define BLE_NFC_SEC_PARAM_KDIST_OWN_ENC 1 #endif // BLE_NFC_SEC_PARAM_KDIST_OWN_ID - Enables Identity Resolving Key and Identity Address // Information distribution by device. #ifndef BLE_NFC_SEC_PARAM_KDIST_OWN_ID #define BLE_NFC_SEC_PARAM_KDIST_OWN_ID 1 #endif // BLE_NFC_SEC_PARAM_KDIST_PEER_ENC - Enables Long Term Key and Master Identification // distribution by peer. #ifndef BLE_NFC_SEC_PARAM_KDIST_PEER_ENC #define BLE_NFC_SEC_PARAM_KDIST_PEER_ENC 1 #endif // BLE_NFC_SEC_PARAM_KDIST_PEER_ID - Enables Identity Resolving Key and Identity Address // Information distribution by peer. #ifndef BLE_NFC_SEC_PARAM_KDIST_PEER_ID #define BLE_NFC_SEC_PARAM_KDIST_PEER_ID 1 #endif // // BLE_NFC_SEC_PARAM_MIN_KEY_SIZE - Minimal size of a security key. // <7=> 7 // <8=> 8 // <9=> 9 // <10=> 10 // <11=> 11 // <12=> 12 // <13=> 13 // <14=> 14 // <15=> 15 // <16=> 16 #ifndef BLE_NFC_SEC_PARAM_MIN_KEY_SIZE #define BLE_NFC_SEC_PARAM_MIN_KEY_SIZE 7 #endif // BLE_NFC_SEC_PARAM_MAX_KEY_SIZE - Maximal size of a security key. // <7=> 7 // <8=> 8 // <9=> 9 // <10=> 10 // <11=> 11 // <12=> 12 // <13=> 13 // <14=> 14 // <15=> 15 // <16=> 16 #ifndef BLE_NFC_SEC_PARAM_MAX_KEY_SIZE #define BLE_NFC_SEC_PARAM_MAX_KEY_SIZE 16 #endif // //========================================================== // // NFC_BLE_PAIR_MSG_ENABLED - nfc_ble_pair_msg - NDEF message for OOB pairing encoder #ifndef NFC_BLE_PAIR_MSG_ENABLED #define NFC_BLE_PAIR_MSG_ENABLED 0 #endif // NFC_CH_COMMON_ENABLED - nfc_ble_pair_common - OOB pairing common data #ifndef NFC_CH_COMMON_ENABLED #define NFC_CH_COMMON_ENABLED 0 #endif // NFC_EP_OOB_REC_ENABLED - nfc_ep_oob_rec - EP record for BLE pairing encoder #ifndef NFC_EP_OOB_REC_ENABLED #define NFC_EP_OOB_REC_ENABLED 0 #endif // NFC_HS_REC_ENABLED - nfc_hs_rec - Handover Select NDEF record encoder #ifndef NFC_HS_REC_ENABLED #define NFC_HS_REC_ENABLED 0 #endif // NFC_LE_OOB_REC_ENABLED - nfc_le_oob_rec - LE record for BLE pairing encoder #ifndef NFC_LE_OOB_REC_ENABLED #define NFC_LE_OOB_REC_ENABLED 0 #endif // NFC_LE_OOB_REC_PARSER_ENABLED - nfc_le_oob_rec_parser - LE record parser #ifndef NFC_LE_OOB_REC_PARSER_ENABLED #define NFC_LE_OOB_REC_PARSER_ENABLED 0 #endif // NFC_NDEF_LAUNCHAPP_MSG_ENABLED - nfc_launchapp_msg - Encoding data for NDEF Application // Launching message for NFC Tag #ifndef NFC_NDEF_LAUNCHAPP_MSG_ENABLED #define NFC_NDEF_LAUNCHAPP_MSG_ENABLED 0 #endif // NFC_NDEF_LAUNCHAPP_REC_ENABLED - nfc_launchapp_rec - Encoding data for NDEF Application // Launching record for NFC Tag #ifndef NFC_NDEF_LAUNCHAPP_REC_ENABLED #define NFC_NDEF_LAUNCHAPP_REC_ENABLED 0 #endif // NFC_NDEF_MSG_ENABLED - nfc_ndef_msg - NFC NDEF Message generator module //========================================================== #ifndef NFC_NDEF_MSG_ENABLED #define NFC_NDEF_MSG_ENABLED 0 #endif // NFC_NDEF_MSG_TAG_TYPE - NFC Tag Type // <2=> Type 2 Tag // <4=> Type 4 Tag #ifndef NFC_NDEF_MSG_TAG_TYPE #define NFC_NDEF_MSG_TAG_TYPE 2 #endif // // NFC_NDEF_MSG_PARSER_ENABLED - nfc_ndef_msg_parser - NFC NDEF message parser module //========================================================== #ifndef NFC_NDEF_MSG_PARSER_ENABLED #define NFC_NDEF_MSG_PARSER_ENABLED 0 #endif // NFC_NDEF_MSG_PARSER_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NFC_NDEF_MSG_PARSER_LOG_ENABLED #define NFC_NDEF_MSG_PARSER_LOG_ENABLED 0 #endif // NFC_NDEF_MSG_PARSER_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NFC_NDEF_MSG_PARSER_LOG_LEVEL #define NFC_NDEF_MSG_PARSER_LOG_LEVEL 3 #endif // NFC_NDEF_MSG_PARSER_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_NDEF_MSG_PARSER_INFO_COLOR #define NFC_NDEF_MSG_PARSER_INFO_COLOR 0 #endif // // // NFC_NDEF_RECORD_ENABLED - nfc_ndef_record - NFC NDEF Record generator module #ifndef NFC_NDEF_RECORD_ENABLED #define NFC_NDEF_RECORD_ENABLED 0 #endif // NFC_NDEF_RECORD_PARSER_ENABLED - nfc_ndef_record_parser - NFC NDEF Record parser module //========================================================== #ifndef NFC_NDEF_RECORD_PARSER_ENABLED #define NFC_NDEF_RECORD_PARSER_ENABLED 0 #endif // NFC_NDEF_RECORD_PARSER_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NFC_NDEF_RECORD_PARSER_LOG_ENABLED #define NFC_NDEF_RECORD_PARSER_LOG_ENABLED 0 #endif // NFC_NDEF_RECORD_PARSER_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NFC_NDEF_RECORD_PARSER_LOG_LEVEL #define NFC_NDEF_RECORD_PARSER_LOG_LEVEL 3 #endif // NFC_NDEF_RECORD_PARSER_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_NDEF_RECORD_PARSER_INFO_COLOR #define NFC_NDEF_RECORD_PARSER_INFO_COLOR 0 #endif // // // NFC_NDEF_TEXT_RECORD_ENABLED - nfc_text_rec - Encoding data for a text record for NFC Tag #ifndef NFC_NDEF_TEXT_RECORD_ENABLED #define NFC_NDEF_TEXT_RECORD_ENABLED 0 #endif // NFC_NDEF_URI_MSG_ENABLED - nfc_uri_msg - Encoding data for NDEF message with URI record for // NFC Tag #ifndef NFC_NDEF_URI_MSG_ENABLED #define NFC_NDEF_URI_MSG_ENABLED 0 #endif // NFC_NDEF_URI_REC_ENABLED - nfc_uri_rec - Encoding data for a URI record for NFC Tag #ifndef NFC_NDEF_URI_REC_ENABLED #define NFC_NDEF_URI_REC_ENABLED 0 #endif // NFC_T2T_HAL_ENABLED - nfc_t2t_hal - Hardware Abstraction Layer for NFC library. //========================================================== #ifndef NFC_T2T_HAL_ENABLED #define NFC_T2T_HAL_ENABLED 0 #endif // NFCT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NFCT_CONFIG_IRQ_PRIORITY #define NFCT_CONFIG_IRQ_PRIORITY 6 #endif // HAL_NFC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef HAL_NFC_CONFIG_LOG_ENABLED #define HAL_NFC_CONFIG_LOG_ENABLED 0 #endif // HAL_NFC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef HAL_NFC_CONFIG_LOG_LEVEL #define HAL_NFC_CONFIG_LOG_LEVEL 3 #endif // HAL_NFC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef HAL_NFC_CONFIG_INFO_COLOR #define HAL_NFC_CONFIG_INFO_COLOR 0 #endif // HAL_NFC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef HAL_NFC_CONFIG_DEBUG_COLOR #define HAL_NFC_CONFIG_DEBUG_COLOR 0 #endif // HAL_NFC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef HAL_NFC_CONFIG_LOG_LEVEL #define HAL_NFC_CONFIG_LOG_LEVEL 3 #endif // HAL_NFC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef HAL_NFC_CONFIG_INFO_COLOR #define HAL_NFC_CONFIG_INFO_COLOR 0 #endif // HAL_NFC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef HAL_NFC_CONFIG_DEBUG_COLOR #define HAL_NFC_CONFIG_DEBUG_COLOR 0 #endif // // HAL_NFC_CONFIG_DEBUG_PIN_ENABLED - Enables pin debug in the module. //========================================================== #ifndef HAL_NFC_CONFIG_DEBUG_PIN_ENABLED #define HAL_NFC_CONFIG_DEBUG_PIN_ENABLED 0 #endif // HAL_NFC_HCLOCK_ON_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_HCLOCK_ON_DEBUG_PIN #define HAL_NFC_HCLOCK_ON_DEBUG_PIN 11 #endif // HAL_NFC_HCLOCK_OFF_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_HCLOCK_OFF_DEBUG_PIN #define HAL_NFC_HCLOCK_OFF_DEBUG_PIN 12 #endif // HAL_NFC_NFC_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_NFC_EVENT_DEBUG_PIN #define HAL_NFC_NFC_EVENT_DEBUG_PIN 24 #endif // HAL_NFC_DETECT_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_DETECT_EVENT_DEBUG_PIN #define HAL_NFC_DETECT_EVENT_DEBUG_PIN 25 #endif // HAL_NFC_TIMER4_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_TIMER4_EVENT_DEBUG_PIN #define HAL_NFC_TIMER4_EVENT_DEBUG_PIN 28 #endif // HAL_NFC_HCLOCK_ON_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_HCLOCK_ON_DEBUG_PIN #define HAL_NFC_HCLOCK_ON_DEBUG_PIN 31 #endif // HAL_NFC_HCLOCK_OFF_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_HCLOCK_OFF_DEBUG_PIN #define HAL_NFC_HCLOCK_OFF_DEBUG_PIN 31 #endif // HAL_NFC_NFC_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_NFC_EVENT_DEBUG_PIN #define HAL_NFC_NFC_EVENT_DEBUG_PIN 31 #endif // HAL_NFC_DETECT_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_DETECT_EVENT_DEBUG_PIN #define HAL_NFC_DETECT_EVENT_DEBUG_PIN 31 #endif // HAL_NFC_TIMER4_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_TIMER4_EVENT_DEBUG_PIN #define HAL_NFC_TIMER4_EVENT_DEBUG_PIN 31 #endif // // // NFC_T2T_PARSER_ENABLED - nfc_type_2_tag_parser - Parser for decoding Type 2 Tag data //========================================================== #ifndef NFC_T2T_PARSER_ENABLED #define NFC_T2T_PARSER_ENABLED 0 #endif // NFC_T2T_PARSER_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NFC_T2T_PARSER_LOG_ENABLED #define NFC_T2T_PARSER_LOG_ENABLED 0 #endif // NFC_T2T_PARSER_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NFC_T2T_PARSER_LOG_LEVEL #define NFC_T2T_PARSER_LOG_LEVEL 3 #endif // NFC_T2T_PARSER_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_T2T_PARSER_INFO_COLOR #define NFC_T2T_PARSER_INFO_COLOR 0 #endif // // // NFC_T4T_APDU_ENABLED - nfc_t4t_apdu - APDU encoder/decoder for Type 4 Tag //========================================================== #ifndef NFC_T4T_APDU_ENABLED #define NFC_T4T_APDU_ENABLED 0 #endif // NFC_T4T_APDU_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NFC_T4T_APDU_LOG_ENABLED #define NFC_T4T_APDU_LOG_ENABLED 0 #endif // NFC_T4T_APDU_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NFC_T4T_APDU_LOG_LEVEL #define NFC_T4T_APDU_LOG_LEVEL 3 #endif // NFC_T4T_APDU_LOG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_T4T_APDU_LOG_COLOR #define NFC_T4T_APDU_LOG_COLOR 0 #endif // // // NFC_T4T_CC_FILE_PARSER_ENABLED - nfc_t4t_cc_file - Capability Container file for Type 4 Tag //========================================================== #ifndef NFC_T4T_CC_FILE_PARSER_ENABLED #define NFC_T4T_CC_FILE_PARSER_ENABLED 0 #endif // NFC_T4T_CC_FILE_PARSER_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NFC_T4T_CC_FILE_PARSER_LOG_ENABLED #define NFC_T4T_CC_FILE_PARSER_LOG_ENABLED 0 #endif // NFC_T4T_CC_FILE_PARSER_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NFC_T4T_CC_FILE_PARSER_LOG_LEVEL #define NFC_T4T_CC_FILE_PARSER_LOG_LEVEL 3 #endif // NFC_T4T_CC_FILE_PARSER_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_T4T_CC_FILE_PARSER_INFO_COLOR #define NFC_T4T_CC_FILE_PARSER_INFO_COLOR 0 #endif // // // NFC_T4T_HAL_ENABLED - nfc_t4t_hal - Hardware Abstraction Layer for NFC library. //========================================================== #ifndef NFC_T4T_HAL_ENABLED #define NFC_T4T_HAL_ENABLED 0 #endif // NFCT_CONFIG_IRQ_PRIORITY - Interrupt priority // Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice // <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef NFCT_CONFIG_IRQ_PRIORITY #define NFCT_CONFIG_IRQ_PRIORITY 6 #endif // HAL_NFC_CONFIG_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef HAL_NFC_CONFIG_LOG_ENABLED #define HAL_NFC_CONFIG_LOG_ENABLED 0 #endif // HAL_NFC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef HAL_NFC_CONFIG_LOG_LEVEL #define HAL_NFC_CONFIG_LOG_LEVEL 3 #endif // HAL_NFC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef HAL_NFC_CONFIG_INFO_COLOR #define HAL_NFC_CONFIG_INFO_COLOR 0 #endif // HAL_NFC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef HAL_NFC_CONFIG_DEBUG_COLOR #define HAL_NFC_CONFIG_DEBUG_COLOR 0 #endif // HAL_NFC_CONFIG_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef HAL_NFC_CONFIG_LOG_LEVEL #define HAL_NFC_CONFIG_LOG_LEVEL 3 #endif // HAL_NFC_CONFIG_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef HAL_NFC_CONFIG_INFO_COLOR #define HAL_NFC_CONFIG_INFO_COLOR 0 #endif // HAL_NFC_CONFIG_DEBUG_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef HAL_NFC_CONFIG_DEBUG_COLOR #define HAL_NFC_CONFIG_DEBUG_COLOR 0 #endif // // HAL_NFC_CONFIG_DEBUG_PIN_ENABLED - Enables pin debug in the module. //========================================================== #ifndef HAL_NFC_CONFIG_DEBUG_PIN_ENABLED #define HAL_NFC_CONFIG_DEBUG_PIN_ENABLED 0 #endif // HAL_NFC_HCLOCK_ON_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_HCLOCK_ON_DEBUG_PIN #define HAL_NFC_HCLOCK_ON_DEBUG_PIN 31 #endif // HAL_NFC_HCLOCK_OFF_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_HCLOCK_OFF_DEBUG_PIN #define HAL_NFC_HCLOCK_OFF_DEBUG_PIN 31 #endif // HAL_NFC_NFC_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_NFC_EVENT_DEBUG_PIN #define HAL_NFC_NFC_EVENT_DEBUG_PIN 31 #endif // HAL_NFC_DETECT_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_DETECT_EVENT_DEBUG_PIN #define HAL_NFC_DETECT_EVENT_DEBUG_PIN 31 #endif // HAL_NFC_TIMER4_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_TIMER4_EVENT_DEBUG_PIN #define HAL_NFC_TIMER4_EVENT_DEBUG_PIN 31 #endif // HAL_NFC_HCLOCK_ON_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_HCLOCK_ON_DEBUG_PIN #define HAL_NFC_HCLOCK_ON_DEBUG_PIN 31 #endif // HAL_NFC_HCLOCK_OFF_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_HCLOCK_OFF_DEBUG_PIN #define HAL_NFC_HCLOCK_OFF_DEBUG_PIN 31 #endif // HAL_NFC_NFC_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_NFC_EVENT_DEBUG_PIN #define HAL_NFC_NFC_EVENT_DEBUG_PIN 31 #endif // HAL_NFC_DETECT_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_DETECT_EVENT_DEBUG_PIN #define HAL_NFC_DETECT_EVENT_DEBUG_PIN 31 #endif // HAL_NFC_TIMER4_EVENT_DEBUG_PIN - Pin number // <0=> 0 (P0.0) // <1=> 1 (P0.1) // <2=> 2 (P0.2) // <3=> 3 (P0.3) // <4=> 4 (P0.4) // <5=> 5 (P0.5) // <6=> 6 (P0.6) // <7=> 7 (P0.7) // <8=> 8 (P0.8) // <9=> 9 (P0.9) // <10=> 10 (P0.10) // <11=> 11 (P0.11) // <12=> 12 (P0.12) // <13=> 13 (P0.13) // <14=> 14 (P0.14) // <15=> 15 (P0.15) // <16=> 16 (P0.16) // <17=> 17 (P0.17) // <18=> 18 (P0.18) // <19=> 19 (P0.19) // <20=> 20 (P0.20) // <21=> 21 (P0.21) // <22=> 22 (P0.22) // <23=> 23 (P0.23) // <24=> 24 (P0.24) // <25=> 25 (P0.25) // <26=> 26 (P0.26) // <27=> 27 (P0.27) // <28=> 28 (P0.28) // <29=> 29 (P0.29) // <30=> 30 (P0.30) // <31=> 31 (P0.31) // <32=> 32 (P1.0) // <33=> 33 (P1.1) // <34=> 34 (P1.2) // <35=> 35 (P1.3) // <36=> 36 (P1.4) // <37=> 37 (P1.5) // <38=> 38 (P1.6) // <39=> 39 (P1.7) // <40=> 40 (P1.8) // <41=> 41 (P1.9) // <42=> 42 (P1.10) // <43=> 43 (P1.11) // <44=> 44 (P1.12) // <45=> 45 (P1.13) // <46=> 46 (P1.14) // <47=> 47 (P1.15) // <4294967295=> Not connected #ifndef HAL_NFC_TIMER4_EVENT_DEBUG_PIN #define HAL_NFC_TIMER4_EVENT_DEBUG_PIN 31 #endif // // // NFC_T4T_HL_DETECTION_PROCEDURES_ENABLED - nfc_t4t_hl_detection_procedures - NDEF Detection // Procedure for Type 4 Tag //========================================================== #ifndef NFC_T4T_HL_DETECTION_PROCEDURES_ENABLED #define NFC_T4T_HL_DETECTION_PROCEDURES_ENABLED 0 #endif // NFC_T4T_HL_DETECTION_PROCEDURES_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NFC_T4T_HL_DETECTION_PROCEDURES_LOG_ENABLED #define NFC_T4T_HL_DETECTION_PROCEDURES_LOG_ENABLED 0 #endif // NFC_T4T_HL_DETECTION_PROCEDURES_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NFC_T4T_HL_DETECTION_PROCEDURES_LOG_LEVEL #define NFC_T4T_HL_DETECTION_PROCEDURES_LOG_LEVEL 3 #endif // NFC_T4T_HL_DETECTION_PROCEDURES_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_T4T_HL_DETECTION_PROCEDURES_INFO_COLOR #define NFC_T4T_HL_DETECTION_PROCEDURES_INFO_COLOR 0 #endif // // APDU_BUFF_SIZE - Size (in bytes) of the buffer for APDU storage #ifndef APDU_BUFF_SIZE #define APDU_BUFF_SIZE 250 #endif // CC_STORAGE_BUFF_SIZE - Size (in bytes) of the buffer for CC file storage #ifndef CC_STORAGE_BUFF_SIZE #define CC_STORAGE_BUFF_SIZE 64 #endif // // NFC_T4T_TLV_BLOCK_PARSER_ENABLED - nfc_t4t_tlv_block - TLV block for Type 4 Tag //========================================================== #ifndef NFC_T4T_TLV_BLOCK_PARSER_ENABLED #define NFC_T4T_TLV_BLOCK_PARSER_ENABLED 0 #endif // NFC_T4T_TLV_BLOCK_PARSER_LOG_ENABLED - Enables logging in the module. //========================================================== #ifndef NFC_T4T_TLV_BLOCK_PARSER_LOG_ENABLED #define NFC_T4T_TLV_BLOCK_PARSER_LOG_ENABLED 0 #endif // NFC_T4T_TLV_BLOCK_PARSER_LOG_LEVEL - Default Severity level // <0=> Off // <1=> Error // <2=> Warning // <3=> Info // <4=> Debug #ifndef NFC_T4T_TLV_BLOCK_PARSER_LOG_LEVEL #define NFC_T4T_TLV_BLOCK_PARSER_LOG_LEVEL 3 #endif // NFC_T4T_TLV_BLOCK_PARSER_INFO_COLOR - ANSI escape code prefix. // <0=> Default // <1=> Black // <2=> Red // <3=> Green // <4=> Yellow // <5=> Blue // <6=> Magenta // <7=> Cyan // <8=> White #ifndef NFC_T4T_TLV_BLOCK_PARSER_INFO_COLOR #define NFC_T4T_TLV_BLOCK_PARSER_INFO_COLOR 0 #endif // // // //========================================================== // nRF_Segger_RTT //========================================================== // segger_rtt - SEGGER RTT //========================================================== // SEGGER_RTT_CONFIG_BUFFER_SIZE_UP - Size of upstream buffer. // Note that either @ref NRF_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE // or this value is actually used. It depends on which one is bigger. #ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_UP #define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 4096 #endif // SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS - Size of upstream buffer. #ifndef SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS #define SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS 2 #endif // SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN - Size of upstream buffer. #ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN #define SEGGER_RTT_CONFIG_BUFFER_SIZE_DOWN 16 #endif // SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS - Size of upstream buffer. #ifndef SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS #define SEGGER_RTT_CONFIG_MAX_NUM_DOWN_BUFFERS 2 #endif // SEGGER_RTT_CONFIG_DEFAULT_MODE - RTT behavior if the buffer is full. // The following modes are supported: // - SKIP - Do not block, output nothing. // - TRIM - Do not block, output as much as fits. // - BLOCK - Wait until there is space in the buffer. // <0=> SKIP // <1=> TRIM // <2=> BLOCK_IF_FIFO_FULL #ifndef SEGGER_RTT_CONFIG_DEFAULT_MODE #define SEGGER_RTT_CONFIG_DEFAULT_MODE 0 #endif // //========================================================== // //========================================================== // nRF_SoftDevice //========================================================== // NRF_SDH_BLE_ENABLED - nrf_sdh_ble - SoftDevice BLE event handler //========================================================== #ifndef NRF_SDH_BLE_ENABLED #define NRF_SDH_BLE_ENABLED 1 #endif // BLE Stack configuration - Stack configuration parameters // The SoftDevice handler will configure the stack with these parameters when calling @ref // nrf_sdh_ble_default_cfg_set. Other libraries might depend on these values; keep them // up-to-date even if you are not explicitely calling @ref nrf_sdh_ble_default_cfg_set. //========================================================== // NRF_SDH_BLE_GAP_DATA_LENGTH <27-251> // Requested BLE GAP data length to be negotiated. #ifndef NRF_SDH_BLE_GAP_DATA_LENGTH #define NRF_SDH_BLE_GAP_DATA_LENGTH 27 #endif // NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - Maximum number of peripheral links. #ifndef NRF_SDH_BLE_PERIPHERAL_LINK_COUNT #define NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 1 #endif // NRF_SDH_BLE_CENTRAL_LINK_COUNT - Maximum number of central links. #ifndef NRF_SDH_BLE_CENTRAL_LINK_COUNT #define NRF_SDH_BLE_CENTRAL_LINK_COUNT 0 #endif // NRF_SDH_BLE_TOTAL_LINK_COUNT - Total link count. // Maximum number of total concurrent connections using the default configuration. #ifndef NRF_SDH_BLE_TOTAL_LINK_COUNT #define NRF_SDH_BLE_TOTAL_LINK_COUNT 1 #endif // NRF_SDH_BLE_GAP_EVENT_LENGTH - GAP event length. // The time set aside for this connection on every connection interval in 1.25 ms units. #ifndef NRF_SDH_BLE_GAP_EVENT_LENGTH #define NRF_SDH_BLE_GAP_EVENT_LENGTH 6 #endif // NRF_SDH_BLE_GATT_MAX_MTU_SIZE - Static maximum MTU size. #ifndef NRF_SDH_BLE_GATT_MAX_MTU_SIZE #define NRF_SDH_BLE_GATT_MAX_MTU_SIZE 23 #endif // NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE - Attribute Table size in bytes. The size must be a multiple // of 4. #ifndef NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE #define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1408 #endif // NRF_SDH_BLE_VS_UUID_COUNT - The number of vendor-specific UUIDs. #ifndef NRF_SDH_BLE_VS_UUID_COUNT #define NRF_SDH_BLE_VS_UUID_COUNT 1 #endif // NRF_SDH_BLE_SERVICE_CHANGED - Include the Service Changed characteristic in the Attribute // Table. #ifndef NRF_SDH_BLE_SERVICE_CHANGED #define NRF_SDH_BLE_SERVICE_CHANGED 1 #endif // //========================================================== // BLE Observers - Observers and priority levels //========================================================== // NRF_SDH_BLE_OBSERVER_PRIO_LEVELS - Total number of priority levels for BLE observers. // This setting configures the number of priority levels available for BLE event handlers. // The priority level of a handler determines the order in which it receives events, with // respect to other handlers. #ifndef NRF_SDH_BLE_OBSERVER_PRIO_LEVELS #define NRF_SDH_BLE_OBSERVER_PRIO_LEVELS 4 #endif // BLE Observers priorities - Invididual priorities //========================================================== // BLE_ADV_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Advertising module. #ifndef BLE_ADV_BLE_OBSERVER_PRIO #define BLE_ADV_BLE_OBSERVER_PRIO 1 #endif // BLE_ANCS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Apple Notification Service Client. #ifndef BLE_ANCS_C_BLE_OBSERVER_PRIO #define BLE_ANCS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_ANS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Alert Notification Service Client. #ifndef BLE_ANS_C_BLE_OBSERVER_PRIO #define BLE_ANS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_BAS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Battery Service. #ifndef BLE_BAS_BLE_OBSERVER_PRIO #define BLE_BAS_BLE_OBSERVER_PRIO 2 #endif // BLE_BAS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Battery Service Client. #ifndef BLE_BAS_C_BLE_OBSERVER_PRIO #define BLE_BAS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_BPS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Blood Pressure Service. #ifndef BLE_BPS_BLE_OBSERVER_PRIO #define BLE_BPS_BLE_OBSERVER_PRIO 2 #endif // BLE_CONN_PARAMS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Connection parameters module. #ifndef BLE_CONN_PARAMS_BLE_OBSERVER_PRIO #define BLE_CONN_PARAMS_BLE_OBSERVER_PRIO 1 #endif // BLE_CONN_STATE_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Connection State module. #ifndef BLE_CONN_STATE_BLE_OBSERVER_PRIO #define BLE_CONN_STATE_BLE_OBSERVER_PRIO 0 #endif // BLE_CSCS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Cycling Speed and Cadence Service. #ifndef BLE_CSCS_BLE_OBSERVER_PRIO #define BLE_CSCS_BLE_OBSERVER_PRIO 2 #endif // BLE_CTS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Current Time Service Client. #ifndef BLE_CTS_C_BLE_OBSERVER_PRIO #define BLE_CTS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_DB_DISC_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Database Discovery module. #ifndef BLE_DB_DISC_BLE_OBSERVER_PRIO #define BLE_DB_DISC_BLE_OBSERVER_PRIO 1 #endif // BLE_DFU_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the DFU Service. #ifndef BLE_DFU_BLE_OBSERVER_PRIO #define BLE_DFU_BLE_OBSERVER_PRIO 2 #endif // BLE_DIS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Device Information Client. #ifndef BLE_DIS_C_BLE_OBSERVER_PRIO #define BLE_DIS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_GLS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Glucose Service. #ifndef BLE_GLS_BLE_OBSERVER_PRIO #define BLE_GLS_BLE_OBSERVER_PRIO 2 #endif // BLE_HIDS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Human Interface Device Service. #ifndef BLE_HIDS_BLE_OBSERVER_PRIO #define BLE_HIDS_BLE_OBSERVER_PRIO 2 #endif // BLE_HRS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Heart Rate Service. #ifndef BLE_HRS_BLE_OBSERVER_PRIO #define BLE_HRS_BLE_OBSERVER_PRIO 2 #endif // BLE_HRS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Heart Rate Service Client. #ifndef BLE_HRS_C_BLE_OBSERVER_PRIO #define BLE_HRS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_HTS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Health Thermometer Service. #ifndef BLE_HTS_BLE_OBSERVER_PRIO #define BLE_HTS_BLE_OBSERVER_PRIO 2 #endif // BLE_IAS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Immediate Alert Service. #ifndef BLE_IAS_BLE_OBSERVER_PRIO #define BLE_IAS_BLE_OBSERVER_PRIO 2 #endif // BLE_IAS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Immediate Alert Service Client. #ifndef BLE_IAS_C_BLE_OBSERVER_PRIO #define BLE_IAS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_LBS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the LED Button Service. #ifndef BLE_LBS_BLE_OBSERVER_PRIO #define BLE_LBS_BLE_OBSERVER_PRIO 2 #endif // BLE_LBS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the LED Button Service Client. #ifndef BLE_LBS_C_BLE_OBSERVER_PRIO #define BLE_LBS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_LLS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Link Loss Service. #ifndef BLE_LLS_BLE_OBSERVER_PRIO #define BLE_LLS_BLE_OBSERVER_PRIO 2 #endif // BLE_LNS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Location Navigation Service. #ifndef BLE_LNS_BLE_OBSERVER_PRIO #define BLE_LNS_BLE_OBSERVER_PRIO 2 #endif // BLE_NUS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the UART Service. #ifndef BLE_NUS_BLE_OBSERVER_PRIO #define BLE_NUS_BLE_OBSERVER_PRIO 2 #endif // BLE_NUS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the UART Central Service. #ifndef BLE_NUS_C_BLE_OBSERVER_PRIO #define BLE_NUS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_OTS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Object transfer service. #ifndef BLE_OTS_BLE_OBSERVER_PRIO #define BLE_OTS_BLE_OBSERVER_PRIO 2 #endif // BLE_OTS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Object transfer service client. #ifndef BLE_OTS_C_BLE_OBSERVER_PRIO #define BLE_OTS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_RSCS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Running Speed and Cadence Service. #ifndef BLE_RSCS_BLE_OBSERVER_PRIO #define BLE_RSCS_BLE_OBSERVER_PRIO 2 #endif // BLE_RSCS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Running Speed and Cadence Client. #ifndef BLE_RSCS_C_BLE_OBSERVER_PRIO #define BLE_RSCS_C_BLE_OBSERVER_PRIO 2 #endif // BLE_TPS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the TX Power Service. #ifndef BLE_TPS_BLE_OBSERVER_PRIO #define BLE_TPS_BLE_OBSERVER_PRIO 2 #endif // BSP_BTN_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Button Control module. #ifndef BSP_BTN_BLE_OBSERVER_PRIO #define BSP_BTN_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the NFC pairing library. #ifndef NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO #define NFC_BLE_PAIR_LIB_BLE_OBSERVER_PRIO 1 #endif // NRF_BLE_BMS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Bond Management Service. #ifndef NRF_BLE_BMS_BLE_OBSERVER_PRIO #define NRF_BLE_BMS_BLE_OBSERVER_PRIO 2 #endif // NRF_BLE_CGMS_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Contiuon Glucose Monitoring Service. #ifndef NRF_BLE_CGMS_BLE_OBSERVER_PRIO #define NRF_BLE_CGMS_BLE_OBSERVER_PRIO 2 #endif // NRF_BLE_ES_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Eddystone module. #ifndef NRF_BLE_ES_BLE_OBSERVER_PRIO #define NRF_BLE_ES_BLE_OBSERVER_PRIO 2 #endif // NRF_BLE_GATTS_C_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the GATT Service Client. #ifndef NRF_BLE_GATTS_C_BLE_OBSERVER_PRIO #define NRF_BLE_GATTS_C_BLE_OBSERVER_PRIO 2 #endif // NRF_BLE_GATT_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the GATT module. #ifndef NRF_BLE_GATT_BLE_OBSERVER_PRIO #define NRF_BLE_GATT_BLE_OBSERVER_PRIO 1 #endif // NRF_BLE_QWR_BLE_OBSERVER_PRIO // Priority with which BLE events are dispatched to the Queued writes module. #ifndef NRF_BLE_QWR_BLE_OBSERVER_PRIO #define NRF_BLE_QWR_BLE_OBSERVER_PRIO 2 #endif // NRF_BLE_SCAN_OBSERVER_PRIO // Priority for dispatching the BLE events to the Scanning Module. #ifndef NRF_BLE_SCAN_OBSERVER_PRIO #define NRF_BLE_SCAN_OBSERVER_PRIO 1 #endif // PM_BLE_OBSERVER_PRIO - Priority with which BLE events are dispatched to the Peer Manager // module. #ifndef PM_BLE_OBSERVER_PRIO #define PM_BLE_OBSERVER_PRIO 1 #endif // //========================================================== // //========================================================== // // NRF_SDH_ENABLED - nrf_sdh - SoftDevice handler //========================================================== #ifndef NRF_SDH_ENABLED #define NRF_SDH_ENABLED 1 #endif // Dispatch model // This setting configures how Stack events are dispatched to the application. //========================================================== // NRF_SDH_DISPATCH_MODEL // NRF_SDH_DISPATCH_MODEL_INTERRUPT: SoftDevice events are passed to the application from the // interrupt context. NRF_SDH_DISPATCH_MODEL_APPSH: SoftDevice events are scheduled using @ref // app_scheduler. NRF_SDH_DISPATCH_MODEL_POLLING: SoftDevice events are to be fetched manually. // <0=> NRF_SDH_DISPATCH_MODEL_INTERRUPT // <1=> NRF_SDH_DISPATCH_MODEL_APPSH // <2=> NRF_SDH_DISPATCH_MODEL_POLLING #ifndef NRF_SDH_DISPATCH_MODEL #define NRF_SDH_DISPATCH_MODEL 0 #endif // //========================================================== // Clock - SoftDevice clock configuration //========================================================== // NRF_SDH_CLOCK_LF_SRC - SoftDevice clock source. // <0=> NRF_CLOCK_LF_SRC_RC // <1=> NRF_CLOCK_LF_SRC_XTAL // <2=> NRF_CLOCK_LF_SRC_SYNTH #ifndef NRF_SDH_CLOCK_LF_SRC #define NRF_SDH_CLOCK_LF_SRC 1 #endif // NRF_SDH_CLOCK_LF_RC_CTIV - SoftDevice calibration timer interval. #ifndef NRF_SDH_CLOCK_LF_RC_CTIV #define NRF_SDH_CLOCK_LF_RC_CTIV 0 #endif // NRF_SDH_CLOCK_LF_RC_TEMP_CTIV - SoftDevice calibration timer interval under constant // temperature. How often (in number of calibration intervals) the RC oscillator shall be // calibrated if the temperature has not changed. #ifndef NRF_SDH_CLOCK_LF_RC_TEMP_CTIV #define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 0 #endif // NRF_SDH_CLOCK_LF_ACCURACY - External clock accuracy used in the LL to compute timing. // <0=> NRF_CLOCK_LF_ACCURACY_250_PPM // <1=> NRF_CLOCK_LF_ACCURACY_500_PPM // <2=> NRF_CLOCK_LF_ACCURACY_150_PPM // <3=> NRF_CLOCK_LF_ACCURACY_100_PPM // <4=> NRF_CLOCK_LF_ACCURACY_75_PPM // <5=> NRF_CLOCK_LF_ACCURACY_50_PPM // <6=> NRF_CLOCK_LF_ACCURACY_30_PPM // <7=> NRF_CLOCK_LF_ACCURACY_20_PPM // <8=> NRF_CLOCK_LF_ACCURACY_10_PPM // <9=> NRF_CLOCK_LF_ACCURACY_5_PPM // <10=> NRF_CLOCK_LF_ACCURACY_2_PPM // <11=> NRF_CLOCK_LF_ACCURACY_1_PPM #ifndef NRF_SDH_CLOCK_LF_ACCURACY #define NRF_SDH_CLOCK_LF_ACCURACY 7 #endif // //========================================================== // SDH Observers - Observers and priority levels //========================================================== // NRF_SDH_REQ_OBSERVER_PRIO_LEVELS - Total number of priority levels for request observers. // This setting configures the number of priority levels available for the SoftDevice request // event handlers. The priority level of a handler determines the order in which it receives // events, with respect to other handlers. #ifndef NRF_SDH_REQ_OBSERVER_PRIO_LEVELS #define NRF_SDH_REQ_OBSERVER_PRIO_LEVELS 2 #endif // NRF_SDH_STATE_OBSERVER_PRIO_LEVELS - Total number of priority levels for state observers. // This setting configures the number of priority levels available for the SoftDevice state // event handlers. The priority level of a handler determines the order in which it receives // events, with respect to other handlers. #ifndef NRF_SDH_STATE_OBSERVER_PRIO_LEVELS #define NRF_SDH_STATE_OBSERVER_PRIO_LEVELS 2 #endif // NRF_SDH_STACK_OBSERVER_PRIO_LEVELS - Total number of priority levels for stack event // observers. This setting configures the number of priority levels available for the SoftDevice // stack event handlers (ANT, BLE, SoC). The priority level of a handler determines the order in // which it receives events, with respect to other handlers. #ifndef NRF_SDH_STACK_OBSERVER_PRIO_LEVELS #define NRF_SDH_STACK_OBSERVER_PRIO_LEVELS 2 #endif // State Observers priorities - Invididual priorities //========================================================== // CLOCK_CONFIG_STATE_OBSERVER_PRIO // Priority with which state events are dispatched to the Clock driver. #ifndef CLOCK_CONFIG_STATE_OBSERVER_PRIO #define CLOCK_CONFIG_STATE_OBSERVER_PRIO 0 #endif // POWER_CONFIG_STATE_OBSERVER_PRIO // Priority with which state events are dispatched to the Power driver. #ifndef POWER_CONFIG_STATE_OBSERVER_PRIO #define POWER_CONFIG_STATE_OBSERVER_PRIO 0 #endif // RNG_CONFIG_STATE_OBSERVER_PRIO // Priority with which state events are dispatched to this module. #ifndef RNG_CONFIG_STATE_OBSERVER_PRIO #define RNG_CONFIG_STATE_OBSERVER_PRIO 0 #endif // //========================================================== // Stack Event Observers priorities - Invididual priorities //========================================================== // NRF_SDH_ANT_STACK_OBSERVER_PRIO // This setting configures the priority with which ANT events are processed with respect to // other events coming from the stack. Modify this setting if you need to have ANT events // dispatched before or after other stack events, such as BLE or SoC. Zero is the highest // priority. #ifndef NRF_SDH_ANT_STACK_OBSERVER_PRIO #define NRF_SDH_ANT_STACK_OBSERVER_PRIO 0 #endif // NRF_SDH_BLE_STACK_OBSERVER_PRIO // This setting configures the priority with which BLE events are processed with respect to // other events coming from the stack. Modify this setting if you need to have BLE events // dispatched before or after other stack events, such as ANT or SoC. Zero is the highest // priority. #ifndef NRF_SDH_BLE_STACK_OBSERVER_PRIO #define NRF_SDH_BLE_STACK_OBSERVER_PRIO 0 #endif // NRF_SDH_SOC_STACK_OBSERVER_PRIO // This setting configures the priority with which SoC events are processed with respect to // other events coming from the stack. Modify this setting if you need to have SoC events // dispatched before or after other stack events, such as ANT or BLE. Zero is the highest // priority. #ifndef NRF_SDH_SOC_STACK_OBSERVER_PRIO #define NRF_SDH_SOC_STACK_OBSERVER_PRIO 0 #endif // //========================================================== // //========================================================== // // NRF_SDH_SOC_ENABLED - nrf_sdh_soc - SoftDevice SoC event handler //========================================================== #ifndef NRF_SDH_SOC_ENABLED #define NRF_SDH_SOC_ENABLED 1 #endif // SoC Observers - Observers and priority levels //========================================================== // NRF_SDH_SOC_OBSERVER_PRIO_LEVELS - Total number of priority levels for SoC observers. // This setting configures the number of priority levels available for the SoC event handlers. // The priority level of a handler determines the order in which it receives events, with // respect to other handlers. #ifndef NRF_SDH_SOC_OBSERVER_PRIO_LEVELS #define NRF_SDH_SOC_OBSERVER_PRIO_LEVELS 2 #endif // SoC Observers priorities - Invididual priorities //========================================================== // BLE_ADV_SOC_OBSERVER_PRIO // Priority with which SoC events are dispatched to the Advertising module. #ifndef BLE_ADV_SOC_OBSERVER_PRIO #define BLE_ADV_SOC_OBSERVER_PRIO 1 #endif // BLE_DFU_SOC_OBSERVER_PRIO // Priority with which BLE events are dispatched to the DFU Service. #ifndef BLE_DFU_SOC_OBSERVER_PRIO #define BLE_DFU_SOC_OBSERVER_PRIO 1 #endif // CLOCK_CONFIG_SOC_OBSERVER_PRIO // Priority with which SoC events are dispatched to the Clock driver. #ifndef CLOCK_CONFIG_SOC_OBSERVER_PRIO #define CLOCK_CONFIG_SOC_OBSERVER_PRIO 0 #endif // POWER_CONFIG_SOC_OBSERVER_PRIO // Priority with which SoC events are dispatched to the Power driver. #ifndef POWER_CONFIG_SOC_OBSERVER_PRIO #define POWER_CONFIG_SOC_OBSERVER_PRIO 0 #endif // //========================================================== // //========================================================== // // //========================================================== // <<< end of configuration section >>> #endif // SDK_CONFIG_H ================================================ FILE: examples/nrf5/apps/memfault_demo_app/memfault_demo_app_nrf52.ld ================================================ /* Linker script to configure memory regions. */ SEARCH_DIR(.) GROUP(-lgcc -lc -lnosys) MEMORY { FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0x7B000 MEMFAULT_CORES (rw) : ORIGIN = 0xA1000, LENGTH = 0x40000 /* Note: most production apps would have a bootloader somewhere in this range! */ RSVD (rwx) : ORIGIN = 0xE1000, LENGTH = 0x1F000 RAM (rwx) : ORIGIN = 0x200057b8, LENGTH = 0x3a848 } __MemfaultCoreStorageStart = ORIGIN(MEMFAULT_CORES); __MemfaultCoreStorageEnd = ORIGIN(MEMFAULT_CORES) + LENGTH(MEMFAULT_CORES); __MemfaultCoredumpRamStart = ORIGIN(RAM); __MfltCoredumpRamEnd = ORIGIN(RAM) + LENGTH(RAM); SECTIONS { log_fmt 0xF0000000 (INFO): { KEEP(*(*.log_fmt_hdr)) KEEP(*(log_fmt)) } } SECTIONS { . = ALIGN(4); .mem_section_dummy_ram : { } .cli_sorted_cmd_ptrs : { PROVIDE(__start_cli_sorted_cmd_ptrs = .); KEEP(*(.cli_sorted_cmd_ptrs)) PROVIDE(__stop_cli_sorted_cmd_ptrs = .); } > RAM .fs_data : { PROVIDE(__start_fs_data = .); KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM .log_dynamic_data : { PROVIDE(__start_log_dynamic_data = .); KEEP(*(SORT(.log_dynamic_data*))) PROVIDE(__stop_log_dynamic_data = .); } > RAM .log_filter_data : { PROVIDE(__start_log_filter_data = .); KEEP(*(SORT(.log_filter_data*))) PROVIDE(__stop_log_filter_data = .); } > RAM } INSERT AFTER .data; SECTIONS { .mem_section_dummy_rom : { } .sdh_soc_observers : { PROVIDE(__start_sdh_soc_observers = .); KEEP(*(SORT(.sdh_soc_observers*))) PROVIDE(__stop_sdh_soc_observers = .); } > FLASH .pwr_mgmt_data : { PROVIDE(__start_pwr_mgmt_data = .); KEEP(*(SORT(.pwr_mgmt_data*))) PROVIDE(__stop_pwr_mgmt_data = .); } > FLASH .sdh_ble_observers : { PROVIDE(__start_sdh_ble_observers = .); KEEP(*(SORT(.sdh_ble_observers*))) PROVIDE(__stop_sdh_ble_observers = .); } > FLASH .sdh_stack_observers : { PROVIDE(__start_sdh_stack_observers = .); KEEP(*(SORT(.sdh_stack_observers*))) PROVIDE(__stop_sdh_stack_observers = .); } > FLASH .sdh_req_observers : { PROVIDE(__start_sdh_req_observers = .); KEEP(*(SORT(.sdh_req_observers*))) PROVIDE(__stop_sdh_req_observers = .); } > FLASH .sdh_state_observers : { PROVIDE(__start_sdh_state_observers = .); KEEP(*(SORT(.sdh_state_observers*))) PROVIDE(__stop_sdh_state_observers = .); } > FLASH .nrf_queue : { PROVIDE(__start_nrf_queue = .); KEEP(*(.nrf_queue)) PROVIDE(__stop_nrf_queue = .); } > FLASH .nrf_balloc : { PROVIDE(__start_nrf_balloc = .); KEEP(*(.nrf_balloc)) PROVIDE(__stop_nrf_balloc = .); } > FLASH .cli_command : { PROVIDE(__start_cli_command = .); KEEP(*(.cli_command)) PROVIDE(__stop_cli_command = .); } > FLASH .crypto_data : { PROVIDE(__start_crypto_data = .); KEEP(*(SORT(.crypto_data*))) PROVIDE(__stop_crypto_data = .); } > FLASH .log_const_data : { PROVIDE(__start_log_const_data = .); KEEP(*(SORT(.log_const_data*))) PROVIDE(__stop_log_const_data = .); } > FLASH .log_backends : { PROVIDE(__start_log_backends = .); KEEP(*(SORT(.log_backends*))) PROVIDE(__stop_log_backends = .); } > FLASH } INSERT AFTER .text SECTIONS { /** Integrate build id as described in memfault/core/build_info.h */ .note.gnu.build-id : { __start_gnu_build_id_start = .; KEEP(*(.note.gnu.build-id)) } > FLASH } INSERT AFTER .text INCLUDE "nrf_common.ld" ================================================ FILE: examples/nrf5/apps/memfault_demo_app/src/cli.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! CLI implementation for Memfault NRF5 Demo application #include "memfault/components.h" #include "nrf.h" // Includes CMSIS headers #include "nrf_cli.h" #include "nrf_cli_rtt.h" #include "nrf_log.h" // Find your key on https://app.memfault.com/ under 'Settings': sMfltHttpClientConfig g_mflt_http_client_config = { .api_key = "", }; NRF_CLI_RTT_DEF(cli_rtt); // NB: Using '\n' as newline character so only works on OSX/Linux :) NRF_CLI_DEF(m_cli, "rtt_cli:~$ ", &cli_rtt.transport, '\n', 4); void mflt_cli_init(void) { APP_ERROR_CHECK(nrf_cli_init(&m_cli, NULL, false, false, NRF_LOG_SEVERITY_INFO)); nrf_cli_start(&m_cli); m_cli.p_ctx->internal.flag.echo = 0; } void mflt_cli_try_process(void) { nrf_cli_process(&m_cli); } static void prv_clear_core_cmd(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_demo_cli_cmd_clear_core(argc, argv); } static void prv_get_core_cmd(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_demo_cli_cmd_get_core(argc, argv); } static void prv_crash_example(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_demo_cli_cmd_crash(argc, argv); } static void prv_test_log(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_demo_cli_cmd_test_log(argc, argv); } static void prv_trigger_logs(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_demo_cli_cmd_trigger_logs(argc, argv); } static void prv_trace_example(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_demo_cli_cmd_trace_event_capture(argc, argv); } static void prv_get_device_info(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_demo_cli_cmd_get_device_info(argc, argv); } static void prv_system_reboot_cmd(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_demo_cli_cmd_system_reboot(argc, argv); } static void prv_export_data_cmd(nrf_cli_t const *p_cli, size_t argc, char **argv) { memfault_data_export_dump_chunks(); } static void prv_coredump_storage_test_cmd(nrf_cli_t const *p_cli, size_t argc, char **argv) { __disable_irq(); memfault_coredump_storage_debug_test_begin(); __enable_irq(); memfault_coredump_storage_debug_test_finish(); } static void prv_hang_example(nrf_cli_t const *p_cli, size_t argc, char **argv) { MEMFAULT_LOG_DEBUG("Hanging system and waiting for watchdog!"); while (1) { } } NRF_CLI_CMD_REGISTER(crash, NULL, "trigger a crash", prv_crash_example); NRF_CLI_CMD_REGISTER(test_log, NULL, "writes test logs to log buffer", prv_test_log); NRF_CLI_CMD_REGISTER(trigger_logs, NULL, "trigger capture of current log buffer contents", prv_trigger_logs); NRF_CLI_CMD_REGISTER(trace, NULL, "capture trace event", prv_trace_example); NRF_CLI_CMD_REGISTER(core_storage_test, NULL, "verify coredump storage implementation with test patterns", prv_coredump_storage_test_cmd); NRF_CLI_CMD_REGISTER(clear_core, NULL, "clear the core", prv_clear_core_cmd); NRF_CLI_CMD_REGISTER(get_core, NULL, "gets the core", prv_get_core_cmd); NRF_CLI_CMD_REGISTER(get_device_info, NULL, "display device information", prv_get_device_info); NRF_CLI_CMD_REGISTER(reboot, NULL, "reboots system and tracks it with a trace event", prv_system_reboot_cmd); NRF_CLI_CMD_REGISTER(export, NULL, "Can be used to dump chunks to console or post via GDB", prv_export_data_cmd); NRF_CLI_CMD_REGISTER(hang, NULL, "Force a hang to test software watchdog functionality", prv_hang_example); // nrf_cli_help_print() doesn't work from the top level CLI so add a little shim 'help' function // for better discoverability of memfault added commands static const nrf_cli_static_entry_t *s_avail_mflt_cmds[] = { &CONCAT_3(nrf_cli_, crash, _raw), &CONCAT_3(nrf_cli_, test_log, _raw), &CONCAT_3(nrf_cli_, trigger_logs, _raw), &CONCAT_3(nrf_cli_, trace, _raw), &CONCAT_3(nrf_cli_, core_storage_test, _raw), &CONCAT_3(nrf_cli_, clear_core, _raw), &CONCAT_3(nrf_cli_, get_core, _raw), &CONCAT_3(nrf_cli_, get_device_info, _raw), &CONCAT_3(nrf_cli_, reboot, _raw), &CONCAT_3(nrf_cli_, export, _raw), &CONCAT_3(nrf_cli_, hang, _raw), }; static void prv_help_cmd(nrf_cli_t const *p_cli, size_t argc, char **argv) { MEMFAULT_LOG_RAW("Available Memfault Commands:"); for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_avail_mflt_cmds); i++) { const nrf_cli_static_entry_t *cmd = s_avail_mflt_cmds[i]; MEMFAULT_LOG_RAW("%s: %s", cmd->p_syntax, cmd->p_help); } } NRF_CLI_CMD_REGISTER(help, NULL, "Display available memfault commands", prv_help_cmd); ================================================ FILE: examples/nrf5/apps/memfault_demo_app/src/main.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A minimal demo app for the NRF52 using the nRF5 v15.2 SDK that allows for crashes to be //! generated from a RTT CLI and coredumps to be saved #include #include #include "app_error.h" #include "app_timer.h" #include "memfault/components.h" #include "memfault/ports/watchdog.h" #include "mflt_cli.h" #include "nordic_common.h" #include "nrf.h" #include "nrf_drv_clock.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "nrf_pwr_mgmt.h" #include "nrfx_wdt.h" static void timers_init(void) { ret_code_t err_code; err_code = app_timer_init(); APP_ERROR_CHECK(err_code); } static void log_init(void) { const ret_code_t err_code = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(err_code); NRF_LOG_DEFAULT_BACKENDS_INIT(); } static void prv_wdt_event_handler(void) { // Note: This handler should be entirely unreachable as the software watchdog should kick in // first but just in case let's reboot if it is invoked. MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_HardwareWatchdog); memfault_platform_reboot(); MEMFAULT_UNREACHABLE; } static nrfx_wdt_channel_id s_wdt_channel_id; static void prv_hardware_watchdog_enable(void) { nrfx_wdt_config_t config = NRFX_WDT_DEAFULT_CONFIG; uint32_t err_code = nrfx_wdt_init(&config, prv_wdt_event_handler); APP_ERROR_CHECK(err_code); err_code = nrfx_wdt_channel_alloc(&s_wdt_channel_id); APP_ERROR_CHECK(err_code); nrfx_wdt_enable(); } static void prv_hardware_watchdog_feed(void) { nrfx_wdt_channel_feed(s_wdt_channel_id); } int main(void) { nrf_drv_clock_init(); nrf_drv_clock_lfclk_request(NULL); log_init(); timers_init(); mflt_cli_init(); memfault_platform_boot(); prv_hardware_watchdog_enable(); memfault_software_watchdog_enable(); while (1) { prv_hardware_watchdog_feed(); memfault_software_watchdog_feed(); mflt_cli_try_process(); if (NRF_LOG_PROCESS() == false) { nrf_pwr_mgmt_run(); } } } ================================================ FILE: examples/nrf5/apps/memfault_demo_app/src/mflt_cli.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! Experimental CLI for diagnostics using nrf SDK void mflt_cli_init(void); void mflt_cli_try_process(void); ================================================ FILE: examples/nrf5/apps/memfault_demo_app/third_party/memfault/memfault_metrics_heartbeat_config.def ================================================ ================================================ FILE: examples/nrf5/apps/memfault_demo_app/third_party/memfault/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" #define MEMFAULT_USE_GNU_BUILD_ID 1 #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 #define MEMFAULT_COMPACT_LOG_ENABLE 1 // Enable capture of entire ISR state at time of crash #define MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT 64 ================================================ FILE: examples/nrf5/apps/memfault_demo_app/third_party/memfault/memfault_platform_log_config.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! An example implementation of overriding the Memfault logging macros by //! placing definitions in memfault_platform_log_config.h and adding //! -DMEMFAULT_PLATFORM_HAS_LOG_CONFIG=1 to the compiler flags #pragma once #include "memfault/core/compiler.h" #include "nrf_delay.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "sdk_config.h" //! Note: NRF_LOG_FLUSH() needs to be called if NRF_LOG_DEFERRED=1 in order //! for string formatters to print #define _MEMFAULT_PORT_LOG_IMPL(_level, mflt_level, fmt, ...) \ do { \ MEMFAULT_SDK_LOG_SAVE(mflt_level, fmt, ##__VA_ARGS__); \ NRF_LOG_##_level("MFLT: " fmt, ##__VA_ARGS__); \ NRF_LOG_FLUSH(); \ } while (0) #define MEMFAULT_LOG_DEBUG(fmt, ...) \ _MEMFAULT_PORT_LOG_IMPL(DEBUG, kMemfaultPlatformLogLevel_Debug, fmt, ##__VA_ARGS__) #define MEMFAULT_LOG_INFO(fmt, ...) \ _MEMFAULT_PORT_LOG_IMPL(INFO, kMemfaultPlatformLogLevel_Info, fmt, ##__VA_ARGS__) #define MEMFAULT_LOG_WARN(fmt, ...) \ _MEMFAULT_PORT_LOG_IMPL(WARNING, kMemfaultPlatformLogLevel_Warning, fmt, ##__VA_ARGS__) #define MEMFAULT_LOG_ERROR(fmt, ...) \ _MEMFAULT_PORT_LOG_IMPL(ERROR, kMemfaultPlatformLogLevel_Error, fmt, ##__VA_ARGS__) //! Note: nrf_delay_ms() is called to give the host a chance to drain the buffers and avoid Segger //! RTT overruns (data will be dropped on the floor otherwise). NRF_LOG_FLUSH() is a no-op for the //! RTT logging backend unfortunately. #define MEMFAULT_LOG_RAW(fmt, ...) \ do { \ NRF_LOG_INTERNAL_RAW_INFO(fmt "\n", ##__VA_ARGS__); \ NRF_LOG_FLUSH(); \ nrf_delay_ms(1); \ } while (0) ================================================ FILE: examples/nrf5/apps/memfault_demo_app/third_party/memfault/memfault_platform_port.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Glue layer between the Memfault SDK and the underlying platform #include "app_util_platform.h" #include "memfault/components.h" #include "memfault/ports/reboot_reason.h" #include "nrf.h" #if !defined(CONFIG_MEMFAULT_EVENT_STORAGE_SIZE) #define CONFIG_MEMFAULT_EVENT_STORAGE_SIZE 512 #endif #if !defined(CONFIG_MEMFAULT_LOGGING_RAM_SIZE) #define CONFIG_MEMFAULT_LOGGING_RAM_SIZE 512 #endif static void prv_get_device_serial(char *buf, size_t buf_len) { // We will use the 64bit NRF "Device identifier" as the serial number const size_t nrf_uid_num_words = 2; size_t curr_idx = 0; for (size_t i = 0; i < nrf_uid_num_words; i++) { uint32_t lsw = NRF_FICR->DEVICEID[i]; const size_t bytes_per_word = 4; for (size_t j = 0; j < bytes_per_word; j++) { size_t space_left = buf_len - curr_idx; uint8_t val = (lsw >> (j * 8)) & 0xff; size_t bytes_written = snprintf(&buf[curr_idx], space_left, "%02X", (int)val); if (bytes_written < space_left) { curr_idx += bytes_written; } else { // we are out of space, return what we got, it's NULL terminated return; } } } } void memfault_platform_get_device_info(struct MemfaultDeviceInfo *info) { static char s_device_serial[32]; static bool s_init = false; if (!s_init) { prv_get_device_serial(s_device_serial, sizeof(s_device_serial)); s_init = true; } *info = (struct MemfaultDeviceInfo){ .device_serial = s_device_serial, .hardware_version = "pca10056", .software_version = "1.0.0-dev", .software_type = "nrf-main", }; } //! Last function called after a coredump is saved. Should perform //! any final cleanup and then reset the device void memfault_platform_reboot(void) { NVIC_SystemReset(); MEMFAULT_UNREACHABLE; } bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { //! RTC is not configured so don't capture time on device return false; } int memfault_platform_boot(void) { // static RAM storage where logs will be stored. Storage can be any size // you want but you will want it to be able to hold at least a couple logs. static uint8_t s_mflt_log_buf_storage[CONFIG_MEMFAULT_LOGGING_RAM_SIZE]; memfault_log_boot(s_mflt_log_buf_storage, sizeof(s_mflt_log_buf_storage)); memfault_build_info_dump(); memfault_device_info_dump(); memfault_platform_reboot_tracking_boot(); static uint8_t s_event_storage[CONFIG_MEMFAULT_EVENT_STORAGE_SIZE]; const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(evt_storage); memfault_reboot_tracking_collect_reset_info(evt_storage); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); MEMFAULT_LOG_INFO("Memfault Initialized!"); return 0; } ================================================ FILE: examples/nrf5/apps/memfault_demo_app/third_party/memfault/memfault_trace_reason_user_config.def ================================================ // File for holding custom error traces: // https://mflt.io/error-tracing ================================================ FILE: examples/nrf5/apps/memfault_demo_app/third_party/memfault/sdk_overrides/app_error.h ================================================ /** * Copyright (c) 2013 - 2018, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /** @file * * @defgroup app_error Common application error handler * @{ * @ingroup app_common * * @brief Common application error handler and macros for utilizing a common error handler. */ #ifdef APP_ERROR_H__ #error "Please rename or remove components/libraries/util/app_error.h from the NRF SDK,\ as the Memfault SDK needs to override this header file." #endif #ifndef MEMFAULT_APP_ERROR_H__ #define MEMFAULT_APP_ERROR_H__ #include #include #include #include "app_error_weak.h" #include "nordic_common.h" #include "nrf.h" #include "sdk_errors.h" #ifdef ANT_STACK_SUPPORT_REQD #include "ant_error.h" #endif // ANT_STACK_SUPPORT_REQD #include #ifdef __cplusplus extern "C" { #endif #define NRF_FAULT_ID_SDK_RANGE_START \ (0x00004000) /**< The start of the range of error IDs defined in the SDK. */ /**@defgroup APP_ERROR_FAULT_IDS Fault ID types * @{ */ #define NRF_FAULT_ID_SDK_ERROR \ (NRF_FAULT_ID_SDK_RANGE_START + \ 1) /**< An error stemming from a call to @ref APP_ERROR_CHECK or @ref APP_ERROR_CHECK_BOOL. \ The info parameter is a pointer to an @ref error_info_t variable. */ #define NRF_FAULT_ID_SDK_ASSERT \ (NRF_FAULT_ID_SDK_RANGE_START + \ 2) /**< An error stemming from a call to ASSERT (nrf_assert.h). The info parameter is a \ pointer to an @ref assert_info_t variable. */ /**@} */ /**@brief Structure containing info about an error of the type @ref NRF_FAULT_ID_SDK_ERROR. */ typedef struct { uint32_t line_num; /**< The line number where the error occurred. */ uint8_t const *p_file_name; /**< The file in which the error occurred. */ uint32_t err_code; /**< The error code representing the error that occurred. */ } error_info_t; /**@brief Structure containing info about an error of the type @ref NRF_FAULT_ID_SDK_ASSERT. */ typedef struct { uint16_t line_num; /**< The line number where the error occurred. */ uint8_t const *p_file_name; /**< The file in which the error occurred. */ } assert_info_t; /**@brief Defines required by app_error_handler assembler instructions. */ #define APP_ERROR_ERROR_INFO_OFFSET_LINE_NUM (offsetof(error_info_t, line_num)) #define APP_ERROR_ERROR_INFO_OFFSET_P_FILE_NAME (offsetof(error_info_t, p_file_name)) #define APP_ERROR_ERROR_INFO_OFFSET_ERR_CODE (offsetof(error_info_t, err_code)) #define APP_ERROR_ERROR_INFO_SIZE (sizeof(error_info_t)) #define APP_ERROR_ERROR_INFO_SIZE_ALIGNED_8BYTE \ ALIGN_NUM(APP_ERROR_ERROR_INFO_SIZE, sizeof(uint64_t)) /**@brief Function for error handling, which is called when an error has occurred. * * @param[in] error_code Error code supplied to the handler. * @param[in] line_num Line number where the handler is called. * @param[in] p_file_name Pointer to the file name. */ void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t *p_file_name); /**@brief Function for error handling, which is called when an error has occurred. * * @param[in] error_code Error code supplied to the handler. */ void app_error_handler_bare(ret_code_t error_code); /**@brief Function for saving the parameters and entering an eternal loop, for debug purposes. * * @param[in] id Fault identifier. See @ref NRF_FAULT_IDS. * @param[in] pc The program counter of the instruction that triggered the fault, or 0 if * unavailable. * @param[in] info Optional additional information regarding the fault. Refer to each fault * identifier for details. */ void app_error_save_and_stop(uint32_t id, uint32_t pc, uint32_t info); /**@brief Function for logging details of error and flushing logs. * * @param[in] id Fault identifier. See @ref NRF_FAULT_IDS. * @param[in] pc The program counter of the instruction that triggered the fault, or 0 if * unavailable. * @param[in] info Optional additional information regarding the fault. Refer to each fault * identifier for details. */ void app_error_log_handle(uint32_t id, uint32_t pc, uint32_t info); /**@brief Macro for calling error handler function. * * @param[in] ERR_CODE Error code supplied to the error handler. */ #define APP_ERROR_HANDLER(ERR_CODE) MEMFAULT_ASSERT_RECORD(ERR_CODE) #if 0 // Overridden by memfault #ifdef DEBUG #define APP_ERROR_HANDLER(ERR_CODE) \ do { \ app_error_handler((ERR_CODE), __LINE__, (uint8_t *)__FILE__); \ } while (0) #else #define APP_ERROR_HANDLER(ERR_CODE) \ do { \ app_error_handler_bare((ERR_CODE)); \ } while (0) #endif #endif /**@brief Macro for calling error handler function if supplied error code any other than * NRF_SUCCESS. * * @param[in] ERR_CODE Error code supplied to the error handler. */ #define APP_ERROR_CHECK(ERR_CODE) \ do { \ const uint32_t LOCAL_ERR_CODE = (ERR_CODE); \ if (LOCAL_ERR_CODE != NRF_SUCCESS) { \ APP_ERROR_HANDLER(LOCAL_ERR_CODE); \ } \ } while (0) /**@brief Macro for calling error handler function if supplied boolean value is false. * * @param[in] BOOLEAN_VALUE Boolean value to be evaluated. */ #define APP_ERROR_CHECK_BOOL(BOOLEAN_VALUE) \ do { \ const uint32_t LOCAL_BOOLEAN_VALUE = (BOOLEAN_VALUE); \ if (!LOCAL_BOOLEAN_VALUE) { \ APP_ERROR_HANDLER(0); \ } \ } while (0) #ifdef __cplusplus } #endif #endif // MEMFAULT_APP_ERROR_H__ /** @} */ ================================================ FILE: examples/qp/README.md ================================================ ## Memfault for Quantum Leaps' QP™ This folder contains an example integration of the Memfault SDK using the QP/C port provided in `ports/qp`. The demo was tested using the [STM32F407 discovery board](https://www.st.com/en/evaluation-tools/stm32f4discovery.html) but should work for any STM32F4xx based board. ## Getting Started Make sure you have read the instructions in the `README.md` in the root of the SDK and performed the installation steps that are mentioned there. ### Setup _Note: these instructions reference the `$MEMFAULT_SDK_ROOT` variable for various paths. Either substitute the path to the Memfault SDK, or set it before running the snippets, i.e. `export MEMFAULT_SDK_ROOT=absolute/path/to/memfault/sdk`._ 1. Go into the demo app directory: ```bash $ cd $MEMFAULT_SDK_ROOT/examples/qp/apps/memfault_demo_app ``` 2. Clone the QP/C repo ```bash $ git clone https://github.com/QuantumLeaps/qpc.git --branch v6.6.0+ qpc ``` 3. Apply patches to integrate Memfault SDK ```bash $ cd qpc $ patch include/qassert.h $MEMFAULT_SDK_ROOT/ports/qp/qassert.h.patch $ patch src/qf_pkg.h $MEMFAULT_SDK_ROOT/ports/qp/qf_pkg.h.patch ``` ### Memfault Project Key A Project Key will need to be baked into the demo app to enable it to communicate with Memfault's web services. Go to https://app.memfault.com/, navigate to the project you want to use and select 'Settings'. Copy the 'Project API Key' and paste it into `$MEMFAULT_SDK_ROOT/examples/qp/apps/memfault_demo_app/src/main.c`, replacing `` with your Project Key. ### Building the App After performing the steps above, run `make` from the demo app directory: ```bash $ cd $MEMFAULT_SDK_ROOT/examples/qp/apps/memfault_demo_app $ EMBEDDED_MFLT_SDK_ROOT=$MEMFAULT_SDK_ROOT QPC_DIR=qpc make ``` The target is built to: `$MEMFAULT_SDK_ROOT/examples/qp/apps/memfault_demo_app/build/memfault_demo_app.elf` ### Flashing the App Run the `st-util` in one terminal: ``` $ st-util st-util 1.5.1 2019-12-12T15:22:42 INFO common.c: Loading device parameters.... 2019-12-12T15:22:42 INFO common.c: Device connected is: F4 device, id 0x10076413 [...] ``` Then flash the .elf using GDB: ``` $ arm-none-eabi-gdb --eval-command="target remote localhost:4242" --ex="mon reset" --ex="load" --ex="mon reset" \ --se=$MEMFAULT_SDK_ROOT/examples/qp/apps/memfault_demo_app/build/memfault_demo_app.elf --batch Generating /path/to/memfault-sdk/examples/qp/apps/memfault_demo_app/build/memfault_demo_app.elf ``` ### Running the App The USART2 peripheral of the board is used as a console/shell. Follow the instructions in the [user manual](https://www.st.com/content/ccc/resource/technical/document/user_manual/70/fe/4a/3f/e7/e1/4f/7d/DM00039084.pdf/files/DM00039084.pdf/jcr:content/translations/en.DM00039084.pdf) (see section 6.1.3 "ST-LINK/V2-A VCP configuration") to connect the RX/TX to either the on-board or an external USB-to-serial adapter. At this point, the application should be running and you can open a console to run the [Memfault demo CLI](https://mflt.io/demo-cli). To get started, run `help` to see short descriptions of each command. ``` $ miniterm.py --raw /dev/cu.usbserial* 115200 --- Miniterm on /dev/cu.usbserial-1420 115200,8,N,1 --- --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- Memfault QP demo app started... mflt> help get_core: Get coredump info clear_core: Clear an existing coredump export: Export base64-encoded chunks. To upload data see https://mflt.io/chunk-data-export get_device_info: Get device info help: Lists all commands ``` ### Causing a crash Detach the debugger now and hard-reset the board. Otherwise, if the debugger is still attached while crashing, the demo application will pause at a breakpoint instruction. Command `test_hardfault` will trigger a hard fault due to a bad instruction fetch at a non-existing address, `0xbadcafe`: ``` mflt> test_hardfault Memfault QP demo app started... mflt> ``` Upon crashing, a coredump will be written to noinit RAM. After that the demo app will restart immediately. To check whether the coredump has been captured, try running the `get_core` command after the device reboots: ``` mflt> get_core Has coredump with size: 584 ``` This confirms that a coredump of 584 bytes has been captured. ### Posting Memfault Data for Analysis #### Uploading Symbols Memfault needs the symbols for the firmware in order to analyze the coredump. The ELF is located at `build/memfault_demo_app.elf`. This .elf contains the symbols (debug information) amongst other things. [More information on Build Ids and uploading Symbol Files can be found here](https://mflt.io/symbol-file-build-ids). #### Posting captured data to Memfault The STM32F407 board does not have the capability to connect to the internet directly. Therefore, for debug purposes, the messages to push to the Memfault cloud can also be dumped from the CLI using the `export` command: ``` mflt> export MC:SLMTgQlDT1JFAgYAA/QNFAABTAYAGwGAAADQHwAgSHEAIOnhDAABAQYAIbYlAQDgHwAgYCAAIAD2AAACDgAbtiUBANAfACCldAAApHQKABM=: MC:wE0hsEgAINAfACAMDgABFAYAKYGX7c6KNvuWbdNF6vmTpvwqJEu6Ag4AAQoGABVERU1PU0VSSUFMCg4AARAGACExLjAuMCs4Y2VkMjQ2YjY=: MC:wJsBZgsOAAEKBgAVemVwaHlyLWFwcAQOAAEOBgAdcWVtdV9jb3J0ZXhfbTMHDgABBAYAASgGAAEFDgABBAYACQGAAAAGDgABAgoAAQEGAAk=: MC:wOgBJO0A4BwGAAUIAAcGAAEBKgABAQYACRjtAOAMGgAF4CABBgAJBOAA4BAGAAEBFgABBwYAAQEGAAkE7QDgCAYAAwYIDAABAQYACfztAOA=: MC:wLUCBA4AAQEIAAfhAOAEBgABYAYAAQEIAAfiAOAEDgABAQgAB+MA4AQOAAEBCAAH5ADgIAYACiAEgDIgAQEGAAlUDAAgKAYABgEKAAELBgA=: MC:wIIDC/MBAADYAQYAJwIAAHgXACB4FwAgAAIAAD3mAAABBgAPeBcAIAACAAAGIElUaW1lclRhc2tGcmVlU3RhY2s6IDMyMjQBIjxpbmY+IG0=: MC:wM8DZmx0OgYgQUhlYXBfQnl0ZXNGcmVlOiA0MDIwASo8aW5mPiBtZmx0OgYgmQFNYWluU3RhY2tfTWluQnl0ZXNGcmVlOiAzMjI0ASI8aW4=: MC:wJwEZj4gbWZsdDogSGVhcnRiZWF0IGtleXMvdmFsdWVzOgEtPGluZj4gbWZsdDoGIFdNZW1mYXVsdFNka01ldHJpY19JbnRlcnZhbE1zOiA=: MC:wOkEMAE7PGluZj4gbWZsdDoGIHNNZW1mYXVsdFNka01ldHJpY19VbmV4cGVjdGVkUmVib290Q291bnQ6IG51bGwBJTxpbmY+IG1mbHQ6BiA=: ... ``` The command will print out a sequence of base64-encoded chunks like above. You can copy & paste this output into the ["Chunks Debug" view](https://mflt.io/chunks-debug) in the Memfault UI or upload using the [desktop CLI tool](https://mflt.io/chunk-data-export). For more details on how to use to CLI to explore each of Memfault's subsystems, see the [Memfault docs](https://mflt.io/demo-cli). ================================================ FILE: examples/qp/apps/memfault_demo_app/Makefile ================================================ EMBEDDED_MFLT_SDK_ROOT ?= QPC_DIR ?= Q ?= @ ifeq ($(EMBEDDED_MFLT_SDK_ROOT),) $(error Please set EMBEDDED_MFLT_SDK_ROOT environment variable to point to the where the Memfault Firmware SDK is installed.\ Download it from here: https://github.com/memfault/memfault-firmware-sdk/releases) endif ifeq ($(QPC_DIR),) $(error Please set QPC_DIR environment variable to point to the where qpc/ is installed.\ Download it from here: https://github.com/QuantumLeaps/qp-bundle/releases) endif MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) SRC_DIR := $(dir $(MKFILE_PATH))src CC = arm-none-eabi-gcc LD = arm-none-eabi-ld OCPY = arm-none-eabi-objcopy ODUMP = arm-none-eabi-objdump MKDIR ?= mkdir RM ?= rm BUILD_DIR := $(CURDIR)/build PROJECT ?= memfault_demo_app MEMFAULT_COMPONENTS := core panics util demo http MEMFAULT_SDK_ROOT := $(EMBEDDED_MFLT_SDK_ROOT) include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk MEMFAULT_PORT_SRCS := \ $(EMBEDDED_MFLT_SDK_ROOT)/ports/panics/src/memfault_platform_ram_backed_coredump.c QF_SRC_DIR := $(QPC_DIR)/src/qf QV_SRC_DIR := $(QPC_DIR)/src/qv QP_SRCS := \ $(QF_SRC_DIR)/qep_hsm.c \ $(QF_SRC_DIR)/qep_msm.c \ $(QF_SRC_DIR)/qf_act.c \ $(QF_SRC_DIR)/qf_actq.c \ $(QF_SRC_DIR)/qf_defer.c \ $(QF_SRC_DIR)/qf_dyn.c \ $(QF_SRC_DIR)/qf_mem.c \ $(QF_SRC_DIR)/qf_ps.c \ $(QF_SRC_DIR)/qf_qact.c \ $(QF_SRC_DIR)/qf_qeq.c \ $(QF_SRC_DIR)/qf_qmact.c \ $(QF_SRC_DIR)/qf_time.c \ $(QV_SRC_DIR)/qv.c \ $(QPC_DIR)/ports/arm-cm/qv/gnu/qv_port.c QP_INCLUDES := \ $(QPC_DIR)/include \ $(QPC_DIR)/src \ $(QPC_DIR)/ports/arm-cm/qv/gnu \ $(QPC_DIR)/3rd_party/CMSIS/Include \ $(QPC_DIR)/3rd_party/stm32f4-discovery \ $(QPC_DIR)/3rd_party/stm32f4-discovery/inc \ SRCS := \ $(SRC_DIR)/bsp.c \ $(SRC_DIR)/main.c \ $(SRC_DIR)/platform_reference_impl/memfault_platform_core.c \ $(SRC_DIR)/platform_reference_impl/memfault_platform_log.c \ $(SRC_DIR)/startup_stm32f4xx.c \ $(QPC_DIR)/3rd_party/stm32f4-discovery/system_stm32f4xx.c \ $(QPC_DIR)/3rd_party/stm32f4-discovery/src/stm32f4xx_gpio.c \ $(QPC_DIR)/3rd_party/stm32f4-discovery/src/stm32f4xx_rcc.c \ $(QPC_DIR)/3rd_party/stm32f4-discovery/src/stm32f4xx_usart.c \ $(QP_SRCS) \ $(MEMFAULT_COMPONENTS_SRCS) \ $(MEMFAULT_PORT_SRCS) INCLUDES := \ $(SRC_DIR) \ $(SRC_DIR)/../config \ $(MEMFAULT_COMPONENTS_INC_FOLDERS) \ $(QP_INCLUDES) CFLAGS += \ -mcpu=cortex-m4 \ -mthumb \ -Wall \ -Werror \ -std=c11 \ -O0 \ -g3 \ -ffreestanding \ -ffunction-sections \ -fdata-sections # system_stm32f4xx.c trips a "misleading indentation" error: CFLAGS += -Wno-misleading-indentation LDFLAGS += \ -specs=nosys.specs \ -Wl,--build-id \ -Wl,--gc-sections \ -Wl,-Map=$(BUILD_DIR)/$(PROJECT).map \ -T $(SRC_DIR)/arm-generic.ld DEFINES += CFLAGS += $(foreach i,$(INCLUDES),-I$(i)) CFLAGS += $(foreach d,$(DEFINES),-D$(d)) .PHONY: all all: $(BUILD_DIR)/$(PROJECT).elf .PHONY : clean clean: -$(RM) -rf $(BUILD_DIR) $(BUILD_DIR)/$(PROJECT).elf: $(SRCS) @echo "Generating $@" @$(MKDIR) -p $(BUILD_DIR) $(Q) $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ ================================================ FILE: examples/qp/apps/memfault_demo_app/config/memfault_metrics_heartbeat_config.def ================================================ //! Define custom system metrics to track. For example, // MEMFAULT_METRICS_KEY_DEFINE(main_task_stack_hwm, kMemfaultMetricType_Unsigned) // MEMFAULT_METRICS_KEY_DEFINE(ble_min_rssi, kMemfaultMetricType_Signed) // MEMFAULT_METRICS_KEY_DEFINE(mcu_sleep_time_ms, kMemfaultMetricType_Timer) ================================================ FILE: examples/qp/apps/memfault_demo_app/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" ================================================ FILE: examples/qp/apps/memfault_demo_app/config/memfault_trace_reason_user_config.def ================================================ ================================================ FILE: examples/qp/apps/memfault_demo_app/src/arm-generic.ld ================================================ STACK_SIZE = 8192; HEAP_SIZE = 0; ROM_SIZE = 1024K; RAM_SIZE = 64K; MEMORY { ROM (rx) : ORIGIN = 0x08000000, LENGTH = ROM_SIZE RAM (xrw) : ORIGIN = 0x20000000, LENGTH = RAM_SIZE } PROVIDE(__sram_start = ORIGIN(RAM)); PROVIDE(__sram_end = ORIGIN(RAM) + LENGTH(RAM)); SECTIONS { .isr_vector : { KEEP(*(.isr_vector)) . = ALIGN(4); } >ROM .text : { . = ALIGN(4); *(.text) *(.text*) *(.rodata) *(.rodata*) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); } >ROM .gnu_build_id : { PROVIDE(g_gnu_build_id = .); *(.note.gnu.build-id) } > ROM _etext = .; .stack : { __stack_start__ = .; . = . + STACK_SIZE; . = ALIGN(4); __stack_end__ = .; } >RAM /* See memfault_platform_ram_backed_coredump.c */ .coredump_noinit (NOLOAD) : { KEEP(*(*.mflt_coredump)) } >RAM .data : AT (_etext) { __data_load = LOADADDR (.data); __data_start = .; *(.data) *(.data*) . = ALIGN(4); __data_end__ = .; _edata = __data_end__; } >RAM .bss : { __bss_start__ = .; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; __bss_end__ = .; } >RAM __exidx_start = .; .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >RAM __exidx_end = .; PROVIDE ( end = _ebss ); PROVIDE ( _end = _ebss ); PROVIDE ( __end__ = _ebss ); .heap : { __heap_start__ = .; . = . + HEAP_SIZE; . = ALIGN(4); __heap_end__ = .; } >RAM } ================================================ FILE: examples/qp/apps/memfault_demo_app/src/bsp.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "bsp.h" #include "memfault/demo/shell.h" #include "qf_port.h" #include "stm32f4xx.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_usart.h" void bsp_init(void) { SystemCoreClockUpdate(); // Use the automatic FPU state preservation and the FPU lazy stacking. FPU->FPCCR |= (1U << FPU_FPCCR_ASPEN_Pos) | (1U << FPU_FPCCR_LSPEN_Pos); // Enable USART2: RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_Init(GPIOA, &(GPIO_InitTypeDef){ .GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3, .GPIO_Mode = GPIO_Mode_AF, .GPIO_Speed = GPIO_Speed_50MHz, .GPIO_OType = GPIO_OType_PP, .GPIO_PuPd = GPIO_PuPd_UP, }); GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); /* TX = PA2 */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); /* RX = PA3 */ USART_Init(USART2, &(USART_InitTypeDef){ .USART_BaudRate = 115200, .USART_WordLength = USART_WordLength_8b, .USART_StopBits = USART_StopBits_1, .USART_Parity = USART_Parity_No, .USART_HardwareFlowControl = USART_HardwareFlowControl_None, .USART_Mode = USART_Mode_Tx | USART_Mode_Rx, }); USART_Cmd(USART2, ENABLE); SysTick_Config(SystemCoreClock / BSP_TICKS_PER_SEC); NVIC_SetPriorityGrouping(0U); NVIC_SetPriority(SysTick_IRQn, QF_AWARE_ISR_CMSIS_PRI); } int bsp_send_char_over_uart(char c) { while ((USART2->SR & USART_FLAG_TXE) == 0); USART2->DR = c; while ((USART2->SR & USART_FLAG_TXE) == 0); return 1; } bool bsp_read_char_from_uart(char *out_char) { if ((USART2->SR & USART_SR_RXNE) == 0) { return false; } *out_char = USART2->DR; return true; } void SysTick_Handler(void) { // Process time events for tick rate 0 QF_TICK_X(0U, 0); } ================================================ FILE: examples/qp/apps/memfault_demo_app/src/bsp.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief Minimal BSP for Memfault demo app with QP on stm32f4xx #include #define BSP_TICKS_PER_SEC (100) //! Initializes the BSP. void bsp_init(void); //! Synchronously sends a character. int bsp_send_char_over_uart(char c); //! Synchronously receives a character. bool bsp_read_char_from_uart(char *out_char); ================================================ FILE: examples/qp/apps/memfault_demo_app/src/main.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "bsp.h" #include "memfault/components.h" #include "qf.h" #include "qf_port.h" void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { *info = (sMemfaultDeviceInfo){ .device_serial = "DEMOSERIAL", .software_type = "qp-main", .software_version = "1.0.0", .hardware_version = "stm32f407g-disc1", }; } sMfltHttpClientConfig g_mflt_http_client_config = { .api_key = "", }; size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { // These symbols are defined by the linker extern uint32_t __sram_start; extern uint32_t __sram_end; uint32_t sram_start_address = (uint32_t)&__sram_start; uint32_t sram_end_address = (uint32_t)&__sram_end; if (((uint32_t)start_addr >= sram_start_address) && (sram_start_address < sram_end_address)) { return MEMFAULT_MIN(desired_size, sram_end_address - sram_start_address); } return 0; } void main(void) { bsp_init(); MEMFAULT_LOG_INFO("Memfault QP demo app started..."); memfault_build_info_dump(); memfault_device_info_dump(); memfault_demo_shell_boot(&(sMemfaultShellImpl){ .send_char = bsp_send_char_over_uart, }); QF_init(); QF_run(); } void QF_onStartup(void) { } void QF_onCleanup(void) { } void QV_onIdle(void) { char c; if (bsp_read_char_from_uart(&c)) { memfault_demo_shell_receive_char(c); } } // void Q_onAssert(char const *module, int loc) { // NOTE: After applying the ports/qp/qassert.h.patch and ports/qp/qf_pkg.h.patch files from the // Memfault Firmware SDK, the Q_onAssert function will no longer be necessary. Instead, Memfault's // assertion handling will be used. To ensure that no code relies on Q_onAssert, we recommend // removing the Q_onAssert function and replace any usage of it with the MEMFAULT_ASSERT macro // from memfault/panics/assert.h. // } ================================================ FILE: examples/qp/apps/memfault_demo_app/src/platform_reference_impl/memfault_platform_core.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #include "stm32f4xx.h" int memfault_platform_boot(void) { return 0; } MEMFAULT_NORETURN void memfault_platform_reboot(void) { memfault_platform_halt_if_debugging(); NVIC_SystemReset(); MEMFAULT_UNREACHABLE; } ================================================ FILE: examples/qp/apps/memfault_demo_app/src/platform_reference_impl/memfault_platform_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @brief //! An example implementation of the logging Memfault API #include #include #include #include #include "bsp.h" #include "memfault/core/platform/debug_log.h" #ifndef MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES #define MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES (128) #endif static void prv_send_log_to_uart(const char *str, size_t size) { for (size_t i = 0; i < size; ++i) { bsp_send_char_over_uart(str[i]); } bsp_send_char_over_uart('\n'); } static void prv_log(const char *fmt, va_list *args) { char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; const size_t size = vsnprintf(log_buf, sizeof(log_buf), fmt, *args); prv_send_log_to_uart(log_buf, size); } void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { va_list args; va_start(args, fmt); prv_log(fmt, &args); va_end(args); } void memfault_platform_log_raw(const char *fmt, ...) { va_list args; va_start(args, fmt); prv_log(fmt, &args); va_end(args); } ================================================ FILE: examples/qp/apps/memfault_demo_app/src/startup_stm32f4xx.c ================================================ /* Copyright (c) 2011 - 2014 ARM LIMITED All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of ARM nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------*/ #include "memfault/panics/assert.h" /* start and end of stack defined in the linker script ---------------------*/ /*extern int __stack_start__;*/ extern int __stack_end__; /* Function prototypes -----------------------------------------------------*/ void Default_Handler(void); /* Default empty handler */ void Reset_Handler(void); /* Reset Handler */ void SystemInit(void); /* CMSIS system initialization */ /*---------------------------------------------------------------------------- * weak aliases for each Exception handler to the Default_Handler. * Any function with the same name will override these definitions. */ /* Cortex-M Processor fault exceptions... */ void NMI_Handler(void) __attribute__((weak)); void HardFault_Handler(void) __attribute__((weak)); void MemManage_Handler(void) __attribute__((weak)); void BusFault_Handler(void) __attribute__((weak)); void UsageFault_Handler(void) __attribute__((weak)); /* Cortex-M Processor non-fault exceptions... */ void SVC_Handler(void) __attribute__((weak, alias("Default_Handler"))); void DebugMon_Handler(void) __attribute__((weak, alias("Default_Handler"))); void PendSV_Handler(void) __attribute__((weak, alias("Default_Handler"))); void SysTick_Handler(void) __attribute__((weak, alias("Default_Handler"))); /* external interrupts... */ void WWDG_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void PVD_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TAMP_STAMP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void RTC_WKUP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void FLASH_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void RCC_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void EXTI0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void EXTI1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void EXTI2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void EXTI3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void EXTI4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA1_Stream0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA1_Stream1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA1_Stream2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA1_Stream3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA1_Stream4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA1_Stream5_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA1_Stream6_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void ADC_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CAN1_TX_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CAN1_RX0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CAN1_RX1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CAN1_SCE_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void EXTI9_5_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM1_BRK_TIM9_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM1_UP_TIM10_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM1_TRG_COM_TIM11_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM1_CC_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void I2C1_EV_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void I2C1_ER_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void I2C2_EV_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void I2C2_ER_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void SPI1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void SPI2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void USART1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void USART2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void USART3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void EXTI15_10_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void RTC_Alarm_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void OTG_FS_WKUP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM8_BRK_TIM12_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM8_UP_TIM13_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM8_TRG_COM_TIM14_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM8_CC_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA1_Stream7_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void FSMC_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void SDIO_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM5_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void SPI3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void UART4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void UART5_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM6_DAC_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void TIM7_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA2_Stream0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA2_Stream1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA2_Stream2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA2_Stream3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA2_Stream4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void ETH_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void ETH_WKUP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CAN2_TX_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CAN2_RX0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CAN2_RX1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CAN2_SCE_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void OTG_FS_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA2_Stream5_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA2_Stream6_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DMA2_Stream7_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void USART6_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void I2C3_EV_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void I2C3_ER_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void OTG_HS_EP1_OUT_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void OTG_HS_EP1_IN_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void OTG_HS_WKUP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void OTG_HS_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void DCMI_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void CRYP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void HASH_RNG_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); void FPU_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); /*..........................................................................*/ __attribute__((section(".isr_vector"))) int const g_pfnVectors[] = { (int)&__stack_end__, /* Top of Stack */ (int)&Reset_Handler, /* Reset Handler */ (int)&NMI_Handler, /* NMI Handler */ (int)&HardFault_Handler, /* Hard Fault Handler */ (int)&MemManage_Handler, /* The MPU fault handler */ (int)&BusFault_Handler, /* The bus fault handler */ (int)&UsageFault_Handler, /* The usage fault handler */ 0, /* Reserved */ 0, /* Reserved */ 0, /* Reserved */ 0, /* Reserved */ (int)&SVC_Handler, /* SVCall handler */ (int)&DebugMon_Handler, /* Debug monitor handler */ 0, /* Reserved */ (int)&PendSV_Handler, /* The PendSV handler */ (int)&SysTick_Handler, /* The SysTick handler */ /*IRQ handlers... */ (int)&WWDG_IRQHandler, /* Window WatchDog */ (int)&PVD_IRQHandler, /* PVD through EXTI Line detection */ (int)&TAMP_STAMP_IRQHandler, /* Tamper and TimeStamps through the EXTI line */ (int)&RTC_WKUP_IRQHandler, /* RTC Wakeup through the EXTI line */ (int)&FLASH_IRQHandler, /* FLASH */ (int)&RCC_IRQHandler, /* RCC */ (int)&EXTI0_IRQHandler, /* EXTI Line0 */ (int)&EXTI1_IRQHandler, /* EXTI Line1 */ (int)&EXTI2_IRQHandler, /* EXTI Line2 */ (int)&EXTI3_IRQHandler, /* EXTI Line3 */ (int)&EXTI4_IRQHandler, /* EXTI Line4 */ (int)&DMA1_Stream0_IRQHandler, /* DMA1 Stream 0 */ (int)&DMA1_Stream1_IRQHandler, /* DMA1 Stream 1 */ (int)&DMA1_Stream2_IRQHandler, /* DMA1 Stream 2 */ (int)&DMA1_Stream3_IRQHandler, /* DMA1 Stream 3 */ (int)&DMA1_Stream4_IRQHandler, /* DMA1 Stream 4 */ (int)&DMA1_Stream5_IRQHandler, /* DMA1 Stream 5 */ (int)&DMA1_Stream6_IRQHandler, /* DMA1 Stream 6 */ (int)&ADC_IRQHandler, /* ADC1, ADC2 and ADC3s */ (int)&CAN1_TX_IRQHandler, /* CAN1 TX */ (int)&CAN1_RX0_IRQHandler, /* CAN1 RX0 */ (int)&CAN1_RX1_IRQHandler, /* CAN1 RX1 */ (int)&CAN1_SCE_IRQHandler, /* CAN1 SCE */ (int)&EXTI9_5_IRQHandler, /* External Line[9:5]s */ (int)&TIM1_BRK_TIM9_IRQHandler, /* TIM1 Break and TIM9 */ (int)&TIM1_UP_TIM10_IRQHandler, /* TIM1 Update and TIM10 */ (int)&TIM1_TRG_COM_TIM11_IRQHandler, /* TIM1 Trigger and Commutation and TIM11 */ (int)&TIM1_CC_IRQHandler, /* TIM1 Capture Compare */ (int)&TIM2_IRQHandler, /* TIM2 */ (int)&TIM3_IRQHandler, /* TIM3 */ (int)&TIM4_IRQHandler, /* TIM4 */ (int)&I2C1_EV_IRQHandler, /* I2C1 Event */ (int)&I2C1_ER_IRQHandler, /* I2C1 Error */ (int)&I2C2_EV_IRQHandler, /* I2C2 Event */ (int)&I2C2_ER_IRQHandler, /* I2C2 Error */ (int)&SPI1_IRQHandler, /* SPI1 */ (int)&SPI2_IRQHandler, /* SPI2 */ (int)&USART1_IRQHandler, /* USART1 */ (int)&USART2_IRQHandler, /* USART2 */ (int)&USART3_IRQHandler, /* USART3 */ (int)&EXTI15_10_IRQHandler, /* External Line[15:10]s */ (int)&RTC_Alarm_IRQHandler, /* RTC Alarm (A and B) through EXTI Line */ (int)&OTG_FS_WKUP_IRQHandler, /* USB OTG FS Wakeup through EXTI line */ (int)&TIM8_BRK_TIM12_IRQHandler, /* TIM8 Break and TIM12 */ (int)&TIM8_UP_TIM13_IRQHandler, /* TIM8 Update and TIM13 */ (int)&TIM8_TRG_COM_TIM14_IRQHandler, /* TIM8 Trigger and Commutation and TIM14 */ (int)&TIM8_CC_IRQHandler, /* TIM8 Capture Compare */ (int)&DMA1_Stream7_IRQHandler, /* DMA1 Stream7 */ (int)&FSMC_IRQHandler, /* FSMC */ (int)&SDIO_IRQHandler, /* SDIO */ (int)&TIM5_IRQHandler, /* TIM5 */ (int)&SPI3_IRQHandler, /* SPI3 */ (int)&UART4_IRQHandler, /* UART4 */ (int)&UART5_IRQHandler, /* UART5 */ (int)&TIM6_DAC_IRQHandler, /* TIM6 and DAC1&2 underrun errors */ (int)&TIM7_IRQHandler, /* TIM7 */ (int)&DMA2_Stream0_IRQHandler, /* DMA2 Stream 0 */ (int)&DMA2_Stream1_IRQHandler, /* DMA2 Stream 1 */ (int)&DMA2_Stream2_IRQHandler, /* DMA2 Stream 2 */ (int)&DMA2_Stream3_IRQHandler, /* DMA2 Stream 3 */ (int)&DMA2_Stream4_IRQHandler, /* DMA2 Stream 4 */ (int)Ð_IRQHandler, /* Ethernet */ (int)Ð_WKUP_IRQHandler, /* Ethernet Wakeup through EXTI line*/ (int)&CAN2_TX_IRQHandler, /* CAN2 TX */ (int)&CAN2_RX0_IRQHandler, /* CAN2 RX0 */ (int)&CAN2_RX1_IRQHandler, /* CAN2 RX1 */ (int)&CAN2_SCE_IRQHandler, /* CAN2 SCE */ (int)&OTG_FS_IRQHandler, /* USB OTG FS */ (int)&DMA2_Stream5_IRQHandler, /* DMA2 Stream 5 */ (int)&DMA2_Stream6_IRQHandler, /* DMA2 Stream 6 */ (int)&DMA2_Stream7_IRQHandler, /* DMA2 Stream 7 */ (int)&USART6_IRQHandler, /* USART6 */ (int)&I2C3_EV_IRQHandler, /* I2C3 event */ (int)&I2C3_ER_IRQHandler, /* I2C3 error */ (int)&OTG_HS_EP1_OUT_IRQHandler, /* USB OTG HS End Point 1 Out */ (int)&OTG_HS_EP1_IN_IRQHandler, /* USB OTG HS End Point 1 In */ (int)&OTG_HS_WKUP_IRQHandler, /* USB OTG HS Wakeup through EXTI */ (int)&OTG_HS_IRQHandler, /* USB OTG HS */ (int)&DCMI_IRQHandler, /* DCMI */ (int)&CRYP_IRQHandler, /* CRYP crypto */ (int)&HASH_RNG_IRQHandler, /* Hash and Rng */ (int)&FPU_IRQHandler, /* FPU */ }; /* reset handler -----------------------------------------------------------*/ void Reset_Handler(void) { extern int main(void); extern int __libc_init_array(void); extern unsigned __data_start; /* start of .data in the linker script */ extern unsigned __data_end__; /* end of .data in the linker script */ extern unsigned const __data_load; /* initialization values for .data */ extern unsigned __bss_start__; /* start of .bss in the linker script */ extern unsigned __bss_end__; /* end of .bss in the linker script */ extern void software_init_hook(void) __attribute__((weak)); unsigned const *src; unsigned *dst; SystemInit(); /* CMSIS system initialization */ /* copy the data segment initializers from flash to RAM... */ src = &__data_load; for (dst = &__data_start; dst < &__data_end__; ++dst, ++src) { *dst = *src; } /* zero fill the .bss segment in RAM... */ for (dst = &__bss_start__; dst < &__bss_end__; ++dst) { *dst = 0; } /* init hook provided? */ if (&software_init_hook != (void (*)(void))(0)) { /* give control to the RTOS */ software_init_hook(); /* this will also call __libc_init_array */ } else { /* call all static constructors in C++ (comment out in C programs) */ //__libc_init_array(); (void)main(); /* application's entry point; should never return! */ } /* the previous code should not return, but assert just in case... */ MEMFAULT_ASSERT_RECORD(0); } /*..........................................................................*/ void Default_Handler(void) { MEMFAULT_ASSERT_RECORD(0); } /****** End Of File *********************************************************/ ================================================ FILE: examples/stm32/README.md ================================================ # Memfault for STM32 Devices The subdirectories in this folder contain example integrations of the memfault coredump feature for STM32 MCUs. The example integrations are set up to store coredumps to regions of internal microflash. They make use of the STM32Cube HAL for the flash interface to the coredump (`stm32*\_hal_flash.c` & `stm32*\_hal_flash_ex.c`) which can be downloaded for free on the STM32 website. Within each subdirectory, more detailed descriptions can be found about how to integrate memfault into the target MCU ================================================ FILE: examples/stm32/stm32h743i/Makefile_test.mk ================================================ # Arguments that a typical make setup should already have defined BUILDDIR := ./build CFLAGS := -mcpu=cortex-m7 -mthumb CC := arm-none-eabi-gcc # # Directory locations which need to be specified prior to including the # example STM32 makefile # EMBEDDED_MFLT_SDK_ROOT := ../../.. STM32_CUBE_DIR := ../cube/fw_h7 include memfault_sdk.mk # Simple 'all' and 'clean' rules. These should already exist in a real # makefile target all: $(EMBEDDED_MFLT_SDK_OBJS) @echo "Success" clean: rm -rf $(BUILDDIR) # An example set of rules for emitting the code needed for an stm32 memfault # integration out to a build directory VPATH += $(sort $(dir $(EMBEDDED_MFLT_SDK_SRCS))) $(EMBEDDED_MFLT_SDK_OBJS): $(EMBEDDED_MFLT_OBJ_DIR)/%.o: ./%.c $(MAKEFILE_LIST) @echo Compiling $(/examples/stm32/stm32h743i/chibios-memfault-integration.patch` The patch does three things: - includes `memfault_sdk.mk` into the ChibiOS build system - defines a new rule for building the files needed for memfault (sdk, platform port, and flash stm32 hal files) - forces an assert in the main loop within main.c for simple testing ## Testing At this point you should be able to run `make` and compile the code & then load the resultant elf at `build/ch.elf` onto your board. For example, I'm connected to the nucleo board with JLink & GDB: `JLinkGDBServer -if swd -device STM32H753ZI -speed auto -port 2331` And run the following to flash the board: `arm-none-eabi-gdb-py --eval-command="target remote localhost:2331" --ex="mon reset" --ex="load" --ex="mon reset" --se=build/ch.elf` Soon after boot, you should hit the assert in the main loop and a breakpoint: ``` memfault_platform_halt_if_debugging () at ./memfault-firmware-sdk/examples/stm32/stm32h743i/platform_reference_impl/memfault_platform_core.c:17 17 __asm("bkpt"); (gdb) bt #0 memfault_platform_halt_if_debugging () at ./memfault-firmware-sdk/examples/stm32/stm32h743i/platform_reference_impl/memfault_platform_core.c:17 #1 0x08009af2 in memfault_fault_handling_assert (pc=, lr=lr@entry=0x800034d , extra=extra@entry=0) at ./memfault-firmware-sdk/components/panics/src/memfault_fault_handling_arm.c:162 #2 0x0800967e in main () at main.c:76 ``` You'll want to step over the breakpoint and then continue. At this point the coredump saving logic will run and the next breakpoint hit will be right before reboot ``` (gdb) next memfault_fault_handling_assert (pc=, lr=lr@entry=0x800034d , extra=extra@entry=0) at ./memfault-firmware-sdk/components/panics/src/memfault_fault_handling_arm.c:174 174 *icsr |= nmipendset_mask; (gdb) continue Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. memfault_platform_halt_if_debugging () at ./memfault-firmware-sdk/examples/stm32/stm32h743i/platform_reference_impl/memfault_platform_core.c:17 17 __asm("bkpt"); (gdb) bt #0 memfault_platform_halt_if_debugging () at ./memfault-firmware-sdk/examples/stm32/stm32h743i/platform_reference_impl/memfault_platform_core.c:17 #1 memfault_platform_reboot () at ./memfault-firmware-sdk/examples/stm32/stm32h743i/platform_reference_impl/memfault_platform_core.c:22 #2 0x08009a24 in memfault_fault_handler (regs=, reason=) at ./memfault-firmware-sdk/components/panics/src/memfault_fault_handling_arm.c:124 #3 #4 0x00000000 in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?) ``` Now you can extract the coredump via gdb. The example port is configured to save coredumps in the last sector of flash and can be extracted from GDB using: `dump binary memory memfault-core.bin 0x81E0000 0x8200000` You can then navigate to the Issues page on the memfault dashboard and click on "Manual Upload" to upload the file collected. ================================================ FILE: examples/stm32/stm32h743i/chibios-memfault-integration.patch ================================================ diff --git a/demos/STM32/RT-STM32H743I-NUCLEO144/Makefile b/demos/STM32/RT-STM32H743I-NUCLEO144/Makefile index 61879fd8c..47ba2a59c 100644 --- a/demos/STM32/RT-STM32H743I-NUCLEO144/Makefile +++ b/demos/STM32/RT-STM32H743I-NUCLEO144/Makefile @@ -30,7 +30,9 @@ endif # Enable this if you want link time optimizations (LTO). ifeq ($(USE_LTO),) - USE_LTO = yes +# Older versions of GCC have a lot of known issues with respect to LTO. +# Disable for now, for better portability + USE_LTO = no endif # Enable this if you want to see the full log while compiling. @@ -164,6 +166,14 @@ ULIBDIR = # List all user libraries here ULIBS = +EMBEDDED_MFLT_SDK_ROOT := ./memfault-firmware-sdk +STM32_CUBE_DIR := ./stm32_cube +include $(EMBEDDED_MFLT_SDK_ROOT)/examples/stm32/stm32h743i/memfault_sdk.mk + +UINCDIR += $(EMBEDDED_MFLT_SDK_INCLUDES) +ULIBS += $(EMBEDDED_MFLT_SDK_OBJS) + + # # End of user section ############################################################################## @@ -184,6 +194,21 @@ include $(RULESPATH)/rules.mk # Custom rules # +# Custom rule for building flash drivers within STM32 Cube HAL + +VPATH += $(sort $(dir $(EMBEDDED_MFLT_SDK_SRCS))) + +$(EMBEDDED_MFLT_SDK_OBJS): $(EMBEDDED_MFLT_OBJ_DIR)/%.o: ./%.c $(MAKEFILE_LIST) + @echo Compiling $( #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "stm32h7xx_hal_flash.h" // NOTE: Region to be used for saving coredump // NOTE: The stm32h743zi has two flash "banks" each of size 1MB ranging // from 0x8000000 - 0x8200000 // // As an example, we will store crash information in the last sector of the second bank struct { // uint32_t bank_start_addr; // the offset within the bank to start writing coredumps at uint32_t bank_start_off; // the offset within the bank to stop writing coredumps at uint32_t bank_end_off; // the id of the bank uint32_t bank_id; // the size of an erase sector uint32_t sector_size; } s_coredump_flash_storage = { .bank_start_addr = 0x8100000, .bank_start_off = 0x0E0000, .bank_end_off = 0x100000, .bank_id = FLASH_BANK_2, .sector_size = 128 * 1024, }; const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { static sMfltCoredumpRegion s_coredump_regions[2]; // NOTE: This is just an example of regions which could be collected // Typically sizes are derived from variables added to the .ld script of the port // Beginning of DTCM-RAM (Total size is 128kB) s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT((void *)0x20000000, 32 * 1024); // Beginning of AXI SRAM (Total size is 512kB) s_coredump_regions[1] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT((void *)0x24000000, 32 * 1024); *num_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions); return s_coredump_regions; } // Error writing to flash - should never happen & likely detects a configuration error // Call our reboot handler which will halt the device if a debugger is attached and then reboot __attribute__((optimize("O0"))) __attribute__((noinline)) static void prv_coredump_writer_assert_and_reboot(int error_code) { memfault_platform_reboot(); } void memfault_platform_coredump_storage_clear(void) { const uint32_t sector_id = s_coredump_flash_storage.bank_start_off / s_coredump_flash_storage.sector_size; FLASH_EraseInitTypeDef s_erase_cfg = { .TypeErase = FLASH_TYPEERASE_SECTORS, .Banks = s_coredump_flash_storage.bank_id, .Sector = sector_id, .NbSectors = 1, .VoltageRange = FLASH_VOLTAGE_RANGE_4 }; uint32_t SectorError = 0; HAL_FLASH_Unlock(); { int res = HAL_FLASHEx_Erase(&s_erase_cfg, &SectorError); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } HAL_FLASH_Lock(); } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = s_coredump_flash_storage.bank_end_off - s_coredump_flash_storage.bank_start_off, .sector_size = s_coredump_flash_storage.sector_size, }; } // NOTE: The internal STM32H74 flash uses 10 ECC bits over 32 byte "memory words". Since the ECC // bits are also in NOR flash, this means 32 byte hunks can only be written once since an updated // in the future would likely cause the ECC to go bad. // // In practice this means, writes must be issued 32 bytes at a time. The code below accomplishes // this by only issuing writes when 32 bytes have been collected. The Memfault coredump writer is // guaranteed to issue writes sequentially with the exception of the header which is at the // beginning of the coredump region and written last. A future update of the Memfault SDK will // incorporate this logic inside the SDK itself. #define COREDUMP_STORAGE_WRITE_SIZE 32 typedef struct { uint8_t data[COREDUMP_STORAGE_WRITE_SIZE]; uint32_t address; uint32_t bytes_written; } sCoredumpWorkingBuffer; MEMFAULT_ALIGNED(8) static sCoredumpWorkingBuffer s_working_buffer_header; MEMFAULT_ALIGNED(8) static sCoredumpWorkingBuffer s_working_buffer; static sCoredumpWorkingBuffer *prv_get_working_buf(uint32_t offset) { return (offset == 0) ? &s_working_buffer_header : &s_working_buffer; } static void prv_write_block(sCoredumpWorkingBuffer *blk) { const uint32_t start_addr = s_coredump_flash_storage.bank_start_addr + s_coredump_flash_storage.bank_start_off; const uint32_t addr = start_addr + blk->address; HAL_FLASH_Unlock(); { const uint32_t res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, addr, (uint32_t)blk->data); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } HAL_FLASH_Lock(); *blk = (sCoredumpWorkingBuffer){ 0 }; } static void prv_try_flush(void) { sCoredumpWorkingBuffer *hdr_block = &s_working_buffer_header; sCoredumpWorkingBuffer *data_block = &s_working_buffer; if (hdr_block->bytes_written == COREDUMP_STORAGE_WRITE_SIZE) { prv_write_block(hdr_block); // the header is flushed last so if we still have data we need to flush that too if (data_block->bytes_written != 0) { prv_write_block(data_block); } } if (data_block->bytes_written == COREDUMP_STORAGE_WRITE_SIZE) { prv_write_block(data_block); } } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if ((s_coredump_flash_storage.bank_start_off + offset + data_len) > s_coredump_flash_storage.bank_end_off) { return false; } const uint8_t *datap = data; uint32_t start_addr = offset; uint32_t page_aligned_start_address = (start_addr / COREDUMP_STORAGE_WRITE_SIZE) * COREDUMP_STORAGE_WRITE_SIZE; // we have to copy data into a temporary buffer because we can only issue 32 // byte aligned writes that are 32 bytes in length if (page_aligned_start_address != start_addr) { sCoredumpWorkingBuffer *working_buffer = prv_get_working_buf(page_aligned_start_address); uint32_t bytes_to_write = MEMFAULT_MIN( (page_aligned_start_address + COREDUMP_STORAGE_WRITE_SIZE) - start_addr, data_len); uint32_t write_offset = start_addr - page_aligned_start_address; memcpy(&working_buffer->data[write_offset], datap, bytes_to_write); working_buffer->bytes_written += bytes_to_write; working_buffer->address = page_aligned_start_address; prv_try_flush(); start_addr += bytes_to_write; data_len -= bytes_to_write; datap += bytes_to_write; } for (uint32_t i = 0; i < data_len; i += COREDUMP_STORAGE_WRITE_SIZE) { const uint32_t size = MEMFAULT_MIN(COREDUMP_STORAGE_WRITE_SIZE, data_len - i); sCoredumpWorkingBuffer *working_buffer = prv_get_working_buf(start_addr + i); memcpy(&working_buffer->data, &datap[i], size); working_buffer->bytes_written += size; working_buffer->address = start_addr + i; prv_try_flush(); } return true; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if ((s_coredump_flash_storage.bank_start_off + offset + read_len) > s_coredump_flash_storage.bank_end_off) { return false; } // The internal flash is memory mapped const uint32_t start_addr = s_coredump_flash_storage.bank_start_addr + s_coredump_flash_storage.bank_start_off; memcpy(data, (void *)(start_addr + offset), read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { // NOTE: For now, we will just clear the entire region for coredumps when an erase is issued // but this could just erase the sectors covered by erase_size memfault_platform_coredump_storage_clear(); return true; } ================================================ FILE: examples/stm32/stm32h743i/platform_reference_impl/memfault_platform_device_info.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Example STM32H7 specific routines for populating version data. #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/platform/device_info.h" #ifndef MEMFAULT_NRF_MAIN_SOFTWARE_VERSION #define MEMFAULT_NRF_MAIN_SOFTWARE_VERSION "1.0.0" #endif #ifndef MEMFAULT_NRF_SOFTWARE_TYPE #define MEMFAULT_NRF_SOFTWARE_TYPE "stm32-main" #endif #ifndef MEMFAULT_NRF_HW_REVISION #define MEMFAULT_NRF_HW_REVISION "nucleo-h743zi2" #endif #define STM32_UID_LENGTH (96 / 8) static char prv_int_to_ascii_hex(uint8_t val) { return val < 10 ? (char)val + '0' : (char)(val - 10) + 'A'; } // For demo purposes, use the STM32 96-bit UID as the device serial number static char *prv_get_device_serial(void) { static char s_device_serial[STM32_UID_LENGTH * 2 + 1]; const uint8_t *uid_base = (uint8_t *)0x1FF1E800; for (size_t i = 0; i < STM32_UID_LENGTH; i++) { const uint8_t val = uid_base[i]; const int curr_idx = i * 2; s_device_serial[curr_idx] = prv_int_to_ascii_hex((val >> 4) & 0xf); s_device_serial[curr_idx + 1] = prv_int_to_ascii_hex(val & 0xf); } return s_device_serial; } void memfault_platform_get_device_info(struct MemfaultDeviceInfo *info) { *info = (struct MemfaultDeviceInfo){ .device_serial = prv_get_device_serial(), .hardware_version = MEMFAULT_NRF_HW_REVISION, .software_version = MEMFAULT_NRF_MAIN_SOFTWARE_VERSION, .software_type = MEMFAULT_NRF_SOFTWARE_TYPE, }; } ================================================ FILE: examples/stm32/stm32h743i/platform_reference_impl/memfault_trace_reason_user_config.def ================================================ // File for holding custom error traces: // https://mflt.io/error-tracing ================================================ FILE: examples/stm32/stm32h743i/platform_reference_impl/stm32h7xx_hal_conf.h ================================================ /** ****************************************************************************** * @file stm32h7xx_hal_conf_template.h * @author MCD Application Team * @brief HAL configuration template file. * This file should be copied to the application folder and renamed * to stm32h7xx_hal_conf.h. ****************************************************************************** * @attention * *

© Copyright (c) 2017 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __STM32H7xx_HAL_CONF_H #define __STM32H7xx_HAL_CONF_H #include #include "stm32h7xx.h" #ifdef __cplusplus extern "C" { #endif /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ /* ########################## Module Selection ############################## */ /** * @brief This is the list of modules to be used in the HAL driver */ #define HAL_FLASH_MODULE_ENABLED /* ########################## Oscillator Values adaptation ####################*/ /** * @brief Adjust the value of External High Speed oscillator (HSE) used in your application. * This value is used by the RCC HAL module to compute the system frequency * (when HSE is used as system clock source, directly or through the PLL). */ #if !defined(HSE_VALUE) #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */ #endif /* HSE_VALUE */ #if !defined(HSE_STARTUP_TIMEOUT) #define HSE_STARTUP_TIMEOUT ((uint32_t)5000) /*!< Time out for HSE start up, in ms */ #endif /* HSE_STARTUP_TIMEOUT */ /** * @brief Internal oscillator (CSI) default value. * This value is the default CSI value after Reset. */ #if !defined(CSI_VALUE) #define CSI_VALUE ((uint32_t)4000000) /*!< Value of the Internal oscillator in Hz*/ #endif /* CSI_VALUE */ /** * @brief Internal High Speed oscillator (HSI) value. * This value is used by the RCC HAL module to compute the system frequency * (when HSI is used as system clock source, directly or through the PLL). */ #if !defined(HSI_VALUE) #define HSI_VALUE ((uint32_t)64000000) /*!< Value of the Internal oscillator in Hz*/ #endif /* HSI_VALUE */ /** * @brief External Low Speed oscillator (LSE) value. * This value is used by the UART, RTC HAL module to compute the system frequency */ #if !defined(LSE_VALUE) #define LSE_VALUE ((uint32_t)32768) /*!< Value of the External oscillator in Hz*/ #endif /* LSE_VALUE */ #if !defined(LSE_STARTUP_TIMEOUT) #define LSE_STARTUP_TIMEOUT ((uint32_t)5000) /*!< Time out for LSE start up, in ms */ #endif /* LSE_STARTUP_TIMEOUT */ #if !defined(LSI_VALUE) #define LSI_VALUE ((uint32_t)32000) /*!< LSI Typical Value in Hz*/ #endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz \ The real value may vary depending on the variations \ in voltage and temperature.*/ /** * @brief External clock source for I2S peripheral * This value is used by the I2S HAL module to compute the I2S clock source * frequency, this source is inserted directly through I2S_CKIN pad. */ #if !defined(EXTERNAL_CLOCK_VALUE) #define EXTERNAL_CLOCK_VALUE 12288000U /*!< Value of the External clock in Hz*/ #endif /* EXTERNAL_CLOCK_VALUE */ /* Tip: To avoid modifying this file each time you need to use different HSE, === you can define the HSE value in your toolchain compiler preprocessor. */ /* ########################### System Configuration ######################### */ /** * @brief This is the HAL system configuration section */ #define VDD_VALUE ((uint32_t)3300) /*!< Value of VDD in mv */ #define TICK_INT_PRIORITY ((uint32_t)0x0F) /*!< tick interrupt priority */ #define USE_RTOS 0 #define USE_SD_TRANSCEIVER 1U /*!< use uSD Transceiver */ #define USE_SPI_CRC 1U /*!< use CRC in SPI */ /* ########################### Ethernet Configuration ######################### */ #define ETH_TX_DESC_CNT 4 /* number of Ethernet Tx DMA descriptors */ #define ETH_RX_DESC_CNT 4 /* number of Ethernet Rx DMA descriptors */ #define ETH_MAC_ADDR0 ((uint8_t)0x02) #define ETH_MAC_ADDR1 ((uint8_t)0x00) #define ETH_MAC_ADDR2 ((uint8_t)0x00) #define ETH_MAC_ADDR3 ((uint8_t)0x00) #define ETH_MAC_ADDR4 ((uint8_t)0x00) #define ETH_MAC_ADDR5 ((uint8_t)0x00) /* ########################## Assert Selection ############################## */ /** * @brief Uncomment the line below to expanse the "assert_param" macro in the * HAL drivers code */ /* #define USE_FULL_ASSERT 1 */ /* Includes ------------------------------------------------------------------*/ /** * @brief Include module's header file */ #ifdef HAL_RCC_MODULE_ENABLED #include "stm32h7xx_hal_rcc.h" #endif /* HAL_RCC_MODULE_ENABLED */ #ifdef HAL_GPIO_MODULE_ENABLED #include "stm32h7xx_hal_gpio.h" #endif /* HAL_GPIO_MODULE_ENABLED */ #ifdef HAL_DMA_MODULE_ENABLED #include "stm32h7xx_hal_dma.h" #endif /* HAL_DMA_MODULE_ENABLED */ #ifdef HAL_MDMA_MODULE_ENABLED #include "stm32h7xx_hal_mdma.h" #endif /* HAL_MDMA_MODULE_ENABLED */ #ifdef HAL_HASH_MODULE_ENABLED #include "stm32h7xx_hal_hash.h" #endif /* HAL_HASH_MODULE_ENABLED */ #ifdef HAL_DCMI_MODULE_ENABLED #include "stm32h7xx_hal_dcmi.h" #endif /* HAL_DCMI_MODULE_ENABLED */ #ifdef HAL_DMA2D_MODULE_ENABLED #include "stm32h7xx_hal_dma2d.h" #endif /* HAL_DMA2D_MODULE_ENABLED */ #ifdef HAL_DFSDM_MODULE_ENABLED #include "stm32h7xx_hal_dfsdm.h" #endif /* HAL_DFSDM_MODULE_ENABLED */ #ifdef HAL_ETH_MODULE_ENABLED #include "stm32h7xx_hal_eth.h" #endif /* HAL_ETH_MODULE_ENABLED */ #ifdef HAL_EXTI_MODULE_ENABLED #include "stm32h7xx_hal_exti.h" #endif /* HAL_EXTI_MODULE_ENABLED */ #ifdef HAL_CORTEX_MODULE_ENABLED #include "stm32h7xx_hal_cortex.h" #endif /* HAL_CORTEX_MODULE_ENABLED */ #ifdef HAL_ADC_MODULE_ENABLED #include "stm32h7xx_hal_adc.h" #endif /* HAL_ADC_MODULE_ENABLED */ #ifdef HAL_FDCAN_MODULE_ENABLED #include "stm32h7xx_hal_fdcan.h" #endif /* HAL_FDCAN_MODULE_ENABLED */ #ifdef HAL_CEC_MODULE_ENABLED #include "stm32h7xx_hal_cec.h" #endif /* HAL_CEC_MODULE_ENABLED */ #ifdef HAL_COMP_MODULE_ENABLED #include "stm32h7xx_hal_comp.h" #endif /* HAL_COMP_MODULE_ENABLED */ #ifdef HAL_CRC_MODULE_ENABLED #include "stm32h7xx_hal_crc.h" #endif /* HAL_CRC_MODULE_ENABLED */ #ifdef HAL_CRYP_MODULE_ENABLED #include "stm32h7xx_hal_cryp.h" #endif /* HAL_CRYP_MODULE_ENABLED */ #ifdef HAL_DAC_MODULE_ENABLED #include "stm32h7xx_hal_dac.h" #endif /* HAL_DAC_MODULE_ENABLED */ #ifdef HAL_FLASH_MODULE_ENABLED #include "stm32h7xx_hal_flash.h" #endif /* HAL_FLASH_MODULE_ENABLED */ #ifdef HAL_HRTIM_MODULE_ENABLED #include "stm32h7xx_hal_hrtim.h" #endif /* HAL_HRTIM_MODULE_ENABLED */ #ifdef HAL_HSEM_MODULE_ENABLED #include "stm32h7xx_hal_hsem.h" #endif /* HAL_HSEM_MODULE_ENABLED */ #ifdef HAL_SRAM_MODULE_ENABLED #include "stm32h7xx_hal_sram.h" #endif /* HAL_SRAM_MODULE_ENABLED */ #ifdef HAL_NOR_MODULE_ENABLED #include "stm32h7xx_hal_nor.h" #endif /* HAL_NOR_MODULE_ENABLED */ #ifdef HAL_NAND_MODULE_ENABLED #include "stm32h7xx_hal_nand.h" #endif /* HAL_NAND_MODULE_ENABLED */ #ifdef HAL_I2C_MODULE_ENABLED #include "stm32h7xx_hal_i2c.h" #endif /* HAL_I2C_MODULE_ENABLED */ #ifdef HAL_I2S_MODULE_ENABLED #include "stm32h7xx_hal_i2s.h" #endif /* HAL_I2S_MODULE_ENABLED */ #ifdef HAL_IWDG_MODULE_ENABLED #include "stm32h7xx_hal_iwdg.h" #endif /* HAL_IWDG_MODULE_ENABLED */ #ifdef HAL_JPEG_MODULE_ENABLED #include "stm32h7xx_hal_jpeg.h" #endif /* HAL_JPEG_MODULE_ENABLED */ #ifdef HAL_MDIOS_MODULE_ENABLED #include "stm32h7xx_hal_mdios.h" #endif /* HAL_MDIOS_MODULE_ENABLED */ #ifdef HAL_MMC_MODULE_ENABLED #include "stm32h7xx_hal_mmc.h" #endif /* HAL_MMC_MODULE_ENABLED */ #ifdef HAL_LPTIM_MODULE_ENABLED #include "stm32h7xx_hal_lptim.h" #endif /* HAL_LPTIM_MODULE_ENABLED */ #ifdef HAL_LTDC_MODULE_ENABLED #include "stm32h7xx_hal_ltdc.h" #endif /* HAL_LTDC_MODULE_ENABLED */ #ifdef HAL_OPAMP_MODULE_ENABLED #include "stm32h7xx_hal_opamp.h" #endif /* HAL_OPAMP_MODULE_ENABLED */ #ifdef HAL_PWR_MODULE_ENABLED #include "stm32h7xx_hal_pwr.h" #endif /* HAL_PWR_MODULE_ENABLED */ #ifdef HAL_QSPI_MODULE_ENABLED #include "stm32h7xx_hal_qspi.h" #endif /* HAL_QSPI_MODULE_ENABLED */ #ifdef HAL_RAMECC_MODULE_ENABLED #include "stm32h7xx_hal_ramecc.h" #endif /* HAL_HCD_MODULE_ENABLED */ #ifdef HAL_RNG_MODULE_ENABLED #include "stm32h7xx_hal_rng.h" #endif /* HAL_RNG_MODULE_ENABLED */ #ifdef HAL_RTC_MODULE_ENABLED #include "stm32h7xx_hal_rtc.h" #endif /* HAL_RTC_MODULE_ENABLED */ #ifdef HAL_SAI_MODULE_ENABLED #include "stm32h7xx_hal_sai.h" #endif /* HAL_SAI_MODULE_ENABLED */ #ifdef HAL_SD_MODULE_ENABLED #include "stm32h7xx_hal_sd.h" #endif /* HAL_SD_MODULE_ENABLED */ #ifdef HAL_SDRAM_MODULE_ENABLED #include "stm32h7xx_hal_sdram.h" #endif /* HAL_SDRAM_MODULE_ENABLED */ #ifdef HAL_SPI_MODULE_ENABLED #include "stm32h7xx_hal_spi.h" #endif /* HAL_SPI_MODULE_ENABLED */ #ifdef HAL_SPDIFRX_MODULE_ENABLED #include "stm32h7xx_hal_spdifrx.h" #endif /* HAL_SPDIFRX_MODULE_ENABLED */ #ifdef HAL_SWPMI_MODULE_ENABLED #include "stm32h7xx_hal_swpmi.h" #endif /* HAL_SWPMI_MODULE_ENABLED */ #ifdef HAL_TIM_MODULE_ENABLED #include "stm32h7xx_hal_tim.h" #endif /* HAL_TIM_MODULE_ENABLED */ #ifdef HAL_UART_MODULE_ENABLED #include "stm32h7xx_hal_uart.h" #endif /* HAL_UART_MODULE_ENABLED */ #ifdef HAL_USART_MODULE_ENABLED #include "stm32h7xx_hal_usart.h" #endif /* HAL_USART_MODULE_ENABLED */ #ifdef HAL_IRDA_MODULE_ENABLED #include "stm32h7xx_hal_irda.h" #endif /* HAL_IRDA_MODULE_ENABLED */ #ifdef HAL_SMARTCARD_MODULE_ENABLED #include "stm32h7xx_hal_smartcard.h" #endif /* HAL_SMARTCARD_MODULE_ENABLED */ #ifdef HAL_SMBUS_MODULE_ENABLED #include "stm32h7xx_hal_smbus.h" #endif /* HAL_SMBUS_MODULE_ENABLED */ #ifdef HAL_WWDG_MODULE_ENABLED #include "stm32h7xx_hal_wwdg.h" #endif /* HAL_WWDG_MODULE_ENABLED */ #ifdef HAL_PCD_MODULE_ENABLED #include "stm32h7xx_hal_pcd.h" #endif /* HAL_PCD_MODULE_ENABLED */ #ifdef HAL_HCD_MODULE_ENABLED #include "stm32h7xx_hal_hcd.h" #endif /* HAL_HCD_MODULE_ENABLED */ /* Exported macro ------------------------------------------------------------*/ #ifdef USE_FULL_ASSERT /** * @brief The assert_param macro is used for function's parameters check. * @param expr: If expr is false, it calls assert_failed function * which reports the name of the source file and the source * line number of the call that failed. * If expr is true, it returns no value. * @retval None */ #define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__)) /* Exported functions ------------------------------------------------------- */ void assert_failed(uint8_t *file, uint32_t line); #else #define assert_param(expr) ((void)0U) #endif /* USE_FULL_ASSERT */ #ifdef __cplusplus } #endif #endif /* __STM32H7xx_HAL_CONF_H */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ ================================================ FILE: examples/stm32/stm32h743i/platform_reference_impl/stm32h7xx_hal_stubs.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "stm32h7xx_hal.h" uint32_t HAL_GetTick(void) { return 0; } ================================================ FILE: examples/wiced/README.md ================================================ # Memfault for WICED SDK This folder contains support files to make it easy to integrate Memfault into a WICED SDK based project. The demo app is tested on a [BCM943364WCD1] evaluation board. The instructions below also assume the BCM943364WCD1 board. The library and demo app are tested with v6.2.0 of the WICED SDK, but may also work with other versions. ## Getting Started Make sure you have read the instructions in the `README.md` in the root of the SDK and performed the installation steps that are mentioned there. ### Adding Memfault to the WICED SDK 1. Download and install the WICED SDK 6.2.0. 2. Create a symlink from `WICED-SDK-6.2.0/43xxx_Wi-Fi/apps/memfault_demo_app` to `memfault_sdk/examples/wiced/apps/memfault_demo_app`. For example: ```bash $ cd WICED-SDK-6.2.0/43xxx_Wi-Fi/apps $ ln -s /path/to/memfault_sdk/examples/wiced/apps/memfault_demo_app memfault_demo_app ``` 3. Create a symlink from `WICED-SDK-6.2.0/43xxx_Wi-Fi/libraries/memfault` to `memfault_sdk/examples/wiced/libraries/memfault`. For example: ```bash $ cd WICED-SDK-6.2.0/43xxx_Wi-Fi/libraries $ ln -s /path/to/memfault_sdk/examples/wiced/libraries/memfault memfault ``` 4. Optionally, to make the make the invoke tasks work, add a symlink inside the folder `memfault_sdk/examples/wiced/wiced_sdk` called `43xxx_Wi-Fi`, pointing to where you have installed the SDK. The final folder hierarchy should look like: ``` - examples - wiced - wiced_sdk - 43xxx_Wi-Fi - apps - doc - ... etc ... ``` ### Modifications to the WICED SDK The WICED SDK defines a hard fault handler that Memfault needs to override in order to capture a coredump when a hard fault happens. To do this, find the `HardFaultException` function and comment it out. For ARM Cortex M4 targets, it is defined in `WICED-SDK-6.2.0/43xxx_Wi-Fi/WICED/platform/ARM_CM4/hardfault_handler.c`. If you do not remove this function from the SDK code, the project will fail to link with error "multiple definition of `HardFaultException'". ### Building the demo app The SDK should now be able to automatically find the Memfault components and build the demo app. Now you can build the demo app: using pyinvoke: ```bash $ cd memfault_sdk $ invoke wiced.build ``` or ```bash $ cd /path/to/WICED-SDK-6.2.0/43xxx_Wi-Fi/ $ ./make memfault_demo_app-BCM943364WCD1-SDIO-debug ``` ### Flashing the demo app using pyinvoke: ```bash $ cd memfault_sdk $ invoke wiced.flash ``` or ```bash $ cd /path/to/WICED-SDK-6.2.0/43xxx_Wi-Fi/ $ ./make memfault_demo_app-BCM943364WCD1-SDIO-debug download download_apps run # You'll also want to clear the DEBUGEN bit ... otherwise a crash will trigger a breakpoint and the # system will hang. The easiest way to do this is by cycling power. The invoke command fixes this # automagically by executing the "stm32f4xx.cpu cortex_m disconnect" openocd command ``` ### Attaching the debug console (via UART) using pyinvoke: ```bash $ cd memfault_sdk $ invoke wiced.console ``` or ```bash $ miniterm.py --raw /dev/cu.usbserial-* 115200 ``` You can now type commands from the [Memfault demo CLI](https://mflt.io/demo-cli). To get started, run `help` to see short descriptions of each command. ```bash $ invoke wiced.console --- Miniterm on /dev/cu.usbserial-14101 115200,8,N,1 --- --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- help Console Commands: ? help [ []] - Print help message or command example. $? - Print return value of last executed command. get_core - Get coredump info clear_core - Clear an existing coredump post_core - Post coredump to Memfault ... etc ... ``` To detach, press `ctrl+]` (or `cmd+]` on macOS). ## Using the demo app The demo app is a simple console based app that has commands to cause a crash in several ways. Upon crashing, the `memfault/panics` component of the Memfault SDK will capture a coredump and save it to the external SPI flash. Once a coredump is captured, it can be sent via WiFi to Memfault's web services to get analyzed and desymbolicated. The `memfault/http` component of the Memfault SDK is used to talk to Memfault's web services. Let's walk through this process step by step: ### Memfault Project Key An Project Key will need to be baked into the demo app to enable it to communicate with Memfault's web services. Go to https://app.memfault.com/, navigate to the project you want to use and select 'Settings'. Copy the 'Project Key' and paste it into `apps/memfault_demo_app/memfault_demo_app.c`, replacing `` with your Project Key. Save the file and rebuild, reflash the project and attach the debug console (see instruction above). ### Checking the device info As a sanity check, let's request the device info from the debug console, enter `get_device_info` and press enter: ``` > get_device_info S/N: 00A0507510F0 SW type: wiced-main SW version: 1.0.0 HW version: wiced-proto ``` In the platform reference implementation for WICED, the hardware version is hard-coded to `wiced-proto`, software type is hard-coded to `wiced-main` and the WiFi MAC address is used as serial number. You can change this to match your requirements (see `libraries/memfault/platform_reference_impl/memfault_platform_device_info.c`). ### Causing a crash Command `test_hardfault` will trigger a hard fault due to a bad instruction fetch at a non-existing address, `0xbadcafe`: ``` > test_hardfault Starting WICED vWiced_006.002.001.0002 Platform BCM943364WCD1 initialised Started ThreadX v5.8 Initialising NetX_Duo v5.10_sp3 ... etc ... ``` Upon crashing, the coredump will be written to SPI flash. Note this can take a up to 15 seconds. Once done, the board should reboot and show the console prompt again. To check whether the coredump has been captured, try running the `get_core` command: ``` > get_core Has coredump with size: 131312 ``` This confirms that a coredump of 131312 bytes (the entire SRAM contents) has been captured to SPI flash. ### Posting the coredump to Memfault for analysis #### Uploading Symbols Memfault needs the symbols for the firmware in order to analyze the coredump. When the WICED SDK builds the app, it creates an .elf file at `/path/to/WICED-SDK-x.x.x/43xxx_Wi-Fi/build/memfault_demo_app-BCM943364WCD1-SDIO-release/binary/memfault_demo_app-BCM943364WCD1-SDIO-debug.elf`. This .elf contains the symbols (debug information) amongst other things. [More information on Build Ids and uploading Symbol Files can be found here](https://mflt.io/symbol-file-build-ids). #### Connect WiFi Before we can send the coredump, we need to make sure WiFi is connected to the internet. Type `join wpa2 ` in the console. You should see something like: ```Joining : Successfully joined : Obtaining IPv4 address via DHCP DHCP CLIENT hostname WICED IP IPv4 network ready IP: 192.168.1.2 Setting IPv6 link-local address IPv6 network ready IP: FE80:0000:0000:0000:02A0:50FF:FE75:10F0 ``` #### Post coredump Once connected, type `post_core` and press enter. You should see: ```Posting coredump... Result: 0 ``` The coredump is now being processed by Memfault's services and will show up shortly under Issues. If it does not, take a look at the FAQ in the `README.md` in the root of the SDK. For more details on how to use to CLI to explore each of Memfault's subsystems, see the [Memfault docs](https://mflt.io/demo-cli). # Integrating into existing WICED projects Adding Memfault to existing WICED projects is relatively simple. The Memfault SDK comes with .mk files that are compatible with WICED SDK's components. This has been tested with WICED SDK v6.2.0. ## Basic integration steps - Add symlinks and necessary modifications to the WICED SDK. See [Adding Memfault to the WICED SDK](#adding-memfault) above for details. - Add dependencies to Memfault's components in your app's .mk file: ``` $(NAME)_COMPONENTS := ... existing dependencies ... \ libraries/memfault/core \ libraries/memfault/demo \ libraries/memfault/http \ libraries/memfault/panics \ libraries/memfault/platform_reference_impl \ ``` The `demo` is not strictly necessary and should only be added if you are also using the `utilities/command_console` component from the WICED SDK. The `demo` component contains console command implementation to test out various Memfault SDK features. See [Using the demo app](#using-demo-app) for an impression of these commands. - Add configuration and initialization code: ``` // main.c // Find your key on https://app.memfault.com/ under 'Settings': sMfltHttpClientConfig g_mflt_http_client_config = { .api_key = "", }; void application_start(void) { wiced_init(); memfault_platform_boot(); ... your code ... } ``` - To hook up the console commands, please refer to the `memfault_demo_app.c` example. - The component `libraries/memfault/platform_reference_impl` contain implementations of platform dependencies that the Memfault SDK relies upon. They serve as a good example and starting point, but it's very likely you will need to customize some of the files to your use case. For example, by the software type defaults to `"wiced-main"`, the software version defaults to `"1.0.0"` and the hardware version defaults to `"wiced-proto"`. This needs to be changed to use the mechanisms you already have in place in your project to get the software type, software version and hardware version. Please refer to the `memfault_sdk/components/*/README.md` files to learn more about the customization options for each component. # FAQ If you run into any issues, please do not hesitate to reach out for help. - I'm getting a linker error "multiple definition of `HardFaultException'" after adding`libraries/memfault/http` as a dependency to my project. - Make sure to comment out the `HardFaultException` function from the WICED SDK. See [Modifications to the WICED SDK](#modifications) above. [bcm943364wcd1]: https://www.cypress.com/documentation/development-kitsboards/bcm943364wcd1evb-evaluation-and-development-kit ================================================ FILE: examples/wiced/apps/memfault_demo_app/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" ================================================ FILE: examples/wiced/apps/memfault_demo_app/memfault_demo_app.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Memfault WICED Demo App #include #include "command_console.h" #include "command_console_wifi.h" #include "memfault/core/debug_log.h" #include "memfault/core/platform/core.h" #include "memfault/demo/cli.h" #include "memfault/http/http_client.h" #include "memfault_platform_wiced.h" #include "platform_dct.h" #include "wiced.h" #include "wiced_apps_common.h" #include "wiced_dct_common.h" #include "wwd_assert.h" #define MAX_LINE_LENGTH (128) #define MAX_HISTORY_LENGTH (10) static char line_buffer[MAX_LINE_LENGTH]; static char history_buffer_storage[MAX_LINE_LENGTH * MAX_HISTORY_LENGTH]; // Find your key on https://app.memfault.com/ under 'Settings': sMfltHttpClientConfig g_mflt_http_client_config = { .api_key = "", }; static int prv_get_core_region(int argc, char *argv[]) { uint32_t flash_start; uint32_t flash_end; if (!memfault_platform_get_spi_start_and_end_addr(&flash_start, &flash_end)) { MEMFAULT_LOG_ERROR("Failed to get SPI start & end addresses"); return 0; } MEMFAULT_LOG_INFO("Coredump region SPI start: 0x%" PRIx32 " end: 0x%" PRIx32 " (size: %" PRIu32 " bytes)", flash_start, flash_end, flash_end - flash_start); return 0; } static const command_t commands[] = { { "get_core", memfault_demo_cli_cmd_get_core, 0, NULL, NULL, NULL, "Get coredump info" }, { "clear_core", memfault_demo_cli_cmd_clear_core, 0, NULL, NULL, NULL, "Clear an existing coredump" }, { "post_core", memfault_demo_cli_cmd_post_core, 0, NULL, NULL, NULL, "Post coredump to Memfault" }, { "crash", memfault_demo_cli_cmd_crash, 1, NULL, NULL, "" ESCAPE_SPACE_PROMPT, "Trigger a crash" }, { "get_core_region", prv_get_core_region, 0, NULL, NULL, NULL, "Get coredump SPI region info" }, { "get_device_info", memfault_demo_cli_cmd_get_device_info, 0, NULL, NULL, NULL, "Get device info" }, // Copied from command_console_wifi.h -- not using WIFI_COMMANDS to avoid dragging all the wifi // code { "join", join, 2, NULL, NULL, " [key] [channel] [ip netmask " "gateway]" ESCAPE_SPACE_PROMPT, "Join an AP. DHCP assumed if no IP address provided" }, CMD_TABLE_END, }; void application_start(void) { wiced_init(); memfault_platform_boot(); WPRINT_APP_INFO(("Memfault Test App on WICED at your service!\n")); // Expose the test app functionality through console commands: command_console_init(STDIO_UART, MAX_LINE_LENGTH, line_buffer, MAX_HISTORY_LENGTH, history_buffer_storage, " "); console_add_cmd_table(commands); WPRINT_APP_INFO(("Type 'help' to list available commands...\n")); } ================================================ FILE: examples/wiced/apps/memfault_demo_app/memfault_demo_app.mk ================================================ NAME := MemfaultTestApp GLOBAL_DEFINES += DEBUG THIS_DIR := $(dir $(word $(words $(MAKEFILE_LIST)), $(MAKEFILE_LIST))) MEMFAULT_SDK_ROOT := ../../../.. GLOBAL_INCLUDES += \ $(MEMFAULT_SDK_ROOT)/components/include \ config $(NAME)_SOURCES := memfault_demo_app.c \ $(NAME)_COMPONENTS := \ utilities/command_console \ utilities/command_console/wifi \ utilities/wiced_log \ libraries/memfault/core \ libraries/memfault/demo \ libraries/memfault/http \ libraries/memfault/panics \ libraries/memfault/util \ libraries/memfault/platform_reference_impl \ VALID_OSNS_COMBOS := ThreadX-NetX_Duo FreeRTOS-LwIP VALID_PLATFORMS := \ BCM943362WCD4 \ BCM943362WCD6 \ BCM943362WCD8 \ BCM943364WCD1 \ CYW94343WWCD1_EVB \ BCM943438WCD1 \ BCM94343WWCD2 \ CY8CKIT_062 \ NEB1DX* \ CYW9MCU7X9N364 \ CYW943907AEVAL1F \ CYW954907AEVAL1F \ CYW9WCD2REFAD2* \ CYW9WCD760PINSDAD2 \ CYW943455EVB* \ CYW943012EVB* ================================================ FILE: examples/wiced/libraries/memfault/core/core.mk ================================================ NAME := MemfaultCore $(NAME)_SOURCES := \ src/arch_arm_cortex_m.c \ src/memfault_build_id.c \ src/memfault_core_utils.c \ src/memfault_data_packetizer.c \ src/memfault_event_storage.c \ src/memfault_log.c \ src/memfault_log_data_source.c \ src/memfault_ram_reboot_info_tracking.c \ src/memfault_sdk_assert.c \ src/memfault_serializer_helper.c \ $(NAME)_COMPONENTS := $(NAME)_INCLUDES += include GLOBAL_INCLUDES += include VALID_OSNS_COMBOS := ThreadX-NetX_Duo FreeRTOS-LwIP VALID_PLATFORMS := \ BCM943362WCD4 \ BCM943362WCD6 \ BCM943362WCD8 \ BCM943364WCD1 \ CYW94343WWCD1_EVB \ BCM943438WCD1 \ BCM94343WWCD2 \ CY8CKIT_062 \ NEB1DX* \ CYW9MCU7X9N364 \ CYW943907AEVAL1F \ CYW954907AEVAL1F \ CYW9WCD2REFAD2* \ CYW9WCD760PINSDAD2 \ CYW943455EVB* \ CYW943012EVB* ================================================ FILE: examples/wiced/libraries/memfault/demo/demo.mk ================================================ NAME := MemfaultDemo $(NAME)_SOURCES := \ src/http/memfault_demo_http.c \ src/memfault_demo_core.c \ src/panics/memfault_demo_cli_aux.c \ src/panics/memfault_demo_panics.c $(NAME)_COMPONENTS := \ libraries/memfault/core \ libraries/memfault/http \ libraries/memfault/panics \ libraries/memfault/platform_reference_impl $(NAME)_INCLUDES += include GLOBAL_INCLUDES += include VALID_OSNS_COMBOS := ThreadX-NetX_Duo FreeRTOS-LwIP VALID_PLATFORMS := \ BCM943362WCD4 \ BCM943362WCD6 \ BCM943362WCD8 \ BCM943364WCD1 \ CYW94343WWCD1_EVB \ BCM943438WCD1 \ BCM94343WWCD2 \ CY8CKIT_062 \ NEB1DX* \ CYW9MCU7X9N364 \ CYW943907AEVAL1F \ CYW954907AEVAL1F \ CYW9WCD2REFAD2* \ CYW9WCD760PINSDAD2 \ CYW943455EVB* \ CYW943012EVB* ================================================ FILE: examples/wiced/libraries/memfault/http/http.mk ================================================ NAME := MemfaultHttp $(NAME)_SOURCES := \ src/memfault_http_client.c \ src/memfault_http_client_post_chunk.c \ src/memfault_http_utils.c \ $(NAME)_COMPONENTS := \ libraries/memfault/core \ libraries/memfault/panics \ protocols/HTTP_client $(NAME)_INCLUDES += include GLOBAL_INCLUDES += include VALID_OSNS_COMBOS := ThreadX-NetX_Duo FreeRTOS-LwIP VALID_PLATFORMS := \ BCM943362WCD4 \ BCM943362WCD6 \ BCM943362WCD8 \ BCM943364WCD1 \ CYW94343WWCD1_EVB \ BCM943438WCD1 \ BCM94343WWCD2 \ CY8CKIT_062 \ NEB1DX* \ CYW9MCU7X9N364 \ CYW943907AEVAL1F \ CYW954907AEVAL1F \ CYW9WCD2REFAD2* \ CYW9WCD760PINSDAD2 \ CYW943455EVB* \ CYW943012EVB* ================================================ FILE: examples/wiced/libraries/memfault/panics/panics.mk ================================================ NAME := MemfaultPanics $(NAME)_SOURCES := \ src/memfault_coredump.c \ src/memfault_fault_handling_arm.c \ src/memfault_coredump_regions_armv7.c \ src/memfault_coredump_sdk_regions.c $(NAME)_COMPONENTS := \ drivers/spi_flash \ libraries/memfault/core \ $(NAME)_INCLUDES += include GLOBAL_INCLUDES += include VALID_OSNS_COMBOS := ThreadX-NetX_Duo FreeRTOS-LwIP VALID_PLATFORMS := \ BCM943362WCD4 \ BCM943362WCD6 \ BCM943362WCD8 \ BCM943364WCD1 \ CYW94343WWCD1_EVB \ BCM943438WCD1 \ BCM94343WWCD2 \ CY8CKIT_062 \ NEB1DX* \ CYW9MCU7X9N364 \ CYW943907AEVAL1F \ CYW954907AEVAL1F \ CYW9WCD2REFAD2* \ CYW9WCD760PINSDAD2 \ CYW943455EVB* \ CYW943012EVB* ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions which could be used //! for coredump collection on the WICED platform #include #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/platform/crc32.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" // The ordering here is deliberate because some includes are missing in these headers :/ #include "platform_dct.h" #include "platform_peripheral.h" #include "spi_flash.h" #include "waf_platform.h" #include "wiced_apps_common.h" #include "wiced_framework.h" #include "wiced_result.h" #include "wiced_utilities.h" #include "wiced_waf_common.h" #ifndef PLATFORM_SFLASH_PERIPHERAL_ID #define PLATFORM_SFLASH_PERIPHERAL_ID (0) #endif #ifndef SFLASH_SECTOR_SIZE #define SFLASH_SECTOR_SIZE (4096) #endif // Default to using the OTA_APP scratch space in the external SPI flash to store the coredump data: #ifndef MEMFAULT_PLATFORM_COREDUMP_WICED_DCT_APP_INDEX #define MEMFAULT_PLATFORM_COREDUMP_WICED_DCT_APP_INDEX (DCT_OTA_APP_INDEX) #endif //! Default storage size to allocate for storing coredump data. //! Ensure to allocate a bit of extra headroom for metadata. #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_SIZE_BYTES #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_SIZE_BYTES (130 * 1024) #endif #ifndef MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY #define MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY (0) #endif // These symbols are defined in the WICED SDK linker scripts: extern uint32_t link_stack_location; extern uint32_t link_stack_end; // These symbols are defined in the memfault_platform_coredump.ld linker script: extern uint32_t __MfltCoredumpRamStart; extern uint32_t __MfltCoredumpRamEnd; typedef struct sMemfaultPlatformCoredumpCtx { // Physical flash addresses: uint32_t flash_start; uint32_t flash_end; uint32_t crc; } sMemfaultPlatformCoredumpCtx; static sMemfaultPlatformCoredumpCtx *s_memfault_platform_coredump_ctx; const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { // Let's collect the callstack at the time of crash static sMfltCoredumpRegion s_coredump_regions[1]; #if (MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY == 1) // Capture only the interrupt stack. Use only if there is not enough storage to capture all of // RAM. s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&link_stack_location, link_stack_size); #else // Capture all of RAM. Recommended: it enables broader post-mortem analyses, but has larger // storage requirements. s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( &__MfltCoredumpRamStart, (uint32_t)&__MfltCoredumpRamEnd - (uint32_t)&__MfltCoredumpRamStart); #endif *num_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions); return s_coredump_regions; } static wiced_result_t prv_init(void) { wiced_app_t app; WICED_VERIFY(wiced_framework_app_open(MEMFAULT_PLATFORM_COREDUMP_WICED_DCT_APP_INDEX, &app)); uint32_t current_size = 0; WICED_VERIFY(wiced_framework_app_get_size(&app, ¤t_size)); // Resize the SPI flash partition (only if needed): if (current_size < MEMFAULT_PLATFORM_COREDUMP_STORAGE_SIZE_BYTES) { WICED_VERIFY(wiced_framework_app_set_size(&app, MEMFAULT_PLATFORM_COREDUMP_STORAGE_SIZE_BYTES)); } sflash_handle_t sflash_handle; app_header_t app_header; const image_location_t *const app_header_location = &app.app_header_location; WICED_VERIFY(init_sflash(&sflash_handle, PLATFORM_SFLASH_PERIPHERAL_ID, SFLASH_WRITE_ALLOWED)); WICED_VERIFY(sflash_read(&sflash_handle, app_header_location->detail.external_fixed.location, &app_header, sizeof(app_header_t))); WICED_VERIFY(deinit_sflash(&sflash_handle)); if (app_header.count != 1) { MEMFAULT_LOG_ERROR("Coredump partition must be contiguous! %d parts found", app_header.count); return WICED_ERROR; } // Let's heap-allocate this. Stacks run downwards on ARM. The stacks generally sit below the heap // (unless they're allocated on the heap...) In case of a really bad stack-overflow, the data will // have a higher likelihood of surviving when living on the heap: s_memfault_platform_coredump_ctx = malloc(sizeof(*s_memfault_platform_coredump_ctx)); if (!s_memfault_platform_coredump_ctx) { return WICED_ERROR; } // Calculate the physical SPI flash addresses: const uint32_t flash_start = SFLASH_SECTOR_SIZE * app_header.sectors[0].start; const uint32_t flash_end = flash_start + (SFLASH_SECTOR_SIZE * app_header.sectors[0].count); const uint32_t actual_size = flash_end - flash_start; if (actual_size < MEMFAULT_PLATFORM_COREDUMP_STORAGE_SIZE_BYTES) { MEMFAULT_LOG_ERROR("WICED set size failed? %" PRIu32, actual_size); return WICED_ERROR; } *s_memfault_platform_coredump_ctx = (sMemfaultPlatformCoredumpCtx){ .flash_start = flash_start, .flash_end = flash_end, }; // CRC32 to protect it, just in case the data get clobbered: s_memfault_platform_coredump_ctx->crc = memfault_platform_crc32( s_memfault_platform_coredump_ctx, offsetof(sMemfaultPlatformCoredumpCtx, crc)); return WICED_SUCCESS; } bool memfault_platform_coredump_boot(void) { return (WICED_SUCCESS == prv_init()); } static bool prv_get_start_and_end_addr(uint32_t *flash_start, uint32_t *flash_end) { const uint32_t actual_crc = memfault_platform_crc32(s_memfault_platform_coredump_ctx, offsetof(sMemfaultPlatformCoredumpCtx, crc)); if (actual_crc != s_memfault_platform_coredump_ctx->crc) { return false; } *flash_start = s_memfault_platform_coredump_ctx->flash_start; *flash_end = s_memfault_platform_coredump_ctx->flash_end; return true; } bool memfault_platform_get_spi_start_and_end_addr(uint32_t *flash_start, uint32_t *flash_end) { return prv_get_start_and_end_addr(flash_start, flash_end); } static bool prv_offset_to_addr(uint32_t offset, size_t read_len, uint32_t *addr_out) { uint32_t flash_start; uint32_t flash_end; if (!prv_get_start_and_end_addr(&flash_start, &flash_end)) { return false; // bad crc } const uint32_t start_addr = flash_start + offset; if (start_addr + read_len > flash_end) { return false; } *addr_out = start_addr; return true; } void memfault_platform_coredump_storage_clear(void) { const uint32_t zero = 0; // Assuming NOR flash! memfault_platform_coredump_storage_write(0, &zero, sizeof(zero)); } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { uint32_t flash_start; uint32_t flash_end; uint32_t size = 0; if (prv_get_start_and_end_addr(&flash_start, &flash_end)) { size = flash_end - flash_start; } *info = (sMfltCoredumpStorageInfo){ .size = size, .sector_size = SFLASH_SECTOR_SIZE, }; } static wiced_result_t prv_write(uint32_t addr, const void *data, size_t data_len) { sflash_handle_t sflash_handle; WICED_VERIFY(init_sflash(&sflash_handle, PLATFORM_SFLASH_PERIPHERAL_ID, SFLASH_WRITE_ALLOWED)); WICED_VERIFY(sflash_write(&sflash_handle, addr, data, data_len)); WICED_VERIFY(deinit_sflash(&sflash_handle)); return WICED_SUCCESS; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { uint32_t addr; if (!prv_offset_to_addr(offset, data_len, &addr)) { return false; } return (WICED_SUCCESS == prv_write(addr, data, data_len)); } static wiced_result_t prv_read(uint32_t addr, void *data, size_t read_len) { sflash_handle_t sflash_handle; WICED_VERIFY(init_sflash(&sflash_handle, PLATFORM_SFLASH_PERIPHERAL_ID, SFLASH_WRITE_ALLOWED)); WICED_VERIFY(sflash_read(&sflash_handle, addr, data, read_len)); WICED_VERIFY(deinit_sflash(&sflash_handle)); return WICED_SUCCESS; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { uint32_t addr; if (!prv_offset_to_addr(offset, read_len, &addr)) { return false; } return (WICED_SUCCESS == prv_read(addr, data, read_len)); } static wiced_result_t prv_erase(uint32_t addr, size_t erase_size) { sflash_handle_t sflash_handle; WICED_VERIFY(init_sflash(&sflash_handle, PLATFORM_SFLASH_PERIPHERAL_ID, SFLASH_WRITE_ALLOWED)); for (uint32_t sector_base = addr; sector_base < addr + erase_size; sector_base += SFLASH_SECTOR_SIZE) { WICED_VERIFY(sflash_sector_erase(&sflash_handle, sector_base)); } WICED_VERIFY(deinit_sflash(&sflash_handle)); return WICED_SUCCESS; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { uint32_t addr; if (erase_size % SFLASH_SECTOR_SIZE != 0) { return false; } if (!prv_offset_to_addr(offset, erase_size, &addr)) { return false; } // Feed the watchdog since erases can take a little bit to complete platform_watchdog_kick(); return (WICED_SUCCESS == prv_erase(addr, erase_size)); } ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_coredump.ld ================================================ /* * Defines __MfltCoredumpRamStart and __MfltCoredumpRamEnd that point to beginning and end of the SRAM region. */ SECTIONS { .mflt_coredump_symbols : { __MfltCoredumpRamStart = ORIGIN(SRAM); __MfltCoredumpRamEnd = ORIGIN(SRAM) + LENGTH(SRAM); } } ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_crc32.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! An example implementation of the CRC platform APIs for the WICED SDK #include "crc.h" #include "memfault/core/platform/crc32.h" uint32_t memfault_platform_crc32(const void *data, size_t data_len) { if (data == NULL) { return 0; } return WICED_CRC_FUNCTION_NAME(crc32)((void *)data, data_len, CRC32_INIT_VALUE); } ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_debug_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! An example implementation of the logging memfault API for the WICED platform #include #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/platform/debug_log.h" #include "wwd_debug.h" #ifndef MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES #define MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES (128) #endif static const char *TAG MEMFAULT_UNUSED = "mflt"; static void prv_print(const char *fmt, va_list *args) { #ifdef ENABLE_JLINK_TRACE if (WPRINT_PLATFORM_PERMISSION_FUNC()) { char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; vsnprintf(log_buf, sizeof(log_buf), fmt, *args) RTT_printf("%s\n", log_buf); } #else vprintf(fmt, *args); printf("\n"); #endif } void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { (void)level; va_list args; va_start(args, fmt); prv_print(fmt, &args); va_end(args); } void memfault_platform_log_raw(const char *fmt, ...) { va_list args; va_start(args, fmt); prv_print(fmt, &args); va_end(args); } ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_device_info.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Reference implementation of platform dependencies for Memfault device info APIs #include #include #include "memfault/core/platform/device_info.h" #include "wiced_result.h" #include "wwd_wifi.h" #ifndef MEMFAULT_WICED_MAIN_SOFTWARE_VERSION #define MEMFAULT_WICED_MAIN_SOFTWARE_VERSION "1.0.0" #endif #ifndef MEMFAULT_WICED_SOFTWARE_TYPE #define MEMFAULT_WICED_SOFTWARE_TYPE "wiced-main" #endif #ifndef MEMFAULT_WICED_HW_REVISION #define MEMFAULT_WICED_HW_REVISION "wiced-proto" #endif static wiced_mac_t s_memfault_platform_device_info_mac = { 0 }; bool memfault_platform_device_info_boot(void) { // NOTE: This API will use an RTOS lock, so we cannot run this at the time of capturing the // coredump! Use the WiFi station MAC address as unique device id: return (WWD_SUCCESS == wwd_wifi_get_mac_address(&s_memfault_platform_device_info_mac, WWD_STA_INTERFACE)); } static void prv_get_device_serial(char *buf, size_t buf_len) { size_t curr_idx = 0; for (size_t i = 0; i < sizeof(s_memfault_platform_device_info_mac); i++) { size_t space_left = buf_len - curr_idx; int bytes_written = snprintf(&buf[curr_idx], space_left, "%02X", (int)s_memfault_platform_device_info_mac.octet[i]); if (bytes_written < space_left) { curr_idx += bytes_written; } else { // we are out of space, return what we got, it's NULL terminated return; } } } void memfault_platform_get_device_info(struct MemfaultDeviceInfo *info) { static char s_device_serial[32]; prv_get_device_serial(s_device_serial, sizeof(s_device_serial)); *info = (struct MemfaultDeviceInfo){ .device_serial = s_device_serial, .hardware_version = MEMFAULT_WICED_HW_REVISION, .software_version = MEMFAULT_WICED_MAIN_SOFTWARE_VERSION, .software_type = MEMFAULT_WICED_SOFTWARE_TYPE, }; } ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_fault_handling_arm_gcc.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fault handler shims to link the WICED SDK fault handlers to the Memfault ARM exception handlers //! This allows for crash information to be collected when the system hits a fault. #include "memfault/core/compiler.h" #include "memfault/panics/fault_handling.h" // WICED SDK uses unconventional names for the ARM exception handlers. // This file provides shims to jump to the implementations that libmemfault provides. // Note we're using a tiny bit of assembly here -- 'b' (branch without link) -- // instead of doing a C function call, which would/could lead to 'bl' (branch with link) // and cause the LR to get changed. // // This wastes a couple bytes of code space. More efficient would be to use the // --defsym link option, but it does not seem possible to use this with WICED SDK's // makefile set up. The GLOBAL_LDFLAGS variables almost makes it possible, but the // problem is that GLOBAL_LDFLAGS is *also* used to link DCT.elf, which will fail when // using --defsym to define these handlers... MEMFAULT_NAKED_FUNC void HardFaultException(void) { __asm("b HardFault_Handler"); } MEMFAULT_NAKED_FUNC void MemManageException(void) { __asm("b MemoryManagement_Handler"); } MEMFAULT_NAKED_FUNC void BusFaultException(void) { __asm("b BusFault_Handler"); } MEMFAULT_NAKED_FUNC void UsageFaultException(void) { __asm("b UsageFault_Handler"); } MEMFAULT_NAKED_FUNC void NMIException(void) { __asm("b NMI_Handler"); } ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_http_client.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Reference implementation of platform dependencies for the Memfault HTTP Client when using the //! WICED SDK #include #include #include #include "http.h" #include "http_client.h" #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer.h" #include "memfault/core/debug_log.h" #include "memfault/core/errors.h" #include "memfault/core/math.h" #include "memfault/http/platform/http_client.h" #include "memfault/panics/assert.h" #include "memfault_platform_wiced.h" #include "wiced_management.h" #include "wiced_tls.h" #include "wwd_debug.h" #define MEMFAULT_DNS_TIMEOUT_MS (10000) #define MEMFAULT_HTTP_CONNECT_TIMEOUT_MS (10000) #define MEMFAULT_HTTP_POST_DATA_READ_BUFFER_SIZE (256) #define MEMFAULT_HTTP_MIME_TYPE_BINARY "application/octet-stream" // API expects colon+space in fieldname :/ #define MEMFAULT_HTTP_PROJECT_KEY_HEADER_WICED MEMFAULT_HTTP_PROJECT_KEY_HEADER ": " typedef struct MfltHttpClient { http_client_t client; http_client_configuration_info_t config; bool is_dns_lookup_done; wiced_ip_address_t ip_address; bool is_request_pending; bool request_error_occurred; http_request_t request; MemfaultHttpClientResponseCallback callback; void *callback_ctx; } sMfltHttpClient; int memfault_platform_http_response_get_status(const sMfltHttpResponse *response, uint32_t *status_out) { MEMFAULT_ASSERT(response); http_response_t *wiced_response = (http_response_t *)response; http_status_line_t status_line = { 0 }; if (WICED_SUCCESS != http_get_status_line(wiced_response->response_hdr, wiced_response->response_hdr_length, &status_line)) { return -1; } if (status_out) { *status_out = status_line.code; } return 0; } static void prv_finalize_request_and_run_callback(sMfltHttpClient *client, http_response_t *response) { if (!client->is_request_pending) { return; } if (client->callback) { client->callback((const sMfltHttpResponse *)response, client->callback_ctx); } uint32_t http_status = 0; const int rv = memfault_platform_http_response_get_status((const sMfltHttpResponse *)response, &http_status); if (rv != 0) { MEMFAULT_LOG_ERROR("Request failed. No HTTP status: %d", rv); return; } client->request_error_occurred = (http_status < 200) || (http_status >= 300); client->callback = NULL; client->callback_ctx = NULL; http_request_deinit(&client->request); client->is_request_pending = false; } static void prv_handle_data_received(sMfltHttpClient *client, http_response_t *response) { if (response->request != &client->request) { MEMFAULT_LOG_DEBUG("Recv data for different req"); return; } if (response->response_hdr == NULL) { // Will it come later? MEMFAULT_LOG_DEBUG("NULL header"); return; } http_status_line_t status_line; if (WICED_SUCCESS != http_get_status_line(response->response_hdr, response->response_hdr_length, &status_line)) { MEMFAULT_LOG_DEBUG("Couldn't parse status line"); return; } prv_finalize_request_and_run_callback(client, response); } static void prv_http_event_handler(http_client_t *wiced_client, http_event_t event, http_response_t *response) { MEMFAULT_STATIC_ASSERT(offsetof(sMfltHttpClient, client) == 0, "Expecting first member to be http_client_t client"); sMfltHttpClient *client = (sMfltHttpClient *)wiced_client; switch (event) { case HTTP_CONNECTED: break; case HTTP_DISCONNECTED: { prv_finalize_request_and_run_callback(client, NULL); break; } case HTTP_DATA_RECEIVED: { prv_handle_data_received(client, response); break; } default: break; } } sMfltHttpClient *memfault_platform_http_client_create(void) { if (wiced_network_is_up(WICED_STA_INTERFACE) == WICED_FALSE) { // If the network is not up (WiFi is not joined) http_client_init() will trip an assert... :/ // Race prone? goto error; } sMfltHttpClient *client = malloc(sizeof(sMfltHttpClient)); if (!client) { goto error; } memset(client, 0, sizeof(*client)); if (WICED_SUCCESS != http_client_init(&client->client, WICED_STA_INTERFACE, prv_http_event_handler, NULL)) { goto error; } const char *hostname = MEMFAULT_HTTP_GET_CHUNKS_API_HOST(); client->config = (http_client_configuration_info_t){ .flag = (http_client_configuration_flags_t)(HTTP_CLIENT_CONFIG_FLAG_SERVER_NAME | HTTP_CLIENT_CONFIG_FLAG_MAX_FRAGMENT_LEN), .server_name = (uint8_t *)hostname, .max_fragment_length = TLS_FRAGMENT_LENGTH_1024, }; if (WICED_SUCCESS != http_client_configure(&client->client, &client->config)) { http_client_deinit(&client->client); goto error; } /* if you set hostname, library will make sure subject name in the server certificate is matching * with host name you are trying to connect. pass NULL if you don't want to enable this check */ client->client.peer_cn = (uint8_t *)hostname; return client; error: free(client); return NULL; } static bool prv_do_dns_lookup(sMfltHttpClient *client) { if (client->is_dns_lookup_done) { return true; } const wiced_result_t dns_rv = wiced_hostname_lookup((const char *)client->config.server_name, &client->ip_address, MEMFAULT_DNS_TIMEOUT_MS, WICED_STA_INTERFACE); if (WICED_SUCCESS != dns_rv) { MEMFAULT_LOG_ERROR("DNS lookup failed: %d", dns_rv); return false; } client->is_dns_lookup_done = true; return true; } static wiced_result_t prv_send_chunk_in_http_request(sMfltHttpClient *client, const char *url, MemfaultHttpClientResponseCallback callback, void *ctx) { uint8_t *buffer = malloc(MEMFAULT_HTTP_POST_DATA_READ_BUFFER_SIZE); if (!buffer) { MEMFAULT_LOG_ERROR("%s: malloc fail", __func__); return WICED_ERROR; } sPacketizerMetadata metadata; const sPacketizerConfig cfg = { // We will send an entire memfault data message in a single http request .enable_multi_packet_chunk = true, }; bool md = memfault_packetizer_begin(&cfg, &metadata); if (!md) { // we should only be in this function when there _is_ data available goto error; } char content_length[12] = { 0 }; snprintf(content_length, sizeof(content_length), "%d", (int)metadata.single_chunk_message_length); const http_header_field_t headers[] = { [0] = { .field = HTTP_HEADER_HOST, .field_length = sizeof(HTTP_HEADER_HOST) - 1, .value = (char *)client->config.server_name, .value_length = strlen((const char *)client->config.server_name), }, [1] = { .field = MEMFAULT_HTTP_PROJECT_KEY_HEADER_WICED, .field_length = sizeof(MEMFAULT_HTTP_PROJECT_KEY_HEADER_WICED) - 1, .value = (char *)g_mflt_http_client_config.api_key, .value_length = strlen((const char *)g_mflt_http_client_config.api_key), }, [2] = { .field = HTTP_HEADER_CONTENT_LENGTH, .field_length = sizeof(HTTP_HEADER_CONTENT_LENGTH) - 1, .value = content_length, .value_length = strlen(content_length), }, [3] = { .field = HTTP_HEADER_CONTENT_TYPE, .field_length = sizeof(HTTP_HEADER_CONTENT_TYPE) - 1, .value = MEMFAULT_HTTP_MIME_TYPE_BINARY, .value_length = sizeof(MEMFAULT_HTTP_MIME_TYPE_BINARY) - 1, } }; http_request_t *const request = &client->request; WICED_VERIFY(http_request_init(request, &client->client, HTTP_POST, url, HTTP_1_1)); WICED_VERIFY(http_request_write_header(request, headers, MEMFAULT_ARRAY_SIZE(headers))); WICED_VERIFY(http_request_write_end_header(request)); eMemfaultPacketizerStatus packetizer_status; do { size_t buffer_len = MEMFAULT_HTTP_POST_DATA_READ_BUFFER_SIZE; packetizer_status = memfault_packetizer_get_next(buffer, &buffer_len); if (packetizer_status == kMemfaultPacketizerStatus_NoMoreData) { break; } const wiced_result_t write_rv = http_request_write(request, buffer, buffer_len); if (WICED_SUCCESS != write_rv) { MEMFAULT_LOG_ERROR("http_request_write failed: %u", write_rv); goto error; } } while (packetizer_status != kMemfaultPacketizerStatus_EndOfChunk); // we have finished assembling one chunk so flush the request const wiced_result_t flush_rv = http_request_flush(request); if (WICED_SUCCESS != flush_rv) { MEMFAULT_LOG_ERROR("http_request_flush failed: %u", flush_rv); goto error; } free(buffer); client->callback = callback; client->callback_ctx = ctx; client->is_request_pending = true; return WICED_SUCCESS; error: http_request_deinit(&client->request); free(buffer); return WICED_ERROR; } // return different error codes from each exit point so it's easier to determine what went wrong typedef enum { kMemfaultPlatformHttpPost_AlreadyPending = -1, kMemfaultPlatformHttpPost_GetSpiAddressFailed = -2, kMemfaultPlatformHttpPost_DnsLookupFailed = -3, kMemfaultPlatformHttpPost_BuildUrlFailed = -4, kMemfaultPlatformHttpPost_HttpRequestFailure = -5, } eMemfaultPlatformHttpPost; int memfault_platform_http_client_post_data(sMfltHttpClient *client, MemfaultHttpClientResponseCallback callback, void *ctx) { if (client->is_request_pending) { MEMFAULT_LOG_ERROR("Data post request already pending!"); return kMemfaultPlatformHttpPost_AlreadyPending; } // Early exit if there is no new data to send if (!memfault_packetizer_data_available()) { return kMfltPostDataStatus_NoDataFound; } if (!prv_do_dns_lookup(client)) { return kMemfaultPlatformHttpPost_DnsLookupFailed; } char url_buffer[MEMFAULT_HTTP_URL_BUFFER_SIZE]; if (!memfault_http_build_url(url_buffer, MEMFAULT_HTTP_CHUNKS_API_SUBPATH)) { return kMemfaultPlatformHttpPost_BuildUrlFailed; } MEMFAULT_LOG_DEBUG("Posting data to %s", url_buffer); const http_security_t security = g_mflt_http_client_config.disable_tls ? HTTP_NO_SECURITY : HTTP_USE_TLS; WICED_VERIFY(http_client_connect(&client->client, &client->ip_address, MEMFAULT_HTTP_GET_CHUNKS_API_PORT(), security, MEMFAULT_HTTP_CONNECT_TIMEOUT_MS)); // Drain all the data that is available to be sent while (memfault_packetizer_data_available()) { int rv = prv_send_chunk_in_http_request(client, url_buffer, callback, ctx); if (rv != WICED_SUCCESS) { return MEMFAULT_PLATFORM_SPECIFIC_ERROR(rv); } // Wait for the in-flight http request to complete memfault_platform_http_client_wait_until_requests_completed(client, MEMFAULT_HTTP_CONNECT_TIMEOUT_MS); if (client->request_error_occurred) { MEMFAULT_LOG_ERROR("Terminating data transfer because error occurred"); memfault_packetizer_abort(); return kMemfaultPlatformHttpPost_HttpRequestFailure; } } return 0; } int memfault_platform_http_client_wait_until_requests_completed(sMfltHttpClient *client, uint32_t timeout_ms) { uint32_t waited_ms = 0; while (client->is_request_pending) { // Could also be implemented using a semaphore wiced_rtos_delay_milliseconds(100); waited_ms += 100; if (waited_ms >= timeout_ms) { return -1; } } return 0; } int memfault_platform_http_client_destroy(sMfltHttpClient *client) { http_client_deinit((http_client_t *)client); free(client); return 0; } ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_impl.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! Reference implementation of the memfault platform header for the WICED platform #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/errors.h" #include "memfault/core/platform/core.h" #include "memfault/http/root_certs.h" #include "memfault_platform_wiced.h" #include "platform_assert.h" // import WICED_TRIGGER_BREAKPOINT #include "platform_cmsis.h" // import NVIC_SystemReset & CoreDebug #include "wiced_result.h" #include "wiced_tls.h" // return different error codes from each exit point so it's easier to determine what went wrong typedef enum { kMemfaultPlatformBoot_DeviceInfoBootFailed = -1, kMemfaultPlatformBoot_CoredumpBootFailed = -2, } eMemfaultPlatformBoot; int memfault_platform_boot(void) { if (!memfault_platform_device_info_boot()) { MEMFAULT_LOG_ERROR("memfault_platform_device_info_boot() failed"); return kMemfaultPlatformBoot_DeviceInfoBootFailed; } if (!memfault_platform_coredump_boot()) { MEMFAULT_LOG_ERROR("memfault_platform_coredump_boot() failed"); return kMemfaultPlatformBoot_CoredumpBootFailed; } const wiced_result_t result = wiced_tls_init_root_ca_certificates( MEMFAULT_ROOT_CERTS_PEM, sizeof(MEMFAULT_ROOT_CERTS_PEM) - 1); if (result != WICED_SUCCESS) { MEMFAULT_LOG_ERROR("wiced_tls_init_root_ca_certificates() failed: %u", result); return MEMFAULT_PLATFORM_SPECIFIC_ERROR(result); } return 0; } MEMFAULT_NORETURN void memfault_platform_reboot(void) { memfault_platform_halt_if_debugging(); NVIC_SystemReset(); MEMFAULT_UNREACHABLE; } ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/memfault_platform_wiced.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include //! Initializes the device memfault_platform_device_info module. bool memfault_platform_device_info_boot(void); //! Initializes memfault_platform_coredump module. //! @return true iff successful. bool memfault_platform_coredump_boot(void); //! Get the physical start and end addresses of the area that is allocated in the SPI flash //! after calling memfault_platform_coredump_boot(). This is mainly for debugging. //! @return true iff successful. If false is returned, it means memfault_platform_coredump_boot() //! had not been called, or the data has been corrupted (CRC mismatch). bool memfault_platform_get_spi_start_and_end_addr(uint32_t *flash_start, uint32_t *flash_end); ================================================ FILE: examples/wiced/libraries/memfault/platform_reference_impl/platform_reference_impl.mk ================================================ NAME := MemfaultPlatformReferenceImpl $(NAME)_SOURCES := memfault_platform_coredump.c \ memfault_platform_crc32.c \ memfault_platform_debug_log.c \ memfault_platform_device_info.c \ memfault_platform_fault_handling_arm_gcc.c \ memfault_platform_http_client.c \ memfault_platform_impl.c \ $(NAME)_COMPONENTS := drivers/spi_flash \ libraries/memfault/core \ GLOBAL_LDFLAGS += -Wl,-T $(SOURCE_ROOT)libraries/memfault/platform_reference_impl/memfault_platform_coredump.ld GLOBAL_INCLUDES += . VALID_OSNS_COMBOS := ThreadX-NetX_Duo FreeRTOS-LwIP VALID_PLATFORMS := BCM943362WCD4 \ BCM943362WCD6 \ BCM943362WCD8 \ BCM943364WCD1 \ CYW94343WWCD1_EVB \ BCM943438WCD1 \ BCM94343WWCD2 \ CY8CKIT_062 \ NEB1DX* \ CYW9MCU7X9N364 \ CYW943907AEVAL1F \ CYW954907AEVAL1F \ CYW9WCD2REFAD2* \ CYW9WCD760PINSDAD2 \ CYW943455EVB* \ CYW943012EVB* ================================================ FILE: examples/wiced/libraries/memfault/util/util.mk ================================================ NAME := MemfaultUtil $(NAME)_SOURCES := \ src/memfault_chunk_transport.c \ src/memfault_crc16_ccitt.c \ src/memfault_circular_buffer.c \ src/memfault_minimal_cbor.c \ src/memfault_varint.c \ $(NAME)_COMPONENTS := $(NAME)_INCLUDES += include GLOBAL_INCLUDES += include VALID_OSNS_COMBOS := ThreadX-NetX_Duo FreeRTOS-LwIP VALID_PLATFORMS := \ BCM943362WCD4 \ BCM943362WCD6 \ BCM943362WCD8 \ BCM943364WCD1 \ CYW94343WWCD1_EVB \ BCM943438WCD1 \ BCM94343WWCD2 \ CY8CKIT_062 \ NEB1DX* \ CYW9MCU7X9N364 \ CYW943907AEVAL1F \ CYW954907AEVAL1F \ CYW9WCD2REFAD2* \ CYW9WCD760PINSDAD2 \ CYW943455EVB* \ CYW943012EVB* ================================================ FILE: examples/zephyr/nucleo_wba55cg/.gitignore ================================================ /.west /bootloader /build /modules /tools /zephyr ================================================ FILE: examples/zephyr/nucleo_wba55cg/README.md ================================================ # Memfault Zephyr `nucleo_wba55cg` Example Application This folder contains an example integration of the Memfault SDK using the port provided in `ports/zephyr` The demo was tested on the following boards: - [ST NUCLEO-WBA55CG board](https://docs.zephyrproject.org/3.7.0/boards/st/nucleo_wba55cg/doc/nucleo_wba55cg.html). - [ST NUCLEO-H563ZI board](https://docs.zephyrproject.org/3.7.0/boards/st/nucleo_h563zi/doc/index.html) ## Prerequisite We assume you already have a working Zephyr toolchain installed locally. Step-by-step instructions can be found in the [Zephyr Documentation](https://docs.zephyrproject.org/2.5.0/getting_started/index.html#build-hello-world). ## Setup From a Zephyr-enabled shell environment (`west` tool is available), initialize the workspace: ```bash west init -l memfault_demo_app west update west blobs fetch stm32 ``` ## Building ```bash west build --board nucleo_wba55cg memfault_demo_app [...] [271/271] Linking C executable zephyr/zephyr.elf ``` ## Flashing The build can be flashed on the development board using `west flash` ( See Zephyr ["Building, Flashing, & Debugging" documentation](https://docs.zephyrproject.org/3.6.0/guides/west/build-flash-debug.html?highlight=building%20flashing#flashing-west-flash)) The default runner is `STM32_Programmer_CLI`, which must be available on `PATH` when running `west flash`. `STM32_Programmer_CLI` is installed [standalone](https://www.st.com/en/development-tools/stm32cubeprog.html) or from the STM32CubeIDE installation. ================================================ FILE: examples/zephyr/nucleo_wba55cg/memfault_demo_app/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13.1) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(memfault_demo_app) zephyr_include_directories(config) target_sources(app PRIVATE src/main.c) ================================================ FILE: examples/zephyr/nucleo_wba55cg/memfault_demo_app/VERSION ================================================ VERSION_MAJOR = 0 VERSION_MINOR = 0 PATCHLEVEL = 1 VERSION_TWEAK = 0 EXTRAVERSION = ================================================ FILE: examples/zephyr/nucleo_wba55cg/memfault_demo_app/config/memfault_metrics_heartbeat_config.def ================================================ // Define custom system metrics to track. See https://mflt.io/embedded-metrics. // For example: // // MEMFAULT_METRICS_KEY_DEFINE(main_task_stack_hwm, kMemfaultMetricType_Unsigned) // MEMFAULT_METRICS_KEY_DEFINE(ble_min_rssi, kMemfaultMetricType_Signed) // MEMFAULT_METRICS_KEY_DEFINE(mcu_sleep_time_ms, kMemfaultMetricType_Timer) ================================================ FILE: examples/zephyr/nucleo_wba55cg/memfault_demo_app/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" ================================================ FILE: examples/zephyr/nucleo_wba55cg/memfault_demo_app/config/memfault_trace_reason_user_config.def ================================================ //! Define custom error reasons that can be filtered & searched //! on in the Memfault UI, i.e //! MEMFAULT_TRACE_REASON_DEFINE(critical_error) //! See documentation here: https://mflt.io/error-tracing ================================================ FILE: examples/zephyr/nucleo_wba55cg/memfault_demo_app/prj.conf ================================================ # General config CONFIG_SHELL=y CONFIG_LOG=y # CONFIG_LOG_BACKEND_UART=y ensures logs (and printk's, because of # CONFIG_LOG_PRINTK=y) are available before the console thread is started. CONFIG_LOG_BACKEND_UART=y # Logs are going to the UART directly, don't need to also emit them to the shell CONFIG_SHELL_LOG_BACKEND=n CONFIG_LOG_MODE_IMMEDIATE=y # To catch stack overflows at the point of overflow CONFIG_MPU_STACK_GUARD=y # Memfault configuration CONFIG_MEMFAULT=y CONFIG_MEMFAULT_LOGGING_ENABLE=y # Enable asserts, now that Memfault is enabled to catch crashes CONFIG_ASSERT=y CONFIG_BT=y CONFIG_LOG=y CONFIG_BT_SMP=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_DIS=y CONFIG_BT_DIS_PNP=n CONFIG_BT_BAS=y CONFIG_BT_HRS=y CONFIG_BT_DEVICE_NAME="Memfault Sample App" CONFIG_BT_DEVICE_APPEARANCE=833 CONFIG_BT_SHELL=y ================================================ FILE: examples/zephyr/nucleo_wba55cg/memfault_demo_app/src/main.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include #include #include #include "memfault/components.h" #include "memfault/ports/zephyr/http.h" LOG_MODULE_REGISTER(mflt_app, LOG_LEVEL_DBG); // Blink code taken from the zephyr/samples/basic/blinky example. static void blink_forever(void) { /* 1000 msec = 1 sec */ #define SLEEP_TIME_MS 1000 /* The devicetree node identifier for the "led0" alias. */ #define LED0_NODE DT_ALIAS(led0) static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios); int ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); if (ret < 0) { return; } while (1) { gpio_pin_toggle_dt(&led); k_msleep(SLEEP_TIME_MS); } } // put the blink_forever in its own thread. K_THREAD_DEFINE(blink_forever_thread, 1024, blink_forever, NULL, NULL, NULL, K_PRIO_PREEMPT(8), 0, 0); int main(void) { printk(MEMFAULT_BANNER_COLORIZED "\n"); LOG_INF("Memfault Demo App! Board %s\n", CONFIG_BOARD); memfault_device_info_dump(); return 0; } ================================================ FILE: examples/zephyr/nucleo_wba55cg/memfault_demo_app/west.yml ================================================ # West manifest for Memfault Example Application manifest: remotes: - name: zephyrproject-rtos url-base: https://github.com/zephyrproject-rtos projects: - name: zephyr remote: zephyrproject-rtos revision: v3.7.0 import: # Limit the Zephyr modules to the required set name-allowlist: - cmsis - hal_stm32 - picolibc - tinycrypt - name: memfault-firmware-sdk path: modules/lib/memfault-firmware-sdk url: https://github.com/memfault/memfault-firmware-sdk # Memfault recommends pinning to a specific release tag for production revision: master ================================================ FILE: examples/zephyr/qemu/.gitignore ================================================ /.west /bootloader /build /modules /tools /zephyr ================================================ FILE: examples/zephyr/qemu/README.md ================================================ # Example Memfault Zephyr QEMU application Based on [Zephyr's Example Application](https://github.com/zephyrproject-rtos/example-application), this provides a minimal reference for Memfault integration. > Note: this example can also target other boards and should work normally. > It's primarily tested on the `mps2/an385` (the default board), but also: > > - `b_u585i_iot02a` > - `mimxrt1170_evk/mimxrt1176/cm7` > - `nrf52840dk_nrf52840` > - `nucleo_f756zg` > - `nucleo_l496zg` > > Note: You may need to add the board HAL module to the `name-allowlist` in the applications > `west.yml` (modules limited to reduce `west update` time). ## Usage After setting up a Zephyr development environment (see [Getting Started](https://docs.zephyrproject.org/latest/getting_started/index.html) to set up), you can run the following commands to test the application: ```shell # initialize this project ❯ west init --local qemu-app ❯ west update # build and run the target program ❯ west build qemu-app ❯ west build --target run *** Booting Zephyr OS build zephyr-v4.0.0 *** [00:00:00.000,000] mflt: GNU Build ID: 4ffb5879ed5923582035133086015bbf65504364 [00:00:00.000,000] main: 👋 Memfault Demo App! Board mps2/an385 [00:00:00.000,000] mflt: S/N: DEMOSERIAL [00:00:00.000,000] mflt: SW type: zephyr-app [00:00:00.000,000] mflt: SW version: 1.0.0+6c108c40f1 [00:00:00.000,000] mflt: HW version: mps2/an385 uart:~$ ``` ================================================ FILE: examples/zephyr/qemu/qemu-app/CMakeLists.txt ================================================ # Zephyr Example Application cmake_minimum_required(VERSION 3.20.0) # The default board for this example is the mps2/an385, but can be # overridden by passing --board= to west or -DBOARD= to cmake set(BOARD mps2/an385) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) target_sources(app PRIVATE src/cdr.c src/main.c src/metrics.c ) zephyr_include_directories(config) project(qemu-app LANGUAGES C VERSION 1.0.0) # Generate a git hash that's used as part of the software_version, eg # 1.0.0+12345678. Permit overriding at command line for CI builds. if (NOT DEFINED ZEPHYR_MEMFAULT_EXAMPLE_GIT_SHA1) EXECUTE_PROCESS( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND git rev-parse --short HEAD RESULT_VARIABLE commit_sha1 OUTPUT_VARIABLE ZEPHYR_MEMFAULT_EXAMPLE_GIT_SHA1 OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY ) endif() zephyr_compile_definitions( ZEPHYR_MEMFAULT_EXAMPLE_GIT_SHA1=\"${ZEPHYR_MEMFAULT_EXAMPLE_GIT_SHA1}\" ) zephyr_compile_options(--param=min-pagesize=0x1000) # For Zephyr >=3.0.0,<3.6.0, explicitly enable -Werror if (${KERNEL_VERSION_MAJOR}.${KERNEL_VERSION_MINOR}.${KERNEL_PATCHLEVEL} VERSION_GREATER_EQUAL 3.0.0 AND ${KERNEL_VERSION_MAJOR}.${KERNEL_VERSION_MINOR}.${KERNEL_PATCHLEVEL} VERSION_LESS 3.6.0) zephyr_compile_options(-Werror) endif() ================================================ FILE: examples/zephyr/qemu/qemu-app/Kconfig ================================================ # Application Kconfig file # # This is loaded when building this directory with west. menu "Memfault Example Application Configuration" config ZEPHYR_MEMFAULT_EXAMPLE_AUTO_SETTINGS bool default y # Force on CONFIG_GPIO if the led0 alias is enabled select GPIO if $(dt_alias_enabled,led0) help Non-user-settable configuration options for the example app. config ZEPHYR_MEMFAULT_EXAMPLE_THREAD_TOGGLE bool "Example thread create + abort toggling every 10 seconds" default n help Enables creating and aborting an example thread every 10 seconds in the example app config ZEPHYR_MEMFAULT_EXAMPLE_METRICS bool "Use metrics to monitor memory usage and execution time in the example app" default y depends on MEMFAULT && MEMFAULT_HEAP_STATS depends on THREAD_STACK_INFO depends on THREAD_RUNTIME_STATS config ZEPHYR_MEMFAULT_EXAMPLE_SOFTWARE_VERSION string "Software Version" default "1.0.0" help The software version to report to Memfault. # Stub expressions, to support backwards compatibility with older Zephyr # versions where these options were not available. config COMPILER_WARNINGS_AS_ERRORS # v3.6.0+ bool "Treat warnings as errors" config CRC # v3.3.0+ bool "Cyclic redundancy check (CRC) Support" config BUILD_OUTPUT_META # v3.0.0+ bool "Create a build meta file" endmenu source "Kconfig.zephyr" ================================================ FILE: examples/zephyr/qemu/qemu-app/LICENSE ================================================ Copyright (c) 2019 - Present, Memfault All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code or in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 2. Neither the name of Memfault nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 3. This software, with or without modification, must only be used with the Memfault services and integrated with the Memfault server. 4. Any software provided in binary form under this license must not be reverse engineered, decompiled, modified and/or disassembled. THIS SOFTWARE IS PROVIDED BY MEMFAULT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MEMFAULT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/apollo4p_blue_kxr_evb.conf ================================================ CONFIG_MEMFAULT_COREDUMP_STORAGE_AMBIQ_MRAM=y ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/apollo4p_blue_kxr_evb.overlay ================================================ /* Adjust the fixed partitions in flash (MRAM) to add a 'memfault_coredump' partition for storing coredumps */ &flash0 { partitions { storage_partition: partition@1e4000 { label = "storage"; reg = < 0x1e4000 0x2000 >; }; memfault_coredump_partition: partition@1e6000 { reg = <0x1e6000 0x2000>; }; }; }; ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/apollo510_evb.conf ================================================ CONFIG_MEMFAULT_COREDUMP_STORAGE_AMBIQ_MRAM=y ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/apollo510_evb.overlay ================================================ /* Adjust the fixed partitions in flash (MRAM) to add a 'memfault_coredump' partition for storing coredumps */ /* Remove the standalone flash0 node, and replace it with a new one set as compatible with Ambiq flash driver */ &flash0 { status = "disabled"; }; / { chosen { zephyr,flash = &flash1; }; soc { flash: flash-controller@410000 { compatible = "ambiq,flash-controller"; reg = < 0x410000 0x3f0000 >; #address-cells = < 0x1 >; #size-cells = < 0x1 >; flash1: flash@410000 { compatible = "soc-nv-flash"; reg = < 0x410000 0x3f0000 >; erase-block-size = < 0x2000 >; write-block-size = < 0x10 >; partitions { compatible = "fixed-partitions"; #address-cells = < 0x1 >; #size-cells = < 0x1 >; memfault_coredump_partition: partition@7FC000 { reg = < 0x7FC000 0x4000 >; }; }; }; }; }; }; ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/b_u585i_iot02a.conf ================================================ CONFIG_MEMFAULT_COREDUMP_STORAGE_STM32U5_FLASH=y # Needed for HWINFO CONFIG_POWEROFF=y ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/b_u585i_iot02a.overlay ================================================ /* Adjust the fixed partitions in internal flash to add a 'memfault_coredump_partition' for storing coredumps. */ &flash0 { partitions { storage_partition: partition@f0000 { label = "storage"; reg = < 0xf0000 0x2000 >; }; memfault_coredump_partition: partition@f2000 { label = "memfault_coredump_partition"; reg = <0xf2000 0x2000>; }; }; }; ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/mps2_an385.conf ================================================ CONFIG_MPU_STACK_GUARD=y CONFIG_WATCHDOG=y CONFIG_WDOG_CMSDK_APB_START_AT_BOOT=y # This isn't actually needed- it just adds a reboot in the driver, and we wrap # the NMI handler anyway, but there is a Kconfig dependency in the CMSDK APB # watchdog driver so we are forced to include it. CONFIG_RUNTIME_NMI=y # Disable the hwinfo module. Enabled by default in the Zephyr port, but there is no # hwinfo implementation for the QEMU target. Disabling it saves ~100B of code space. CONFIG_HWINFO=n ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/mps2_an385.overlay ================================================ /* Add emulated RTC device. */ / { aliases { rtc = &rtc; }; rtc: rtc { status = "okay"; compatible = "zephyr,rtc-emul"; alarms-count = <2>; }; }; ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/nucleo_f756zg.conf ================================================ CONFIG_ADC=y CONFIG_SENSOR=y ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/nucleo_f756zg.overlay ================================================ / { aliases { die-temp0 = &die_temp; }; }; &die_temp { status = "okay"; }; &adc1 { pinctrl-0 = <&adc1_in0_pa0>; pinctrl-names = "default"; st,adc-prescaler = <4>; status = "okay"; }; ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/nucleo_l496zg.conf ================================================ CONFIG_BBRAM=y CONFIG_BBRAM_SHELL=y CONFIG_RTC=y ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/nucleo_l496zg.overlay ================================================ / { aliases { backup-register = &bbram; }; }; &bbram { status = "okay"; }; ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/qemu_cortex_m3.conf ================================================ CONFIG_STACK_SENTINEL=y # Disable the hwinfo module. Enabled by default in the Zephyr port, but there is no # hwinfo implementation for the QEMU target. Disabling it saves ~100B of code space. CONFIG_HWINFO=n ================================================ FILE: examples/zephyr/qemu/qemu-app/boards/qemu_cortex_m3.overlay ================================================ /* Add emulated RTC device. */ / { aliases { rtc = &rtc; }; rtc: rtc { status = "okay"; compatible = "zephyr,rtc-emul"; alarms-count = <2>; }; }; ================================================ FILE: examples/zephyr/qemu/qemu-app/config/memfault_metrics_heartbeat_config.def ================================================ MEMFAULT_METRICS_KEY_DEFINE(MainStack_MinBytesFree, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(cpu_usage_main_pct, kMemfaultMetricType_Unsigned, 10) MEMFAULT_METRICS_KEY_DEFINE(shell_uart_stack_free_bytes, kMemfaultMetricType_Unsigned) // Example "cli" session MEMFAULT_METRICS_SESSION_KEY_DEFINE(cli) MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(cmd_name, 16, cli) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(time, kMemfaultMetricType_Unsigned, cli) // Pathological key name, to test that heartbeat key extraction handles it correctly MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(__cli__cli__, kMemfaultMetricType_Unsigned, cli) #if defined(CONFIG_MEMFAULT_METRICS_THREADS) // Thread metrics MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_main_pct_max, kMemfaultMetricType_Unsigned, CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_shell_uart_pct_max, kMemfaultMetricType_Unsigned, CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #endif ================================================ FILE: examples/zephyr/qemu/qemu-app/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" #ifdef __cplusplus extern "C" { #endif #if defined(CONFIG_MEMFAULT_SOC_FAMILY_ESP32) #define ZEPHYR_DATA_REGION_START _data_start #define ZEPHYR_DATA_REGION_END _data_end #endif #ifdef __cplusplus } #endif ================================================ FILE: examples/zephyr/qemu/qemu-app/prj.conf ================================================ # This file contains selected Kconfig options for the application. # Memfault configuration CONFIG_MEMFAULT=y CONFIG_MEMFAULT_LOGGING_ENABLE=y # Enable UART console and shell for testing memfault CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y CONFIG_SHELL=y # This makes the qemu guest run closer to real time instead of super fast CONFIG_QEMU_ICOUNT=n CONFIG_LOG=y # Using immediate mode to avoid dropping bursts of log messages, eg when running # `mflt export` to dump base64-chunks to the console CONFIG_LOG_MODE_IMMEDIATE=y CONFIG_LOG_BACKEND_UART=y # Disable shell log output to prevent duplicate logs, since UART backend is # already enabled. Shell log backend enables later than UART backend, and won't # show the boot splash messages, so it's simpler to just enable the UART # backend. Note that the RTT backend is enabled by default- that's ok, and could # be useful if the board doesn't have a UART available. CONFIG_SHELL_LOG_BACKEND=n # Disable logging of printk, to improve 'mflt export' performance under deferred # logging mode. CONFIG_LOG_PRINTK=n # Enable heap stats explicitly. It's enabled by default when heap is enabled, # see below, but in a default app, it won't be enabled because heap is disabled. # For this reference example, we select it explicitly. CONFIG_MEMFAULT_HEAP_STATS=y # Increase the heap pool and main stack sizes for the example app. Setting a # non-zero memory pool size is required for heap stats, otherwise there's no # k_malloc used CONFIG_HEAP_MEM_POOL_SIZE=4096 CONFIG_MAIN_STACK_SIZE=4096 # Enable Zephyr runtime asserts CONFIG_ASSERT=y # Enable Memfault Compact Logs CONFIG_MEMFAULT_COMPACT_LOG=y # Enable Memfault self test CONFIG_MEMFAULT_SHELL_SELF_TEST=y CONFIG_MEMFAULT_SHELL_SELF_TEST_COREDUMP_STORAGE=y # Enable emulated RTC driver. Disabled by default because the RTC needs to be # explicitly updated at runtime to wall clock time, otherwise it will print # errors. # CONFIG_RTC=y # CONFIG_RTC_EMUL=y # CONFIG_RTC_SHELL=y # Enable warnings as errors CONFIG_COMPILER_WARNINGS_AS_ERRORS=y # v3.6.0+ # Enable Thread run-time stats CONFIG_THREAD_RUNTIME_STATS=y # Enable Zephyr CRC implementation, for demonstration purposes only. The # Memfault SDK provides a built-in CRC-16 implementation used for Memfault chunk # packets. CONFIG_CRC=y # v3.3.0+ ================================================ FILE: examples/zephyr/qemu/qemu-app/src/cdr.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Sample CDR implementation. See https://mflt.io/custom-data-recordings for //! more information // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include MEMFAULT_ZEPHYR_INCLUDE(shell/shell.h) #include "memfault/components.h" // clang-format on LOG_MODULE_REGISTER(cdr); #include "memfault/components.h" // Only read the CDR payload when this is set static bool s_cdr_ready_to_send = false; // keep track of the offset into the CDR data; the packetizer may call // repeatedly depending on chunk size constraints static size_t s_cdr_read_offset = 0; // set of MIME types for this payload static const char *mimetypes[] = { MEMFAULT_CDR_TEXT }; // the actual payload is just a simple string in this example static const char cdr_payload[] = "hello cdr!"; // the CDR metadata structure static sMemfaultCdrMetadata s_cdr_metadata = { .start_time = { .type = kMemfaultCurrentTimeType_Unknown, }, .mimetypes = mimetypes, .num_mimetypes = MEMFAULT_ARRAY_SIZE(mimetypes), // in this case, the data size is fixed. typically it would be set in the // prv_has_cdr_cb() function, and likely variable size .data_size_bytes = sizeof(cdr_payload) - 1, .duration_ms = 0, .collection_reason = "example cdr upload", }; // called to see if there's any data available; uses the *metadata output to set // the header fields in the chunked message sent to Memfault static bool prv_has_cdr_cb(sMemfaultCdrMetadata *metadata) { *metadata = s_cdr_metadata; return s_cdr_ready_to_send; } // called by the packetizer to read up to .data_size_bytes of CDR data static bool prv_read_data_cb(uint32_t offset, void *data, size_t data_len) { if (offset != s_cdr_read_offset) { LOG_ERR("Unexpected offset: %d vs %d", offset, s_cdr_read_offset); s_cdr_read_offset = 0; return false; } const size_t copy_len = MEMFAULT_MIN(data_len, sizeof(cdr_payload) - offset); LOG_INF("Reading %d bytes from offset %d", copy_len, offset); memcpy(data, ((uint8_t *)cdr_payload) + offset, copy_len); s_cdr_read_offset += copy_len; return true; } // called when all data has been drained from the read callback static void prv_mark_cdr_read_cb(void) { s_cdr_ready_to_send = false; // only reset this offset when the data has been read s_cdr_read_offset = 0; } // Set up the callback functions. This CDR Source Implementation structure must // have a lifetime through the duration of the program- typically setting it to // 'const' is appropriate const sMemfaultCdrSourceImpl g_custom_data_recording_source = { .has_cdr_cb = prv_has_cdr_cb, .read_data_cb = prv_read_data_cb, .mark_cdr_read_cb = prv_mark_cdr_read_cb, }; //! Shell function to stage a sample CDR payload for collection next time //! the packetizer runs static int prv_stage_cdr(const struct shell *shell, size_t argc, char **argv) { ARG_UNUSED(shell); ARG_UNUSED(argc); ARG_UNUSED(argv); s_cdr_ready_to_send = true; s_cdr_read_offset = 0; sMemfaultCurrentTime timestamp; if (memfault_platform_time_get_current(×tamp)) { s_cdr_metadata.start_time = timestamp; } else { s_cdr_metadata.start_time.type = kMemfaultCurrentTimeType_Unknown; } return 0; } SHELL_CMD_REGISTER(cdr, NULL, "Stages a sample CDR payload", prv_stage_cdr); ================================================ FILE: examples/zephyr/qemu/qemu-app/src/cdr.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Sample CDR implementation. See https://mflt.io/custom-data-recordings for //! more information #include "memfault/components.h" //! Defined by the module, must be registered with the Memfault SDK on system //! initialization. extern const sMemfaultCdrSourceImpl g_custom_data_recording_source; ================================================ FILE: examples/zephyr/qemu/qemu-app/src/main.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Example app main // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include #include MEMFAULT_ZEPHYR_INCLUDE(device.h) #include MEMFAULT_ZEPHYR_INCLUDE(devicetree.h) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/gpio.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include MEMFAULT_ZEPHYR_INCLUDE(shell/shell.h) #include "cdr.h" #include "memfault/components.h" #include "memfault/ports/zephyr/core.h" // clang-format on LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); #if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS //! Size of memory to allocate on main stack //! This requires a larger allocation due to the method used to measure stack usage #define STACK_ALLOCATION_SIZE (CONFIG_MAIN_STACK_SIZE >> 2) //! Size of memory to allocate on system heap #define HEAP_ALLOCATION_SIZE (CONFIG_HEAP_MEM_POOL_SIZE >> 3) //! Value to sleep for observing metrics changes #define METRICS_OBSERVE_PERIOD MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS //! Array of heap pointers static void *heap_ptrs[4] = { NULL }; //! Keep a reference to the main thread for stack info static struct k_thread *s_main_thread = NULL; #endif // CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS // Blink code taken from the zephyr/samples/basic/blinky example. static void blink_forever(void) { #if DT_NODE_HAS_PROP(DT_ALIAS(led0), gpios) /* 1000 msec = 1 sec */ #define SLEEP_TIME_MS 1000 /* The devicetree node identifier for the "led0" alias. */ #define LED0_NODE DT_ALIAS(led0) static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios); int ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); if (ret < 0) { return; } while (1) { gpio_pin_toggle_dt(&led); k_msleep(SLEEP_TIME_MS); } #else // no led on this board, just sleep forever k_sleep(K_FOREVER); #endif } void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { *info = (sMemfaultDeviceInfo){ .device_serial = "DEMOSERIAL", .software_type = "zephyr-app", .software_version = CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_SOFTWARE_VERSION "+" ZEPHYR_MEMFAULT_EXAMPLE_GIT_SHA1, .hardware_version = CONFIG_BOARD, }; } #if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_THREAD_TOGGLE K_THREAD_STACK_DEFINE(test_thread_stack_area, 1024); static struct k_thread test_thread; static void prv_test_thread_function(void *arg0, void *arg1, void *arg2) { ARG_UNUSED(arg0); ARG_UNUSED(arg1); ARG_UNUSED(arg2); k_sleep(K_FOREVER); } static void prv_test_thread_work_handler(struct k_work *work) { ARG_UNUSED(work); static bool started = false; if (started) { LOG_INF("ending test_thread ❌"); k_thread_abort(&test_thread); started = false; } else { LOG_INF("starting test_thread ✅"); k_thread_create(&test_thread, test_thread_stack_area, K_THREAD_STACK_SIZEOF(test_thread_stack_area), prv_test_thread_function, NULL, NULL, NULL, 7, 0, K_FOREVER); k_thread_name_set(&test_thread, "test_thread"); k_thread_start(&test_thread); started = true; } } K_WORK_DEFINE(s_test_thread_work, prv_test_thread_work_handler); //! Timer handlers run from an ISR so we dispatch the heartbeat job to the //! worker task static void prv_test_thread_timer_expiry_handler(struct k_timer *dummy) { k_work_submit(&s_test_thread_work); } K_TIMER_DEFINE(s_test_thread_timer, prv_test_thread_timer_expiry_handler, NULL); static void prv_init_test_thread_timer(void) { k_timer_start(&s_test_thread_timer, K_SECONDS(10), K_SECONDS(10)); // fire one time right away k_work_submit(&s_test_thread_work); } #else static void prv_init_test_thread_timer(void) { } #endif // CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_THREAD_TOGGLE #if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS #if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO) //! Helper function to collect metric value on main thread stack usage. static void prv_collect_main_thread_stack_free(void) { if (s_main_thread == NULL) { return; } size_t unused = 0; int rc = k_thread_stack_space_get(s_main_thread, &unused); if (rc == 0) { rc = MEMFAULT_METRIC_SET_UNSIGNED(MainStack_MinBytesFree, unused); if (rc) { LOG_ERR("Error[%d] setting MainStack_MinBytesFree", rc); } } else { LOG_ERR("Error getting thread stack usage[%d]", rc); } } #endif // defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO) static void prv_collect_main_thread_run_stats(void) { static uint64_t s_prev_main_thread_cycles = 0; static uint64_t s_prev_cpu_all_cycles = 0; if (s_main_thread == NULL) { return; } k_thread_runtime_stats_t rt_stats_main = { 0 }; k_thread_runtime_stats_t rt_stats_all = { 0 }; k_thread_runtime_stats_get(s_main_thread, &rt_stats_main); k_thread_runtime_stats_all_get(&rt_stats_all); // Calculate difference since last heartbeat uint64_t current_main_thread_cycles = rt_stats_main.execution_cycles - s_prev_main_thread_cycles; // Note: execution_cycles = idle + non-idle, total_cycles = non-idle uint64_t current_cpu_total_cycles = rt_stats_all.execution_cycles - s_prev_cpu_all_cycles; // Calculate percent of main thread execution vs total CPU time. The metric is // defined as scaled *10- the value is recorded as percent * 10 (1000), and // will be scaled down on ingestion to a float. if (current_cpu_total_cycles > 0) { uint32_t main_thread_cpu_time = (current_main_thread_cycles * 1000) / current_cpu_total_cycles; MEMFAULT_METRIC_SET_UNSIGNED(cpu_usage_main_pct, main_thread_cpu_time); } // Update previous values s_prev_main_thread_cycles = current_main_thread_cycles; s_prev_cpu_all_cycles = current_cpu_total_cycles; } //! Shell function to exercise example memory metrics //! //! This function demonstrates the change in stack and heap memory metrics //! as memory is allocated and deallocated from these regions. //! //! @warning This code uses `memfault_metrics_heartbeat_debug_trigger` which is not intended //! to be used in production code. This functions use here is solely to demonstrate the metrics //! values changing. Production applications should rely on the heartbeat timer to trigger //! collection static int prv_run_example_memory_metrics(const struct shell *shell, size_t argc, char **argv) { ARG_UNUSED(shell); ARG_UNUSED(argc); ARG_UNUSED(argv); if (s_main_thread == NULL) { return 0; } // Next two loops demonstrate heap usage metric for (size_t i = 0; i < ARRAY_SIZE(heap_ptrs); i++) { heap_ptrs[i] = k_malloc(HEAP_ALLOCATION_SIZE); } // Collect data after allocation memfault_metrics_heartbeat_debug_trigger(); for (size_t i = 0; i < ARRAY_SIZE(heap_ptrs); i++) { k_free(heap_ptrs[i]); heap_ptrs[i] = NULL; } // Collect data after deallocation memfault_metrics_heartbeat_debug_trigger(); return 0; } SHELL_CMD_REGISTER(memory_metrics, NULL, "Collects runtime memory metrics from application", prv_run_example_memory_metrics); #if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO) //! Callback to collect stack usage for specific threads static void prv_collect_thread_stack_usage_cb(const struct k_thread *thread, void *user_data) { // table mapping thread names to metric IDs static const struct { const char *name; MemfaultMetricId id; } threads[] = { { "shell_uart", MEMFAULT_METRICS_KEY(shell_uart_stack_free_bytes) }, }; // scan for a matching thread name for (size_t i = 0; i < ARRAY_SIZE(threads); i++) { if (strncmp(thread->name, threads[i].name, CONFIG_THREAD_MAX_NAME_LEN) == 0) { // get the stack usage and record in the matching metric id size_t stack_free_bytes; k_thread_stack_space_get(thread, &stack_free_bytes); memfault_metrics_heartbeat_set_unsigned(threads[i].id, stack_free_bytes); } } } #endif // defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO) // Override function to collect the app metric MainStack_MinBytesFree // and print current metric values void memfault_metrics_heartbeat_collect_data(void) { prv_collect_main_thread_run_stats(); #if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO) prv_collect_main_thread_stack_free(); k_thread_foreach(prv_collect_thread_stack_usage_cb, NULL); #endif memfault_metrics_heartbeat_debug_print(); } //! Helper function to demonstrate changes in stack metrics static void prv_run_stack_metrics_example(void) { volatile uint8_t stack_array[STACK_ALLOCATION_SIZE]; memset((uint8_t *)stack_array, 0, STACK_ALLOCATION_SIZE); } #endif // CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS #if defined(CONFIG_MEMFAULT_FAULT_HANDLER_RETURN) #include MEMFAULT_ZEPHYR_INCLUDE(fatal.h) void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) { printk("User fatal handler invoked with reason: %d. Rebooting!\n", reason); memfault_platform_reboot(); } #endif static int prv_metrics_session(const struct shell *shell, size_t argc, char **argv) { const char *cmd_name_string = "UNKNOWN"; if (argc > 1) { cmd_name_string = argv[1]; } shell_print(shell, "Executing a test metrics session named 'cli'\n"); MEMFAULT_METRICS_SESSION_START(cli); // API v1 // MEMFAULT_METRIC_SET_STRING(cli_cmd_name, cmd_name_string); // API v2 MEMFAULT_METRIC_SESSION_SET_STRING(cmd_name, cli, cmd_name_string); MEMFAULT_METRICS_SESSION_END(cli); return 0; } SHELL_CMD_REGISTER(metrics_session, NULL, "Trigger a one-time metrics session for testing", prv_metrics_session); static int prv_session_crash(const struct shell *shell, size_t argc, char **argv) { ARG_UNUSED(shell); ARG_UNUSED(argc); ARG_UNUSED(argv); LOG_ERR("Triggering crash"); MEMFAULT_METRICS_SESSION_START(cli); MEMFAULT_ASSERT(0); return 0; } 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) static int prv_log_overflow(const struct shell *shell, size_t argc, char **argv) { // get log line count from command line int log_line_count = 0; if (argc > 1) { log_line_count = strtoul(argv[1], NULL, 0); } for (int i = 0; i < log_line_count; i++) { LOG_INF("Log line %d", i); } shell_print(shell, "Dropped/recorded log lines: %d/%d", memfault_log_get_dropped_count(), memfault_log_get_recorded_count()); return 0; } SHELL_CMD_REGISTER(log_overflow, NULL, "generate a number of log lines", prv_log_overflow); #if !defined(CONFIG_MEMFAULT_CRC16_BUILTIN) #include MEMFAULT_ZEPHYR_INCLUDE(sys/crc.h) uint16_t memfault_crc16_compute(uint16_t crc_initial_value, const void *data, size_t data_len_bytes) { return crc16_itu_t(crc_initial_value, data, data_len_bytes); } #endif #if defined(CONFIG_MEMFAULT_PLATFORM_TIME_SINCE_BOOT_CUSTOM) uint64_t memfault_platform_get_time_since_boot_ms(void) { // Simple custom implementation, identical to the default one, to demonstrate // how to override the time since boot function. return k_uptime_get(); } #endif #if defined(CONFIG_WDOG_CMSDK_APB_START_AT_BOOT) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/watchdog.h) // Watchdog feed timer static void prv_watchdog_feed_timer_handler(struct k_timer *dummy) { ARG_UNUSED(dummy); const struct device *const wdt = DEVICE_DT_GET(DT_ALIAS(watchdog0)); // the CMSDK WDOG doesn't use a channel, pass 0 wdt_feed(wdt, 0); } K_TIMER_DEFINE(s_watchdog_feed_timer, prv_watchdog_feed_timer_handler, NULL); static int prv_start_watchdog_feed_timer() { // Start the watchdog feed timer to prevent the watchdog from resetting the system k_timer_start(&s_watchdog_feed_timer, K_MSEC(100), K_MSEC(100)); return 0; } SYS_INIT(prv_start_watchdog_feed_timer, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); #endif // CONFIG_WDOG_CMSDK_APB_START_AT_BOOT int main(void) { LOG_INF("👋 Memfault Demo App! Board %s\n", CONFIG_BOARD); memfault_device_info_dump(); memfault_cdr_register_source(&g_custom_data_recording_source); #if !defined(CONFIG_MEMFAULT_RECORD_REBOOT_ON_SYSTEM_INIT) memfault_zephyr_collect_reset_info(); #endif #if CONFIG_ZEPHYR_MEMFAULT_EXAMPLE_METRICS s_main_thread = k_current_get(); // @warning This code uses `memfault_metrics_heartbeat_debug_trigger` which is not intended // to be used in production code. This functions use here is solely to demonstrate the metrics // values changing. Production applications should rely on the heartbeat timer to trigger // collection // Collect a round of metrics to show initial stack usage memfault_metrics_heartbeat_debug_trigger(); prv_run_stack_metrics_example(); // Collect another round to show change in stack metrics memfault_metrics_heartbeat_debug_trigger(); #endif prv_init_test_thread_timer(); blink_forever(); return 0; } ================================================ FILE: examples/zephyr/qemu/qemu-app/src/metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Metrics for QEMU sample app. #if defined(CONFIG_MEMFAULT_METRICS_THREADS) #include "memfault/ports/zephyr/thread_metrics.h" //! Set the list of threads to monitor for stack usage MEMFAULT_METRICS_DEFINE_THREAD_METRICS( #if defined(CONFIG_MEMFAULT_METRICS_THREADS_DEFAULTS) { .thread_name = "idle", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle_pct_max), }, { .thread_name = "sysworkq", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_sysworkq_pct_max), }, #endif { .thread_name = "main", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_main_pct_max), }, { .thread_name = "shell_uart", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_shell_uart_pct_max), }); #endif // CONFIG_MEMFAULT_METRICS_THREADS ================================================ FILE: examples/zephyr/qemu/qemu-app/west.yml ================================================ # West manifest for Memfault Example Application manifest: self: path: qemu-app group-filter: # Disable the ambiq hal by default; it's compatible only with Zephyr v4.3.0. # To enable it, run: # west config manifest.group-filter +ambiq # west update - "-ambiq" remotes: - name: zephyrproject-rtos url-base: https://github.com/zephyrproject-rtos - name: memfault url-base: https://github.com/memfault projects: - name: zephyr remote: zephyrproject-rtos revision: v4.4.0 import: # Limit the Zephyr modules to the required set name-allowlist: - cmsis - cmsis_6 - picolibc - hal_ambiq - hal_nxp - hal_stm32 # Override the hal_ambiq module to use our patched version, which supports # MRAM flash driver on Apollo510 - name: hal_ambiq path: modules/hal/ambiq remote: memfault revision: zephyr-v4.3.0-apollo510-mram-patch groups: - ambiq - name: memfault-firmware-sdk path: modules/lib/memfault-firmware-sdk remote: memfault # Note: normally this would be pinned to a specific SDK tag instead of # master revision: master ================================================ FILE: examples/zephyr/stm32l4_disco/.ci-project-setup.json ================================================ { "v2_0": { "branch": "v2.0-branch", "commit": "7c0ec4358a205f83d2c6cc91d6dfc0a3106989a4", "notes:": "v2.0.0 tag" } } ================================================ FILE: examples/zephyr/stm32l4_disco/.gitignore ================================================ build/* modules/* ================================================ FILE: examples/zephyr/stm32l4_disco/README.md ================================================ ## Memfault for Zephyr RTOS This folder contains an example integration of the Memfault SDK using the port provided in `ports/zephyr` The demo was tested using the [STM32L4 Discovery kit](https://www.st.com/en/evaluation-tools/b-l475e-iot01a.html). However, the example application can be compiled for any other ARM Cortex-M board supported by Zephyr such as the nRF52840 (`--board=nrf52840dk_nrf52840`). For the purposes of this demo we will be using the v2.5 release of Zephyr. ## Getting Started Make sure you have read the instructions in the `README.md` in the root of the SDK and performed the installation steps that are mentioned there. ### Prerequisite We assume you already have a working Zephyr toolchain installed locally. Step-by-step instructions can be found in the [Zephyr Documentation](https://docs.zephyrproject.org/2.5.0/getting_started/index.html#build-hello-world). ### Setup 1. Clone the repo ```bash $ git clone https://github.com/zephyrproject-rtos/zephyr.git --branch v2.5-branch zephyr ``` 2. Create a virtual environment ```bash python3 -m venv .venv source .venv/bin/activate pip install -r zephyr/scripts/requirements.txt ``` 3. Initialize a new build environment: ```bash $ west init -l zephyr/ $ west update ``` 4. When using the STM32L4 discovery board, the following patch is also needed due to bugs in the stack: ```diff diff --git a/drivers/wifi/eswifi/Kconfig.eswifi b/drivers/wifi/eswifi/Kconfig.eswifi index 6468b98113..5f80c918cd 100644 --- a/drivers/wifi/eswifi/Kconfig.eswifi +++ b/drivers/wifi/eswifi/Kconfig.eswifi @@ -9,7 +9,7 @@ menuconfig WIFI_ESWIFI select WIFI_OFFLOAD select NET_OFFLOAD select NET_SOCKETS - select NET_SOCKETS_OFFLOAD + imply NET_SOCKETS_OFFLOAD select GPIO if WIFI_ESWIFI diff --git a/drivers/wifi/eswifi/eswifi_socket.c b/drivers/wifi/eswifi/eswifi_socket.c index e31ca0eecd..119f55778d 100644 --- a/drivers/wifi/eswifi/eswifi_socket.c +++ b/drivers/wifi/eswifi/eswifi_socket.c @@ -301,6 +301,6 @@ int __eswifi_socket_new(struct eswifi_dev *eswifi, int family, int type, k_delayed_work_init(&socket->read_work, eswifi_off_read_work); socket->usage = 1; LOG_DBG("Socket index %d", socket->index); - + net_context_set_state(socket->context, NET_CONTEXT_CONNECTED); return socket->index; } ``` You can find this patch at [`stm32l4_disco_zephyr2.5_wifi.patch`](./stm32l4_disco_zephyr2.5_wifi.patch), which can be applied by this command (assuming the `zephyr` repo is in the current working directory): ```bash git -C zephyr apply < stm32l4_disco_zephyr2.5_wifi.patch ``` ### Memfault Project Key An API key will need to be baked into the demo app to enable it to communicate with Memfault's web services. Go to https://app.memfault.com/, navigate to the project you want to use and select 'Settings'. Copy the 'Project Key' and paste it into `$MEMFAULT_SDK/examples/zephyr/apps/memfault_demo_app/src/main.c`, replacing `` with your Project Key. ### Building ```bash $ cd $MEMFAULT_SDK/examples/zephyr/ $ west build -b disco_l475_iot1 apps/memfault_demo_app [...] [271/271] Linking C executable zephyr/zephyr.elf ``` ### Flashing The build can be flashed on the development board using `west flash` ( See Zephyr ["Building, Flashing, & Debugging" documentation](https://docs.zephyrproject.org/2.5.0/guides/west/build-flash-debug.html?highlight=building%20flashing#flashing-west-flash)) Or, alternatively, if you are using a SEGGER JLink: 1. In one terminal, start a GDB Server ```bash $ JLinkGDBServer -if swd -device STM32L475VG ``` 2. In another terminal, load `zephyr.elf` with GDB: ```bash arm-none-eabi-gdb-py --eval-command="target remote localhost:2331" --ex="mon reset" --ex="load" --ex="mon reset" --se=build/zephyr/zephyr.elf ``` ### Using the App: At this point, the application should be running and you can open a console to run the [Memfault demo CLI](https://mflt.io/demo-cli). To get started, run `mflt help` to see short descriptions of each command. ```bash $ miniterm.py /dev/cu.usbmodem* 115200 --raw *** Booting Zephyr OS build zephyr-v2.5.0-56-gec0aa8331a65 *** : GNU Build ID: c20cef04e29e3ae7784002c3650d48f3a0b7b07d : S/N: DEMOSERIAL : SW type: zephyr-app : SW version: 1.0.0+c20cef : HW version: disco_l475_iot1 [00:00:00.578,000] mflt: Memfault Demo App! Board disco_l475_iot1 uart:~$ mflt help Subcommands: clear_core :clear coredump collected export :dump chunks collected by Memfault SDK using https://mflt.io/chunk-data-export get_core :check if coredump is stored and present get_device_info :display device information get_latest_release :checks to see if new ota payload is available post_chunks :Post Memfault data to cloud test :commands to verify memfault data collection (https://mflt.io/mcu-test-commands) post_chunks :Post Memfault data to cloud get_latest_release :checks to see if new ota payload is available ``` The `mflt test` subgroup contains commands for testing Memfault functionality: ```bash uart:~$ mflt test help test - commands to verify memfault data collection (https://mflt.io/mcu-test-commands) Subcommands: assert :trigger memfault assert busfault :trigger a busfault hang :trigger a hang hardfault :trigger a hardfault memmanage :trigger a memory management fault usagefault :trigger a usage fault zassert :trigger a zephyr assert reboot :trigger a reboot and record it using memfault heartbeat :trigger an immediate capture of all heartbeat metrics log_capture :trigger capture of current log buffer contents logs :writes test logs to log buffer trace :capture an example trace event ``` ### Causing a crash Command `mflt test hardfault` will trigger a hard fault due to a bad instruction fetch at a non-existing address, `0xbadcafe`: ``` uart:~$ mflt test hardfault ... etc ... ``` Upon crashing, a coredump will be written to noinit RAM. To check whether the coredump has been captured, try running the `get_core` command after the device reboots: ``` uart:~$ mflt get_core : Has coredump with size: 580 ``` This confirms that a coredump of 580 bytes has been captured. ### Posting Memfault Data for Analysis #### Uploading Symbols Memfault needs the symbols for the firmware in order to analyze the coredump. The ELF is located at `build/zephyr/zephyr.elf`. This .elf contains the symbols (debug information) amongst other things. [More information on Build Ids and uploading Symbol Files can be found here](https://mflt.io/symbol-file-build-ids). #### Connect WiFi Before we can send the coredump, we need to make sure WiFi is connected to the internet. Type `wifi connect 0 ` in the console. You should see something like: ``` Connection requested Connected uart:~$ ``` #### Post data Once connected, type `mflt post_chunks` and press enter. You should see: ``` uart:~$ mflt post_chunks : Memfault Message Post Complete: Parse Status 0 HTTP Status 202! : Body: Accepted : Memfault Message Post Complete: Parse Status 0 HTTP Status 202! : Body: Accepted : No more data to send ``` A coredump is now being processed by Memfault's services and will show up shortly under Issues. If it does not, take a look at the FAQ in the `README.md` in the root of the SDK. #### Without WiFi The network stack dependencies can also be disabled completely by using the `CONFIG_MEMFAULT_HTTP_SUPPORT` Kconfig option. When using a board that does not have a network stack enabled or for debug purposes, the messages to push to the Memfault cloud can also be dumped from the CLI using the `mflt export` command: ``` uart:~$ mflt export : MC:CAKmAgIDAQpqemVwaHlyLWFwcAlsMS4wLjArYzIwY2VmBm9kaXNjb19sNDc1X2lvdDEEogECBQD8Fw==: ``` You can then copy and paste the output into the "Chunks Debug" view in the Memfault UI for your project https://app.memfault.com/. For more details on how to use the CLI to explore each of Memfault's subsystems, see the [Memfault docs](https://mflt.io/demo-cli). ================================================ FILE: examples/zephyr/stm32l4_disco/apps/memfault_demo_app/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13.1) # The Zephyr port to target (i.e "v2.0", "v2.4") if(NOT DEFINED ENV{MEMFAULT_ZEPHYR_PORT_TARGET}) # Lar set(ENV{MEMFAULT_ZEPHYR_PORT_TARGET} v2.4) endif() # The path to the memfault-firmware-sdk if(NOT DEFINED ENV{MEMFAULT_SDK_DIR}) set(ENV{MEMFAULT_SDK_DIR} ${CMAKE_CURRENT_LIST_DIR}/../../../..) endif() # Add the memfault-firmware-sdk Zephyr port to the project # NB: CONFIG_MEMFAULT=y will also need to be set in prj.conf to enable set(MEMFAULT_ZEPHYR_PORT_TARGET $ENV{MEMFAULT_ZEPHYR_PORT_TARGET}) list(APPEND ZEPHYR_EXTRA_MODULES $ENV{MEMFAULT_SDK_DIR}/ports) include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) project(memfault_demo_app) zephyr_include_directories(config) target_sources(app PRIVATE src/main.c) ================================================ FILE: examples/zephyr/stm32l4_disco/apps/memfault_demo_app/config/memfault_metrics_heartbeat_config.def ================================================ ================================================ FILE: examples/zephyr/stm32l4_disco/apps/memfault_demo_app/config/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" ================================================ FILE: examples/zephyr/stm32l4_disco/apps/memfault_demo_app/config/memfault_trace_reason_user_config.def ================================================ // File for holding custom error traces: // https://mflt.io/error-tracing ================================================ FILE: examples/zephyr/stm32l4_disco/apps/memfault_demo_app/prj.conf ================================================ # General config CONFIG_NEWLIB_LIBC=y # Networking config CONFIG_NETWORKING=y CONFIG_NET_IPV4=y CONFIG_NET_IPV6=y CONFIG_NET_TCP=y CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_POSIX_NAMES=y CONFIG_POSIX_API=n CONFIG_DNS_RESOLVER=y CONFIG_DNS_SERVER_IP_ADDRESSES=y CONFIG_DNS_SERVER1="8.8.8.8" # Network debug config CONFIG_NET_LOG=y CONFIG_SHELL=y # Networking Config: CONFIG_NETWORKING=y CONFIG_NET_IPV4=y CONFIG_NET_IPV6=n CONFIG_NET_SOCKETS=y # Enable SimpleLink WiFi Driver and Socket Offload CONFIG_WIFI=y CONFIG_WIFI_LOG_LEVEL_ERR=y CONFIG_NET_L2_WIFI_SHELL=y # Options necessary to enable Mbed TLS logging. Useful for debugging low level TLS errors such as # handshake & setup aborts # CONFIG_MBEDTLS_DEBUG=y # CONFIG_MBEDTLS_DEBUG_LEVEL=4 # CONFIG_NET_SOCKETS_LOG_LEVEL_DBG=y # Required for logging backends to work as expected CONFIG_LOG=y CONFIG_LOG_PRINTK=y # Debugging CONFIG_NET_LOG=y CONFIG_WIFI_LOG_LEVEL_DBG=n CONFIG_DEBUG=y CONFIG_ASSERT=y CONFIG_SHELL_STACK_SIZE=4096 CONFIG_MAIN_STACK_SIZE=4096 CONFIG_MBEDTLS=y CONFIG_MBEDTLS_BUILTIN=y CONFIG_MBEDTLS_ENABLE_HEAP=y CONFIG_MBEDTLS_HEAP_SIZE=34000 CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=4096 CONFIG_NET_SOCKETS_SOCKOPT_TLS=y # Disable socket offload because attempts to use eswifi socket offload # hangs during setsockopt() calls CONFIG_NET_SOCKETS_OFFLOAD=n # Must match the number of certs being added with tls_credential_add CONFIG_TLS_MAX_CREDENTIALS_NUMBER=5 # The maximum number of certs to be used when resolving root of trust on a socket CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS=5 CONFIG_MBEDTLS_USER_CONFIG_ENABLE=y CONFIG_MBEDTLS_USER_CONFIG_FILE="memfault-mbedtls.conf" CONFIG_REBOOT=y CONFIG_INIT_STACKS=y CONFIG_MPU_STACK_GUARD=y # Memfault configuration CONFIG_MEMFAULT=y CONFIG_MEMFAULT_HTTP_ENABLE=y # Use MBEDTLS for Memfault HTTP sockets CONFIG_MEMFAULT_HTTP_USES_MBEDTLS=y CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD=y CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_INTERVAL_SECS=60 CONFIG_MEMFAULT_LOGGING_ENABLE=y CONFIG_MEMFAULT_LOGGING_RAM_SIZE=512 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=1024 # Currently not supported on older Zephyr, so explicitly disable until this # sample is updated CONFIG_MEMFAULT_METRICS_CPU_TEMP=n CONFIG_MEMFAULT_DTS_IN_ELF=n CONFIG_MEMFAULT_METRICS_WIFI=n ================================================ FILE: examples/zephyr/stm32l4_disco/apps/memfault_demo_app/sample.yaml ================================================ sample: description: Memfault SDK example integration built on top of Hello World Sample App name: Memfault Demo App ================================================ FILE: examples/zephyr/stm32l4_disco/apps/memfault_demo_app/src/main.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include #include "memfault/components.h" #include "memfault/ports/zephyr/http.h" // clang-format on LOG_MODULE_REGISTER(mflt_app, LOG_LEVEL_DBG); void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { *info = (sMemfaultDeviceInfo){ .device_serial = "DEMOSERIAL", .software_type = "zephyr-app", .software_version = "1.0.0-dev", .hardware_version = CONFIG_BOARD, }; } int main(void) { LOG_INF("Memfault Demo App! Board %s\n", CONFIG_BOARD); memfault_device_info_dump(); memfault_zephyr_port_install_root_certs(); return 0; } ================================================ FILE: examples/zephyr/stm32l4_disco/stm32l4_disco_zephyr2.5_wifi.patch ================================================ From 4a4a13fa00ef0d04d9be239d145ddeb173ac72a1 Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Thu, 23 Jun 2022 10:45:20 -0400 Subject: [PATCH] Patch for stm32l4_disco wifi on Zephyr 2.5.1 This patch is for Zephyr v2.5-branch (52e06334fbeabd5d764935c1fc47d7225216fff4), and fixes the eswifi driver for the Memfault demo app. --- drivers/wifi/eswifi/Kconfig.eswifi | 2 +- drivers/wifi/eswifi/eswifi_socket.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/wifi/eswifi/Kconfig.eswifi b/drivers/wifi/eswifi/Kconfig.eswifi index 6468b98113..5f80c918cd 100644 --- a/drivers/wifi/eswifi/Kconfig.eswifi +++ b/drivers/wifi/eswifi/Kconfig.eswifi @@ -9,7 +9,7 @@ menuconfig WIFI_ESWIFI select WIFI_OFFLOAD select NET_OFFLOAD select NET_SOCKETS - select NET_SOCKETS_OFFLOAD + imply NET_SOCKETS_OFFLOAD select GPIO if WIFI_ESWIFI diff --git a/drivers/wifi/eswifi/eswifi_socket.c b/drivers/wifi/eswifi/eswifi_socket.c index e31ca0eecd..a3705535ab 100644 --- a/drivers/wifi/eswifi/eswifi_socket.c +++ b/drivers/wifi/eswifi/eswifi_socket.c @@ -302,5 +302,6 @@ int __eswifi_socket_new(struct eswifi_dev *eswifi, int family, int type, socket->usage = 1; LOG_DBG("Socket index %d", socket->index); + net_context_set_state(socket->context, NET_CONTEXT_CONNECTED); return socket->index; } -- 2.36.1 ================================================ FILE: idf_component.yml ================================================ description: Memfault SDK for embedded systems. Observability, logging, crash reporting, and OTA all in one service url: https://github.com/memfault/memfault-firmware-sdk repository: https://github.com/memfault/memfault-firmware-sdk.git documentation: https://docs.memfault.com/ issues: https://github.com/memfault/memfault-firmware-sdk/issues ================================================ FILE: makefiles/MemfaultWorker.mk ================================================ # A convenience helper makefile that can be used to collect the sources and include # flags needed for the Memfault SDK based on the components used # # USAGE # If you are using a Make build system, to pick up the Memfault include paths & source # files needed for a project, you can just add the following lines: # # MEMFAULT_SDK_ROOT := # MEMFAULT_COMPONENTS := # include $(MEMFAULT_SDK_ROOT)/makefiles/MemfaultWorker.mk # += $(MEMFAULT_COMPONENTS_SRCS) # += $(MEMFAULT_COMPONENTS_INC_FOLDERS) # A utility to easily assert that a Makefile variable is defined and non-empty # Argument 1: The variable to check # Argument 2: The error message to display if the variable is not defined memfault_assert_arg_defined = \ $(if $(value $(strip $1)),,$(error Undefined $1:$2)) MEMFAULT_VALID_COMPONENTS := core demo http panics util metrics $(call memfault_assert_arg_defined,MEMFAULT_COMPONENTS,\ Must be set to one or more of "$(MEMFAULT_VALID_COMPONENTS)") $(call memfault_assert_arg_defined,MEMFAULT_SDK_ROOT,\ Must define the path to the root of the Memfault SDK) MEMFAULT_COMPONENTS_DIR := $(MEMFAULT_SDK_ROOT)/components MEMFAULT_COMPONENTS_INC_FOLDERS := $(MEMFAULT_COMPONENTS_DIR)/include MEMFAULT_COMPONENTS_SRCS = \ $(foreach component, $(MEMFAULT_COMPONENTS), \ $(sort $(wildcard $(MEMFAULT_COMPONENTS_DIR)/$(component)/src/*.c)) \ ) ifneq ($(filter demo,$(MEMFAULT_COMPONENTS)),) # The demo component is enabled so let's pick up component specific cli commands MEMFAULT_COMPONENTS_SRCS += \ $(foreach component, $(MEMFAULT_COMPONENTS), \ $(sort $(wildcard $(MEMFAULT_COMPONENTS_DIR)/demo/src/$(component)/*.c)) \ ) endif MEMFAULT_COMPONENTS_SRCS := \ $(patsubst %memfault_fault_handling_xtensa.c, , $(MEMFAULT_COMPONENTS_SRCS)) ================================================ FILE: ports/README.md ================================================ # Ports This folder contains ports of the Memfault platform dependencies for common SDKs and RTOS'. The files within are intended to be used in two ways: - For many common configurations, port files and include directories can be added directly to your build system. - For further customization, the desired ports can be used as a template that is copied into your own project and extended upon. ================================================ FILE: ports/atmel/saml1x/rcause_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! Reset Controller's (RSTC) "Reset Cause" (RCAUSE) register. //! //! More details can be found in the "RSTC - Reset Controller" of the //! "SAM L10/L11 Family" Datasheet #include "memfault/ports/reboot_reason.h" #include "sam.h" void memfault_reboot_reason_get(sResetBootupInfo *info) { const uint32_t reset_cause = (uint32_t)REG_RSTC_RCAUSE; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; if (reset_cause & RSTC_RCAUSE_POR_Msk) { reset_reason = kMfltRebootReason_PowerOnReset; } else if (reset_cause & RSTC_RCAUSE_BODCORE_Msk) { reset_reason = kMfltRebootReason_BrownOutReset; } else if (reset_cause & RSTC_RCAUSE_BODVDD_Msk) { reset_reason = kMfltRebootReason_BrownOutReset; } else if (reset_cause & RSTC_RCAUSE_EXT_Msk) { reset_reason = kMfltRebootReason_PinReset; } else if (reset_cause & RSTC_RCAUSE_WDT_Msk) { reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & RSTC_RCAUSE_SYST_Msk) { reset_reason = kMfltRebootReason_SoftwareReset; } *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/cypress/psoc6/README.md ================================================ # Memfault Library for ModusToolbox:tm: To get started with the Memfault ModusToolbox:tm: port see the integration guide available [here](https://mflt.io/mtb-integration-guide). ## Port Configuration The configuration files for this port contain parameters to modify the port and the SDK in general. The `configs/` directory contains the default values. ### Metrics Configuration The port comes with two sets of built-in metrics for WiFi and memory tracking. These metrics are controlled by these two definitions: - MEMFAULT_PORT_WIFI_TRACKING_ENABLED - MEMFAULT_PORT_MEMORY_TRACKING_ENABLED Setting these to 0 will disable the corresponding metrics. ### SDK Configuration A set of typical defaults is provided in `configs/memfault_mtb_platform_config.h`. If you wish to override other SDK values, simply create an application configuration file at `/configs/memfault_platform_config.h` ================================================ FILE: ports/cypress/psoc6/configs/memfault_metrics_mtb_heartbeat_config.def ================================================ #if MEMFAULT_PORT_MEMORY_TRACKING_ENABLED MEMFAULT_METRICS_KEY_DEFINE(Heap_TotalSize, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Heap_MinBytesFree, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Heap_BytesFree, kMemfaultMetricType_Unsigned) #endif #if MEMFAULT_PORT_WIFI_TRACKING_ENABLED MEMFAULT_METRICS_KEY_DEFINE(Wifi_ConnectingTime, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(Wifi_ConnectedTime, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(Wifi_ConnectCount, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Wifi_ReconnectCount, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Wifi_ConnectFailureCount, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Wifi_DisconnectCount, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Wifi_SignalStrength, kMemfaultMetricType_Signed) MEMFAULT_METRICS_KEY_DEFINE(Wifi_Channel, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Wifi_TxBytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Wifi_RxBytes, kMemfaultMetricType_Unsigned) #endif #if __has_include("memfault_metrics_heartbeat_config.def") #include "memfault_metrics_heartbeat_config.def" #endif ================================================ FILE: ports/cypress/psoc6/configs/memfault_mtb_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Default PSOC6 configuration #ifdef __cplusplus extern "C" { #endif //! Pick up logging macros from memfault_platform_log_config.h #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 //! Change the name of the coredump noinit section to align with default //! PSOC6 .ld of KEEP(*(.noinit)) #define MEMFAULT_PLATFORM_COREDUMP_NOINIT_SECTION_NAME ".mflt_coredump.noinit" #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE 8192 #define MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE \ "ports/cypress/psoc6/configs/memfault_metrics_mtb_heartbeat_config.def" // Include users config if present #if __has_include("memfault_platform_config.h") #include "memfault_platform_config.h" #endif //! Defines specific to PSOC6 Port //! Note that we include them last so an end user settings above override the defaults. #include "psoc6_default_config.h" #ifdef __cplusplus } #endif ================================================ FILE: ports/cypress/psoc6/configs/memfault_platform_log_config.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! // Logging depends on how your configuration does logging. See // https://docs.memfault.com/docs/mcu/self-serve/#logging-dependency #ifdef __cplusplus extern "C" { #endif #include #include "memfault/core/log.h" #define MEMFAULT_LOG_DEBUG(fmt, ...) \ do { \ MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Debug, fmt, ##__VA_ARGS__); \ printf("[D] " fmt "\n", ##__VA_ARGS__); \ } while (0) #define MEMFAULT_LOG_INFO(fmt, ...) \ do { \ MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, fmt, ##__VA_ARGS__); \ printf("[I] " fmt "\n", ##__VA_ARGS__); \ } while (0) #define MEMFAULT_LOG_WARN(fmt, ...) \ do { \ MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Warning, fmt, ##__VA_ARGS__); \ printf("[W] " fmt "\n", ##__VA_ARGS__); \ } while (0) #define MEMFAULT_LOG_ERROR(fmt, ...) \ do { \ MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Error, fmt, ##__VA_ARGS__); \ printf("[E] " fmt "\n", ##__VA_ARGS__); \ } while (0) #define MEMFAULT_LOG_RAW(fmt, ...) \ do { \ printf(fmt "\n", ##__VA_ARGS__); \ } while (0) #ifdef __cplusplus } #endif ================================================ FILE: ports/cypress/psoc6/memfault_bss.ld ================================================ /* Custom section to be included in the linker command file to support recovery of all FreeRTOS tasks when a coredump is collected. */ SECTIONS { .mflt_bss (NOLOAD) : { __memfault_capture_bss_start = .; *tasks.o(.bss COMMON .bss*) *timers*.o(.bss COMMON .bss*) __memfault_capture_bss_end = .; } } INSERT BEFORE .bss; ================================================ FILE: ports/cypress/psoc6/memfault_platform_core.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Implementation of core dependencies and setup for PSOC6 based products that are using the //! ModusToolbox SDK #include "memfault/components.h" #include "memfault/ports/freertos.h" #include "memfault/ports/reboot_reason.h" #if MEMFAULT_PORT_MEMORY_TRACKING_ENABLED #if defined(__GNUC__) && !defined(__ARMCC_VERSION) #include #endif #endif /* MEMFAULT_PORT_MEMORY_TRACKING_ENABLED */ #if MEMFAULT_PORT_WIFI_TRACKING_ENABLED #include "cy_wcm.h" #endif /* MEMFAULT_PORT_WIFI_TRACKING_ENABLED */ #ifndef MEMFAULT_EVENT_STORAGE_RAM_SIZE #define MEMFAULT_EVENT_STORAGE_RAM_SIZE 1024 #endif #ifndef MEMFAULT_LOG_STORAGE_RAM_SIZE #define MEMFAULT_LOG_STORAGE_RAM_SIZE 512 #endif #if MEMFAULT_PORT_WIFI_TRACKING_ENABLED static cy_wcm_wlan_statistics_t s_last_wlan_statistics = { 0 }; #endif void memfault_metrics_heartbeat_collect_sdk_data(void) { #if MEMFAULT_PORT_MEMORY_TRACKING_ENABLED #if defined(__GNUC__) && !defined(__ARMCC_VERSION) struct mallinfo mall_info = mallinfo(); // linker symbols for location of heap extern uint32_t __HeapBase; extern uint32_t __HeapLimit; const uint32_t heap_total_size = (uint32_t)&__HeapLimit - (uint32_t)&__HeapBase; MEMFAULT_METRIC_SET_UNSIGNED(Heap_TotalSize, heap_total_size); MEMFAULT_METRIC_SET_UNSIGNED(Heap_MinBytesFree, heap_total_size - mall_info.arena); MEMFAULT_METRIC_SET_UNSIGNED(Heap_BytesFree, heap_total_size - mall_info.uordblks); #endif /* defined(__GNUC__) && !defined(__ARMCC_VERSION) */ #endif /* MEMFAULT_PORT_MEMORY_TRACKING_ENABLED */ #if MEMFAULT_PORT_WIFI_TRACKING_ENABLED cy_wcm_associated_ap_info_t ap_info; cy_rslt_t result = cy_wcm_get_associated_ap_info(&ap_info); if (result == CY_RSLT_SUCCESS) { // Note: RSSI in dBm. (<-90=Very poor, >-30=Excellent) // A good way to determine whether or not issues an device is facing are due to poor signal // strength MEMFAULT_METRIC_SET_SIGNED(Wifi_SignalStrength, ap_info.signal_strength); MEMFAULT_METRIC_SET_UNSIGNED(Wifi_Channel, ap_info.channel); } cy_wcm_wlan_statistics_t curr_stat; result = cy_wcm_get_wlan_statistics(CY_WCM_INTERFACE_TYPE_STA, &curr_stat); if (result == CY_RSLT_SUCCESS) { MEMFAULT_METRIC_SET_UNSIGNED(Wifi_TxBytes, curr_stat.tx_bytes - s_last_wlan_statistics.tx_bytes); MEMFAULT_METRIC_SET_UNSIGNED(Wifi_TxBytes, curr_stat.rx_bytes - s_last_wlan_statistics.rx_bytes); s_last_wlan_statistics = curr_stat; } #endif /* MEMFAULT_PORT_WIFI_TRACKING_ENABLED */ } #if MEMFAULT_PORT_WIFI_TRACKING_ENABLED static void prv_wcm_event_cb(cy_wcm_event_t event, cy_wcm_event_data_t *event_data) { switch (event) { case CY_WCM_EVENT_CONNECTING: MEMFAULT_METRIC_TIMER_START(Wifi_ConnectingTime); break; case CY_WCM_EVENT_CONNECTED: MEMFAULT_METRIC_TIMER_STOP(Wifi_ConnectingTime); MEMFAULT_METRIC_TIMER_START(Wifi_ConnectedTime); MEMFAULT_METRIC_ADD(Wifi_ConnectCount, 1); break; case CY_WCM_EVENT_CONNECT_FAILED: MEMFAULT_METRIC_ADD(Wifi_ConnectFailureCount, 1); MEMFAULT_METRIC_TIMER_STOP(Wifi_ConnectingTime); MEMFAULT_METRIC_TIMER_STOP(Wifi_ConnectedTime); break; case CY_WCM_EVENT_RECONNECTED: MEMFAULT_METRIC_ADD(Wifi_ReconnectCount, 1); MEMFAULT_METRIC_TIMER_START(Wifi_ConnectedTime); break; case CY_WCM_EVENT_DISCONNECTED: MEMFAULT_METRIC_TIMER_STOP(Wifi_ConnectedTime); MEMFAULT_METRIC_ADD(Wifi_DisconnectCount, 1); break; case CY_WCM_EVENT_IP_CHANGED: break; case CY_WCM_EVENT_INITIATED_RETRY: break; case CY_WCM_EVENT_STA_JOINED_SOFTAP: break; case CY_WCM_EVENT_STA_LEFT_SOFTAP: break; default: break; } } void memfault_wcm_metrics_boot(void) { cy_wcm_register_event_callback(&prv_wcm_event_cb); } #endif /* MEMFAULT_PORT_WIFI_TRACKING_ENABLED */ MEMFAULT_WEAK void memfault_platform_reboot(void) { NVIC_SystemReset(); while (1) { } // unreachable } size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { static const struct { uint32_t start_addr; size_t length; } s_mcu_mem_regions[] = { { .start_addr = CY_SRAM_BASE, .length = CY_SRAM_SIZE }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_mcu_mem_regions); i++) { const uint32_t lower_addr = s_mcu_mem_regions[i].start_addr; const uint32_t upper_addr = lower_addr + s_mcu_mem_regions[i].length; if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); } } return 0; } int memfault_platform_boot(void) { memfault_build_info_dump(); memfault_device_info_dump(); memfault_freertos_port_boot(); memfault_platform_reboot_tracking_boot(); static uint8_t s_event_storage[MEMFAULT_EVENT_STORAGE_RAM_SIZE]; static uint8_t s_log_storage[MEMFAULT_LOG_STORAGE_RAM_SIZE]; const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(evt_storage); memfault_reboot_tracking_collect_reset_info(evt_storage); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); memfault_log_boot(s_log_storage, sizeof(s_log_storage)); MEMFAULT_LOG_INFO("Memfault Initialized!"); return 0; } ================================================ FILE: ports/cypress/psoc6/memfault_platform_coredump_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A coredump port which scoops up all the FreeRTOS state in the system at the time of crash //! To override and provide a custom port, simply add this file to the .cyignore for your project //! so it is not included in the build: //! (i.e $(SEARCH_memfault-firmware-sdk)/ports/cypress/psoc6/memfault_platform_coredump_regions.c) #include #include #include "cy_device_headers.h" #include "cy_syslib.h" #include "memfault/components.h" #include "memfault/panics/arch/arm/cortex_m.h" #include "memfault/ports/freertos_coredump.h" #define MEMFAULT_COREDUMP_MAX_TASK_REGIONS ((MEMFAULT_PLATFORM_MAX_TRACKED_TASKS) * 2) static sMfltCoredumpRegion s_coredump_regions[MEMFAULT_COREDUMP_MAX_TASK_REGIONS + 2 /* active stack(s) */ + 1 /* _kernel variable */ + 1 /* __memfault_capture_start */ ]; //! Note: If you get a linker error because these symbols are missing, your forgot to add the //! Memfault ld file to you LDFLAGS: //! LDFLAGS += -T$(SEARCH_memfault-firmware-sdk)/ports/cypress/psoc6/memfault_bss.ld extern uint32_t __memfault_capture_bss_end; extern uint32_t __memfault_capture_bss_start; //! Note: This function is called early on boot before the C runtime is loaded //! We hook into this function in order to scrub the void Cy_OnResetUser(void) { const size_t memfault_region_size = (uint32_t)&__memfault_capture_bss_end - (uint32_t)&__memfault_capture_bss_start; memset((uint32_t *)&__memfault_capture_bss_start, 0x0, memfault_region_size); } const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { int region_idx = 0; const size_t active_stack_size_to_collect = 512; // first, capture the active stack (and ISR if applicable) const bool msp_was_active = (crash_info->exception_reg_state->exc_return & (1 << 2)) == 0; size_t stack_size_to_collect = memfault_platform_sanitize_address_range( crash_info->stack_address, MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT); s_coredump_regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(crash_info->stack_address, stack_size_to_collect); region_idx++; if (msp_was_active) { // System crashed in an ISR but the running task state is on PSP so grab that too void *psp = (void *)(uintptr_t)__get_PSP(); // Collect a little bit more stack for the PSP since there is an // exception frame that will have been stacked on it as well const uint32_t extra_stack_bytes = 128; stack_size_to_collect = memfault_platform_sanitize_address_range( psp, active_stack_size_to_collect + extra_stack_bytes); s_coredump_regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(psp, stack_size_to_collect); region_idx++; } // Scoop up memory regions necessary to perform unwinds of the FreeRTOS tasks const size_t memfault_region_size = (uint32_t)&__memfault_capture_bss_end - (uint32_t)&__memfault_capture_bss_start; s_coredump_regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&__memfault_capture_bss_start, memfault_region_size); region_idx++; region_idx += memfault_freertos_get_task_regions( &s_coredump_regions[region_idx], MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx); *num_regions = region_idx; return &s_coredump_regions[0]; } ================================================ FILE: ports/cypress/psoc6/memfault_platform_http.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Implements dependency functions required to utilize Memfault's http client //! for posting data collected via HTTPS #include #include "cy_secure_sockets.h" #include "cy_tls.h" #include "memfault/components.h" struct MfltHttpClient { bool active; cy_socket_t handle; cy_socket_sockaddr_t sock_addr; }; typedef struct MfltHttpResponse { uint32_t status_code; } sMfltHttpResponse; static sMfltHttpClient s_client; static cy_rslt_t prv_teardown_connection(sMfltHttpClient *client) { cy_rslt_t result = cy_socket_disconnect(client->handle, 0); if (result != CY_RSLT_SUCCESS) { MEMFAULT_LOG_ERROR("cy_socket_disconnect failed: rv=0x%x", (int)result); } cy_socket_delete(client->handle); client->active = false; return 0; } static cy_rslt_t prv_disconnect_handler(cy_socket_t socket_handle, void *arg) { MEMFAULT_LOG_DEBUG("Memfault Socket Disconnected dropped"); prv_teardown_connection(&s_client); return 0; } static cy_rslt_t prv_open_socket(sMfltHttpClient *client) { cy_rslt_t result = cy_socket_create(CY_SOCKET_DOMAIN_AF_INET, CY_SOCKET_TYPE_STREAM, CY_SOCKET_IPPROTO_TLS, &client->handle); if (result != CY_RSLT_SUCCESS) { MEMFAULT_LOG_ERROR("cy_socket_create failed, rv=0x%x", (int)result); return result; } cy_socket_opt_callback_t disconnect_listener = { .callback = prv_disconnect_handler, .arg = NULL }; result = cy_socket_setsockopt(client->handle, CY_SOCKET_SOL_SOCKET, CY_SOCKET_SO_DISCONNECT_CALLBACK, &disconnect_listener, sizeof(cy_socket_opt_callback_t)); if (result != CY_RSLT_SUCCESS) { MEMFAULT_LOG_ERROR("cy_socket_setsockopt CY_SOCKET_SO_DISCONNECT_CALLBACK failed, rv=0x%x", (int)result); return result; } cy_socket_tls_auth_mode_t tls_auth_mode = CY_SOCKET_TLS_VERIFY_REQUIRED; result = cy_socket_setsockopt(client->handle, CY_SOCKET_SOL_TLS, CY_SOCKET_SO_TLS_AUTH_MODE, &tls_auth_mode, sizeof(cy_socket_tls_auth_mode_t)); if (result != CY_RSLT_SUCCESS) { MEMFAULT_LOG_ERROR("cy_socket_setsockopt CY_SOCKET_SO_TLS_AUTH_MODE failed, rv=0x%x", (int)result); } result = cy_socket_connect(client->handle, &client->sock_addr, sizeof(cy_socket_sockaddr_t)); if (result != CY_RSLT_SUCCESS) { MEMFAULT_LOG_ERROR("cy_socket_connect failed, 0x%x", (int)result); } return result; } sMfltHttpClient *memfault_platform_http_client_create(void) { if (s_client.active) { MEMFAULT_LOG_ERROR("Memfault HTTP client already in use"); return NULL; } s_client = (sMfltHttpClient){ .sock_addr = { .port = MEMFAULT_HTTP_GET_CHUNKS_API_PORT(), }, }; cy_rslt_t result = cy_socket_gethostbyname(MEMFAULT_HTTP_CHUNKS_API_HOST, CY_SOCKET_IP_VER_V4, &s_client.sock_addr.ip_address); if (result != CY_RSLT_SUCCESS) { MEMFAULT_LOG_ERROR("DNS lookup failed: 0x%x", (int)result); return NULL; } result = prv_open_socket(&s_client); if (result != CY_RSLT_SUCCESS) { return NULL; } s_client.active = true; return &s_client; } static bool prv_try_send(sMfltHttpClient *client, const uint8_t *buf, size_t buf_len) { cy_rslt_t idx = 0; while (idx != buf_len) { uint32_t bytes_sent = 0; int rv = cy_socket_send(client->handle, &buf[idx], buf_len - idx, CY_SOCKET_FLAGS_NONE, &bytes_sent); if (rv == CY_RSLT_SUCCESS && bytes_sent > 0) { idx += bytes_sent; continue; } MEMFAULT_LOG_ERROR("Data Send Error: bytes_sent=%d, cy_rslt=0x%x", (int)bytes_sent, rv); return false; } return true; } static bool prv_send_data(const void *data, size_t data_len, void *ctx) { sMfltHttpClient *client = (sMfltHttpClient *)ctx; return prv_try_send(client, data, data_len); } static bool prv_read_socket_data(sMfltHttpClient *client, void *buf, size_t *buf_len) { uint32_t buf_len_out; cy_rslt_t result = cy_socket_recv(client->handle, buf, *buf_len, CY_SOCKET_FLAGS_NONE, &buf_len_out); *buf_len = (size_t)buf_len_out; return result == CY_RSLT_SUCCESS; } int memfault_platform_http_response_get_status(const sMfltHttpResponse *response, uint32_t *status_out) { MEMFAULT_SDK_ASSERT(response != NULL); *status_out = response->status_code; return 0; } static int prv_wait_for_http_response(sMfltHttpClient *client) { sMemfaultHttpResponseContext ctx = { 0 }; while (1) { // We don't expect any response that needs to be parsed so // just use an arbitrarily small receive buffer char buf[32]; size_t bytes_read = sizeof(buf); if (!prv_read_socket_data(client, buf, &bytes_read)) { return -1; } bool done = memfault_http_parse_response(&ctx, buf, bytes_read); if (done) { MEMFAULT_LOG_DEBUG("Response Complete: Parse Status %d HTTP Status %d!", (int)ctx.parse_error, ctx.http_status_code); MEMFAULT_LOG_DEBUG("Body: %s", ctx.http_body); return ctx.http_status_code; } } } int memfault_platform_http_client_post_data(sMfltHttpClient *client, MemfaultHttpClientResponseCallback callback, void *ctx) { if (!client->active) { return -1; } const sPacketizerConfig cfg = { // let a single msg span many "memfault_packetizer_get_next" calls .enable_multi_packet_chunk = true, }; // will be populated with size of entire message queued for sending sPacketizerMetadata metadata; const bool data_available = memfault_packetizer_begin(&cfg, &metadata); if (!data_available) { MEMFAULT_LOG_DEBUG("No more data to send"); return kMfltPostDataStatus_NoDataFound; } memfault_http_start_chunk_post(prv_send_data, client, metadata.single_chunk_message_length); // Drain all the data that is available to be sent while (1) { // Arbitrarily sized send buffer. uint8_t buf[128]; size_t buf_len = sizeof(buf); eMemfaultPacketizerStatus status = memfault_packetizer_get_next(buf, &buf_len); if (status == kMemfaultPacketizerStatus_NoMoreData) { break; } if (!prv_try_send(client, buf, buf_len)) { // unexpected failure, abort in-flight transaction memfault_packetizer_abort(); return -1; } if (status == kMemfaultPacketizerStatus_EndOfChunk) { break; } } // we've sent a chunk, drain status sMfltHttpResponse response = { .status_code = prv_wait_for_http_response(client), }; if (callback) { callback(&response, ctx); } return 0; } int memfault_platform_http_client_destroy(sMfltHttpClient *client) { if (!client->active) { return -1; } prv_teardown_connection(client); return 0; } int memfault_platform_http_client_wait_until_requests_completed(sMfltHttpClient *client, uint32_t timeout_ms) { // No-op because memfault_platform_http_client_post_data() is synchronous return 0; } ================================================ FILE: ports/cypress/psoc6/memfault_psoc6_port.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! psoc6 specific port APIs #ifdef __cplusplus extern "C" { #endif //! Tracks Wi-Fi Connection Manager (wcm) subsystem with memfault heartbeats //! //! @note: This must be called after cy_wcm_init() is called void memfault_wcm_metrics_boot(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/cypress/psoc6/psoc6_default_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Defines configuration specific to PSOC6 Port //! //! These options can be changed from the DEFINES section in a MTB Makefile, i.e //! DEFINES+=MEMFAULT_PORT_MEMORY_TRACKING_ENABLED=0 or overridden with a custom //! "memfault_platform_config.h" #ifdef __cplusplus extern "C" { #endif //! Enable collection of metrics around heap utilization using heartbeat metrics #ifndef MEMFAULT_PORT_MEMORY_TRACKING_ENABLED #define MEMFAULT_PORT_MEMORY_TRACKING_ENABLED 1 #endif //! Enables collection of statistics around Wi-Fi using heartbeat metrics #ifndef MEMFAULT_PORT_WIFI_TRACKING_ENABLED #define MEMFAULT_PORT_WIFI_TRACKING_ENABLED 1 #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/cypress/psoc6/res_cause_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the Reset //! Reason using the PSOC6 Peripheral HAL: https://github.com/Infineon/mtb-pdl-cat1 #include "cy_syslib.h" #include "memfault/components.h" #include "memfault/ports/reboot_reason.h" //! Note: The default PSOC62 linker scripts have a KEEP(*(.noinit)) that we can use MEMFAULT_PUT_IN_SECTION(".mflt_reboot_tracking.noinit") static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; const uint32_t reset_cause = Cy_SysLib_GetResetReason(); MEMFAULT_PRINT_RESET_INFO("Reset Reason, Cy_SysLib_GetResetReason=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Cause: "); switch (reset_cause) { case 0: // Per code comments indicates "POR, XRES, or BOD": // https://github.com/Infineon/mtb-pdl-cat1/blob/3c6aebd/personalities/platform/power-1.3.cypersonality#L205 MEMFAULT_PRINT_RESET_INFO(" POR, XRES, or BOD"); reset_reason = kMfltRebootReason_PowerOnReset; break; case CY_SYSLIB_RESET_HWWDT: MEMFAULT_PRINT_RESET_INFO(" HW WDT"); reset_reason = kMfltRebootReason_HardwareWatchdog; break; case CY_SYSLIB_RESET_ACT_FAULT: // unauthorized protection violations MEMFAULT_PRINT_RESET_INFO(" ACT Fault"); reset_reason = kMfltRebootReason_UnknownError; break; case CY_SYSLIB_RESET_DPSLP_FAULT: // failed to enter deep sleep MEMFAULT_PRINT_RESET_INFO(" DPSLP Fault"); reset_reason = kMfltRebootReason_UnknownError; break; #if defined(CY_IP_M33SYSCPUSS) || defined(CY_IP_M7CPUSS) case CY_SYSLIB_RESET_TC_DBGRESET: MEMFAULT_PRINT_RESET_INFO(" TC DBGRESET"); reset_reason = kMfltRebootReason_Assert; break; #endif case CY_SYSLIB_RESET_SOFT: MEMFAULT_PRINT_RESET_INFO(" Software Reset"); reset_reason = kMfltRebootReason_SoftwareReset; break; case CY_SYSLIB_RESET_SWWDT0: case CY_SYSLIB_RESET_SWWDT1: case CY_SYSLIB_RESET_SWWDT2: case CY_SYSLIB_RESET_SWWDT3: MEMFAULT_PRINT_RESET_INFO(" Software Watchdog"); reset_reason = kMfltRebootReason_SoftwareWatchdog; break; case CY_SYSLIB_RESET_CSV_LOSS_WAKEUP: case CY_SYSLIB_RESET_CSV_ERROR_WAKEUP: MEMFAULT_PRINT_RESET_INFO(" Clock-Supervision Logic Reset"); reset_reason = kMfltRebootReason_ClockFailure; break; case CY_SYSLIB_RESET_HIB_WAKEUP: MEMFAULT_PRINT_RESET_INFO(" Hibernation Wakeup"); reset_reason = kMfltRebootReason_LowPower; break; #ifdef CY_IP_M7CPUSS case CY_SYSLIB_RESET_XRES: case CY_SYSLIB_RESET_BODVDDD: case CY_SYSLIB_RESET_BODVDDA: case CY_SYSLIB_RESET_BODVCCD: MEMFAULT_PRINT_RESET_INFO(" Brown Out"); reset_reason = kMfltRebootReason_BrownOutReset; break; case CY_SYSLIB_RESET_OVDVDDD: case CY_SYSLIB_RESET_OVDVDDA: case CY_SYSLIB_RESET_OVDVCCD: case CY_SYSLIB_RESET_OCD_ACT_LINREG: case CY_SYSLIB_RESET_OCD_DPSLP_LINREG: case CY_SYSLIB_RESET_OCD_REGHC: case CY_SYSLIB_RESET_PMIC: case CY_SYSLIB_RESET_PXRES: case CY_SYSLIB_RESET_STRUCT_XRES: reset_reason = kMfltRebootReason_UnknownError; break; case CY_SYSLIB_RESET_PORVDDD: MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); reset_reason = kMfltRebootReason_PowerOnReset; break; #endif default: reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO(" Unknown"); break; } *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } void memfault_platform_reboot_tracking_boot(void) { sResetBootupInfo reset_info = { 0 }; memfault_reboot_reason_get(&reset_info); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); } ================================================ FILE: ports/dialog/da145xx/armcc-fault-handler.patch ================================================ From ce55362c0312d2deff080a9a46a6baa72c0532fb Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Thu, 6 May 2021 15:38:37 -0700 Subject: [PATCH] da145xx: Patch for armcc fault handlers. --- sdk/platform/arch/boot/ARM/startup_DA14531.s | 2 ++ sdk/platform/arch/boot/ARM/startup_DA14585_586.s | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sdk/platform/arch/boot/ARM/startup_DA14531.s b/sdk/platform/arch/boot/ARM/startup_DA14531.s index 54031fe..6029e03 100644 --- a/sdk/platform/arch/boot/ARM/startup_DA14531.s +++ b/sdk/platform/arch/boot/ARM/startup_DA14531.s @@ -374,6 +374,7 @@ cold_reset IMPORT NMI_HandlerC NMI_Handler\ PROC + EXPORT NMI_Handler [WEAK] movs r0, #4 mov r1, lr tst r0, r1 @@ -390,6 +391,7 @@ NMI_stacking_used_MSP IMPORT HardFault_HandlerC HardFault_Handler\ PROC + EXPORT HardFault_Handler [WEAK] movs r0, #4 mov r1, lr tst r0, r1 diff --git a/sdk/platform/arch/boot/ARM/startup_DA14585_586.s b/sdk/platform/arch/boot/ARM/startup_DA14585_586.s index c7e9382..b8ccb21 100644 --- a/sdk/platform/arch/boot/ARM/startup_DA14585_586.s +++ b/sdk/platform/arch/boot/ARM/startup_DA14585_586.s @@ -130,6 +130,7 @@ Reset_Handler PROC IMPORT NMI_HandlerC NMI_Handler\ PROC + EXPORT NMI_Handler [WEAK] movs r0, #4 mov r1, lr tst r0, r1 @@ -146,6 +147,7 @@ NMI_stacking_used_MSP IMPORT HardFault_HandlerC HardFault_Handler\ PROC + EXPORT HardFault_Handler [WEAK] movs r0, #4 mov r1, lr tst r0, r1 ================================================ FILE: ports/dialog/da145xx/gcc-hardfault.patch ================================================ From 7838000be3586587795653967cb129af7bd4d826 Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Thu, 15 Apr 2021 15:40:19 -0700 Subject: [PATCH] Add support for overriding GCC based hard_fault handler to DA145xx SDK 6.0.14.1114. --- sdk/platform/arch/boot/GCC/startup_DA14531.S | 2 +- sdk/platform/arch/boot/GCC/startup_DA14585_586.S | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/platform/arch/boot/GCC/startup_DA14531.S b/sdk/platform/arch/boot/GCC/startup_DA14531.S index e9a1619..af84196 100644 --- a/sdk/platform/arch/boot/GCC/startup_DA14531.S +++ b/sdk/platform/arch/boot/GCC/startup_DA14531.S @@ -512,8 +512,8 @@ NMI_stacking_used_MSP: .size NMI_Handler, . - NMI_Handler .thumb_func - .globl HardFault_Handler .type HardFault_Handler, %function + .weak HardFault_Handler .fnstart @ HardFault handler diff --git a/sdk/platform/arch/boot/GCC/startup_DA14585_586.S b/sdk/platform/arch/boot/GCC/startup_DA14585_586.S index ac20213..4701ad2 100644 --- a/sdk/platform/arch/boot/GCC/startup_DA14585_586.S +++ b/sdk/platform/arch/boot/GCC/startup_DA14585_586.S @@ -256,8 +256,8 @@ NMI_stacking_used_MSP: .size NMI_Handler, . - NMI_Handler .thumb_func - .globl HardFault_Handler .type HardFault_Handler, %function + .weak HardFault_Handler .fnstart @ HardFault handler ================================================ FILE: ports/dialog/da145xx/gnu-build-id.patch ================================================ From 087b3ec1eb25bfe6d955803a80e1e44077a41b25 Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Thu, 15 Apr 2021 15:18:41 -0700 Subject: [PATCH] Add support for capturing GNU Build Id to DA145xx SDK 6.0.14.1114 --- sdk/common_project_files/ldscripts/ldscript_DA14531.lds.S | 7 +++++++ sdk/common_project_files/ldscripts/ldscript_DA14585_586.lds.S | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/sdk/common_project_files/ldscripts/ldscript_DA14531.lds.S b/sdk/common_project_files/ldscripts/ldscript_DA14531.lds.S index 7b0e0d9..596b9b4 100644 --- a/sdk/common_project_files/ldscripts/ldscript_DA14531.lds.S +++ b/sdk/common_project_files/ldscripts/ldscript_DA14531.lds.S @@ -68,6 +68,13 @@ SECTIONS } > LR_IROM3 __exidx_end = .; + .note.gnu.build-id : + { + /* Integrate build id as described in memfault/core/build_info.h */ + __start_gnu_build_id_start = .; + KEEP(*(.note.gnu.build-id)) + } > LR_IROM3 + /* To copy multiple ROM to RAM sections, * define __STARTUP_COPY_MULTIPLE in startup_DA14531.S */ diff --git a/sdk/common_project_files/ldscripts/ldscript_DA14585_586.lds.S b/sdk/common_project_files/ldscripts/ldscript_DA14585_586.lds.S index 70c0de3..cbd4f8c 100644 --- a/sdk/common_project_files/ldscripts/ldscript_DA14585_586.lds.S +++ b/sdk/common_project_files/ldscripts/ldscript_DA14585_586.lds.S @@ -78,6 +78,13 @@ SECTIONS } > LR_IROM3 __exidx_end = .; + .note.gnu.build-id : + { + /* Integrate build id as described in memfault/core/build_info.h */ + __start_gnu_build_id_start = .; + KEEP(*(.note.gnu.build-id)) + } > LR_IROM3 + /* To copy multiple ROM to RAM sections, * define __STARTUP_COPY_MULTIPLE in startup_DA14585_586.S */ ================================================ FILE: ports/dialog/da145xx/memfault_platform_core.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "datasheet.h" #include "memfault/components.h" #include "memfault/ports/reboot_reason.h" #if !defined(MEMFAULT_EVENT_STORAGE_RAM_SIZE) #if defined(__DA14531__) #define MEMFAULT_EVENT_STORAGE_RAM_SIZE 128 #else #define MEMFAULT_EVENT_STORAGE_RAM_SIZE 512 #endif #endif int memfault_platform_boot(void) { memfault_platform_reboot_tracking_boot(); memfault_build_info_dump(); memfault_device_info_dump(); static uint8_t s_event_storage[MEMFAULT_EVENT_STORAGE_RAM_SIZE]; const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(evt_storage); memfault_reboot_tracking_collect_reset_info(evt_storage); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); return 0; } MEMFAULT_NORETURN void memfault_platform_reboot(void) { const uint16_t tmp = (GetWord16(SYS_CTRL_REG) & ~REMAP_ADR0) | 0 | SW_RESET; SetWord16(SYS_CTRL_REG, tmp); MEMFAULT_UNREACHABLE; } ================================================ FILE: ports/dialog/da145xx/memfault_platform_coredump_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "arch_ram.h" #include "memfault/config.h" #include "memfault/core/math.h" #include "memfault/panics/platform/coredump.h" #ifndef MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY #define MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY 1 #endif #if !MEMFAULT_PLATFORM_COREDUMP_STORAGE_REGIONS_CUSTOM const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { // Let's collect the callstack at the time of crash static sMfltCoredumpRegion s_coredump_regions[1]; #if (MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY == 1) const void *stack_start_addr = crash_info->stack_address; // Capture only the interrupt stack. Use only if there is not enough storage to capture all of // RAM. #if defined(__GNUC__) extern uint32_t __StackTop; const uint32_t stack_top_addr = (uint32_t)&__StackTop; #elif defined(__CC_ARM) extern uint32_t __initial_sp; const uint32_t stack_top_addr = (uint32_t)&__initial_sp; #else #error "Unsupported Compiler." #endif s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( stack_start_addr, (uint32_t)stack_top_addr - (uint32_t)stack_start_addr); #else // Capture all of RAM. Recommended: it enables broader post-mortem analyses, // but has larger storage requirements. s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT((uint32_t *)RAM_1_BASE_ADDR, RAM_END_ADDR - RAM_1_BASE_ADDR); #endif *num_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions); return s_coredump_regions; } #endif size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { static const struct { uint32_t start_addr; size_t length; } s_mcu_mem_regions[] = { // DA14535 projects define both __DA14535__ and __DA14531__, so check for __DA14535__ first #if defined(__DA14535__) { .start_addr = RAM_1_BASE_ADDR, .length = RAM_2_BASE_ADDR - RAM_1_BASE_ADDR }, { .start_addr = RAM_2_BASE_ADDR, .length = RAM_END_ADDR - RAM_2_BASE_ADDR }, #elif defined(__DA14531__) { .start_addr = RAM_1_BASE_ADDR, .length = RAM_2_BASE_ADDR - RAM_1_BASE_ADDR }, { .start_addr = RAM_2_BASE_ADDR, .length = RAM_3_BASE_ADDR - RAM_2_BASE_ADDR }, { .start_addr = RAM_3_BASE_ADDR, .length = RAM_END_ADDR - RAM_3_BASE_ADDR }, #else { .start_addr = RAM_1_BASE_ADDR, .length = RAM_2_BASE_ADDR - RAM_1_BASE_ADDR }, { .start_addr = RAM_2_BASE_ADDR, .length = RAM_3_BASE_ADDR - RAM_2_BASE_ADDR }, { .start_addr = RAM_3_BASE_ADDR, .length = RAM_4_BASE_ADDR - RAM_3_BASE_ADDR }, { .start_addr = RAM_4_BASE_ADDR, .length = RAM_END_ADDR - RAM_4_BASE_ADDR }, #endif }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_mcu_mem_regions); i++) { const uint32_t lower_addr = s_mcu_mem_regions[i].start_addr; const uint32_t upper_addr = lower_addr + s_mcu_mem_regions[i].length; if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); } } return 0; } ================================================ FILE: ports/dialog/da145xx/memfault_platform_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions to use space on the //! external SPI flash connected to the DA145xx for coredump capture. //! //! To use, update "memfault_platform_config.h" with a _free_ address range on the NOR flash to //! capture the coredump. The size provisioned should be >= the size of RAM and be aligned on //! sector boundaries. For example: //! //! #define MEMFAULT_COREDUMP_STORAGE_START_ADDR 0x20000 //! #define MEMFAULT_COREDUMP_STORAGE_END_ADDR 0x30000 #include "memfault/config.h" #if MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH #include #include #include "arch_wdg.h" #include "memfault/panics/platform/coredump.h" #include "spi_flash.h" #if !defined(MEMFAULT_COREDUMP_STORAGE_START_ADDR) || !defined(MEMFAULT_COREDUMP_STORAGE_END_ADDR) #error \ "MEMFAULT_COREDUMP_STORAGE_START_ADDR & MEMFAULT_COREDUMP_STORAGE_END_ADDR must be specified in memfault_platform_config.h" #endif #if ((MEMFAULT_COREDUMP_STORAGE_START_ADDR % SPI_FLASH_SECTOR_SIZE) != 0) #error "MEMFAULT_COREDUMP_STORAGE_START_ADDR should be aligned by the sector size" #endif #if ((MEMFAULT_COREDUMP_STORAGE_END_ADDR % SPI_FLASH_SECTOR_SIZE) != 0) #error "MEMFAULT_COREDUMP_STORAGE_END_ADDR should be aligned by the sector size" #endif void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR, .sector_size = SPI_FLASH_SECTOR_SIZE, }; } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } const uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; // Flash operations are blocking and can take some time... wdg_reload(WATCHDOG_DEFAULT_PERIOD); uint32_t actual_size = 0; if (spi_flash_read_data((uint8_t *)data, address, (uint32_t)read_len, &actual_size) != SPI_FLASH_ERR_OK) { return false; } return (actual_size == read_len); } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } const size_t sector_size = SPI_FLASH_SECTOR_SIZE; if ((offset % sector_size) != 0) { return false; } // Flash operations are blocking and can take some time... wdg_reload(WATCHDOG_DEFAULT_PERIOD); for (size_t sector = offset; sector < erase_size; sector += sector_size) { const uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + sector; if (spi_flash_block_erase(address, SPI_FLASH_OP_SE) != SPI_FLASH_ERR_OK) { return false; } } return true; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if (!prv_op_within_flash_bounds(offset, data_len)) { return false; } const uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; // Flash operations are blocking and can take some time... wdg_reload(WATCHDOG_DEFAULT_PERIOD); uint32_t actual_size = 0; if (spi_flash_write_data((uint8_t *)data, address, (uint32_t)data_len, &actual_size) != SPI_FLASH_ERR_OK) { return false; } return (actual_size == data_len); } // Note: This function is called while the system is running once the coredump has been read. We // clear the first word in this scenario to avoid blocking the system for a long time on an erase. void memfault_platform_coredump_storage_clear(void) { uint32_t clear_word = 0x0; memfault_platform_coredump_storage_write(0, &clear_word, sizeof(clear_word)); } #endif /* MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH */ ================================================ FILE: ports/dialog/da145xx/memfault_platform_debug_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Map memfault logging dependencies to da145xx arch_printf implementation. #include #include #include "memfault/config.h" #include "memfault/core/platform/debug_log.h" #ifdef CFG_PRINTF #include "arch_console.h" #endif #ifndef MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES #define MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES (128) #endif void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { #ifdef CFG_PRINTF va_list args; va_start(args, fmt); char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; vsnprintf(log_buf, sizeof(log_buf), fmt, args); const char *lvl_str = NULL; switch (level) { case kMemfaultPlatformLogLevel_Debug: lvl_str = "D"; break; case kMemfaultPlatformLogLevel_Info: lvl_str = "I"; break; case kMemfaultPlatformLogLevel_Warning: lvl_str = "W"; break; case kMemfaultPlatformLogLevel_Error: lvl_str = "E"; break; default: break; } if (lvl_str) { arch_printf("[%s] MFLT: %s\r\n", lvl_str, log_buf); } va_end(args); #endif } void memfault_platform_log_raw(const char *fmt, ...) { #ifdef CFG_PRINTF va_list args; va_start(args, fmt); arch_vprintf(fmt, args); arch_printf("\r\n"); va_end(args); #endif } ================================================ FILE: ports/dialog/da145xx/memfault_platform_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "app_easy_timer.h" #include "datasheet.h" #include "lld_evt.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/timer.h" bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback callback) { bool rc = true; // Schedule a timer to invoke callback() repeatedly after period_sec if (app_easy_timer(MS_TO_TIMERUNITS(period_sec * 1000), callback) == EASY_TIMER_INVALID_TIMER) { rc = false; } return rc; } uint64_t memfault_platform_get_time_since_boot_ms(void) { if (GetBits16(CLK_RADIO_REG, BLE_ENABLE) == 0) { // The BLE core timer is not running so we just return 0. Generally, the core should always be // running return 0; } uint64_t time = (uint64_t)lld_evt_time_get(); // LLD time is in units of 625us, convert to ms. time = (time * 625ULL) / 1000ULL; // return time since boot in ms, this is used for relative timings. return time; } ================================================ FILE: ports/dialog/da145xx/reset_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! The DA145xx SDK executes the "reset_indication" callback to inform the //! application of the reset reason. This captures the state so it can be //! saved and published by Memfault #include "datasheet.h" #include "memfault/core/platform/reboot_tracking.h" #include "memfault/ports/reboot_reason.h" #if defined(__CC_ARM) // To prevent this array from being initialized when using the ARM compiler it must // be defined as follows. See https://developer.arm.com/documentation/ka003046/latest // for further information. static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE] __attribute__((section("retention_mem_area0"), zero_init)); #else static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE] __SECTION( "retention_mem_area_uninit"); #endif // Since "reset_indication" is called before memfault_platform_boot() // is called we store the state in static variables for access later on in the boot static eMemfaultRebootReason s_reset_reason; static uint32_t s_reset_reason_reg; #if defined(__DA14531__) void reset_indication(uint16_t reset_status) { s_reset_reason_reg = reset_status; s_reset_reason = kMfltRebootReason_PowerOnReset; if (reset_status & HWRESET_STAT) { s_reset_reason = kMfltRebootReason_PinReset; } else if (reset_status & SWRESET_STAT) { s_reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_status & WDOGRESET_STAT) { s_reset_reason = kMfltRebootReason_HardwareWatchdog; } } #else /* DA14585/6 */ void reset_indication(uint16_t por_time) { if (por_time) { s_reset_reason = kMfltRebootReason_PowerOnReset; } else { s_reset_reason = kMfltRebootReason_DeepSleep; } // Does not exist on DA14585/6 platform s_reset_reason_reg = 0; } #endif void memfault_platform_reboot_tracking_boot(void) { sResetBootupInfo reset_info = { 0 }; memfault_reboot_reason_get(&reset_info); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); } void memfault_reboot_reason_get(sResetBootupInfo *info) { *info = (sResetBootupInfo){ .reset_reason_reg = s_reset_reason_reg, .reset_reason = s_reset_reason, }; } ================================================ FILE: ports/dialog/da1468x/gnu-build-id.patch ================================================ From a8a6f2ece11a14d3cde54a177822e12a871d87e7 Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Tue, 16 Mar 2021 11:06:17 -0400 Subject: [PATCH] Add support for capturing GNU Build Id to DA1468x SDK1.0.14.1081 For more details, see https://mflt.io/da1468x-gnu-build-id --- sdk/bsp/ldscripts/ble_projects/sections.ld.h | 13 ++++++++++++- sdk/bsp/ldscripts/non_ble_projects/sections.ld.h | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/sdk/bsp/ldscripts/ble_projects/sections.ld.h b/sdk/bsp/ldscripts/ble_projects/sections.ld.h index 0fc6add..38a5abd 100644 --- a/sdk/bsp/ldscripts/ble_projects/sections.ld.h +++ b/sdk/bsp/ldscripts/ble_projects/sections.ld.h @@ -224,10 +224,21 @@ SECTIONS } > ROM __exidx_end = .; +#if (dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A) && (dg_configEXEC_MODE != MODE_IS_CACHED) + .note.gnu.build-id : AT ( LOADADDR(.ARM.exidx) + SIZEOF(.ARM.exidx) ) +#else + .note.gnu.build-id : +#endif + { + /* Integrate build id as described in memfault/core/build_info.h */ + __start_gnu_build_id_start = .; + KEEP(*(.note.gnu.build-id)) + } > ROM + /* 16 byte alignment is required. Please do not add anything until the __etext * assignment! */ #if (dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A) && (dg_configEXEC_MODE != MODE_IS_CACHED) - .align_s : AT ( LOADADDR(.ARM.exidx) + SIZEOF(.ARM.exidx) ) + .align_s : AT ( LOADADDR(.note.gnu.build-id) + SIZEOF(.note.gnu.build-id) ) #else .align_s : #endif diff --git a/sdk/bsp/ldscripts/non_ble_projects/sections.ld.h b/sdk/bsp/ldscripts/non_ble_projects/sections.ld.h index fde1475..6e98d1d 100644 --- a/sdk/bsp/ldscripts/non_ble_projects/sections.ld.h +++ b/sdk/bsp/ldscripts/non_ble_projects/sections.ld.h @@ -220,10 +220,21 @@ SECTIONS } > ROM __exidx_end = .; +#if (dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A) && (dg_configEXEC_MODE != MODE_IS_CACHED) + .note.gnu.build-id : AT ( LOADADDR(.ARM.exidx) + SIZEOF(.ARM.exidx) ) +#else + .note.gnu.build-id : +#endif + { + /* Integrate build id as described in memfault/core/build_info.h */ + __start_gnu_build_id_start = .; + KEEP(*(.note.gnu.build-id)) + } > ROM + /* 16 byte alignment is required. Please do not add anything until the __etext * assignment! */ #if (dg_configBLACK_ORCA_IC_REV != BLACK_ORCA_IC_REV_A) && (dg_configEXEC_MODE != MODE_IS_CACHED) - .align_s : AT ( LOADADDR(.ARM.exidx) + SIZEOF(.ARM.exidx) ) + .align_s : AT ( LOADADDR(.note.gnu.build-id) + SIZEOF(.note.gnu.build-id) ) #else .align_s : #endif -- 2.31.0 ================================================ FILE: ports/dialog/da1468x/memfault-qspi-coredump-storage.patch ================================================ From ad835b23a3476ed770e21de8a1a13fe1b57c78f9 Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Thu, 1 Apr 2021 17:24:41 -0400 Subject: [PATCH] Enable saving of coredumps in QSPI * Introduces dg_configMEMFAULT & enables by default. Memfault modifications can be disabled by adding #define dg_configMEMFAULT 0 to projects custom_config*.h * Disables fault handlers in startup_ARMCM0.S so Memfault handlers can be picked up and coredumps can be collected * Updates ad_nvms/_flash driver to work from ISRs * Update FreeRTOSConfig.h so memfault sdk can monitor and capture task state --- sdk/bsp/adapters/src/ad_flash.c | 17 +++++++++++++++++ sdk/bsp/adapters/src/ad_nvms_direct.c | 29 ++++++++++++++++++++++ sdk/bsp/config/bsp_defaults.h | 5 ++++ sdk/bsp/free_rtos/include/FreeRTOSConfig.h | 4 +++ sdk/bsp/startup/startup_ARMCM0.S | 4 ++- sdk/bsp/system/sys_man/sys_power_mgr.c | 7 ++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/sdk/bsp/adapters/src/ad_flash.c b/sdk/bsp/adapters/src/ad_flash.c index f6a70f7..4be0497 100644 --- a/sdk/bsp/adapters/src/ad_flash.c +++ b/sdk/bsp/adapters/src/ad_flash.c @@ -47,6 +47,17 @@ PRIVILEGED_DATA static OS_MUTEX flash_mutex; PRIVILEGED_DATA static uint32_t no_cache_flush_base; PRIVILEGED_DATA static uint32_t no_cache_flush_end; +// If an RTOS is in use we need to track the mutex and fail if it's taken. +// If no RTOS, then assume the flash is never locked. +#if dg_configMEMFAULT == 1 +#ifndef OS_BAREMETAL +static uint32_t s_flash_mutex_tracker; +bool memfault_ad_flash_is_locked(void) { return s_flash_mutex_tracker != 0; } +#else +bool memfault_ad_flash_is_locked(void) { return false; } +#endif +#endif + static inline bool is_flash_addr_cached(uint32_t addr) { /* @@ -244,12 +255,18 @@ void ad_flash_lock(void) { #ifndef OS_BAREMETAL OS_MUTEX_GET(flash_mutex, OS_MUTEX_FOREVER); +#if (dg_configMEMFAULT == 1) + ++s_flash_mutex_tracker; +#endif #endif } void ad_flash_unlock(void) { #ifndef OS_BAREMETAL +#if (dg_configMEMFAULT == 1) + if (s_flash_mutex_tracker) --s_flash_mutex_tracker; +#endif OS_MUTEX_PUT(flash_mutex); #endif } diff --git a/sdk/bsp/adapters/src/ad_nvms_direct.c b/sdk/bsp/adapters/src/ad_nvms_direct.c index 3c23223..923f602 100644 --- a/sdk/bsp/adapters/src/ad_nvms_direct.c +++ b/sdk/bsp/adapters/src/ad_nvms_direct.c @@ -40,6 +40,21 @@ static PRIVILEGED_DATA OS_MUTEX lock; #endif +// If an RTOS is in use we need to track the mutex and fail if it's taken. +// If no RTOS, then assume the flash is never locked. See ad_flash.c for +// similar lock tracking for flash writes and erasing. +#if (dg_configMEMFAULT == 1) +#ifndef OS_BAREMETAL +// Not in a header file. Implemented in qspi_coredump_save.h. +bool memfault_platform_saving_coredump(void); + +static uint32_t s_lock_mutex_tracker; +bool memfault_ad_nvms_direct_is_locked(void) { return s_lock_mutex_tracker != 0; } +#else +bool memfault_ad_nvms_direct_is_locked(void) { return 0; } +#endif +#endif + static int ad_nvms_direct_read(struct partition_t *part, uint32_t addr, uint8_t *buf, uint32_t size); static int ad_nvms_direct_write(struct partition_t *part, uint32_t addr, const uint8_t *buf, @@ -150,15 +165,29 @@ static inline uint32_t part_addr(const struct partition_t *part, uint32_t addr) static inline void part_lock(struct partition_t *part) { #ifndef OS_BAREMETAL +#if (dg_configMEMFAULT == 1) + if (!memfault_platform_saving_coredump()) { + OS_MUTEX_GET(lock, OS_MUTEX_FOREVER); + ++s_lock_mutex_tracker; + } +#else OS_MUTEX_GET(lock, OS_MUTEX_FOREVER); #endif +#endif } static inline void part_unlock(struct partition_t *part) { #ifndef OS_BAREMETAL +#if (dg_configMEMFAULT == 1) + if (!memfault_platform_saving_coredump()) { + if (s_lock_mutex_tracker) --s_lock_mutex_tracker; + OS_MUTEX_PUT(lock); + } +#else OS_MUTEX_PUT(lock); #endif +#endif } /* diff --git a/sdk/bsp/config/bsp_defaults.h b/sdk/bsp/config/bsp_defaults.h index 419ea9d..3a57397 100644 --- a/sdk/bsp/config/bsp_defaults.h +++ b/sdk/bsp/config/bsp_defaults.h @@ -2140,6 +2140,11 @@ * \{ */ +/* ----------------------------- Memfault configuration ------------------------------- */ +#ifndef dg_configMEMFAULT +#define dg_configMEMFAULT (1) +#endif + /* ----------------------------- Segger System View configuration ------------------------------- */ /** diff --git a/sdk/bsp/free_rtos/include/FreeRTOSConfig.h b/sdk/bsp/free_rtos/include/FreeRTOSConfig.h index 0270fb4..98d700d 100644 --- a/sdk/bsp/free_rtos/include/FreeRTOSConfig.h +++ b/sdk/bsp/free_rtos/include/FreeRTOSConfig.h @@ -95,6 +94,10 @@ #include "hw_watchdog.h" #include "sys_clock_mgr.h" +#if (dg_configMEMFAULT == 1) +#include "memfault/ports/freertos_trace.h" +#endif + extern uint32_t SystemCoreClock; #define configUSE_PREEMPTION 1 diff --git a/sdk/bsp/startup/startup_ARMCM0.S b/sdk/bsp/startup/startup_ARMCM0.S index e1dfa10..9a3a1a2 100644 --- a/sdk/bsp/startup/startup_ARMCM0.S +++ b/sdk/bsp/startup/startup_ARMCM0.S @@ -388,6 +388,8 @@ SVC_Handler: def_irq_handler XTAL16RDY_Handler def_irq_handler RESERVED31_Handler +/* Memfauilt has their own version */ +#if (dg_configMEMFAULT == 0) #if (dg_configCODE_LOCATION == NON_VOLATILE_IS_FLASH) .section text_retained #endif @@ -477,5 +479,5 @@ Wrong_SP: /* Wait for the WDog to hit or a debug session to start */ b . .size HardFault_Handler, . - HardFault_Handler - +#endif /* (dg_configMEMFAULT == 0) */ .end diff --git a/sdk/bsp/system/sys_man/sys_power_mgr.c b/sdk/bsp/system/sys_man/sys_power_mgr.c index fa7cd92..4244c94 100644 --- a/sdk/bsp/system/sys_man/sys_power_mgr.c +++ b/sdk/bsp/system/sys_man/sys_power_mgr.c @@ -2332,6 +2332,12 @@ bool pm_register_qspi_operation(OS_TASK handle, uint32_t addr, const uint8_t *bu { qspi_ops *op; +#if (dg_configMEMFAULT == 1) + extern bool memfault_platform_saving_coredump(void); + if (memfault_platform_saving_coredump()) { + return false; + } +#endif if (xSemaphorePM == NULL) { ASSERT_WARNING(0); return false; -- 2.31.0 ================================================ FILE: ports/dialog/da1468x/qspi_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A coredump storage implementation that uses the NVMS log partition for storing crash //! information. The Dialog NVMS layer is required. See custom_config_qspi.h to enable both //! the dg_configFLASH_ADAPTER and dg_configNVMS_ADAPTER features. //! //! The Dialog DA14683 USB dev board has the W25Q80EW QSPI Flash (8Bb/1MB). We need 64kB to store //! all of RAM (64kB). The program sector size is 256B and the erase block sizes are: 4kB, 32kB, //! 64kB, or the entire chip. The 4kB erase block is a "sector erase (0x20)" while the larger //! blocks are "block erase (0x52)". The part can handle up to a 104MHz SCLK and has 100k //! write-cycle durability. //! //! The details of the Flash chip are not important for this implementation since Dialog supplies //! a non-volatile memory layer, ad_nvms that takes care of all the details. //! //! To use QSPI for coredump storage the user needs to: //! 1. Add "#define MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH 1" to memfault_platform_config.h //! 1. call ad_nvms_init() before using the functions in this API //! 2. patch the Dialog SDK with the Memfault QSPI coredump storage patch, //! e.g. patch -p1 < /path/to/memfault_qspi_coredump_storage.patch //! 3. call memfault_platform_coredump_storage_boot() from your memfault_platform_boot() //! implementation #include "memfault/config.h" #if MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH #include #include "ad_nvms.h" #include "hw_cpm.h" #include "hw_qspi.h" #include "memfault/core/debug_log.h" #include "memfault/core/platform/core.h" #include "memfault/core/task_watchdog.h" #include "memfault/panics/assert.h" #include "memfault/panics/coredump.h" #include "memfault/ports/watchdog.h" #include "memfault/util/crc16.h" #include "partition_def.h" // Ensure Dialog NVMS setup as needed by this implementation. #if dg_configNVMS_ADAPTER == 0 #error "Port requires dg_configNVMS_ADAPTER 1" #endif #if dg_configFLASH_ADAPTER == 0 #error "Port requires dg_configFLASH_ADAPTER 1" #endif // We default to the NVMS_LOG_PART if the user doesn't specify a partition. #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_PARTITION #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_PARTITION NVMS_LOG_PART #endif // By default, the size used will match the size of the partition selected but we expose a // configuration option so a user can truncate to the beginning of the partition #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES UINT32_MAX #endif // The NVMS partition will appear to us as a block of storage from "address" zero to // partition_size - 1. For this reason we do not need to have explicit start and end addresses. typedef struct nvms_partition_t { nvms_t handle; //! Opaque handle to the partition used for coredumps. size_t size; //! The size in bytes of the partition we will use for coredumps. } nvms_partition_t; #define QSPI_COREDUMP_PART_INIT_MAGIC 0x45524f43 typedef struct { uint32_t magic; nvms_partition_t partition; uint32_t crc; //! Must be last element in the structure. } sQspiCoredumpPartitionInfo; static sQspiCoredumpPartitionInfo s_qspi_coredump_partition_info; //! Set-only flag to prevent PM deferred flash ops. Can't go into //! sQspiCoredumpPartitionInfo because of CRC and changing values of flag. static bool s_memfault_using_qspi; static uint32_t prv_get_partition_info_crc(void) { return memfault_crc16_compute(MEMFAULT_CRC16_INITIAL_VALUE, &s_qspi_coredump_partition_info, offsetof(sQspiCoredumpPartitionInfo, crc)); } static const nvms_partition_t *prv_get_core_partition(void) { if (s_qspi_coredump_partition_info.magic != QSPI_COREDUMP_PART_INIT_MAGIC) { return NULL; } return &s_qspi_coredump_partition_info.partition; } static const nvms_partition_t *prv_validate_and_get_core_partition(void) { const uint32_t crc = prv_get_partition_info_crc(); if (crc != s_qspi_coredump_partition_info.crc) { return NULL; } return prv_get_core_partition(); } // Error writing to flash - should never happen & likely detects a configuration error // Call the reboot handler which will halt the device if a debugger is attached and then reboot MEMFAULT_NO_OPT static void prv_coredump_writer_assert_and_reboot(int error_code) { memfault_platform_halt_if_debugging(); memfault_platform_reboot(); } // There is a non-trivial bit of setup for the NVRAM on Dialog's QSPI. To use QSPI for // coredump storage be sure to call this function early, e.g. from memfault_platform_boot(). // This is Dialog specific and not extern'd in a header file. // NOTE: The user must call ad_nvms_init() before calling this function. void memfault_platform_coredump_storage_boot(void) { partition_entry_t partition_info = { 0 }; const bool exists = ad_nvms_get_partition_info(MEMFAULT_PLATFORM_COREDUMP_STORAGE_PARTITION, &partition_info); if (!exists) { MEMFAULT_LOG_ERROR( "Could not locate partition for coredump storage, has ad_nvms_init() been called?"); return; } // ad_nvms_open() should not fail if above check succeeds. s_qspi_coredump_partition_info.partition.handle = ad_nvms_open(MEMFAULT_PLATFORM_COREDUMP_STORAGE_PARTITION); const size_t partition_size = ad_nvms_get_size(s_qspi_coredump_partition_info.partition.handle); s_qspi_coredump_partition_info.partition.size = MEMFAULT_MIN(partition_size, MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES); s_qspi_coredump_partition_info.magic = QSPI_COREDUMP_PART_INIT_MAGIC; s_qspi_coredump_partition_info.crc = prv_get_partition_info_crc(); } // This function allows sys_power_mgr.c:pm_register_qspi_operation() to // determine if it safe to defer QSPI operations to a worker thread or not. // This is Dialog specific and not extern'd in a header file. bool memfault_platform_saving_coredump(void) { return s_memfault_using_qspi; } // 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_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 if (memfault_ad_flash_is_locked() || memfault_ad_nvms_direct_is_locked()) { return false; } // Unconditionally feed the watchdog. If it's not in use this is benign. If // it is in use this will give us another 2.6s to complete the coredump save. if (memfault_software_watchdog_feed() != 0) { return false; } // Update task watchdog bookkeeping, if it's enabled memfault_task_watchdog_bookkeep(); // Signal to sys_power_mgr.c:pm_register_qspi_operation() that it should not // attempt to use deferred Flash ops at all from this point on. s_memfault_using_qspi = true; return true; } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { MEMFAULT_ASSERT(info); // we are about to perform a sequence of operations on coredump storage // sanity check that the memory holding the info is populated and not corrupted const nvms_partition_t *core_part = prv_validate_and_get_core_partition(); if (core_part == NULL) { *info = (sMfltCoredumpStorageInfo){ 0 }; return; } *info = (sMfltCoredumpStorageInfo){ .size = s_qspi_coredump_partition_info.partition.size, .sector_size = 0, // No longer used }; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t data_len) { MEMFAULT_ASSERT(data && data_len); const nvms_partition_t *core_part = prv_get_core_partition(); if (core_part == NULL) { return false; } if ((offset + data_len) > core_part->size) { return false; } return (size_t)ad_nvms_read(core_part->handle, offset, data, data_len) == data_len; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { // Failing to write while in a fault handler due to bad args? Take the normal reboot path. if (data == NULL || data_len == 0) { prv_coredump_writer_assert_and_reboot(-1); } const nvms_partition_t *core_part = prv_get_core_partition(); if (core_part == NULL) { return false; } // No support for truncation. if ((offset + data_len) > core_part->size) { return false; } return (size_t)ad_nvms_write(core_part->handle, offset, data, data_len) == data_len; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { const nvms_partition_t *core_part = prv_get_core_partition(); if (core_part == NULL) { return false; } if ((offset + erase_size) > core_part->size) { return false; } return ad_nvms_erase_region(core_part->handle, offset, erase_size); } void memfault_platform_coredump_storage_clear(void) { memfault_platform_coredump_storage_erase(0, ad_nvms_erase_size()); } #endif /* MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH */ ================================================ FILE: ports/dialog/da1468x/reset_stat_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the DA1468x's //! "Reset Reason" RESET_STAT_REG Register. //! //! More details can be found in the "CRG Register File" (RESET_STAT_REG)" //! section of the datasheet document for your specific chip. #include #include #include #include #include "hw_cpm.h" #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #include "memfault/ports/reboot_reason.h" #include "osal.h" #include "sdk_defs.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif // Use Dialog's way of locating variables to a no-init section. This allocation is very // Dialog specific so we can put it here within the chip implementation. __RETAINED_UNINIT MEMFAULT_ALIGNED(8) static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; // Called by the user application to get our reboot allocation registered with the core. void memfault_platform_reboot_tracking_boot(void) { // For a detailed explanation about reboot reason storage options check out the guide at: // https://mflt.io/reboot-reason-storage sResetBootupInfo reset_reason = { 0 }; memfault_reboot_reason_get(&reset_reason); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_reason); } // Private helper functions deal with the details of manipulating the CPU's // reset reason register. On some CPUs this is more involved. static uint32_t prv_reset_reason_get(void) { return CRG_TOP->RESET_STAT_REG; } static void prv_reset_reason_clear(uint32_t reset_reas_clear_mask) { (void)reset_reas_clear_mask; // Write zero to clear, not ones. CRG_TOP->RESET_STAT_REG = 0; } // Map chip-specific reset reasons to Memfault reboot reasons. Below is from the // DA14683 datasheet. // // Bit Mode Symbol Description Reset // --- ---- ---------------- ----------------------------------------------------- // 0 R/W PORESET_STAT Indicates that a PowerOn Reset has happened. // 1 R/W HWRESET_STAT Indicates that a HW Reset has happened. // 2 R/W SWRESET_STAT Indicates that a SW Reset has happened. // 3 R/W WDOGRESET_STAT Indicates that a Watchdog has happened. // * Note that it is also set when a POReset has happened. // 4 R/W SWD_HWRESET_STAT Indicates that a write to SWD_RESET_REG has happened. // * Note that it is also set when a POReset has happened. void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); // Consume the reset reason register leaving it cleared in HW. // RESETREAS is part of the always on power domain so it's sticky until a full reset occurs // Therefore, we clear the bits which were set so that don't get logged in future reboots as well const uint32_t reset_reason_reg = prv_reset_reason_get(); prv_reset_reason_clear(reset_reason_reg); // Assume "no bits set" implies POR. uint32_t reset_reason = kMfltRebootReason_PowerOnReset; // Note that POR also sets WD, and SWD bits so check order is important. if (reset_reason_reg & CRG_TOP_RESET_STAT_REG_PORESET_STAT_Msk) { // Important to check PORESET_STAT first. MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); reset_reason = kMfltRebootReason_PowerOnReset; } else if (reset_reason_reg & CRG_TOP_RESET_STAT_REG_SWD_HWRESET_STAT_Msk) { // True SWD reset since POR flag was not set. We just map it to SW reset. MEMFAULT_PRINT_RESET_INFO(" Debugger (SWD)"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_reason_reg & CRG_TOP_RESET_STAT_REG_WDOGRESET_STAT_Msk) { // True WD reset since POR flag was not set. MEMFAULT_PRINT_RESET_INFO(" Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_reason_reg & CRG_TOP_RESET_STAT_REG_SWRESET_STAT_Msk) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_reason_reg & CRG_TOP_RESET_STAT_REG_HWRESET_STAT_Msk) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } *info = (sResetBootupInfo){ .reset_reason_reg = reset_reason_reg, .reset_reason = reset_reason, }; } ================================================ FILE: ports/dialog/da1468x/wdog_software_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Dialog DA1468x implementation of the Memfault watchdog API. //! #include #include #include "hw_watchdog.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/ports/watchdog.h" // The Dialog DA1468x CPU has a simple watchdog implementation that contains an 8-bit counter, // pause/resume control, and a configurable "last chance" exception capability that routes // a watchdog timer expiration to NMI instead of RESET. Each tick of the watchdog counter is // fixed at 10.24ms for a total span of about 2.6s. // // Out of reset the CPU configures the watchdog to generate an NMI when the counter reaches // zero and, if not fed, generates a RESET 16 ticks later (~160ms). This allows us to // check some notion of system goodness and feed the watchdog or if the check indicates // a problem reset the CPU. For the simpler case where one watchdog timer interval should // never expire in normal operation the NMI vector gives us a chance to ensure there is // enough time to save coredump information to Flash if so configured. // // Note that the watchdog can be configured to simply reset when the timer reaches zero // but that is a one-way setting and cannot be undone without a CPU reset. // We default to the max count specified in the dg_configWDOG_RESET_VALUE and // initially use that as the reload value. The user can change this reload // value at by calling memfault_software_watchdog_update_timeout(). static struct { const uint32_t us_per_tick; const uint8_t max_count; uint8_t reload_value; } s_watchdog = { .us_per_tick = 10240, .max_count = dg_configWDOG_RESET_VALUE, .reload_value = dg_configWDOG_RESET_VALUE, }; int memfault_software_watchdog_enable(void) { hw_watchdog_unfreeze(); return 0; } int memfault_software_watchdog_disable(void) { return hw_watchdog_freeze() ? 0 : -1; } int memfault_software_watchdog_feed(void) { hw_watchdog_set_pos_val(s_watchdog.reload_value); return 0; } int memfault_software_watchdog_update_timeout(uint32_t timeout_ms) { // 256 ticks at 10240us/tick. Ensure requested time fits or fail. const uint32_t timeout_us = 1000 * timeout_ms; if (timeout_us > timeout_ms) { // Add one to ensure at least requested time after truncation. const uint32_t num_ticks = timeout_us / s_watchdog.us_per_tick + 1; // Enforce the max limit. if (num_ticks <= s_watchdog.max_count) { // Remember for subsequent feed calls. s_watchdog.reload_value = num_ticks; hw_watchdog_set_pos_val(num_ticks); return 0; } } return -1; } ================================================ FILE: ports/dialog/da1469x/fault-handlers.patch ================================================ From 6c2fc4eb5c013dc0bbc6a0b815bbd5004f8ffb84 Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Thu, 6 Jun 2024 15:16:03 -0400 Subject: [PATCH] Made fault handlers weak so they can be overridden. --- sdk/bsp/peripherals/src/hw_hard_fault.c | 6 +++--- sdk/bsp/startup/DA1469x/GCC/exception_handlers.S | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sdk/bsp/peripherals/src/hw_hard_fault.c b/sdk/bsp/peripherals/src/hw_hard_fault.c index 4498b55..22ded94 100644 --- a/sdk/bsp/peripherals/src/hw_hard_fault.c +++ b/sdk/bsp/peripherals/src/hw_hard_fault.c @@ -157,7 +157,7 @@ void HardFault_HandlerC(unsigned long *hardfault_args) #if (dg_configCODE_LOCATION == NON_VOLATILE_IS_FLASH) __attribute__((section("text_retained"))) #endif -void MemManage_Handler(void) +__WEAK void MemManage_Handler(void) { volatile uint8_t mem_fault_status_reg; volatile uint32_t mem_fault_addr __UNUSED; @@ -179,7 +179,7 @@ void MemManage_Handler(void) #if (dg_configCODE_LOCATION == NON_VOLATILE_IS_FLASH) __attribute__((section("text_retained"))) #endif -void BusFault_Handler(void) +__WEAK void BusFault_Handler(void) { volatile uint8_t bus_fault_status_reg; volatile uint32_t bus_fault_addr __UNUSED; @@ -202,7 +202,7 @@ void BusFault_Handler(void) #if (dg_configCODE_LOCATION == NON_VOLATILE_IS_FLASH) __attribute__((section("text_retained"))) #endif -void UsageFault_Handler(void) +__WEAK void UsageFault_Handler(void) { volatile uint16_t usage_fault_status_reg __UNUSED; diff --git a/sdk/bsp/startup/DA1469x/GCC/exception_handlers.S b/sdk/bsp/startup/DA1469x/GCC/exception_handlers.S index c42fb66..639ef55 100644 --- a/sdk/bsp/startup/DA1469x/GCC/exception_handlers.S +++ b/sdk/bsp/startup/DA1469x/GCC/exception_handlers.S @@ -250,6 +250,7 @@ Reset_Handler: .align 2 .thumb .thumb_func + .weak NMI_Handler .globl NMI_Handler .type NMI_Handler, %function NMI_Handler: @@ -268,6 +269,7 @@ NMI_stacking_using_MSP: .align 2 .thumb .thumb_func + .weak HardFault_Handler .globl HardFault_Handler .type HardFault_Handler, %function HardFault_Handler: ================================================ FILE: ports/dialog/da1469x/freertos-config.patch ================================================ From 1d3628ec06e9a7960505516c591d2b0b12a0fc57 Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Mon, 26 Jun 2021 11:44:21 -0700 Subject: [PATCH] Added memfault specific configuration to freertos config header. --- sdk/free_rtos/include/FreeRTOSConfig.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sdk/free_rtos/include/FreeRTOSConfig.h b/sdk/free_rtos/include/FreeRTOSConfig.h index e0ba35b..87bb5c9 100644 --- a/sdk/free_rtos/include/FreeRTOSConfig.h +++ b/sdk/free_rtos/include/FreeRTOSConfig.h @@ -46,6 +46,10 @@ #include "hw_watchdog.h" #include "../../system/sys_man/sys_clock_mgr_internal.h" +#if defined dg_configUSE_MEMFAULT +#include "memfault/ports/freertos_trace.h" +#endif + extern uint32_t SystemCoreClock; #define configUSE_PREEMPTION 1 @@ -152,12 +156,17 @@ to exclude the API function. */ #define INCLUDE_xTaskGetCurrentTaskHandle 1 #endif /* (dg_configSYSTEMVIEW == 1) */ +#if defined dg_configUSE_MEMFAULT +void vAssertCalled(const char *file, int line); +#define configASSERT(x) do { if ((x) == 0) vAssertCalled( __FILE__, __LINE__ ); } while (0); +#else /* Normal assert() semantics without relying on the provision of an assert.h header file. */ #if (dg_configIMAGE_SETUP == DEVELOPMENT_MODE) #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); hw_watchdog_freeze(); do {} while(1); } #else #define configASSERT( x ) if( ( x ) == 0 ) { } #endif +#endif /* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS standard names - or at least those used in the unmodified vector table. */ ================================================ FILE: ports/dialog/da1469x/gnu-build-id.patch ================================================ From 41018912a63e4c2cb859434e3e459c0412e172ec Mon Sep 17 00:00:00 2001 From: Memfault Inc Date: Mon, 26 Jun 2021 11:28:57 -0700 Subject: [PATCH] Added support for capturing GNU Build Id --- sdk/bsp/ldscripts/ble_projects/sections_da1469x.ld.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sdk/bsp/ldscripts/ble_projects/sections_da1469x.ld.h b/sdk/bsp/ldscripts/ble_projects/sections_da1469x.ld.h index d392ab5..d3d3377 100644 --- a/sdk/bsp/ldscripts/ble_projects/sections_da1469x.ld.h +++ b/sdk/bsp/ldscripts/ble_projects/sections_da1469x.ld.h @@ -219,6 +219,12 @@ SECTIONS __zero_table_end__ = .; } > ROM + .note.gnu.build-id : + { + __start_gnu_build_id_start = .; + KEEP(*(.note.gnu.build-id)) + } > ROM + __etext = .; /* ================================================ FILE: ports/dialog/da1469x/memfault_diagnostic_service.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port of the Memfault Diagnostic GATT Service (MDS) to the DA1469x SDK. //! See mds.h header for more details #include "memfault/ports/ble/mds.h" #if defined(CONFIG_USE_BLE_SERVICES) #if defined(dg_configUSE_MEMFAULT) // clang-format off #include #include #include #include //! Note: not sorting because header order matters #include "ble_gap.h" #include "osal.h" #include "attm_cfg.h" #include "sdk_queue.h" #include "ble_att.h" #include "ble_storage.h" #include "ble_bufops.h" #include "ble_common.h" #include "ble_gatts.h" #include "ble_gattc.h" #include "ble_uuid.h" #include "ble_service.h" #include "FreeRTOS.h" #include "timers.h" #include "memfault/components.h" // clang-format on #if !defined(MEMFAULT_PROJECT_KEY) #error \ "Memfault Project Key not configured. Please visit https://goto.memfault.com/create-key/da1469x" #endif //! Profile makes use of the timer task for checking to see if there is any data to send. By //! default, the FreeRTOS timer task depth is the absolute minimum configMINIMAL_STACK_SIZE (100), //! so we need a little more room to prevent any stack overflows MEMFAULT_STATIC_ASSERT( configTIMER_TASK_STACK_DEPTH >= 256, "configTIMER_TASK_STACK_DEPTH must be >= 256, update your custom_config_*.h"); //! ATT Read, Write & Notification responses will have a 3 byte overhead //! (1 Byte for Opcode + 2 bytes for length) #define MDS_ATT_HEADER_OVERHEAD 3 //! Note: Attributes that are greater than the MTU size can be returned via long attribute reads //! but the maximum allowed attribute value is 512 bytes. (See "3.2.9 Long attribute values" of //! BLE v5.3 Core specification). In practice, all values returned by MDS should be much smaller //! than this. #define MDS_MAX_READ_LEN (512) //! The interval to check for whether or not there is any new data to send #ifndef MDS_POLL_INTERVAL_MS #define MDS_POLL_INTERVAL_MS (60 * 1000) #endif #ifndef MDS_MAX_DATA_URI_LENGTH #define MDS_MAX_DATA_URI_LENGTH 64 #endif #ifndef MDS_URI_BASE //! i.e https://chunks.memfault.com/api/v0/chunks/ #define MDS_URI_BASE \ (MEMFAULT_HTTP_APIS_DEFAULT_SCHEME "://" MEMFAULT_HTTP_CHUNKS_API_HOST "/api/v0/chunks/") #endif #ifndef MDS_DYNAMIC_ACCESS_CONTROL #define MDS_DYNAMIC_ACCESS_CONTROL 0 #endif //! Payload returned via a read to "MDS Supported Features Characteristic" const static uint8_t s_mds_supported_features[] = { // no feature additions since the first spin of the profile 0x0 }; //! Valid SNs used when sending data are 0-31 #define MDS_TOTAL_SEQ_NUMBERS 32 typedef enum { kMdsDataExportMode_StreamingDisabled = 0x00, kMdsDataExportMode_FullStreamingEnabled = 0x01, } eMdsDataExportMode; typedef MEMFAULT_PACKED_STRUCT { // bits 5-7: rsvd for future use // bits 0-4: sequence number uint8_t hdr; uint8_t chunk[]; } sMdsDataExportPayload; typedef struct { ble_service_t svc; uint16_t supported_features_h; uint16_t data_uri_h; uint16_t auth_h; uint16_t device_id_h; uint16_t chunk_val_h; uint16_t chunk_cccd_h; // Note: MDS only allows one active subscriber at any given time // // If a second connection attempts to subscribe while a first connection is already active, the // second request will be ignored. struct { bool active; eMdsDataExportMode mode; uint8_t seq_num; // current sequence number to use uint16_t conn_idx; } subscriber; sMdsDataExportPayload *payload; size_t chunk_len; TimerHandle_t timer; } md_service_t; typedef enum { kMdsAppError_InvalidLength = ATT_ERROR_APPLICATION_ERROR, kMdsAppError_ClientAlreadySubscribed, kMdsAppError_InsufficientLength, kMdsAppError_ClientNotSubscribed, } eMdsAppError; static md_service_t *s_mds; #if !MDS_DYNAMIC_ACCESS_CONTROL //! See mds.h header for more details, we recommend end user override this behavior for production //! applications MEMFAULT_WEAK bool mds_access_enabled(uint16_t connection_handle) { return true; } #endif static void prv_handle_disconnected_evt(ble_service_t *svc, const ble_evt_gap_disconnected_t *evt) { md_service_t *mds = (md_service_t *)svc; if (mds->subscriber.active && (mds->subscriber.conn_idx == evt->conn_idx)) { mds->subscriber.active = false; mds->subscriber.conn_idx = 0; mds->subscriber.seq_num = 0; mds->subscriber.mode = kMdsDataExportMode_StreamingDisabled; } xTimerStop(mds->timer, 0); } static void prv_try_notify(md_service_t *mds, uint16_t conn_idx) { if ((!mds->subscriber.active) || (mds->subscriber.conn_idx != conn_idx)) { // caller has _not_ subscribed for chunk notifications so we do // not want to send the info to them return; } if (mds->subscriber.mode == kMdsDataExportMode_StreamingDisabled) { // client has subscribed but not yet enabled data export return; } uint16_t mtu_size = 0; ble_error_t rv = ble_gattc_get_mtu(conn_idx, &mtu_size); if (rv != BLE_STATUS_OK) { return; } if ((mds->payload == NULL) && memfault_packetizer_data_available()) { mds->payload = OS_MALLOC(mtu_size - MDS_ATT_HEADER_OVERHEAD); if (mds->payload != NULL) { mds->payload->hdr = mds->subscriber.seq_num & 0x1f; mds->chunk_len = mtu_size - MDS_ATT_HEADER_OVERHEAD - sizeof(*mds->payload); memfault_packetizer_get_chunk(&mds->payload->chunk[0], &mds->chunk_len); } } if (mds->chunk_len != 0) { rv = ble_gatts_send_event(conn_idx, mds->chunk_val_h, GATT_EVENT_NOTIFICATION, mds->chunk_len + sizeof(*mds->payload), mds->payload); if (rv == BLE_STATUS_OK) { mds->subscriber.seq_num = (mds->subscriber.seq_num + 1) % MDS_TOTAL_SEQ_NUMBERS; // Note: No need to schedule a retry since we will pump more data when the sent callback is // invoked for the current notification OS_FREE(mds->payload); mds->payload = NULL; mds->chunk_len = 0; return; } } // There's no more data, we were unable to allocate a buffer to hold a chunk or our send request // failed. Let's check to see if there is any more data / try again in 60 seconds. xTimerChangePeriod(mds->timer, pdMS_TO_TICKS(MDS_POLL_INTERVAL_MS), 0); xTimerStart(mds->timer, 0); } //! A timer service run that periodically checks to see if there //! is any new data to send to Memfault while connected static void prv_mds_timer_callback(MEMFAULT_UNUSED TimerHandle_t handle) { if (s_mds == NULL) { // unexpected ... suggests user destroyed MDS while a connection was actively subscribed to it. return; } uint8_t num_conn; uint16_t *conn_idx = NULL; ble_gap_get_connected(&num_conn, &conn_idx); while (num_conn--) { prv_try_notify(s_mds, conn_idx[num_conn]); } if (conn_idx) { OS_FREE(conn_idx); } } static void prv_handle_read_req(ble_service_t *svc, const ble_evt_gatts_read_req_t *evt) { if (!mds_access_enabled(evt->conn_idx)) { ble_gatts_read_cfm(evt->conn_idx, evt->handle, ATT_ERROR_READ_NOT_PERMITTED, 0, NULL); return; } const md_service_t *mds = (md_service_t *)svc; const void *value = NULL; size_t length = 0; char uri[MDS_MAX_DATA_URI_LENGTH]; struct MemfaultDeviceInfo info; if (evt->handle == mds->supported_features_h) { value = &s_mds_supported_features; length = sizeof(s_mds_supported_features); } else if (evt->handle == mds->data_uri_h) { memfault_platform_get_device_info(&info); strncpy(uri, MDS_URI_BASE, sizeof(uri) - 1); uri[sizeof(uri) - 1] = '\0'; const size_t uri_base_length = strlen(MDS_URI_BASE); const size_t device_id_length = strlen(info.device_serial); const size_t total_length = uri_base_length + device_id_length; if (total_length > (sizeof(uri) - 1)) { ble_gatts_read_cfm(evt->conn_idx, evt->handle, kMdsAppError_InsufficientLength, 0, NULL); return; } strcpy(uri, MDS_URI_BASE); strcpy(&uri[uri_base_length], info.device_serial); uri[sizeof(uri) - 1] = '\0'; value = uri; length = total_length; } else if (evt->handle == mds->auth_h) { value = "Memfault-Project-Key:" MEMFAULT_PROJECT_KEY; length = strlen(value); } else if (evt->handle == mds->device_id_h) { memfault_platform_get_device_info(&info); value = info.device_serial; length = strlen(value); } else { ble_gatts_read_cfm(evt->conn_idx, evt->handle, ATT_ERROR_READ_NOT_PERMITTED, 0, NULL); return; } if (length > MDS_MAX_READ_LEN) { // response exceeds minimum mtu size in length ble_gatts_read_cfm(evt->conn_idx, evt->handle, kMdsAppError_InvalidLength, 0, NULL); return; } if (evt->offset > length) { length = 0; } else { length = length - evt->offset; value = ((uint8_t *)value) + evt->offset; } ble_gatts_read_cfm(evt->conn_idx, evt->handle, ATT_ERROR_OK, length, value); } static att_error_t prv_handle_cccd_write(md_service_t *mds, uint16_t conn_idx, uint16_t offset, uint16_t length, const uint8_t *value) { if (offset != 0) { return ATT_ERROR_ATTRIBUTE_NOT_LONG; } if (length != sizeof(uint16_t)) { return kMdsAppError_InvalidLength; } const uint16_t cccd = get_u16(value); const bool subscribe_for_notifs = ((cccd & GATT_CCC_NOTIFICATIONS) != 0); if (!mds->subscriber.active) { // NB: we expect caller to subscribe for notifications each time they connect // so don't persist the mode across disconnects _and_ we only allow one // active subscription at a time. mds->subscriber.active = subscribe_for_notifs; mds->subscriber.conn_idx = conn_idx; } else if (mds->subscriber.conn_idx == conn_idx) { // handle case where client is subscribed (active) and has unsubscribed or re-subscribed for // some reason mds->subscriber.active = subscribe_for_notifs; } else { // only one client can be subscribed at any given time return kMdsAppError_ClientAlreadySubscribed; } return ATT_ERROR_OK; } static att_error_t prv_handle_data_export_write(md_service_t *mds, uint16_t conn_idx, uint16_t offset, uint16_t length, const uint8_t *value) { if (offset != 0) { return ATT_ERROR_ATTRIBUTE_NOT_LONG; } if (length != sizeof(uint8_t)) { return kMdsAppError_InvalidLength; } if ((!mds->subscriber.active) || (mds->subscriber.conn_idx != conn_idx)) { return kMdsAppError_ClientNotSubscribed; } const eMdsDataExportMode cmd = (eMdsDataExportMode)get_u8(value); switch (cmd) { case kMdsDataExportMode_StreamingDisabled: case kMdsDataExportMode_FullStreamingEnabled: break; default: return ATT_ERROR_REQUEST_NOT_SUPPORTED; } mds->subscriber.mode = cmd; prv_try_notify(mds, conn_idx); return ATT_ERROR_OK; } static void prv_handle_write_req(ble_service_t *svc, const ble_evt_gatts_write_req_t *evt) { if (!mds_access_enabled(evt->conn_idx)) { ble_gatts_read_cfm(evt->conn_idx, evt->handle, ATT_ERROR_WRITE_NOT_PERMITTED, 0, NULL); return; } md_service_t *mds = (md_service_t *)svc; att_error_t status = ATT_ERROR_ATTRIBUTE_NOT_FOUND; if (evt->handle == mds->chunk_cccd_h) { status = prv_handle_cccd_write(mds, evt->conn_idx, evt->offset, evt->length, evt->value); } else if (evt->handle == mds->chunk_val_h) { status = prv_handle_data_export_write(mds, evt->conn_idx, evt->offset, evt->length, evt->value); } ble_gatts_write_cfm(evt->conn_idx, evt->handle, status); } static void prv_cleanup_service(ble_service_t *svc) { md_service_t *mds = (md_service_t *)svc; if (mds->payload != NULL) { OS_FREE(mds->payload); } if (mds->timer != NULL) { xTimerDelete(mds->timer, portMAX_DELAY); } OS_FREE(mds); s_mds = NULL; } static void prv_handle_event_sent(ble_service_t *svc, const ble_evt_gatts_event_sent_t *evt) { prv_try_notify((md_service_t *)svc, evt->conn_idx); } void *mds_boot(void) { md_service_t *mds = OS_MALLOC(sizeof(*mds)); memset(mds, 0, sizeof(*mds)); mds->timer = xTimerCreate("mds_timer", pdMS_TO_TICKS(1000), pdFALSE, /* auto reload */ (void *)NULL, prv_mds_timer_callback); mds->svc.disconnected_evt = prv_handle_disconnected_evt; mds->svc.read_req = prv_handle_read_req; mds->svc.write_req = prv_handle_write_req; mds->svc.event_sent = prv_handle_event_sent; mds->svc.cleanup = prv_cleanup_service; const uint16_t num_includes = 0; const uint16_t num_characteristics = 6; const uint16_t num_descriptors = 1; uint16_t num_attr = ble_gatts_get_num_attr(num_includes, num_characteristics, num_descriptors); att_uuid_t uuid; ble_uuid_from_string("54220000-f6a5-4007-a371-722f4ebd8436", &uuid); ble_gatts_add_service(&uuid, GATT_SERVICE_PRIMARY, num_attr); ble_uuid_from_string("54220001-f6a5-4007-a371-722f4ebd8436", &uuid); ble_gatts_add_characteristic(&uuid, GATT_PROP_READ, ATT_PERM_RW, sizeof(uint8_t), GATTS_FLAG_CHAR_READ_REQ, NULL, &mds->supported_features_h); ble_uuid_from_string("54220002-f6a5-4007-a371-722f4ebd8436", &uuid); ble_gatts_add_characteristic(&uuid, GATT_PROP_READ, ATT_PERM_RW, sizeof(uint8_t), GATTS_FLAG_CHAR_READ_REQ, NULL, &mds->device_id_h); ble_uuid_from_string("54220003-f6a5-4007-a371-722f4ebd8436", &uuid); ble_gatts_add_characteristic(&uuid, GATT_PROP_READ, ATT_PERM_RW, sizeof(uint8_t), GATTS_FLAG_CHAR_READ_REQ, NULL, &mds->data_uri_h); ble_uuid_from_string("54220004-f6a5-4007-a371-722f4ebd8436", &uuid); ble_gatts_add_characteristic(&uuid, GATT_PROP_READ, ATT_PERM_RW, sizeof(uint8_t), GATTS_FLAG_CHAR_READ_REQ, NULL, &mds->auth_h); ble_uuid_from_string("54220005-f6a5-4007-a371-722f4ebd8436", &uuid); ble_gatts_add_characteristic(&uuid, GATT_PROP_NOTIFY | GATT_PROP_WRITE, ATT_PERM_RW, sizeof(uint8_t), GATTS_FLAG_CHAR_READ_REQ, NULL, &mds->chunk_val_h); ble_uuid_create16(UUID_GATT_CLIENT_CHAR_CONFIGURATION, &uuid); ble_gatts_add_descriptor(&uuid, ATT_PERM_RW, 2, 0, &mds->chunk_cccd_h); ble_gatts_register_service(&mds->svc.start_h, &mds->supported_features_h, &mds->device_id_h, &mds->data_uri_h, &mds->auth_h, &mds->chunk_val_h, &mds->chunk_cccd_h, 0); mds->svc.end_h = mds->svc.start_h + num_attr; ble_service_add(&mds->svc); s_mds = mds; return &mds->svc; } #endif /* defined(dg_configUSE_MEMFAULT) */ #endif /* defined(CONFIG_USE_BLE_SERVICES) */ ================================================ FILE: ports/dialog/da1469x/memfault_platform_core.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/components.h" #include "memfault/ports/freertos.h" #include "memfault/ports/reboot_reason.h" #include "sdk_defs.h" #ifndef MEMFAULT_EVENT_STORAGE_RAM_SIZE #define MEMFAULT_EVENT_STORAGE_RAM_SIZE 1024 #endif int memfault_platform_boot(void) { memfault_freertos_port_boot(); memfault_platform_reboot_tracking_boot(); memfault_build_info_dump(); memfault_device_info_dump(); static uint8_t s_event_storage[MEMFAULT_EVENT_STORAGE_RAM_SIZE]; const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(evt_storage); memfault_reboot_tracking_collect_reset_info(evt_storage); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); return 0; } MEMFAULT_NORETURN void memfault_platform_reboot(void) { SWRESET; MEMFAULT_UNREACHABLE; } ================================================ FILE: ports/dialog/da1469x/memfault_platform_coredump_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/config.h" #include "memfault/core/math.h" #include "memfault/panics/platform/coredump.h" #include "sdk_defs.h" #ifndef MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY #define MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY 1 #endif #if !MEMFAULT_PLATFORM_COREDUMP_STORAGE_REGIONS_CUSTOM const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { static sMfltCoredumpRegion s_coredump_regions[1]; int region_idx = 0; // first, capture the stack that was active at the time of crash const size_t active_stack_size_to_collect = MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT; s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( crash_info->stack_address, memfault_platform_sanitize_address_range( crash_info->stack_address, active_stack_size_to_collect)); *num_regions = region_idx; return &s_coredump_regions[0]; } #endif size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { const uint32_t ram_start = MEMORY_SYSRAM_BASE; const uint32_t ram_end = MEMORY_SYSRAM_END; if ((uint32_t)start_addr >= ram_start && (uint32_t)start_addr < ram_end) { return MEMFAULT_MIN(desired_size, ram_end - (uint32_t)start_addr); } return 0; } ================================================ FILE: ports/dialog/da1469x/memfault_platform_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions to use space //! on the external SPI flash connected to the DA1469xx for coredump capture //! //! By default, coredumps are saved in the log partition of NVMS Storage but //! the location can be overridden by configuring the macros below. #include "memfault/config.h" #if MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH #include "bsp_memory_defaults.h" #include "memfault/panics/platform/coredump.h" #include "qspi_automode.h" // We default to the NVMS_LOG_PART if the user doesn't specify a partition. #ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR #define MEMFAULT_COREDUMP_STORAGE_START_ADDR NVMS_LOG_PART_START #endif #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES NVMS_LOG_PART_SIZE #endif #if (MEMFAULT_COREDUMP_STORAGE_START_ADDR == NVMS_LOG_PART_START) && \ (MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES > NVMS_LOG_PART_SIZE) #error "MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES exceeds size of NVMS_LOG_PART" #endif #if ((MEMFAULT_COREDUMP_STORAGE_START_ADDR % FLASH_SECTOR_SIZE) != 0) #error "MEMFAULT_COREDUMP_STORAGE_START_ADDR should be aligned by the sector size" #endif #if ((MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES % FLASH_SECTOR_SIZE) != 0) #error "MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES should be aligned by the sector size" #endif //! Note: Backgrounded flash ops rely on FreeRTOS constructs being available and therefore can not //! be used while saving a coredump from a fault handler. To save coredumps and use background //! flash ops, sdk/bsp/memory/src/qspi_automode.c in the DA1469x SDK will need to be patched #if dg_configDISABLE_BACKGROUND_FLASH_OPS == 0 #error "dg_configDISABLE_BACKGROUND_FLASH_OPS must be set to 1 in custom_config_*.h" #endif void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = MEMFAULT_PLATFORM_COREDUMP_STORAGE_MAX_SIZE_BYTES, .sector_size = FLASH_SECTOR_SIZE, }; } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } const uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; uint32_t bytes_read = qspi_automode_read(address, (uint8_t *)data, read_len); return (bytes_read == read_len); } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } const size_t sector_size = FLASH_SECTOR_SIZE; if ((offset % sector_size) != 0) { return false; } for (size_t sector = offset; sector < erase_size; sector += sector_size) { const uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + sector; qspi_automode_erase_flash_sector(address); } return true; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if (!prv_op_within_flash_bounds(offset, data_len)) { return false; } uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; uint32_t bytes_written = 0; while (bytes_written < data_len) { bytes_written += qspi_automode_write_flash_page( address + bytes_written, &((const uint8_t *)data)[bytes_written], data_len - bytes_written); } return true; } // Note: This function is called while the system is running once the coredump has been read. We // clear the first word in this scenario to avoid blocking the system for a long time on an erase. void memfault_platform_coredump_storage_clear(void) { uint32_t clear_word = 0x0; memfault_platform_coredump_storage_write(0, &clear_word, sizeof(clear_word)); } #endif /* MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH */ ================================================ FILE: ports/dialog/da1469x/memfault_platform_debug_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Map memfault logging dependencies to da145xx arch_printf implementation. #include #include #include "memfault/config.h" #include "memfault/core/platform/debug_log.h" #ifndef MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES #define MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES (128) #endif void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { #if defined(CONFIG_RETARGET) || defined(CONFIG_RTT) va_list args; va_start(args, fmt); char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; vsnprintf(log_buf, sizeof(log_buf), fmt, args); const char *lvl_str = NULL; switch (level) { case kMemfaultPlatformLogLevel_Debug: lvl_str = "D"; break; case kMemfaultPlatformLogLevel_Info: lvl_str = "I"; break; case kMemfaultPlatformLogLevel_Warning: lvl_str = "W"; break; case kMemfaultPlatformLogLevel_Error: lvl_str = "E"; break; default: break; } if (lvl_str) { printf("[%s] MFLT: %s\r\n", lvl_str, log_buf); } va_end(args); #endif } void memfault_platform_log_raw(const char *fmt, ...) { #if defined(CONFIG_RETARGET) || defined(CONFIG_RTT) va_list args; va_start(args, fmt); vprintf(fmt, args); printf("\n"); va_end(args); #endif } ================================================ FILE: ports/dialog/da1469x/reset_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! @file //! //! Map DA1469x reboot reasons to memfault definitions #include "DA1469xAB.h" #include "memfault/ports/reboot_reason.h" #include "sdk_defs.h" __RETAINED_UNINIT static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; void memfault_platform_reboot_tracking_boot(void) { sResetBootupInfo reset_info = { 0 }; memfault_reboot_reason_get(&reset_info); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); } void memfault_reboot_reason_get(sResetBootupInfo *info) { eMemfaultRebootReason reset_reason; uint32_t reset_cause = CRG_TOP->RESET_STAT_REG; // Multiple reset bits may be set in different cases. The order these are checked is important // Checks should be done from POR -> HW -> SW, and from most to least specific // First, check if reset bits are 0 as this indicates return from deepsleep // Next, check POR bit. POR bit set for power issue (brown-out, vdd drop, batt drop), or POR event // NB: If POR occurs, all bits are set // Next, check for SWD_HWRESET bit, special type of hardware reset // Next, check for one of watchdog bits (CMAC or WDOG) // Next, check for HW_RESET, indicates reset pin was asserted // Finally, check for SW_RESET // See exception_handlers.S:Wakeup_Reset_Handler in DA1469x SDK, Figure 26 in datasheet if (reset_cause == 0) { reset_reason = kMfltRebootReason_DeepSleep; } else if (reset_cause & CRG_TOP_RESET_STAT_REG_PORESET_STAT_Msk) { reset_reason = kMfltRebootReason_PowerOnReset; } else if (reset_cause & CRG_TOP_RESET_STAT_REG_SWD_HWRESET_STAT_Msk) { reset_reason = kMfltRebootReason_DebuggerHalted; } else if (reset_cause & (CRG_TOP_RESET_STAT_REG_CMAC_WDOGRESET_STAT_Msk | CRG_TOP_RESET_STAT_REG_WDOGRESET_STAT_Msk)) { reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & CRG_TOP_RESET_STAT_REG_HWRESET_STAT_Msk) { reset_reason = kMfltRebootReason_PinReset; } else if (reset_cause & CRG_TOP_RESET_STAT_REG_SWRESET_STAT_Msk) { reset_reason = kMfltRebootReason_SoftwareReset; } else { reset_reason = kMfltRebootReason_Unknown; } #if MEMFAULT_REBOOT_REASON_CLEAR CRG_TOP->RESET_STAT_REG = 0; #endif *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/emlib/README.md ================================================ # Silicon Labs emlib Port This port adds implementations for coredump storage, CLI, reboot reasons, and watchdog features using the Gecko SDK/emblib. ## Compatibility The port has been built against v3.0 of the Gecko SDK. The port has been built and tested against v4.3.0 using SimplicityStudio. ## Features The port provides the following features: - Coredump storage using a linker-defined region in internal Flash - Reboot reason implementation for collecting reboot events - An integration with the Watchdog peripheral to capture watchdog-triggered traces - A Gecko SDK compatible CLI to demo Memfault features ## Adding To Your Project The simplest way to add this to your project is to do the following: 1. Add the source files in `ports/emblib` to your build system 2. Add the include directory at `ports/include` ## CLI Component The CLI component requires building with SimplicityStudio for full functionality. After adding the file as source to your project, you will need to add the following snippets to your .slcp file: ```yaml # In component section component: - instance: [example] id: cli ``` ```yaml # In template_contribution template_contribution: - condition: [cli] name: cli_group priority: 0 value: { name: mflt } - condition: [cli] name: cli_group priority: 0 value: { name: test, id: mflt_test_root, group: mflt } - condition: [cli] name: cli_command priority: 0 value: { name: export, group: mflt, handler: memfault_emlib_cli_export, help: Export data as base64 chunks, } - condition: [cli] name: cli_command priority: 0 value: { name: get_core, group: mflt, handler: memfault_emlib_cli_get_core, help: Get current coredump state, } - condition: [cli] name: cli_command priority: 0 value: { name: clear_core, group: mflt, handler: memfault_emlib_cli_clear_core, help: Clear coredump from storage, } - condition: [cli] name: cli_command priority: 0 value: { name: get_device_info, group: mflt, handler: memfault_emlib_cli_get_device_info, help: Read device info structure, } - condition: [cli] name: cli_command priority: 0 value: { name: assert, group: mflt_test_root, handler: memfault_emlib_cli_assert, help: Triggers assert to collect a coredump, } - condition: [cli] name: cli_command priority: 0 value: { name: busfault, group: mflt_test_root, handler: memfault_emlib_cli_busfault, help: Triggers busfault to collect a coredump, } - condition: [cli] name: cli_command priority: 0 value: { name: hang, group: mflt_test_root, handler: memfault_emlib_cli_hang, help: Triggers hang to collect a coredump, } - condition: [cli] name: cli_command priority: 0 value: { name: hardfault, group: mflt_test_root, handler: memfault_emlib_cli_hardfault, help: Triggers hardfault to collect a coredump, } - condition: [cli] name: cli_command priority: 0 value: { name: memmanage, group: mflt_test_root, handler: memfault_emlib_cli_memmanage, help: Triggers memory management fault to collect a coredump, } - condition: [cli] name: cli_command priority: 0 value: { name: usagefault, group: mflt_test_root, handler: memfault_emlib_cli_usagefault, help: Triggers usage fault to collect a coredump, } - condition: [cli] name: cli_command priority: 0 value: { name: reboot, group: mflt_test_root, handler: memfault_emlib_cli_reboot, help: Triggers reboot to test reboot reason tracking, } - condition: [cli] name: cli_command priority: 0 value: { name: heartbeat, group: mflt_test_root, handler: memfault_emlib_cli_heartbeat, help: Trigger capture of heartbeat metrics, } - condition: [cli] name: cli_command priority: 0 value: { name: logs, group: mflt_test_root, handler: memfault_emlib_cli_logs, help: Writes logs to internal buffers, } - condition: [cli] name: cli_command priority: 0 value: { name: log_capture, group: mflt_test_root, handler: memfault_emlib_cli_log_capture, help: Serializes current log buffer contents, } - condition: [cli] name: cli_command priority: 0 value: { name: trace, group: mflt_test_root, handler: memfault_emlib_cli_trace, help: Captures a trace event, } ``` ================================================ FILE: ports/emlib/memfault_demo_cli.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Adds a basic set of commands for interacting with Memfault SDK #include #include "memfault/components.h" #ifdef SL_COMPONENT_CATALOG_PRESENT #include "sl_component_catalog.h" #endif // SL_COMPONENT_CATALOG_PRESENT #ifdef SL_CATALOG_CLI_PRESENT #include "sl_cli.h" #endif // SL_CATALOG_CLI_PRESENT #ifdef SL_CATALOG_CLI_PRESENT static void prv_adjust_arg_vars(sl_cli_command_arg_t *arguments, int *argc, void ***argv) { *argc = arguments->argc; *argv = arguments->argv; if (arguments->arg_ofs) { *argc = arguments->argc - arguments->arg_ofs + 1; *argv = &arguments->argv[arguments->arg_ofs + 1]; } } void memfault_emlib_cli_clear_core(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_clear_core(argc, (char **)argv); } void memfault_emlib_cli_get_core(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_get_core(argc, (char **)argv); } void memfault_emlib_cli_logs(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_test_log(argc, (char **)argv); } void memfault_emlib_cli_log_capture(sl_cli_command_arg_t *arguments) { (void)arguments; memfault_demo_cli_cmd_trigger_logs(0, NULL); } void memfault_emlib_cli_get_device_info(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_get_device_info(argc, (char **)argv); } void memfault_emlib_cli_export(sl_cli_command_arg_t *arguments) { (void)arguments; memfault_data_export_dump_chunks(); } void memfault_emlib_cli_trace(sl_cli_command_arg_t *arguments) { // For more information on user-defined error reasons, see // the MEMFAULT_TRACE_REASON_DEFINE macro in trace_reason_user.h . MEMFAULT_TRACE_EVENT_WITH_LOG(MemfaultCli_Test, "Num args: %d", arguments->argc); MEMFAULT_LOG_DEBUG("Trace Event Generated!"); } void memfault_emlib_cli_heartbeat(sl_cli_command_arg_t *arguments) { (void)arguments; memfault_metrics_heartbeat_debug_trigger(); } void memfault_emlib_cli_reboot(sl_cli_command_arg_t *arguments) { (void)arguments; MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_UserReset); memfault_platform_reboot(); } void memfault_emlib_cli_assert(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_assert(argc, (char **)argv); } void memfault_emlib_cli_hang(sl_cli_command_arg_t *arguments) { (void)arguments; MEMFAULT_LOG_INFO("Hanging system and waiting for watchdog!"); while (1) { } } void memfault_emlib_cli_busfault(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_busfault(argc, (char **)argv); } void memfault_emlib_cli_hardfault(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_hardfault(argc, (char **)argv); } void memfault_emlib_cli_usagefault(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_usagefault(argc, (char **)argv); } void memfault_emlib_cli_memmanage(sl_cli_command_arg_t *arguments) { int argc = 0; void **argv = NULL; prv_adjust_arg_vars(arguments, &argc, &argv); memfault_demo_cli_cmd_memmanage(argc, (char **)argv); } #endif // SL_CATALOG_CLI_PRESENT ================================================ FILE: ports/emlib/msc_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions to use sectors of internal flash //! on the EFM/EFR Memory System Controller. //! //! To use, update your linker script (.ld file) to expose information about the location to use. //! For example, using the last 64K of the EFM32PG12BxxxF1024 (1MB flash) would look //! something like this: //! //! MEMORY //! { //! /* ... other regions ... */ //! COREDUMP_STORAGE_FLASH (r) : ORIGIN = 0xF0000, LENGTH = 64K //! } //! __MemfaultCoreStorageStart = ORIGIN(COREDUMP_STORAGE_FLASH); //! __MemfaultCoreStorageEnd = ORIGIN(COREDUMP_STORAGE_FLASH) + LENGTH(COREDUMP_STORAGE_FLASH); #include "memfault/config.h" #if MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH #include #include "em_device.h" #include "em_msc.h" #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "memfault/ports/buffered_coredump_storage.h" #ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR extern uint32_t __MemfaultCoreStorageStart[]; #define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_END_ADDR extern uint32_t __MemfaultCoreStorageEnd[]; #define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) #endif // Error writing to flash - should never happen & likely detects a configuration error // Call the reboot handler which will halt the device if a debugger is attached and then reboot MEMFAULT_NO_OPT static void prv_coredump_writer_assert_and_reboot(int error_code) { (void)error_code; memfault_platform_halt_if_debugging(); memfault_platform_reboot(); } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } void memfault_platform_coredump_storage_clear(void) { uint32_t *addr = (void *)MEMFAULT_COREDUMP_STORAGE_START_ADDR; uint32_t zeros = 0; const MSC_Status_TypeDef rv = MSC_WriteWord(addr, &zeros, sizeof(zeros)); if ((rv != mscReturnOk) || ((*addr) != 0)) { MEMFAULT_LOG_ERROR("Failed to clear coredump storage, rv=%d", (int)rv); } } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { const size_t size = MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR; *info = (sMfltCoredumpStorageInfo){ .size = size, .sector_size = FLASH_PAGE_SIZE, }; } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { const uint32_t addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR + blk->write_offset; const MSC_Status_TypeDef rv = MSC_WriteWord((void *)addr, blk->data, sizeof(blk->data)); if (rv != mscReturnOk) { prv_coredump_writer_assert_and_reboot(rv); } return true; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // The internal flash is memory mapped so we can just use memcpy! const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; memcpy(data, (void *)(start_addr + offset), read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } if (((offset % FLASH_PAGE_SIZE) != 0) || (erase_size % FLASH_PAGE_SIZE) != 0) { // configuration error prv_coredump_writer_assert_and_reboot(offset); } const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; for (size_t sector_offset = 0; sector_offset < erase_size; sector_offset += FLASH_PAGE_SIZE) { const MSC_Status_TypeDef rv = MSC_ErasePage((void *)(start_addr + sector_offset)); if (rv != mscReturnOk) { prv_coredump_writer_assert_and_reboot(rv); } } return true; } #endif /* MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH */ ================================================ FILE: ports/emlib/rmu_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset Management Unit" (RMU)'s "Reset Cause" (RSTCAUSE) Register. //! //! More details can be found in the "RMU_RSTCAUSE Register" of the reference manual //! for the specific EFR or EFM chip family. It makes use of APIs that are part of //! the Gecko SDK. #include "memfault/ports/reboot_reason.h" // non-module headers below #include "em_device.h" #include "em_emu.h" #include "em_rmu.h" #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif #if defined(_RMU_RSTCAUSE_MASK) static eMemfaultRebootReason prv_get_and_print_reason(uint32_t reset_cause) { // Find the RMU_RSTCAUSE Register data sheet for the EFM/EFR part for more details // For example, in the EFM32PG12 it's in section "8.3.2 RMU_RSTCAUSE Register" // // Note that some reset types are shared across EFM/EFR MCU families. For the ones // that are not, we wrap the reason with an ifdef. if (reset_cause & RMU_RSTCAUSE_PORST) { MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); return kMfltRebootReason_PowerOnReset; #if defined(RMU_RSTCAUSE_AVDDBOD) } else if (reset_cause & RMU_RSTCAUSE_AVDDBOD) { MEMFAULT_PRINT_RESET_INFO(" AVDD Brown Out"); return kMfltRebootReason_BrownOutReset; #endif #if defined(RMU_RSTCAUSE_DVDDBOD) } else if (reset_cause & RMU_RSTCAUSE_DVDDBOD) { MEMFAULT_PRINT_RESET_INFO(" DVDD Brown Out"); return kMfltRebootReason_BrownOutReset; #endif #if defined(RMU_RSTCAUSE_DECBOD) } else if (reset_cause & RMU_RSTCAUSE_DECBOD) { MEMFAULT_PRINT_RESET_INFO(" DEC Brown Out"); return kMfltRebootReason_BrownOutReset; #endif } else if (reset_cause & RMU_RSTCAUSE_LOCKUPRST) { MEMFAULT_PRINT_RESET_INFO(" Lockup"); return kMfltRebootReason_Lockup; } else if (reset_cause & RMU_RSTCAUSE_SYSREQRST) { MEMFAULT_PRINT_RESET_INFO(" Software"); return kMfltRebootReason_SoftwareReset; } else if (reset_cause & RMU_RSTCAUSE_WDOGRST) { MEMFAULT_PRINT_RESET_INFO(" Watchdog"); return kMfltRebootReason_HardwareWatchdog; #if defined(RMU_RSTCAUSE_EM4WURST) } else if (reset_cause & RMU_RSTCAUSE_EM4RST) { MEMFAULT_PRINT_RESET_INFO(" EM4 Wakeup"); return kMfltRebootReason_DeepSleep; #endif } else if (reset_cause & RMU_RSTCAUSE_EXTRST) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); return kMfltRebootReason_ButtonReset; } MEMFAULT_PRINT_RESET_INFO(" Unknown"); return kMfltRebootReason_Unknown; } #elif defined(_EMU_RSTCTRL_MASK) static eMemfaultRebootReason prv_get_and_print_reason(uint32_t reset_cause) { // Find the EMU_RSTCAUSE Register data sheet for the EFM/EFR part for more details // For example, in the EFR32xG21 it's in section "12.5.13 EMU_RSTCAUSE - Reset cause" // // Note that some reset types are shared across EFM/EFR MCU families. For the ones // that are not, we wrap the reason with an ifdef. if (reset_cause & EMU_RSTCAUSE_POR) { MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); return kMfltRebootReason_PowerOnReset; } else if (reset_cause & EMU_RSTCAUSE_AVDDBOD) { MEMFAULT_PRINT_RESET_INFO(" AVDD Brown Out"); return kMfltRebootReason_BrownOutReset; } else if (reset_cause & EMU_RSTCAUSE_IOVDD0BOD) { MEMFAULT_PRINT_RESET_INFO(" IOVDD0 Brown Out"); return kMfltRebootReason_BrownOutReset; } else if (reset_cause & EMU_RSTCAUSE_DVDDBOD) { MEMFAULT_PRINT_RESET_INFO(" DVDD Brown Out"); return kMfltRebootReason_BrownOutReset; } else if (reset_cause & EMU_RSTCAUSE_DVDDLEBOD) { MEMFAULT_PRINT_RESET_INFO(" DVDDLE Brown Out"); return kMfltRebootReason_BrownOutReset; } else if (reset_cause & EMU_RSTCAUSE_DECBOD) { MEMFAULT_PRINT_RESET_INFO(" DEC Brown Out"); return kMfltRebootReason_BrownOutReset; } else if (reset_cause & EMU_RSTCAUSE_LOCKUP) { MEMFAULT_PRINT_RESET_INFO(" Lockup"); return kMfltRebootReason_Lockup; } else if (reset_cause & EMU_RSTCAUSE_SYSREQ) { MEMFAULT_PRINT_RESET_INFO(" Software"); return kMfltRebootReason_SoftwareReset; } else if (reset_cause & EMU_RSTCAUSE_WDOG0) { MEMFAULT_PRINT_RESET_INFO(" Watchdog 0"); return kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & EMU_RSTCAUSE_WDOG1) { MEMFAULT_PRINT_RESET_INFO(" Watchdog 1"); return kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & EMU_RSTCAUSE_EM4) { MEMFAULT_PRINT_RESET_INFO(" EM4 Wakeup"); return kMfltRebootReason_DeepSleep; } else if (reset_cause & EMU_RSTCAUSE_SETAMPER) { MEMFAULT_PRINT_RESET_INFO(" SE Tamper"); return kMfltRebootReason_UnknownError; #if defined(EMU_RSTCAUSE_SESYSREQ) } else if (reset_cause & EMU_RSTCAUSE_SESYSREQ) { MEMFAULT_PRINT_RESET_INFO(" SE System Reset"); return kMfltRebootReason_SoftwareReset; #endif #if defined(EMU_RSTCAUSE_SYSREQ) } else if (reset_cause & EMU_RSTCAUSE_SYSREQ) { MEMFAULT_PRINT_RESET_INFO("Software Reset"); return kMfltRebootReason_SoftwareReset; #endif #if defined(EMU_RSTCAUSE_SELOCKUP) } else if (reset_cause & EMU_RSTCAUSE_SELOCKUP) { MEMFAULT_PRINT_RESET_INFO("SE Lockup"); return kMfltRebootReason_Lockup; #endif #if defined(EMU_RSTCAUSE_LOCKUP) } else if (reset_cause & EMU_RSTCAUSE_LOCKUP) { MEMFAULT_PRINT_RESET_INFO("Lockup"); return kMfltRebootReason_Lockup; #endif } else if (reset_cause & EMU_RSTCAUSE_PIN) { MEMFAULT_PRINT_RESET_INFO(" SE Pin Reset"); return kMfltRebootReason_PinReset; } MEMFAULT_PRINT_RESET_INFO(" Unknown"); return kMfltRebootReason_Unknown; } #else #error "Unexpected RSTCTRL configuration" #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); // This routine simply reads RMU->RSTCAUSE and zero's out // bits that aren't relevant to the reset. For more details // check out the logic in ${PATH_TO_GECKO_SDK}/platform/emlib/src/em_rmu.c const uint32_t reset_cause = RMU_ResetCauseGet(); MEMFAULT_PRINT_RESET_INFO("Reset Reason, RSTCAUSE=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); eMemfaultRebootReason reset_reason = prv_get_and_print_reason(reset_cause); #if MEMFAULT_REBOOT_REASON_CLEAR // we have read the reset information so clear the bits (since they are sticky across reboots) RMU_ResetCauseClear(); #endif *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/emlib/wdog_software_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A Software Watchdog implementation backed by the //! EFM/EFR WDOG peripheral & the SiLabs Gecko SDK (EMLIB) //! //! The implementation uses the hardware watchdog peripheral (WDOG) but //! configures a warning interrupt to fire at MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS //! and the hardware watchdog to reset the device at MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS * 2. //! //! To configure, compile the file and wire up the IRQ handler to Memfault coredump collection //! by updating your compiler flags: -DMEMFAULT_EXC_HANDLER_WATCHDOG=WDOG0_IRQHandler //! //! Implementation notes: //! - The WDOG peripheral supports discrete timeouts. This port chooses the closest period //! that is less than or equal to the requested value. Valid selections for //! MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS range from 1s - 128s. #include "memfault/ports/watchdog.h" // non-module includes below #include "em_cmu.h" #include "em_wdog.h" #include "memfault/config.h" #include "memfault/core/debug_log.h" #if defined(WDOG0) #define MEMFAULT_WDOG WDOG0 #define MEMFAULT_WDOG_IRQn WDOG0_IRQn #ifndef MEMFAULT_EXC_HANDLER_WATCHDOG #error \ "Port expects following define to be set: -DMEMFAULT_EXC_HANDLER_WATCHDOG=WDOG0_IRQHandler" #endif #elif defined(WDOG) #define MEMFAULT_WDOG WDOG #define MEMFAULT_WDOG_IRQn WDOG_IRQn #ifndef MEMFAULT_EXC_HANDLER_WATCHDOG #error \ "Port expects following define to be set: -DMEMFAULT_EXC_HANDLER_WATCHDOG=WDOG_IRQHandler" #endif #else #error "Could not find an available WDOG to use" #endif #define MEMFAULT_EM_WDOG_FREQ_HZ 1000 #define MEMFAULT_EM_WDOG_WARNING_TIMEOUT_MS(timeout) ((timeout) / 2) #define MEMFAULT_EM_WDOG_PERSEL_TO_TIMEOUT_MS(persel) \ ((1000 * (1 << (3 + persel))) / MEMFAULT_EM_WDOG_FREQ_HZ) #define MEMFAULT_EM_WDOG_MAX_TIMEOUT_MS (MEMFAULT_EM_WDOG_PERSEL_TO_TIMEOUT_MS(wdogPeriod_256k)) // clang-format off static void prv_build_configuration(WDOG_Init_TypeDef *cfg, uint32_t persel) { *cfg = (WDOG_Init_TypeDef) { .enable = true, // freeze watchdog when a debugger halts the system .debugRun = false, #if defined(_WDOG_CFG_EM1RUN_MASK) .em1Run = true, #endif .em2Run = true, .em3Run = true, .em4Block = false, #if defined(_WDOG_CTRL_SWOSCBLOCK_MASK) .swoscBlock = false, #endif .lock = false, #if defined(_WDOG_CTRL_CLKSEL_MASK) // use internal 1kHz clock for largest range .clkSel = wdogClkSelULFRCO, #endif .perSel = persel, #if defined(_WDOG_CTRL_WARNSEL_MASK) || defined(_WDOG_CFG_WARNSEL_MASK) .warnSel = wdogWarnTime50pct, #else #error "Port doesn't support WDOG Variant - no early warning interrupt" #endif #if defined(_WDOG_CTRL_WINSEL_MASK) || defined(_WDOG_CFG_WINSEL_MASK) .winSel = wdogIllegalWindowDisable, #endif #if defined(_WDOG_CTRL_WDOGRSTDIS_MASK) || defined(_WDOG_CFG_WDOGRSTDIS_MASK) // we want a hardware watchdog to trigger a reset .resetDisable = false, #endif }; } // clang-format on static int prv_configure_watchdog_with_timeout(uint32_t timeout_ms) { // NB: An interrupt can be configured to fire at 25%, 50%, or 75% of the watchdog cycle // configured. We'll use this interrupt as our "software watchdog" and configure it to be at // the 50% interval const size_t sw_wdog_max_ms = MEMFAULT_EM_WDOG_WARNING_TIMEOUT_MS(MEMFAULT_EM_WDOG_MAX_TIMEOUT_MS); if (timeout_ms > sw_wdog_max_ms) { MEMFAULT_LOG_ERROR("Requested wdog timeout (%d) exceeds max supported (%d)", (int)timeout_ms, (int)sw_wdog_max_ms); return -1; } uint32_t persel = wdogPeriod_9; while (persel <= wdogPeriod_256k) { const uint32_t warning_timeout_ms = MEMFAULT_EM_WDOG_WARNING_TIMEOUT_MS(MEMFAULT_EM_WDOG_PERSEL_TO_TIMEOUT_MS(persel)); if (warning_timeout_ms > timeout_ms) { break; } persel++; } // choose the closest timeout without going over the desired max persel = (persel == 0) ? 0 : persel - 1; const size_t actual_timeout_ms = MEMFAULT_EM_WDOG_PERSEL_TO_TIMEOUT_MS(persel); MEMFAULT_LOG_DEBUG("Configuring SW Watchdog. SW Timeout=%dms HW Timeout=%dms", (int)MEMFAULT_EM_WDOG_WARNING_TIMEOUT_MS(actual_timeout_ms), actual_timeout_ms); // Low energy clock must be on to use the watchdog #if defined(_SILICON_LABS_32B_SERIES_2) CMU_ClockEnable(cmuClock_WDOG0, true); #else // !defined(_SILICON_LABS_32B_SERIES_2) CMU_ClockEnable(cmuClock_HFLE, true); #endif // defined(_SILICON_LABS_32B_SERIES_2) if (WDOGn_IsLocked(MEMFAULT_WDOG)) { MEMFAULT_LOG_ERROR("Watchdog is locked and cannot be reconfigured"); return -2; } // We are about to (re-)configure the peripheral so disable it WDOGn_Enable(MEMFAULT_WDOG, false); NVIC_DisableIRQ(MEMFAULT_WDOG_IRQn); NVIC_ClearPendingIRQ(MEMFAULT_WDOG_IRQn); // Series 2 chips select the watchdog source via the CMU #if defined(_SILICON_LABS_32B_SERIES_2) CMU_ClockSelectSet(cmuClock_WDOG0, cmuSelect_ULFRCO); #endif // defined(_SILICON_LABS_32B_SERIES_2) WDOG_Init_TypeDef cfg; prv_build_configuration(&cfg, persel); WDOGn_Init(MEMFAULT_WDOG, &cfg); // enable the warning interrupt. This will be used to capture a coredump rather than just letting // the hardware watchdog immediately reboot the system const uint32_t warn_int_mask = 0x2; // "WARN Interrupt Enable" WDOGn_IntClear(MEMFAULT_WDOG, warn_int_mask); WDOGn_IntEnable(MEMFAULT_WDOG, warn_int_mask); // Enable the interrupt in the NVIC and set it to the highest priority. // This way we can even capture hangs inside ISRs! NVIC_SetPriority(MEMFAULT_WDOG_IRQn, 0); NVIC_EnableIRQ(MEMFAULT_WDOG_IRQn); // finally, with everything setup, start the watchdog! WDOGn_Enable(MEMFAULT_WDOG, true); return 0; } int memfault_software_watchdog_update_timeout(uint32_t timeout_ms) { return prv_configure_watchdog_with_timeout(timeout_ms); } int memfault_software_watchdog_enable(void) { const uint32_t timeout_ms = MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS * 1000; return memfault_software_watchdog_update_timeout(timeout_ms); } int memfault_software_watchdog_disable(void) { if (WDOGn_IsLocked(MEMFAULT_WDOG)) { MEMFAULT_LOG_ERROR("Watchdog is locked and cannot be disabled"); return -1; } WDOGn_Enable(MEMFAULT_WDOG, false); return 0; } int memfault_software_watchdog_feed(void) { // NB: A clear request takes ~4ms to propagate through the peripheral. // Enteriung EM2 or EM3 power states while this is in progress will // cause the operation to be aborted. WDOGn_SyncWait() can be used to // block until the operation is complete! WDOGn_Feed(MEMFAULT_WDOG); return 0; } ================================================ FILE: ports/esp_idf/README.md ================================================ # ESP32 Specific Port ## Overview This directory contains an implementation of the dependency functions needed to integrate the Memfault SDK into a ESP-IDF based project. The port has been tested on major versions 3, 4, 5 of ESP-IDF. ## Directories The subdirectories within the folder are titled to align with the ESP-IDF "component" structure. ```plaintext ├── README.md ├── memfault.cmake # cmake helper to include to add the memfault "component" └── memfault ├── CMakeLists.txt ├── common # sourced common across all ESP-IDF versions ├── include ├── v3.x # ESP-IDF sources specific to major version 3 ├── v4.x # ESP-IDF sources specific to major version 4 └── v5.x # ESP-IDF sources specific to major version 5 ``` ## Integrating the SDK A step-by-step integration guide can be found at https://mflt.io/esp-tutorial ================================================ FILE: ports/esp_idf/memfault/CMakeLists.txt ================================================ # Memfault component inclusion can be disabled by setting the environment # variable MEMFAULT_DISABLE=1 when building. This can't be done at the Kconfig # level because we need to load the component before Kconfig runs. if(DEFINED ENV{MEMFAULT_DISABLE}) return() endif() if (CONFIG_MEMFAULT_AUTOMATIC_INIT) message(FATAL_ERROR "CONFIG_MEMFAULT_AUTOMATIC_INIT has been deprecated. Please complete the following steps: 1. Remove CONFIG_MEMFAULT_AUTOMATIC_INIT=y from sdkconfig.default if present 2. Delete your project's generated sdkconfig (be sure to save any in-progress changes) 3. Update your application to call memfault_boot during initialization For more information please see https://docs.memfault.com/docs/mcu/esp32-guide") endif() set(MEMFAULT_SDK_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../..) include(${MEMFAULT_SDK_ROOT}/cmake/Memfault.cmake) # If $ENV{MEMFAULT_PLATFORM_PORT_COMPONENTS} is set, set that to MEMFAULT_PLATFORM_PORT_COMPONENTS # otherwise default it to 'main' if(DEFINED ENV{MEMFAULT_PLATFORM_PORT_COMPONENTS}) set(MEMFAULT_PLATFORM_PORT_COMPONENTS $ENV{MEMFAULT_PLATFORM_PORT_COMPONENTS}) else() set(MEMFAULT_PLATFORM_PORT_COMPONENTS main) message(STATUS "MEMFAULT_PLATFORM_PORT_COMPONENTS not provided, using default ('${MEMFAULT_PLATFORM_PORT_COMPONENTS}')") endif() if(NOT DEFINED MEMFAULT_ESP_HTTP_CLIENT_ENABLE) if(DEFINED ENV{MEMFAULT_ESP_HTTP_CLIENT_ENABLE}) set(MEMFAULT_ESP_HTTP_CLIENT_ENABLE $ENV{MEMFAULT_ESP_HTTP_CLIENT_ENABLE}) else() set(MEMFAULT_ESP_HTTP_CLIENT_ENABLE 1) endif() endif() # 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 # idf_build_get_property was added in 4.0. RISC-V chips weren't added until # v4.3, but we can rely on the build target for figuring out the architecture. if(MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL 4) # Get target architecture to pass to the memfault_library initialization. # IDF_TARGET_ARCH was added in 5.0, so use the older IDF_TARGET variable. idf_build_get_property(target IDF_TARGET) if("${target}" STREQUAL "esp32" OR "${target}" STREQUAL "esp32s2" OR "${target}" STREQUAL "esp32s3") set(ESP_ARCH "ARCH_XTENSA") else() set(ESP_ARCH "ARCH_RISCV") endif() else() # For older versions of esp-idf, we assume the architecture is XTENSA set(ESP_ARCH "ARCH_XTENSA") endif() 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}) include($ENV{IDF_PATH}/tools/cmake/version.cmake OPTIONAL) # Select version-specific porting files if(MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL 6) set(MEMFAULT_ESP_IDF_PORT v6.x) elseif(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) else() message(FATAL_ERROR "${MEMFAULT_IDF_VERSION_ERROR_MSG}") endif() # esp-idf version specific porting files list(APPEND MEMFAULT_COMPONENTS_SRCS ${CMAKE_CURRENT_LIST_DIR}/${MEMFAULT_ESP_IDF_PORT}/memfault_esp_spi_flash.c ) include(${CMAKE_CURRENT_LIST_DIR}/${MEMFAULT_ESP_IDF_PORT}/Memfault-esp-idf-compat.cmake) # This directory holds ports that are common across v3.x and v4.x esp-idf releases set(MEMFAULT_ESP_IDF_PORT_COMMON ${CMAKE_CURRENT_LIST_DIR}/common) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_fault_handler.c ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_core.c ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_coredump.c ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_debug_log.c ${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_CLI_ENABLED) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_demo_cli_cmds.c ) endif() if (CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_http_periodic_upload.c ) endif() if (CONFIG_MEMFAULT_SYSTEM_TIME) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_system_time.c ) endif() if (CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_platform_deep_sleep.c ) endif() list(APPEND MEMFAULT_COMPONENTS_INC_FOLDERS include ${MEMFAULT_SDK_ROOT}/ports/include $ENV{MEMFAULT_PLATFORM_EXTRA_INCLUDES} config ) if (CONFIG_MEMFAULT_LWIP_METRICS) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_SDK_ROOT}/ports/lwip/memfault_lwip_metrics.c) list(APPEND MEMFAULT_COMPONENTS_INC_FOLDERS ${MEMFAULT_SDK_ROOT}/ports/lwip/config) endif() if (CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS) # if CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER is not set, generate an error # message and exit if(NOT CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER) message(FATAL_ERROR "CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER is not set. Please set it to 'y' to use FreeRTOS task runtime stats") endif() list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_sdk_metrics_freertos.c) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_sdk_metrics_thread.c) list(APPEND MEMFAULT_COMPONENTS_INC_FOLDERS ${MEMFAULT_SDK_ROOT}/ports/freertos/config) endif() if (CONFIG_MEMFAULT_MBEDTLS_METRICS) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_SDK_ROOT}/ports/mbedtls/memfault_mbedtls_metrics.c) list(APPEND MEMFAULT_COMPONENTS_INC_FOLDERS ${MEMFAULT_SDK_ROOT}/ports/mbedtls/config) endif() if (CONFIG_MEMFAULT_CLI_SELF_TEST) list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_self_test_platform.c) endif() # For version >= 4.4.3, we can collect smaller coredumps by default # by prioritizing active stack and FreeRTOS regions first. ESP-IDF < 4.4.3 # uses a simpler scheme collecting all of DRAM. See # common/memfault_platform_coredump.c for more info. # Note: CMake does not short-circuit logic statements, nested ifs required # Note: ENV{IDF_VERSION} added in esp-idf 4.4.3 if (DEFINED ENV{IDF_VERSION}) if ($ENV{IDF_VERSION} VERSION_GREATER_EQUAL "4.4.3") list(APPEND MEMFAULT_COMPONENTS_SRCS ${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_freertos_ram_regions.c ) # Add a linker fragment to place FreeRTOS timers and task objects in the same area of dram0.bss set(COMPONENT_ADD_LDFRAGMENTS "${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_esp_freertos.lf") endif() endif() # Register Memfault SDK Component set(COMPONENT_SRCS ${MEMFAULT_COMPONENTS_SRCS}) set(COMPONENT_ADD_INCLUDEDIRS ${MEMFAULT_COMPONENTS_INC_FOLDERS}) list(APPEND COMPONENT_REQUIRES ${MEMFAULT_ESP_IDF_VERSION_SPECIFIC_REQUIRES} ${MEMFAULT_PLATFORM_PORT_COMPONENTS} freertos heap log soc spi_flash console driver ) if(CONFIG_MEMFAULT_LWIP_METRICS) list(APPEND COMPONENT_REQUIRES lwip) endif() if(CONFIG_MEMFAULT_MBEDTLS_METRICS) list(APPEND COMPONENT_REQUIRES mbedtls) endif() if(${MEMFAULT_ESP_HTTP_CLIENT_ENABLE}) list(APPEND COMPONENT_REQUIRES esp_http_client esp_https_ota) endif() register_component() # sdk_overrides/esp_http_client.c needs the (private) esp_http header files: get_filename_component(this_directory_name . ABSOLUTE DIRECTORY) get_filename_component(this_directory_name ${this_directory_name} NAME) mflt_esp32_component_get_target(this_component ${this_directory_name}) # Each esp-idf component is compiled into a static library. esp-idf then links each of these # libraries to the program ELF. This can cause quirks when working with weak and strong symbol # definitions within a static library causing the weak definition to be kept over the strong. # To keep the strong definition around, we can define an undefined symbol to force the linker # to reconsider different outputs. This allows the strong definition to be selected for inclusion # in the program. When we're adding new strong definitions to weak functions internal to the SDK, # the following is needed: # 1. Add an empty stub function in the source file # 2. Add a corresponding target_link_libraries call (see below) with the linker option -u # and specify the stub function name if(CONFIG_MEMFAULT_CLI_SELF_TEST) target_link_libraries(${this_component} INTERFACE "-u memfault_esp_idf_include_self_test_impl") endif() # The below compilation options need to be set after register_component(). if (CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS) # MEMFAULT_USE_NEW_FREERTOS_IDLETASK_RUNTIME_API should be explicitly set to 1 # for ESP-IDF. The API in question, 'ulTaskGetIdleRunTimeCounter', was added # in ESP-IDF v4.3, but didn't have backwards compatibility with the original # name 'xTaskGetIdleRunTimeCounter' until ESP-IDF v4.4 (because ESP-IDF pulled # a non-release upstream FreeRTOS in the interim). The old name # 'xTaskGetIdleRunTimeCounter' was never used in any version of ESP-IDF. # # 'xTaskGetIdleRunTimeCounter' was added in FreeRTOS V10.2.0, renamed to # 'ulTaskGetIdleRunTimeCounter'in V10.3.0, with backwards compatibility # included in mainline FreeRTOS V10.3.0. # # Task runtime stats are disabled on FreeRTOS <10.2.0 entirely, so on earlier # ESP-IDF SDK versions, this is unused. component_compile_options(-DMEMFAULT_USE_NEW_FREERTOS_IDLETASK_RUNTIME_API=1) endif() component_compile_options(-DMEMFAULT_ESP_HTTP_CLIENT_ENABLE=${MEMFAULT_ESP_HTTP_CLIENT_ENABLE}) # FreeRTOS error logging redirect for ESP-IDF compatibility. component_compile_options( -DMEMFAULT_FREERTOS_REGISTRY_FULL_ERROR_LOG_INCLUDE="memfault_platform_freertos_error_log.h" ) # Extra compilation options set globally if (MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "5.0.0") # Empty for now, reserving for the future 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" ) endif() # Set the heartbeat config file to use the ESP-IDF port file, which will bring # in the user's heartbeat config automatically. Set it as a global compiler # option so it properly affects both component compilation and when the metric # keys are used in the application. # # Set the ESP-IDF specific Memfault platform config header as well. # # 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 ) # Compiling with -ggdb3 is only supported on ESP-IDF v5.5 and later, due to # requiring this fix: # https://github.com/espressif/esp-idf/commit/6d945bf0f6a6fc474bc2fee3ce09539172286b6d if (MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "5.5.0") if (CONFIG_MEMFAULT_GGDB3) # Enable -ggdb3 in compile_options. idf_build_set_property( COMPILE_OPTIONS "-ggdb3" APPEND ) endif() endif() # We will intercept the panic handlers enabled by CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH # and run the Memfault Fault Handler instead. # # Benefits here are: # FreeRTOS task list is walked server side instead of on device (so you can get crash data even if the lists are corrupted) # Much more flexibility in debug information collected (e.g. all RAM, just the current stack trace, select stacks and variables) # Data can be posted directly from device to Memfault cloud for deduplication and analysis # set(panic_handler_args "") # In ESP-IDF v5.3.0, the core dump handler function was renamed from # esp_core_dump_to_flash to esp_core_dump_write. It was backported to v5.2.2. if (MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "5.2.2") list(APPEND panic_handler_args "-Wl,--wrap=esp_core_dump_write") # Backported to v5.1.4 elseif((MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "5.1.4") AND (MEMFAULT_IDF_VERSION VERSION_LESS "5.2.0")) list(APPEND panic_handler_args "-Wl,--wrap=esp_core_dump_write") else() list(APPEND panic_handler_args "-Wl,--wrap=esp_core_dump_to_flash") endif() list( APPEND panic_handler_args "-Wl,--wrap=esp_core_dump_init" "-Wl,--wrap=esp_core_dump_image_get" ) target_link_libraries(${this_component} INTERFACE "${panic_handler_args}") # Wrap panic_abort to intercept assert()/abort() calls. Note that support for # this was added in ESP-IDF v4.2; earlier versions are intercepted with the # __assert_func definition provided in memfault_stdlib_assert.c target_link_libraries(${this_component} INTERFACE "-Wl,--wrap=panic_abort") # Wrap allocator functions to capture mbedTLS metrics if(CONFIG_MEMFAULT_MBEDTLS_METRICS) target_link_libraries(${this_component} INTERFACE "-Wl,--wrap=mbedtls_calloc -Wl,--wrap=mbedtls_free") endif() # Wrap to suppress errors when esp_event_create_loop_default() called multiple times if (CONFIG_MEMFAULT_WRAP_EVENT_LOOP_CREATE_DEFAULT) target_link_libraries(${this_component} INTERFACE "-Wl,--wrap=esp_event_loop_create_default") endif() if(CONFIG_MEMFAULT_HEAP_STATS) target_link_libraries(${this_component} INTERFACE "-Wl,--wrap=malloc,--wrap=free") if (CONFIG_MEMFAULT_HEAP_STATS_CALLOC) target_link_libraries(${this_component} INTERFACE "-Wl,--wrap=calloc") endif() endif() if(CONFIG_MEMFAULT_METRICS_BOOT_TIME) target_link_libraries(${this_component} INTERFACE "-Wl,--wrap=esp_startup_start_app") endif() # Include a linker script fragment to support compact logs unconditionally. It's # Include a linker script fragment to support compact logs unconditionally. It's # only populated if compact logs are enabled, and otherwise is ignored. We keep # it unconditionally included for backwards compatibility, so users updating the # SDK don't need to change any build configuration. get_filename_component(compact_log_linker_script ${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_compact_log.ld ABSOLUTE) target_link_libraries( ${this_component} INTERFACE # Note: there is intentionally no space between -T and the linker script # file name. This is required for PlatformIO compatibility- the PlatformIO # build system inserts arguments into the linker command line, so we need to # make sure it can't separate the -T and the linker script file name. -T${compact_log_linker_script} ) # Option to generate the Memfault Build ID, enabled by Kconfig. Only # 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) # 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. set(IDF_PROJECT_EXECUTABLE "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.elf") idf_build_get_property(python PYTHON) idf_build_get_property(elf_target EXECUTABLE GENERATOR_EXPRESSION) 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 "$>" "$>.memfault_log_fmt" COMMAND ${CMAKE_COMMAND} -E echo "*** NOTE: the symbol file to upload to app.memfault.com is $>.memfault_log_fmt ***" # Remove the 'log_fmt' compact log section, which confuses elf2image COMMAND ${CMAKE_OBJCOPY} --remove-section log_fmt "$>" # Update the timestamp of the .memfault_log_fmt file to be newer than its # just-edited dependency, ${IDF_PROJECT_EXECUTABLE}, so that the custom # target doesn't run every time COMMAND ${CMAKE_COMMAND} -E touch "$>.memfault_log_fmt" DEPENDS "$>" ) if (MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "5.6") add_custom_target(memfault_build_id DEPENDS "$>.memfault_log_fmt") idf_build_add_post_elf_dependency("${CMAKE_PROJECT_NAME}.elf" memfault_build_id) else() # On ESP-IDF 5.5 and lower, we'll inject a dependency between # gen_project_binary and the ELF. # 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") if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) add_dependencies(gen_signed_project_binary memfault_build_id) else() add_dependencies(gen_project_binary memfault_build_id) endif() endif() # ESP-IDF >= 5.6 endif() # Link required libraries and add compiler flags needed for FreeRTOS region collection # and LwIP metrics in >= 4.4.3. # Note: CMake does not short-circuit logic statements, nested ifs required if (MEMFAULT_IDF_VERSION VERSION_GREATER_EQUAL "4.4.3") # Policy change requires CMake v3.13+ cmake_minimum_required(VERSION 3.13) # First set new policy for target_link_libraries, this resolves a warning when using on # targets not created in this directory cmake_policy(SET CMP0079 NEW) # Get the name of the ESP FreeRTOS target/library idf_component_get_property(freertos_lib freertos COMPONENT_LIB) # Link this component to FreeRTOS, use INTERFACE because we're only sharing headers target_link_libraries(${freertos_lib} INTERFACE ${this_component}) target_compile_options(${freertos_lib} INTERFACE "-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\"" ) # Lastly ensure that our FreeRTOS trace hooks are defined first by adding this # compile option to the FreeRTOS target to include with all source # This method is an alternative to #include within FreeRTOSConfig.h which esp-idf # makes very difficult to do. # # We exclude PlatformIO projects because PlatformIO's build system mechanism inserts other flags # in between the -include and the freertos_trace.h compile option. PlatformIO projects will need # to include the header independently in their project's .ini file. An example of how to do this # is at https://github.com/memfault/platformio-esp32-espidf if(NOT DEFINED PLATFORMIO_ENABLED) get_filename_component(freertos_trace_header ${MEMFAULT_SDK_ROOT}/ports/include/memfault/ports/freertos_trace.h ABSOLUTE) target_compile_options(${freertos_lib} INTERFACE -include ${freertos_trace_header} ) endif() # Add definition for LWIP_STATS_LARGE to lwip component if using CONFIG_MEMFAULT_LWIP_METRICS if(CONFIG_MEMFAULT_LWIP_METRICS) # Get the name of the LwIP target/library idf_component_get_property(lwip_lib lwip COMPONENT_LIB) # Link this component to LwIP, use INTERFACE because we're only sharing headers target_link_libraries(${lwip_lib} INTERFACE ${this_component}) target_compile_options(${lwip_lib} INTERFACE "-DLWIP_STATS_LARGE=1") endif() endif() ================================================ FILE: ports/esp_idf/memfault/Kconfig ================================================ config MEMFAULT bool default y help This symbol is used to provide a CONFIG_MEMFAULT symbol when compiling. Normally the Memfault component is added at the cmake or idf_component.yml level. If Memfault inclusion needs to be controlled at build time instead, use the environment variable MEMFAULT_DISABLE=1 when building to control whether the Memfault component is included or not. if MEMFAULT menu "Memfault" config MEMFAULT_PROJECT_KEY string "Memfault Project key" default "" help API Key needed to communicate to Memfault. Obtain key at https://mflt.io/project-key config MEMFAULT_EVENT_STORAGE_RAM_SIZE int "The amount of RAM storage to reserve for Memfault Events" default 512 config MEMFAULT_LOG_STORAGE_RAM_SIZE int "The amount of RAM storage to allocate for log storage" default 1024 help Logs stored in this buffer will be captured as part of a Memfault Coredump config MEMFAULT_DATA_CHUNK_HANDLERS_CUSTOM bool "Use custom data chunk handlers" default n help Enable custom implementations of `memfault_esp_port_data_available()` and `memfault_esp_port_get_chunk()`. The option can be used if other MCUs forward data through the ESP32 and these functions need to handle additional sources. config MEMFAULT_CLI_ENABLED bool "Enables the Memfault CLI" default y help By default, Memfault will register CLI commands for pushing data, dumping state of the SDK, and performing device updates. config MEMFAULT_CLI_SELF_TEST bool "Enables the self test command via CLI, `memfault_self_test`" default n depends on MEMFAULT_CLI_ENABLED help Adds a command, `memfault_self_test`, to run a suite of tests used to verify the device's integration with the Memfault SDK. By default, this will run tests to validate device info, component boot, coredump regions, coredump storage capacity, data export, and platform time. A reboot test is available by running `memfault_self_test reboot` if MEMFAULT_CLI_SELF_TEST config MEMFAULT_CLI_SELF_TEST_COREDUMP_STORAGE bool "Adds option to run coredump storage test [EXPERIMENTAL]" depends on !BOOTLOADER_WDT_DISABLE_IN_USER_CODE depends on !ESP_INT_WDT depends on !ESP_TASK_WDT_EN help Enabling this option adds an experimental coredump storage test. The test exercises the coredump storage implementation by erasing, reading, and writing the entire coredump partition. This test may fail due to Flash cache issues. The test requires disabling the three watchdogs (RTC, INT, Task) and will stall the other core while running. This test may be run with `memfault_self_test coredump_storage`. endif menu "Memfault Coredump Settings" config MEMFAULT_COREDUMP_USE_OTA_SLOT bool "Enables the use of an OTA slot for coredump storage" default n help By default, Memfault will attempt to use a coredump partition defined in a partitions*.csv file. If the device has already been shipped and the partition table cannot be modified, an OTA slot can be used instead. config MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS int "Write coredumps starting at this offset (in sectors)" default 0 help By default, Memfault will write coredumps starting at the beginning of the coredump partition. If the device has a bootloader that reserves space at the beginning of the partition, this option can be used to write coredumps starting at a different offset. The full partition will still be erased when invalidating a coredump. config MEMFAULT_COREDUMP_STORAGE_MAX_SIZE int "The maximum size of a coredump (0=partition max size)" default 0 help The maximum size of a coredump. If a coredump is larger than this size, it will be truncated to this size. Must be aligned to SPI_FLASH_SEC_SIZE. When set to =0 (the default), Memfault will attempt to capture the entire RAM region of the device, up to the maximum size of the `coredump` region. If it's desirable to artificially reduce the utilized space in the `coredump` partition, setting a non-zero value will cause the Memfault ESP-IDF port's 'memfault_platform_coredump_storage_get_info()' function to return the threshold value set here as the size of the partition.. config MEMFAULT_COREDUMP_CPU_COUNT int "Number of CPU cores to include in coredumps" default SOC_CPU_CORES_NUM # Symbolic values in the range are not supported by # confgen.py/esp-idf-kconfig until later versions (esp-idf 5+). Hard # code to a max of 2. range 1 2 help The number of CPU cores to include in coredumps. By default, all cores are included. config MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG bool "Capture the task watchdog state in coredumps" default y if ESP_TASK_WDT_EN help Include task watchdog state in coredumps. config MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG_SIZE int "Size of the task watchdog state in coredumps" default 128 depends on MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG help The size of the task watchdog state included in coredumps. config MEMFAULT_COREDUMP_REGIONS_BUILT_IN bool "Enable built-in memfault_platform_coredump_get_regions()" default y help This is a wrapper around memfault_esp_port_coredump_get_regions(). Disable this option to use a custom implementation of memfault_platform_coredump_get_regions(). config MEMFAULT_COREDUMP_REGIONS_THREAD_ONLY bool "Capture only thread stacks in coredumps" default n help When enabled, only thread-related data (stacks, TCBs, and FreeRTOS metadata) will be captured in coredumps, excluding .bss/.data and heap sections. Optionally, the task watchdog state may also be included if enabled. This can be useful for reducing the size of coredumps. config MEMFAULT_PLATFORM_TASK_STACK_SIZE_TO_COLLECT int "The maximum stack size to collect per task in coredumps" default 512 if MEMFAULT_COREDUMP_REGIONS_THREAD_ONLY default 256 help The maximum stack size to collect per task in coredumps. This can be used to limit the amount of stack data collected for each task. endmenu # Memfault Coredump Settings config MEMFAULT_AUTOMATIC_INIT bool "[DEPRECATED] Automatically initialize the SDK when the system is booted" default n help By default, the user is required to initialize the SDK by calling `memfault_boot`. This option is deprecated and not supported. A build error will be generated when enabling this option. menu "Builtin device info implementation" config MEMFAULT_DEFAULT_GET_DEVICE_INFO bool "Enables the default memfault_platform_get_device_info() implementation" default y help Enable a default implementation of memfault_platform_get_device_info(). Disable this config to implement a custom version of the function. config MEMFAULT_DEVICE_INFO_HARDWARE_VERSION string "Hardware version used in memfault_esp_port_get_device_info()" default "$(IDF_TARGET)-proto" config MEMFAULT_DEVICE_INFO_SOFTWARE_VERSION string "Software version used in memfault_esp_port_get_device_info()" default "0.0.0" config MEMFAULT_DEVICE_INFO_SOFTWARE_TYPE string "Software type used in memfault_esp_port_get_device_info()" default "esp32-main" endmenu # Builtin device info implementation config MEMFAULT_PLATFORM_CONFIG_FILE string "Name of the Memfault platform config file" default "memfault_platform_config.h" help The name of the file which contains the Memfault platform config options. This file must be included in the project's include path. menuconfig MEMFAULT_DEEP_SLEEP_SUPPORT bool "Memfault deep sleep support" default n help Enable Memfault deep sleep support. This is required for tracking metrics across deep sleep cycles. This option is only required if the device uses deep sleep. if MEMFAULT_DEEP_SLEEP_SUPPORT config MEMFAULT_DEEP_SLEEP_ENABLE_DEBUG_LOG bool "Enable debug logging for deep sleep" default n help When enabled, debug logging will be enabled for deep sleep support. config MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_ON_SLEEP bool "Enable HTTP upload on deep sleep" default y help When enabled, the device will upload any buffered Memfault data to the Memfault cloud before going into deep sleep. config MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_INTERVAL_SECS int "Interval in seconds between deep sleep HTTP uploads" default 3600 depends on MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_ON_SLEEP help The interval in seconds between deep sleep HTTP uploads. This is the maximum amount of time the device will wait before uploading buffered data to the Memfault cloud. config MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_TRIGGER_LOGS bool "Enable HTTP upload on deep sleep trigger logs" default y depends on MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_ON_SLEEP help Upload buffered logs when performing a deep sleep HTTP upload config MEMFAULT_DEEP_SLEEP_HEARTBEAT_ON_SLEEP bool "Enable heartbeat on deep sleep" default y help When enabled, the device will check the end-of-heartbeat interval before going into deep sleep. If the interval has been reached, the device will capture a heartbeat. config MEMFAULT_DEEP_SLEEP_METRICS bool "Enable deep sleep metrics" default y help Enable default deep sleep metrics such as sleep time and wake count. endif # MEMFAULT_DEEP_SLEEP_SUPPORT choice MEMFAULT_PLATFORM_TIME_SINCE_BOOT prompt "Select the time since boot implementation" default MEMFAULT_PLATFORM_TIME_SINCE_BOOT_DEEP_SLEEP if MEMFAULT_DEEP_SLEEP_SUPPORT default MEMFAULT_PLATFORM_TIME_SINCE_BOOT_ESP_TIMER help Select the implementation of memfault_platform_get_time_since_boot_ms() config MEMFAULT_PLATFORM_TIME_SINCE_BOOT_ESP_TIMER bool "Use esp_timer_get_time() to get time since boot" help Use esp_timer_get_time() to get the time since boot in milliseconds. This is the default implementation and is recommended for most use cases. config MEMFAULT_PLATFORM_TIME_SINCE_BOOT_DEEP_SLEEP bool "Deep-sleep compensating implementation" help Required when esp32 deep sleep is used. See https://docs.memfault.com/docs/mcu/deep-sleep for a detailed guide. config MEMFAULT_PLATFORM_TIME_SINCE_BOOT_CUSTOM bool "User provided implementation" help User must implement memfault_platform_get_time_since_boot_ms(). endchoice config MEMFAULT_USER_CONFIG_SILENT_FAIL bool "Continue with build even if user configuration of Memfault SDK is missing" default y help When enabled, __has_include is used to conditionally include the three Memfault configuration files to a port if they exist instead of failing to compile if they do not: memfault_platform_config.h memfault_metrics_heartbeat_config.def memfault_trace_reason_user_config.def config MEMFAULT_HTTP_CLIENT_TIMEOUT_MS int "The HTTP client timeout in milliseconds" default 5000 help The Memfault HTTP client timeout in milliseconds. This is the maximum amount of time the HTTP client will wait for a response from the server. menu "Metrics" config MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS int "The interval in seconds between heartbeats" default 3600 help The interval in seconds between heartbeat metrics tallying. config MEMFAULT_METRICS_HEARTBEAT_TIMER_ENABLE default y bool "Enable the built-in heartbeat timer" help When enabled, the Memfault SDK will install a timer to trigger heartbeat metrics collection. config MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT bool "Initialize platform-specific connectivity metrics on system init" default y depends on MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME help When enabled, memfault_metrics_boot() will call a platform-implemented memfault_platform_metrics_connectivity_boot() function. If not already implemented for the user's platform, the user must supply their own implementation. This function is typically used to register event handlers to that will mark the connectivity state changes when the device connects and disconnects. Marking these state changes is required to calculate the connectivity time metrics. config MEMFAULT_METRICS_LOGS_ENABLE bool "Enable collection of metrics for log message counts" default y help When enabled, Memfault will collect metrics for log messages. This can be useful for tracking the number of logs generated by the system. config MEMFAULT_LWIP_METRICS bool "Collect TCP/UDP metrics from LwIP" default y select LWIP_STATS config MEMFAULT_FREERTOS_TASK_RUNTIME_STATS bool "Enables the collection of freeRTOS task runtime stats as Memfault metrics" default y select FREERTOS_GENERATE_RUN_TIME_STATS help Collects the CPU usage as a percentage of total runtime. This metric is only available when using esp_timer for runtime stats to avoid issues with counter overflow. Note for FreeRTOS versions < 10.2.0 this will be a no-op. config MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT bool "Enables per-core cpu_usage_pct metrics for multi-core targets" default y depends on MEMFAULT_FREERTOS_TASK_RUNTIME_STATS depends on SOC_CPU_CORES_NUM > 1 help When enabled, collects a separate measure of each CPU core usage as cpu_usage_pct for cpu0 and cpu1_usage_pct for cpu1 config MEMFAULT_MBEDTLS_METRICS bool "Enable mbedTLS metrics" default y depends on ESP_TLS_USING_MBEDTLS && !MBEDTLS_CUSTOM_MEM_ALLOC help Collects metrics measuring current and maximum bytes allocated on the heap during the current heartbeat interval. config MEMFAULT_ESP_WIFI_METRICS bool "Enable ESP WiFi metrics" default y help Collects Memfault metrics measuring the minimum RSSI, time connected to WiFi, and the number of WiFi disconnects. config MEMFAULT_ESP_WIFI_CONNECTIVITY_TIME_METRICS bool "Enable connected time metrics for WiFi connection" default y depends on MEMFAULT_ESP_WIFI_METRICS && MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME help Collects the core metric for connectivity_connected_time based on WiFi connection state. config MEMFAULT_METRICS_CPU_TEMP bool "Enable CPU temperature metric" default y depends on !IDF_TARGET_ESP32 && !ENABLE_ARDUINO_DEPENDS help Collects a Memfault metric for the CPU die temperature. config MEMFAULT_METRICS_MEMORY_USAGE bool "Enable memory usage metrics" default y help Collects Memfault metrics for memory usage stats. config MEMFAULT_METRICS_SYNC_SUCCESS bool "Enable sync success metrics" default y help Memfault user-defined sync success metric component. More information at https://mflt.io/core-metrics . config MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS bool "Enable memfault sync success metrics" default y help Collects Memfault metrics for the number of successful and failed syncs to the Memfault cloud. More information at https://mflt.io/core-metrics . config MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME bool "Enable memfault connectivity time metrics" default y help Memfault connectivity time metric component. More information at https://mflt.io/core-metrics . config MEMFAULT_METRICS_BATTERY_ENABLE bool "Enable battery metrics" default n help Memfault battery metric component. More information at https://mflt.io/core-metrics . config MEMFAULT_METRICS_CHIP_ENABLE bool "Enable chip ID metrics" default y help Collect metrics about chip ID and revision. config MEMFAULT_METRICS_NETWORK_IO bool "Enable network I/O metrics" default y depends on ESP_NETIF_REPORT_DATA_TRAFFIC depends on MEMFAULT_ESP_WIFI_METRICS help Collects network I/O metrics for the device config MEMFAULT_METRICS_BOOT_TIME bool "Enable boot time metrics" default y help Collects boot time metrics for the device config MEMFAULT_METRICS_FLASH_ENABLE bool "Enable collection of flash metrics" default y select SPI_FLASH_ENABLE_COUNTERS help Collects flash metrics for the device endmenu # Metrics config MEMFAULT_ASSERT_ON_ALLOC_FAILURE bool "Assert on allocation failure" default n depends on !HEAP_ABORT_WHEN_ALLOCATION_FAILS help When enabled, the Memfault SDK will assert if any allocation fails. This can be useful for tracking down heap memory issues. Note that this operates similarly to HEAP_ABORT_WHEN_ALLOCATION_FAILS, but the Memfault Issue created will be tagged as "Out of Memory" instead of a generic "Assert". config MEMFAULT_PLATFORM_REBOOT_CUSTOM bool "Provide a custom memfault_platform_reboot() implementation" default n help When enabled, the user must provide their own implementation of memfault_platform_reboot(). This can be useful for platforms that require a specific reboot procedure. config MEMFAULT_HEAP_STATS bool "Heap tracing" default y imply HEAP_USE_HOOKS # HEAP_TRACING instruments malloc/free calls using the same -Wl,--wrap # approach, and is incompatible. depends on !HEAP_TRACING help When enabled, system heap stats are captured as part of the coredump. The heap stats subsystem collects info on basic heap allocation/deallocation (malloc/free). If CONFIG_HEAP_USE_HOOKS is not set, the blocks-in-use count will not be included. config MEMFAULT_HEAP_STATS_MAX_COUNT int "Maximum number of heap allocations to track" default 32 depends on MEMFAULT_HEAP_STATS help The maximum number of heap allocations to track. When the number of allocations exceeds this value, the oldest allocation is removed. config MEMFAULT_HEAP_STATS_CALLOC bool "Enable calloc tracking" default n depends on MEMFAULT_HEAP_STATS help When enabled, calloc tracking is enabled in addition to malloc. Some ESP-IDF libraries use calloc heavily, and can consume the available heap tracking bookkeeping space quickly. config MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE bool "Enable custom reboot reasons" default n help Enable the ability to define custom reboot reasons. This is useful for tracking down the root cause of a reboot when it is not supported by the default set of reboot reasons. config MEMFAULT_COMPACT_LOG_ENABLE bool "Enable compact logging" default n help When enabled, the Memfault SDK will use a compact representation for log messages written using the 'MEMFAULT_LOG_x' macros. Find more information here: https://mflt.io/compact-logs config MEMFAULT_LOG_USE_VPRINTF_HOOK bool "Hook into ESP-IDF logging with esp_log_set_vprintf to send logs to Memfault" default y help ESP-IDF logging provides a hook, `esp_log_set_vprintf()`, to redirect logs to a handler parameter. Enabling this config will call this function automatically in `memfault_boot()`. Disabling this config allows your application to use this hook instead. config MEMFAULT_WRAP_EVENT_LOOP_CREATE_DEFAULT bool "Wrap esp_event_loop_create_default() to suppress ESP_ERR_INVALID_STATE errors" default y help `esp_event_loop_create_default()` returns `ESP_ERR_INVALID_STATE` when called multiple times. This behavior can cause issues when used with `ESP_ERROR_CHECK()`. This option enables a wrapped version to suppress these errors and prevent crashes. config MEMFAULT_USE_MEMFAULT_BUILD_ID bool "Automatically insert Memfault Build ID" default y help Automatically insert the Memfault Build ID during the build process. config MEMFAULT_GGDB3 bool "Enable -ggdb3 support" default y help Enable support for `.debug_macro` output in the ELF, for enhanced debugging. config MEMFAULT_RECORD_REBOOT_ON_BOOT bool "Record reboot reason event during memfault_boot()" default y help Record the reboot reason event during memfault_boot(). Disable this if device info is not valid until later in startup, then include "memfault/esp_port/core.h" and call memfault_esp_port_collect_reset_info() once device info is ready to store the reboot reason event. config MEMFAULT_ENABLE_REBOOT_DIAG_DUMP bool "Print reboot reason information on boot" default y help Print out the MCU-derived and Memfault-stored reboot reason information on boot. Adds a few hundred bytes of code space to pretty print MCU-derived reason (e.g. "Watchdog"). # HTTPS Periodic Upload menuconfig MEMFAULT_HTTP_PERIODIC_UPLOAD bool "Enable a dedicated thread to periodically upload Memfault data over HTTPS" default n depends on ESP_HTTP_CLIENT_ENABLE_HTTPS if MEMFAULT_HTTP_PERIODIC_UPLOAD choice MEMFAULT_HTTP_PERIODIC_UPLOAD_TASK_TYPE prompt "Select task type to create for HTTP periodic upload" # SUPPORT_STATIC_ALLOCATION renamed to FREERTOS_SUPPORT_STATIC_ALLOCATION in 4.0.0 default MEMFAULT_HTTP_PERIODIC_UPLOAD_TASK_STATIC config MEMFAULT_HTTP_PERIODIC_UPLOAD_TASK_STATIC bool "Static memory" depends on (FREERTOS_SUPPORT_STATIC_ALLOCATION || SUPPORT_STATIC_ALLOCATION) config MEMFAULT_HTTP_PERIODIC_UPLOAD_TASK_DYNAMIC bool "Dynamic memory" endchoice config MEMFAULT_HTTP_PERIODIC_UPLOAD_STACK_SIZE int "Stack size for periodic upload task" default 4096 config MEMFAULT_HTTP_PERIODIC_UPLOAD_PRIORITY int "Priority of periodic upload task" default 1 range 1 25 help Set periority of HTTP periodic upload task. Defaults to low priority but can be increased if needed up to configMAX_PRIORITIES - 1 config MEMFAULT_HTTP_PERIODIC_UPLOAD_AUTO_START bool "Start the periodic upload task when memfault_boot() is called" default y config MEMFAULT_HTTP_PERIODIC_UPLOAD_INTERVAL_SECS int "Periodic upload interval to send data in seconds" default 3600 help The first check will run between [60, 60+MEMFAULT_HTTP_PERIODIC_UPLOAD_INTERVAL_SECS]. Subsequent checks will run at MEMFAULT_HTTP_PERIODIC_UPLOAD_INTERVAL_SECS intervals. config MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS bool "Upload log buffer in addition to other data" default n config MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA bool "Execute OTA during periodic HTTP check-in" default n help When enabled, the device will check for an OTA update during the periodic HTTP check-in. If an update is available, it will be downloaded and installed. For more details and options, see "memfault/esp_port/http_client.h" in the Memfault SDK. config MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA_CUSTOM_CBS bool "Use custom OTA callbacks" default n depends on MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA help When enabled, the user can provide custom callbacks to be executed during the OTA process by defining g_memfault_ota_update_handler. endif # MEMFAULT_HTTP_PERIODIC_UPLOAD config MEMFAULT_HTTP_PARTIAL_DOWNLOAD_ENABLE bool "Enable partial download support for OTA updates" default y if ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD help When enabled, the Memfault SDK can download a firmware image in multiple HTTP requests. config MEMFAULT_HTTP_MAX_REQUEST_SIZE int "Maximum size of an HTTP request in bytes" default 0 if !MEMFAULT_HTTP_PARTIAL_DOWNLOAD_ENABLE # set to default MBEDTLS_SSL_IN_CONTENT_LEN - 1kB estimated TLS overhead default 15360 if MEMFAULT_HTTP_PARTIAL_DOWNLOAD_ENABLE help The maximum size of an HTTP request in bytes. This is the maximum amount of data that can be sent in a single HTTP request. config MEMFAULT_USE_NTP bool "Use NTP to set the system time" default y help When enabled, the Memfault SDK will use NTP to set the system time. This is used for timestamping events on device. Disable this config to use a custom time syncing method. config MEMFAULT_NTP_SERVER string "NTP server to use" default "pool.ntp.org" depends on MEMFAULT_USE_NTP help The NTP server to use for setting the system time. config MEMFAULT_SYSTEM_TIME bool "Use system time for event timestamps" default y help When enabled, the Memfault SDK will timestamp events with the on-device system time, `time()`. endmenu endif # !MEMFAULT_DISABLE ================================================ FILE: ports/esp_idf/memfault/common/memfault_compact_log.ld ================================================ /* Custom section to be included in the linker command file to support compact logs. Read more at https://mflt.io/compact-logs */ SECTIONS { log_fmt 0xF0000000 (INFO) : { /* explicitly define this symbol. some versions of the xtensa toolchain (esp-2020r3-8.4.0 at least) incorrectly places it by default at 8 bytes into the log_fmt section, which causes decoding to fail. */ __start_log_fmt = ABSOLUTE(.); KEEP(*(*.log_fmt_hdr)) KEEP(*(log_fmt)) } } ================================================ FILE: ports/esp_idf/memfault/common/memfault_esp_freertos.lf ================================================ # This linker fragment should yield a similar snippet in # /esp-idf/esp_system/ld/sections.ld # The important lines are EXCLUDE_FILE and the symbols surrounding the # task and timer rules #/* Shared RAM */ # .dram0.bss (NOLOAD) : # { # . = ALIGN (8); # _bss_start = ABSOLUTE(.); # # ... # *(EXCLUDE_FILE(*libfreertos.a:tasks.* *libfreertos.a:timers.*) .bss EXCLUDE_FILE(*libfreertos.a:tasks.* *libfreertos.a:timers.*) .bss.*) # *(EXCLUDE_FILE(*libfreertos.a:tasks.* *libfreertos.a:timers.*) COMMON) # ... # # ... # _memfault_capture_tasks_start = ABSOLUTE(.); # *libfreertos.a:tasks.*(.bss .bss.* COMMON) # _memfault_capture_tasks_end = ABSOLUTE(.); # _memfault_capture_timers_start = ABSOLUTE(.); # *libfreertos.a:timers.*(.bss .bss.* COMMON) # _memfault_capture_timers_end = ABSOLUTE(.); # ... # } > dram0_0_seg # Create a scheme to describe input sections to an output section [scheme:memfault] entries: common -> dram0_bss bss -> dram0_bss legacy_bss -> dram0_bss # Create a mapping using the defined scheme. Add linker symbols around # to capture start and end of task and timer location. # Note: ldgen does not properly generate rules for section fragments that include multiple # input sections. To work around this, we duplicate a rule for both bss and common section # fragments [mapping:memfault_freertos] archive: libfreertos.a entries: timers (memfault); bss-> dram0_bss SURROUND(memfault_timers_bss), legacy_bss-> dram0_bss SURROUND(memfault_timers_sbss), common -> dram0_bss SURROUND(memfault_timers_common) tasks (memfault); bss -> dram0_bss SURROUND(memfault_tasks_bss), legacy_bss -> dram0_bss SURROUND(memfault_tasks_sbss), common -> dram0_bss SURROUND(memfault_tasks_common) ================================================ FILE: ports/esp_idf/memfault/common/memfault_fault_handler.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Fault handler hook into ESP-IDF's fault handling system. #include #include #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" #include "esp_private/system_internal.h" #ifdef __XTENSA__ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) #include "xtensa_api.h" #else #include "freertos/xtensa_api.h" #endif #include "memfault/panics/arch/xtensa/xtensa.h" #elif __riscv #include "memfault/panics/arch/riscv/riscv.h" #include "riscv/rvruntime-frames.h" #endif #include "memfault/panics/coredump.h" #include "memfault/panics/fault_handling.h" #ifndef ESP_PLATFORM #error "The port assumes the esp-idf is in use!" #endif // Only include if watchpoint stack overflow detection enabled #if defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) #include "soc/soc_caps.h" #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 void memfault_fault_handling_assert(void *pc, void *lr) { memfault_arch_fault_handling_assert(pc, lr, kMfltRebootReason_Assert); abort(); } // This wrap handles the libc assert() calls, which we cannot // intercept at __assert_func() as usual because that's already supplied by // esp-idf. Note that if the assert path went through // MEMFAULT_ASSERT()->memfault_fault_handling_assert()->abort()->__wrap_panic_abort(), // memfault_arch_fault_handling_assert() is called twice. This isn't a problem, // because there's logic inside memfault_reboot_tracking_mark_reset_imminent() // to ignore the second call, and this gets us the highest-frame PC and LR in // the assert info when MEFMAULT_ASSERT() is called. void IRAM_ATTR __attribute__((noreturn, no_sanitize_undefined)) __real_panic_abort( const char *details); void IRAM_ATTR __attribute__((noreturn, no_sanitize_undefined)) __wrap_panic_abort( const char *details) { void *pc; MEMFAULT_GET_PC(pc); void *lr; MEMFAULT_GET_LR(lr); memfault_arch_fault_handling_assert(pc, lr, kMfltRebootReason_Assert); __real_panic_abort(details); } // 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"); void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { memfault_arch_fault_handling_assert(pc, lr, extra_info->assert_reason); abort(); } //! Convert the esp-idf arch-specific frame info into the Memfault format static sMfltRegState prv_esp_frame_info_to_memfault(const void *frame) { #ifdef __XTENSA__ const XtExcFrame *fp = frame; // Clear "EXCM" bit so we don't have to correct PS.OWB to get a good unwind This will also be // more reflective of the state of the register prior to the "panicHandler" being invoked const uint32_t corrected_ps = fp->ps & ~(PS_EXCM_MASK); sMfltRegState reg = { .collection_type = (uint32_t)kMemfaultEsp32RegCollectionType_ActiveWindow, .pc = fp->pc, .ps = corrected_ps, .sar = fp->sar, // the below registers are not available on the esp32s2; leave them zeroed // in the coredump #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3 .lbeg = fp->lbeg, .lend = fp->lend, .lcount = fp->lcount, #endif .exccause = fp->exccause, .excvaddr = fp->excvaddr, }; // Bulk copy the general registers. This saves a substantial amount of code // size compared to copying each register individually (~500 bytes). MEMFAULT_STATIC_ASSERT( sizeof(reg.a) == offsetof(XtExcFrame, a15) + sizeof(fp->a15) - offsetof(XtExcFrame, a0), "register size mismatch"); memcpy(®.a, &fp->a0, sizeof(reg.a)); return reg; #elif __riscv const RvExcFrame *fp = frame; return (sMfltRegState){ .mepc = fp->mepc, .ra = fp->ra, .sp = fp->sp, .gp = fp->gp, .tp = fp->tp, .t = { fp->t0, fp->t1, fp->t2, fp->t3, fp->t4, fp->t5, fp->t6, }, .s = { fp->s0, fp->s1, fp->s2, fp->s3, fp->s4, fp->s5, fp->s6, fp->s7, fp->s8, fp->s9, fp->s10, fp->s11, }, .a = { fp->a0, fp->a1, fp->a2, fp->a3, fp->a4, fp->a5, fp->a6, fp->a7, }, .mstatus = fp->mstatus, .mtvec = fp->mtvec, .mcause = fp->mcause, .mtval = fp->mtval, .mhartid = fp->mhartid, }; #endif // __XTENSA__ } //! Invoked when a panic is detected in the esp-idf when coredumps are enabled //! //! @note This requires the following sdkconfig options: //! CONFIG_ESP32_ENABLE_COREDUMP=y //! CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=y //! //! @note This is a drop in replacement for the pre-existing flash coredump handler. //! 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 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 // ESP_IDF_VERSION void __wrap_esp_core_dump_to_flash(panic_info_t *info) { #endif // ESP_IDF_VERSION eMemfaultRebootReason reason; // Convert ESP-IDF panic reason to Memfault reboot reason switch (info->exception) { case PANIC_EXCEPTION_DEBUG: reason = kMfltRebootReason_DebuggerHalted; break; case PANIC_EXCEPTION_IWDT: reason = kMfltRebootReason_SoftwareWatchdog; break; case PANIC_EXCEPTION_TWDT: reason = kMfltRebootReason_TaskWatchdog; break; default: // esp_reset_reason_get_hint() is safe to call from panic context. In the // case of Task Watchdog abort, the exception info is not set, so fall // back on the reset reason to classify the reboot. switch (esp_reset_reason_get_hint()) { case ESP_RST_TASK_WDT: reason = kMfltRebootReason_TaskWatchdog; break; default: // Default to HardFault for other types reason = kMfltRebootReason_HardFault; break; } break; } sMfltRegState regs[MEMFAULT_COREDUMP_CPU_COUNT] = { 0 }; #ifdef __XTENSA__ // We can get core_id from info->core or esp_cpu_get_core_id(). int core_id = info->core; regs[core_id] = prv_esp_frame_info_to_memfault(info->frame); #if MEMFAULT_COREDUMP_CPU_COUNT == 2 #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3) #error "Dual core support unavailable for this platform, please contact Memfault support" #endif // If available, the other core's registers are in the ESP-IDF global g_exc_frames. We only // support dual-core SOCs currently. int other_core_id = core_id ^ 1; if (g_exc_frames[other_core_id] != NULL) { regs[other_core_id] = prv_esp_frame_info_to_memfault(g_exc_frames[other_core_id]); } else { // If the other core's frame is not available, we can't collect a coredump // for it. Mark it with the correct format for loading into Memfault, but // leave all other data blank. regs[other_core_id] = (sMfltRegState){ .collection_type = (uint32_t)kMemfaultEsp32RegCollectionType_ActiveWindow, }; } #endif // MEMFAULT_COREDUMP_CPU_COUNT == 2 // If enabled, check if exception was triggered by stackoverflow type #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 */ // Mask to select bits indicating a data breakpoint/watchpoint #define DBREAK_EXCEPTION_MASK (1 << 2) #define _WATCHPOINT_VAL_SHIFT (8) #define _WATCHPOINT_VAL_MASK (0xF << _WATCHPOINT_VAL_SHIFT) // Extracts DBNUM bits to determine watchpoint that triggered exception #define WATCHPOINT_VAL_GET(reg) ((reg & _WATCHPOINT_VAL_MASK) >> _WATCHPOINT_VAL_SHIFT) // Watchpoint to detect stack overflow #define END_OF_STACK_WATCHPOINT_VAL (SOC_CPU_WATCHPOINTS_NUM - 1) if (info->exception == PANIC_EXCEPTION_DEBUG) { // Read debugcause register into debug_cause local var int debug_cause; asm("rsr.debugcause %0" : "=r"(debug_cause)); // Check that DBREAK bit is set and that stack overflow watchpoint was the source if ((debug_cause & DBREAK_EXCEPTION_MASK) && (WATCHPOINT_VAL_GET(debug_cause) == END_OF_STACK_WATCHPOINT_VAL)) { reason = kMfltRebootReason_StackOverflow; } } #endif // defined(CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK) #elif __riscv regs[0] = prv_esp_frame_info_to_memfault(info->frame); #endif // __XTENSA__ memfault_fault_handler(regs, reason); } // Ensure the substituted function signature matches the original function _Static_assert(__builtin_types_compatible_p( #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))) __typeof__(&esp_core_dump_write), __typeof__(&__wrap_esp_core_dump_write)), #else __typeof__(&esp_core_dump_to_flash), __typeof__(&__wrap_esp_core_dump_to_flash)), #endif "Error: core dump handler is not compatible with esp-idf's default implementation"); ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_core.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include #include #include #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/metrics/metrics.h" #ifndef ESP_PLATFORM #error "The port assumes the esp-idf is in use!" #endif #include "esp_attr.h" #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" #include "freertos/semphr.h" #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) #include "esp_cpu.h" #else #include "soc/cpu.h" #endif #include "esp_log.h" #include "esp_system.h" #include "esp_timer.h" // We unfortunately have to traverse to access the panic_restart() declaration #include "../port/include/port/panic_funcs.h" #if defined(CONFIG_MEMFAULT_USE_NTP) // If ESP-IDF version < 5.1, print a warning and disable NTP. The API was added // in this commit: // https://github.com/espressif/esp-idf/commit/2f1d30d155411a90883e3166ad7c4a7ac5042a0f #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) #define MEMFAULT_USE_NTP 1 #else #warning \ "CONFIG_MEMFAULT_USE_NTP is enabled but ESP-IDF version < v5.1. Set CONFIG_MEMFAULT_USE_NTP=n to suppress this warning." #define MEMFAULT_USE_NTP 0 #endif #else #define MEMFAULT_USE_NTP 0 #endif // defined(CONFIG_MEMFAULT_USE_NTP) #if MEMFAULT_USE_NTP #include "esp_netif_sntp.h" #endif static uint8_t s_event_storage[CONFIG_MEMFAULT_EVENT_STORAGE_RAM_SIZE]; static const sMemfaultEventStorageImpl *s_evt_storage; static eMemfaultRebootReason s_reboot_reason; static uint8_t s_log_buf_storage[CONFIG_MEMFAULT_LOG_STORAGE_RAM_SIZE]; // The default '.noninit' section is placed at the end of DRAM, which can easily // move abosolute position if there's any change in the size of that section (eg // through OTA update). To better preserve the absolute position of the reboot // tracking data, we place it in the RTC noinit section '.rtc_noinit' static RTC_NOINIT_ATTR uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; #if !defined(CONFIG_MEMFAULT_DATA_CHUNK_HANDLERS_CUSTOM) MEMFAULT_WEAK bool memfault_esp_port_data_available(void) { return memfault_packetizer_data_available(); } MEMFAULT_WEAK bool memfault_esp_port_get_chunk(void *buf, size_t *buf_len) { return memfault_packetizer_get_chunk(buf, buf_len); } #endif // !defined(CONFIG_MEMFAULT_DATA_CHUNK_HANDLERS_CUSTOM) #if defined(CONFIG_MEMFAULT_PLATFORM_TIME_SINCE_BOOT_ESP_TIMER) uint64_t memfault_platform_get_time_since_boot_ms(void) { const int64_t time_since_boot_us = esp_timer_get_time(); return (uint64_t)(time_since_boot_us / 1000) /* us per ms */; } #endif bool memfault_arch_is_inside_isr(void) { return xPortInIsrContext(); } MEMFAULT_NORETURN void memfault_sdk_assert_func_noreturn(void) { // Note: The esp-idf implements abort which will invoke the esp-idf coredump handler as well as a // chip reboot so we just piggback off of that abort(); } void memfault_platform_halt_if_debugging(void) { #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) if (esp_cpu_dbgr_is_attached()) { #else if (esp_cpu_in_ocd_debug_mode()) { #endif MEMFAULT_BREAKPOINT(); } } static eMemfaultRebootReason prv_record_reboot_reason(void) { eMemfaultRebootReason reboot_reason = kMfltRebootReason_Unknown; int esp_reset_cause = (int)esp_reset_reason(); switch (esp_reset_cause) { case ESP_RST_POWERON: reboot_reason = kMfltRebootReason_PowerOnReset; break; case ESP_RST_SW: reboot_reason = kMfltRebootReason_SoftwareReset; break; case ESP_RST_INT_WDT: reboot_reason = kMfltRebootReason_SoftwareWatchdog; break; case ESP_RST_TASK_WDT: reboot_reason = kMfltRebootReason_TaskWatchdog; break; case ESP_RST_WDT: reboot_reason = kMfltRebootReason_HardwareWatchdog; break; case ESP_RST_DEEPSLEEP: reboot_reason = kMfltRebootReason_DeepSleep; break; case ESP_RST_BROWNOUT: reboot_reason = kMfltRebootReason_BrownOutReset; break; case ESP_RST_PANIC: reboot_reason = kMfltRebootReason_UnknownError; break; case ESP_RST_UNKNOWN: default: reboot_reason = kMfltRebootReason_Unknown; break; } #if defined(CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP) MEMFAULT_LOG_INFO("Reboot Reason, ESP=0x%04x", esp_reset_cause); // 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("Reset Cause: %s", esp_rst_reasons[i].str); break; } } #endif // MEMFAULT_ENABLE_REBOOT_DIAG_DUMP const sResetBootupInfo reset_info = { .reset_reason_reg = esp_reset_cause, .reset_reason = reboot_reason, }; memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); #if defined(CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP) sMfltRebootReason stored_reboot_reason; int rv = memfault_reboot_tracking_get_reboot_reason(&stored_reboot_reason); if (!rv) { MEMFAULT_LOG_INFO("Reset Cause Stored: 0x%04x", stored_reboot_reason.prior_stored_reason); } #endif // MEMFAULT_ENABLE_REBOOT_DIAG_DUMP return reboot_reason; } void memfault_esp_port_collect_reset_info(void) { if (s_evt_storage == NULL) { MEMFAULT_LOG_ERROR("memfault_esp_port_collect_reset_info() called before memfault_boot()"); return; } #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT) if (s_reboot_reason == kMfltRebootReason_DeepSleep) { return; } #endif memfault_reboot_tracking_collect_reset_info(s_evt_storage); } static SemaphoreHandle_t s_memfault_lock; void memfault_lock(void) { xSemaphoreTakeRecursive(s_memfault_lock, portMAX_DELAY); } void memfault_unlock(void) { xSemaphoreGiveRecursive(s_memfault_lock); } int memfault_esp_port_vprintf_log_hook(const char *fmt, va_list args) { // copy result into memfault log buffer collected as part of a coredump char log_buf[MEMFAULT_LOG_MAX_LINE_SAVE_LEN + 1]; const size_t available_space = sizeof(log_buf); const int rv = vsnprintf(log_buf, available_space, fmt, args); if (rv <= 0) { return -1; } if ((rv == 0) && (log_buf[0] == '\n')) { // ignore empty lines return 0; } size_t bytes_written = (size_t)rv; if (bytes_written >= available_space) { bytes_written = available_space - 1; } memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Info, log_buf, bytes_written); return rv; } #if defined(CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK) // The esp32 uses vprintf() for dumping to console. The libc implementation used requires a lot of // stack space. We therefore prevent this function from being inlined so the log_buf allocation // in `memfault_esp_port_vprintf_log_hook()` does not wind up in that path. __attribute__((noinline)) static int prv_copy_log_to_mflt_buffer(const char *fmt, va_list args) { return memfault_esp_port_vprintf_log_hook(fmt, args); } static int prv_memfault_log_wrapper(const char *fmt, va_list args) { prv_copy_log_to_mflt_buffer(fmt, args); // flush to stdout return vprintf(fmt, args); } #endif // defined(CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK) #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. static void prv_alloc_failed_callback(size_t size, uint32_t caps, const char *function_name) { MEMFAULT_LOG_ERROR("Failed to allocate %d bytes with caps %" PRIu32 " in %s", size, caps, function_name); MEMFAULT_ASSERT_WITH_REASON(0, kMfltRebootReason_OutOfMemory); } #endif void memfault_boot(void) { s_memfault_lock = xSemaphoreCreateRecursiveMutex(); // set up log collection so recent logs can be viewed in coredump memfault_log_boot(s_log_buf_storage, sizeof(s_log_buf_storage)); #if defined(CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK) esp_log_set_vprintf(&prv_memfault_log_wrapper); #endif s_reboot_reason = prv_record_reboot_reason(); s_evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(s_evt_storage); #if defined(CONFIG_MEMFAULT_RECORD_REBOOT_ON_BOOT) memfault_esp_port_collect_reset_info(); #endif sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(s_evt_storage, &boot_info); memfault_build_info_dump(); #if defined(CONFIG_MEMFAULT_CLI_ENABLED) // register CLI for easily testing Memfault memfault_register_cli(); #endif #if defined(CONFIG_MEMFAULT_ASSERT_ON_ALLOC_FAILURE) heap_caps_register_failed_alloc_callback(prv_alloc_failed_callback); #endif #if defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_AUTO_START) memfault_esp_port_http_periodic_upload_start(); #endif #if MEMFAULT_USE_NTP esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(CONFIG_MEMFAULT_NTP_SERVER); const esp_err_t err = esp_netif_sntp_init(&config); if (err != ESP_OK) { MEMFAULT_LOG_ERROR("Failed to initialize SNTP, err %d", err); } #endif // Log an error if there is not enough space to save the configured coredump // data. Run the check here, which is after app_init(), so TWDT should be // configured by now if CONFIG_ESP_TASK_WDT_INIT=y. memfault_coredump_storage_check_size(); } #if !defined(CONFIG_MEMFAULT_PLATFORM_REBOOT_CUSTOM) MEMFAULT_NORETURN void memfault_platform_reboot(void) { panic_restart(); } #endif #if defined(CONFIG_MEMFAULT_AUTOMATIC_INIT) // register an initialization routine that will be run from do_global_ctors() static void __attribute__((constructor)) prv_memfault_boot(void) { memfault_boot(); } #endif #if CONFIG_MEMFAULT_HEAP_STATS extern void *__real_malloc(size_t size); extern void *__real_calloc(size_t nmemb, size_t size); extern void __real_free(void *ptr); static atomic_bool s_memfault_heap_stats_locked = ATOMIC_VAR_INIT(false); static bool prv_heap_lock_acquire(void) { // Only call into heap stats from non-ISR context if (memfault_arch_is_inside_isr()) { return false; } bool false_var = false; return atomic_compare_exchange_strong(&s_memfault_heap_stats_locked, &false_var, true); } static void prv_heap_lock_release(void) { atomic_store(&s_memfault_heap_stats_locked, false); } void *__wrap_malloc(size_t size) { void *ptr = __real_malloc(size); // Heap stats requires holding a lock if (prv_heap_lock_acquire()) { MEMFAULT_HEAP_STATS_MALLOC(ptr, size); prv_heap_lock_release(); } return ptr; } void *__wrap_calloc(size_t nmemb, size_t size) { void *ptr = __real_calloc(nmemb, size); // Heap stats requires holding a lock if (prv_heap_lock_acquire()) { MEMFAULT_HEAP_STATS_MALLOC(ptr, nmemb * size); prv_heap_lock_release(); } return ptr; } void __wrap_free(void *ptr) { // Heap stats requires holding a lock if (prv_heap_lock_acquire()) { MEMFAULT_HEAP_STATS_FREE(ptr); prv_heap_lock_release(); } __real_free(ptr); } //! Use the ESP-IDF heap tracing hooks to do the in-use tallying #include "esp_heap_caps.h" void esp_heap_trace_alloc_hook(void *ptr, size_t size, uint32_t caps) { (void)ptr, (void)size, (void)caps; if (prv_heap_lock_acquire()) { memfault_heap_stats_increment_in_use_block_count(); prv_heap_lock_release(); } } void esp_heap_trace_free_hook(void *ptr) { (void)ptr; if (prv_heap_lock_acquire()) { memfault_heap_stats_decrement_in_use_block_count(); prv_heap_lock_release(); } } #endif ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Port of the Memfault SDK to the esp-idf for esp32 devices #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" // non-module headers below #include #include #include #include "esp_idf_version.h" #include "esp_intr_alloc.h" #include "esp_partition.h" // The include order below, especially 'soc/soc.h', is significant to support // the various ESP-IDF versions, where the definitions moved around. It's // possible it could be tidied up but this configuration does work across the // supported ESP-IDF versions. #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) #include "spi_flash_mmap.h" #else #include "esp_spi_flash.h" #include "soc/soc.h" #endif #include "esp_task_wdt.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/task_watchdog.h" #include "memfault/esp_port/coredump.h" #include "memfault/esp_port/spi_flash.h" #include "memfault/util/crc16.h" // Needed for >= v4.4.3 default coredump collection #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 3) #include "memfault/ports/freertos_coredump.h" #endif #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/uart.h" #elif CONFIG_IDF_TARGET_ESP32S2 #include "esp32s2/rom/uart.h" #elif CONFIG_IDF_TARGET_ESP32S3 #include "esp32s3/rom/uart.h" #elif CONFIG_IDF_TARGET_ESP32C3 #include "esp32c3/rom/uart.h" #elif CONFIG_IDF_TARGET_ESP32C6 #include "esp32c6/rom/uart.h" #endif #define MEMFAULT_ESP32_CONSOLE_UART_NUM CONFIG_ESP_CONSOLE_UART_NUM // Factor out issues with Espressif's ESP32 to ESP conversion in sdkconfig #define COREDUMPS_ENABLED (CONFIG_ESP32_ENABLE_COREDUMP || CONFIG_ESP_COREDUMP_ENABLE) #define COREDUMP_TO_FLASH_ENABLED \ (CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH || CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) #if !COREDUMPS_ENABLED || !COREDUMP_TO_FLASH_ENABLED #error \ "Memfault SDK integration requires CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=y sdkconfig setting" #endif #define ESP_IDF_COREDUMP_PART_INIT_MAGIC 0x45524f43 // If there is no coredump partition defined or one cannot be defined // the user can try using an OTA slot instead. #if CONFIG_MEMFAULT_COREDUMP_USE_OTA_SLOT #include "esp_ota_ops.h" #define GET_COREDUMP_PARTITION() esp_ota_get_next_update_partition(NULL); #else #define GET_COREDUMP_PARTITION() \ esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL) #endif typedef struct { uint32_t magic; esp_partition_t partition; uint32_t crc; } sEspIdfCoredumpPartitionInfo; static sEspIdfCoredumpPartitionInfo s_esp32_coredump_partition_info; static const uintptr_t esp32_dram_start_addr = SOC_DRAM_LOW; static const uintptr_t esp32_dram_end_addr = SOC_DRAM_HIGH; static uint32_t prv_get_partition_info_crc(void) { return memfault_crc16_compute(MEMFAULT_CRC16_INITIAL_VALUE, &s_esp32_coredump_partition_info, offsetof(sEspIdfCoredumpPartitionInfo, crc)); } static const esp_partition_t *prv_get_core_partition(void) { if (s_esp32_coredump_partition_info.magic != ESP_IDF_COREDUMP_PART_INIT_MAGIC) { return NULL; } return &s_esp32_coredump_partition_info.partition; } // We use two different default coredump collection methods // due to differences in esp-idf versions. The following helper // is only used for esp-idf >= 4.4.3 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 3) //! Helper function to get bss and common sections of task and timer objects size_t prv_get_freertos_bss_common(sMfltCoredumpRegion *regions, size_t num_regions) { if (regions == NULL || num_regions == 0) { return 0; } size_t region_index = 0; extern uint32_t _memfault_timers_bss_start; extern uint32_t _memfault_timers_bss_end; extern uint32_t _memfault_timers_sbss_start; extern uint32_t _memfault_timers_sbss_end; extern uint32_t _memfault_timers_common_start; extern uint32_t _memfault_timers_common_end; extern uint32_t _memfault_tasks_bss_start; extern uint32_t _memfault_tasks_bss_end; extern uint32_t _memfault_tasks_sbss_start; extern uint32_t _memfault_tasks_sbss_end; extern uint32_t _memfault_tasks_common_start; extern uint32_t _memfault_tasks_common_end; // ldgen has a bug that does not exclude rules matching multiple input sections at the // same time. To work around this, we instead emit a symbol for each section we're attempting // to collect. This means 12 symbols (tasks + timers for sbss, bss, and common). If this is ever // fixed we can remove the need to collect 6 separate regions. // // Note: Some regions may be empty (size zero). That is fine - they are skipped when writing to // flash, so they use no storage. #define REGION_SIZE(start, end) ((uintptr_t)&(end) - (uintptr_t)&(start)) #define ADD_REGION(start, end) \ regions[region_index++] = \ MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&(start), REGION_SIZE(start, end)) ADD_REGION(_memfault_timers_bss_start, _memfault_timers_bss_end); ADD_REGION(_memfault_timers_sbss_start, _memfault_timers_sbss_end); ADD_REGION(_memfault_timers_common_start, _memfault_timers_common_end); ADD_REGION(_memfault_tasks_sbss_start, _memfault_tasks_sbss_end); ADD_REGION(_memfault_tasks_bss_start, _memfault_tasks_bss_end); ADD_REGION(_memfault_tasks_common_start, _memfault_tasks_common_end); return region_index; } #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 3) //! Simple implementation to ensure address is in SRAM range. //! //! @note The function is intentionally defined as weak so someone can //! easily override the port defaults by re-defining a non-weak version of //! the function in another file MEMFAULT_WEAK size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { const uintptr_t start_addr_int = (uintptr_t)start_addr; const uintptr_t end_addr_int = start_addr_int + desired_size; if ((start_addr_int < esp32_dram_start_addr) || (start_addr_int > esp32_dram_end_addr)) { return 0; } if (end_addr_int > esp32_dram_end_addr) { return esp32_dram_end_addr - start_addr_int; } return desired_size; } #if defined(CONFIG_MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG) && \ (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4)) static char s_mflt_task_watchdog_data[CONFIG_MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG_SIZE]; static void prv_task_watchdog_msg_handler(void *opaque, const char *msg) { const int call_count = *(int *)opaque; // increment the call count on every call *(int *)opaque = call_count + 1; // the first call is a caption, skip if (call_count == 0) { s_mflt_task_watchdog_data[0] = '\0'; return; } // after that, this function is called 3 times per triggered task: // msg_handler(opaque, "\n - "); // msg_handler(opaque, name); // msg_handler(opaque, cpu); // // we will copy only the name + cpu, in this format: // |"" ||"" ... if ((call_count - 1) % 3 == 0) { // skip the first call, which is a caption return; } // copy the name if ((call_count - 1) % 3 == 1) { strlcat(s_mflt_task_watchdog_data, "|\"", sizeof(s_mflt_task_watchdog_data)); strlcat(s_mflt_task_watchdog_data, msg, sizeof(s_mflt_task_watchdog_data)); strlcat(s_mflt_task_watchdog_data, "\"", sizeof(s_mflt_task_watchdog_data)); return; } // copy the cpu if ((call_count - 1) % 3 == 2) { strlcat(s_mflt_task_watchdog_data, msg, sizeof(s_mflt_task_watchdog_data)); strlcat(s_mflt_task_watchdog_data, "|", sizeof(s_mflt_task_watchdog_data)); return; } } size_t prv_get_task_watchdog_region(sMfltCoredumpRegion *regions, size_t num_regions) { if (num_regions == 0) { return 0; } // the TWDT might not be initialized, check it first. note: unfortunately this // unconditionally prints an error level log if the TWDT is not initialized, // and there's no other way as of ESP-IDF v5.5 to check the TWDT status. esp_err_t err = esp_task_wdt_status(NULL); if (err == ESP_ERR_INVALID_STATE) { return 0; } // run the task wdt printer to extract the data // keep track of the number of calls to the msg_handler int call_count = 0; err = esp_task_wdt_print_triggered_tasks(prv_task_watchdog_msg_handler, &call_count, NULL); if (err != ESP_OK) { return 0; } else { // attach the s_mflt_task_watchdog_data as a region *regions = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( s_mflt_task_watchdog_data, strnlen(s_mflt_task_watchdog_data, sizeof(s_mflt_task_watchdog_data) - 1)); return 1; } } #endif // CONFIG_MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG //! Collecting active stack, bss, data, and heap. //! //! In esp-idf >= 4.4.3, we additionally collect bss and stack regions for //! FreeRTOS tasks. const sMfltCoredumpRegion *memfault_esp_port_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { static sMfltCoredumpRegion s_coredump_regions[MEMFAULT_ESP_PORT_NUM_REGIONS]; int region_idx = 0; const size_t stack_size = memfault_platform_sanitize_address_range( crash_info->stack_address, MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT); // First, capture the active stack s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(crash_info->stack_address, stack_size); // Second, capture the task regions, if esp-idf >= 4.4.3 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 3) region_idx += memfault_freertos_get_task_regions( &s_coredump_regions[region_idx], MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx); // Third, capture the FreeRTOS-specific bss regions region_idx += prv_get_freertos_bss_common(&s_coredump_regions[region_idx], MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx); #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 3) // Conditionally capture the Task Watchdog data #if defined(CONFIG_MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG) && \ (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4)) region_idx += prv_get_task_watchdog_region(&s_coredump_regions[region_idx], MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx); #endif #if !defined(CONFIG_MEMFAULT_COREDUMP_REGIONS_THREAD_ONLY) // Next, capture all of .bss + .data that we can fit. extern uint32_t _data_start; extern uint32_t _data_end; extern uint32_t _bss_start; extern uint32_t _bss_end; extern uint32_t _heap_start; s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( &_bss_start, (uint32_t)((uintptr_t)&_bss_end - (uintptr_t)&_bss_start)); s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( &_data_start, (uint32_t)((uintptr_t)&_data_end - (uintptr_t)&_data_start)); // Finally, capture as much of the heap as we can fit s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( &_heap_start, (uint32_t)(esp32_dram_end_addr - (uintptr_t)&_heap_start)); #endif // !defined(CONFIG_MEMFAULT_COREDUMP_REGIONS_THREAD_ONLY) *num_regions = region_idx; return &s_coredump_regions[0]; } #if defined(CONFIG_MEMFAULT_COREDUMP_REGIONS_BUILT_IN) //! Built-in Coredump Regions function //! //! @note The function is intentionally defined as weak so someone can //! easily override the port defaults by re-defining a non-weak version of //! the function in another file MEMFAULT_WEAK const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { return memfault_esp_port_coredump_get_regions(crash_info, num_regions); } #endif // CONFIG_MEMFAULT_COREDUMP_REGIONS_BUILT_IN static uint32_t prv_get_adjusted_size(const esp_partition_t *core_part) { return #if CONFIG_MEMFAULT_COREDUMP_STORAGE_MAX_SIZE > 0 MEMFAULT_MIN(core_part->size, CONFIG_MEMFAULT_COREDUMP_STORAGE_MAX_SIZE) #else core_part->size #endif // coredump storage capacity is reduced by the write offset - (CONFIG_MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS * SPI_FLASH_SEC_SIZE); } //! Opens partition system on boot to determine where a coredump can be saved //! //! @note We override the default implementation using the GNU linkers --wrap feature //! @note Function invocation is here: //! https://github.com/espressif/esp-idf/blob/v4.0/components/esp32/cpu_start.c#L415-L422 void __wrap_esp_core_dump_init(void) { const esp_partition_t *const core_part = GET_COREDUMP_PARTITION(); if (core_part == NULL) { MEMFAULT_LOG_ERROR("Coredumps enabled but no partition exists!"); MEMFAULT_LOG_ERROR("Add \"coredump\" to your partition.csv file"); return; } MEMFAULT_LOG_INFO("Coredumps will be saved to '%s' partition", core_part->label); MEMFAULT_LOG_INFO("Using entry %p pointing to address 0x%08" PRIX32 " size %" PRIu32 " writeoffset %d", core_part, core_part->address, prv_get_adjusted_size(core_part), (CONFIG_MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS * SPI_FLASH_SEC_SIZE)); s_esp32_coredump_partition_info = (sEspIdfCoredumpPartitionInfo){ .magic = ESP_IDF_COREDUMP_PART_INIT_MAGIC, .partition = *core_part, }; s_esp32_coredump_partition_info.crc = prv_get_partition_info_crc(); } esp_err_t __wrap_esp_core_dump_image_get(size_t *out_addr, size_t *out_size) { if (out_addr == NULL || out_size == NULL) { return ESP_ERR_INVALID_ARG; } const esp_partition_t *core_part = prv_get_core_partition(); if (core_part == NULL) { return ESP_FAIL; } if (!memfault_coredump_has_valid_coredump(out_size)) { return ESP_ERR_INVALID_SIZE; } *out_addr = core_part->address + CONFIG_MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS * SPI_FLASH_SEC_SIZE; return ESP_OK; } const esp_partition_t *prv_validate_and_get_core_partition(void) { const uint32_t crc = prv_get_partition_info_crc(); if (crc != s_esp32_coredump_partition_info.crc) { return NULL; } return prv_get_core_partition(); } void memfault_platform_coredump_storage_clear(void) { const esp_partition_t *core_part = prv_get_core_partition(); if (core_part == NULL) { return; } const uint32_t invalidate = 0x0; if (core_part->size < sizeof(invalidate)) { return; } const uint32_t clear_address = core_part->address + (CONFIG_MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS * SPI_FLASH_SEC_SIZE); const esp_err_t err = memfault_esp_spi_flash_write(clear_address, &invalidate, sizeof(invalidate)); if (err != ESP_OK) { memfault_platform_log(kMemfaultPlatformLogLevel_Error, "Failed to write data to flash (%d)!", err); } } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { // we are about to perform a sequence of operations on coredump storage // sanity check that the memory holding the info is populated and not corrupted const esp_partition_t *core_part = prv_validate_and_get_core_partition(); if (core_part == NULL) { *info = (sMfltCoredumpStorageInfo){ 0 }; return; } #if defined(CONFIG_MEMFAULT_COREDUMP_STORAGE_LIMIT_SIZE) // confirm MAX_SIZE is aligned to SPI_FLASH_SEC_SIZE MEMFAULT_STATIC_ASSERT( (CONFIG_MEMFAULT_COREDUMP_STORAGE_MAX_SIZE % SPI_FLASH_SEC_SIZE == 0), "Error, check CONFIG_MEMFAULT_COREDUMP_STORAGE_MAX_SIZE value: " MEMFAULT_EXPAND_AND_QUOTE( CONFIG_MEMFAULT_COREDUMP_STORAGE_MAX_SIZE)); #endif *info = (sMfltCoredumpStorageInfo){ .size = prv_get_adjusted_size(core_part), .sector_size = SPI_FLASH_SEC_SIZE, }; } #if !CONFIG_ESP32_PANIC_SILENT_REBOOT #include "esp_private/panic_internal.h" static void prv_panic_safe_putchar(const char c) { panic_print_char(c); } #else /* !CONFIG_ESP32_PANIC_SILENT_REBOOT */ static void prv_panic_safe_putchar(char c) { } #endif /* CONFIG_ESP32_PANIC_SILENT_REBOOT */ static void prv_panic_safe_putstr(const char *str) { int idx = 0; while (str[idx] != 0) { prv_panic_safe_putchar(str[idx]); idx++; } } bool memfault_port_coredump_save_begin(void) { // Update Memfault task watchdog bookkeeping, if it's enabled memfault_task_watchdog_bookkeep(); // Disable the interrupt watchdog (IWDT) if it's enabled, to avoid contention // with the IWDT interrupt when the fault handler is executing. This is safe // to do when we're in the panic handler, because ESP-IDF's // esp_panic_handler() has already enabled the WDT_RWDT to hard-reset the chip // if the panic handler hangs. #if defined(CONFIG_ESP_INT_WDT) // Define ETS_INT_WDT_INUM for compatibility with < 5.1 #if !defined(ETS_INT_WDT_INUM) #define ETS_INT_WDT_INUM (ETS_T1_WDT_INUM) #endif esp_intr_disable_source(ETS_INT_WDT_INUM); #endif // defined(CONFIG_ESP_INT_WDT) prv_panic_safe_putstr("Saving Memfault Coredump!\r\n"); return (memfault_esp_spi_flash_coredump_begin() == 0); } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { const esp_partition_t *core_part = prv_get_core_partition(); if (core_part == NULL) { return false; } const size_t address = core_part->address + offset + (CONFIG_MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS * SPI_FLASH_SEC_SIZE); const esp_err_t err = memfault_esp_spi_flash_write(address, data, data_len); if (err != ESP_OK) { prv_panic_safe_putstr("coredump write failed"); } return (err == ESP_OK); } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { const esp_partition_t *core_part = prv_get_core_partition(); if (core_part == NULL) { return false; } if ((offset + read_len) > core_part->size) { return false; } const uint32_t address = core_part->address + offset + (CONFIG_MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS * SPI_FLASH_SEC_SIZE); const esp_err_t err = memfault_esp_spi_flash_read(address, data, read_len); return (err == ESP_OK); } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { const esp_partition_t *core_part = prv_get_core_partition(); if (core_part == NULL) { return false; } const size_t address = core_part->address + offset; // include the write offset to set the erase size, but erase starting from the // base address so the full partition will be erased erase_size += (CONFIG_MEMFAULT_COREDUMP_STORAGE_WRITE_OFFSET_SECTORS * SPI_FLASH_SEC_SIZE); const esp_err_t err = memfault_esp_spi_flash_erase_range(address, erase_size); if (err != ESP_OK) { prv_panic_safe_putstr("coredump erase failed"); } return (err == ESP_OK); } ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_debug_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! An example implementation of the logging memfault API for the ESP32 platform #include #include #include #include "memfault/config.h" #include "memfault/core/platform/debug_log.h" #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include "esp_log.h" #ifndef MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES #define MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES (128) #endif static const char *TAG __attribute__((unused)) = "mflt"; void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { va_list args; va_start(args, fmt); char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; vsnprintf(log_buf, sizeof(log_buf), fmt, args); switch (level) { case kMemfaultPlatformLogLevel_Debug: ESP_LOGD(TAG, "%s", log_buf); break; case kMemfaultPlatformLogLevel_Info: ESP_LOGI(TAG, "%s", log_buf); break; case kMemfaultPlatformLogLevel_Warning: ESP_LOGW(TAG, "%s", log_buf); break; case kMemfaultPlatformLogLevel_Error: ESP_LOGE(TAG, "%s", log_buf); break; default: break; } va_end(args); } void memfault_platform_log_raw(const char *fmt, ...) { va_list args; va_start(args, fmt); vprintf(fmt, args); printf("\n"); va_end(args); } void memfault_platform_hexdump(eMemfaultPlatformLogLevel level, const void *data, size_t data_len) { switch (level) { case kMemfaultPlatformLogLevel_Debug: ESP_LOG_BUFFER_HEX_LEVEL(TAG, data, data_len, ESP_LOG_DEBUG); break; case kMemfaultPlatformLogLevel_Info: ESP_LOG_BUFFER_HEX_LEVEL(TAG, data, data_len, ESP_LOG_INFO); break; case kMemfaultPlatformLogLevel_Warning: ESP_LOG_BUFFER_HEX_LEVEL(TAG, data, data_len, ESP_LOG_WARN); break; case kMemfaultPlatformLogLevel_Error: ESP_LOG_BUFFER_HEX_LEVEL(TAG, data, data_len, ESP_LOG_ERROR); break; default: break; } } ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_deep_sleep.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include "esp_idf_version.h" // rtc.h is used for ESP-IDF < v5.5, otherwise use esp_rtc_time.h #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) #include "rtc.h" #else #include "esp_rtc_time.h" #endif #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_ENABLE_DEBUG_LOG) #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #endif #include "esp_crc.h" #include "esp_log.h" #include "esp_sleep.h" #include "memfault/components.h" #include "memfault/esp_port/http_client.h" // ESP-IDF < v5.0 has a known bug with RTC time. Emit a build error on earlier // versions to prevent confusion: // https://github.com/espressif/esp-idf/issues/9448#issuecomment-1645354025 #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) #error "ESP-IDF version < v5.0 is not supported." #endif #if !defined(CONFIG_MEMFAULT_PLATFORM_TIME_SINCE_BOOT_DEEP_SLEEP) #error "CONFIG_MEMFAULT_PLATFORM_TIME_SINCE_BOOT_DEEP_SLEEP must be enabled in sdkconfig" #endif // Save some state in RTC memory to preserve it across deep sleep static RTC_NOINIT_ATTR struct MemfaultDeepSleepMetricsBackup { // magic number indicating the data has been backed up uint32_t event_storage_magic; // crc32 over each backup, for integrity uint32_t event_storage_crc32; // the data is stored into a single combined buffer to make it simpler to // manipulate uint8_t event_storage_data[MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES + CONFIG_MEMFAULT_EVENT_STORAGE_RAM_SIZE]; uint32_t metrics_magic; uint32_t metrics_crc32; uint8_t metrics_data[MEMFAULT_METRICS_CONTEXT_SIZE_BYTES]; uint32_t log_magic; uint32_t log_crc32; uint8_t log_data[MEMFAULT_LOG_STATE_SIZE_BYTES + CONFIG_MEMFAULT_LOG_STORAGE_RAM_SIZE]; uint32_t last_time_magic; uint64_t heartbeat_last_time; uint64_t upload_last_time; } s_mflt_metrics_backup_data; #define MEMFAULT_DEEP_SLEEP_MAGIC 0x5F3759DF static const char *TAG = "mflt_sleep"; static void prv_clear_backup_data(void) { s_mflt_metrics_backup_data.event_storage_magic = 0; s_mflt_metrics_backup_data.metrics_magic = 0; s_mflt_metrics_backup_data.log_magic = 0; } #if !defined(CONFIG_MEMFAULT_METRICS_HEARTBEAT_TIMER_ENABLE) //! Provide a stub implementation of memfault_platform_metrics_timer_boot(). //! Deep sleep enabled apps will not use a timer to trigger end of heartbeat //! collection, but instead will check on deep sleep entry. bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback callback) { (void)period_sec; (void)callback; return true; } #endif // !defined(MEMFAULT_METRICS_HEARTBEAT_TIMER_ENABLE) uint64_t memfault_platform_get_time_since_boot_ms(void) { // use the RTC timer to get the time since boot. this runs through deep sleep. const uint64_t time_since_boot_us = esp_rtc_get_time_us(); const uint64_t time_since_boot_ms = (uint64_t)(time_since_boot_us / 1000) /* us per ms */; return time_since_boot_ms; } static void prv_backup_event_storage(void) { if (!memfault_event_storage_booted()) { ESP_LOGW(TAG, "Event storage not booted, skipping backup"); return; } sMfltEventStorageSaveState state = memfault_event_storage_get_state(); if (state.context_len == MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES && state.storage_len == CONFIG_MEMFAULT_EVENT_STORAGE_RAM_SIZE) { // Lock when copying the data, to mitigate chances of modification memfault_lock(); { memcpy(&s_mflt_metrics_backup_data.event_storage_data[0], state.context, state.context_len); memmove( &s_mflt_metrics_backup_data.event_storage_data[MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES], state.storage, state.storage_len); } memfault_unlock(); } else { ESP_LOGW(TAG, "Event storage state size mismatch: %" PRIu32 " != %" PRIu32 " or %" PRIu32 " != %" PRIu32 "", (uint32_t)MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES, (uint32_t)state.context_len, (uint32_t)CONFIG_MEMFAULT_EVENT_STORAGE_RAM_SIZE, (uint32_t)state.storage_len); } s_mflt_metrics_backup_data.event_storage_magic = MEMFAULT_DEEP_SLEEP_MAGIC; s_mflt_metrics_backup_data.event_storage_crc32 = esp_crc32_le(0, s_mflt_metrics_backup_data.event_storage_data, sizeof(s_mflt_metrics_backup_data.event_storage_data)); ESP_LOGD(TAG, "Event storage buf usage: %" PRIu32 "/%" PRIu32 "", (uint32_t)memfault_event_storage_bytes_used(), (uint32_t)CONFIG_MEMFAULT_EVENT_STORAGE_RAM_SIZE); ESP_LOGD(TAG, "Event storage backup CRC32: %08" PRIu32 "", s_mflt_metrics_backup_data.event_storage_crc32); } static void prv_backup_metrics(void) { const void *ctx = memfault_metrics_get_state(); // Lock when copying the data, to mitigate chances of modification memfault_lock(); { // Copy the metrics context into the backup buffer memcpy(&s_mflt_metrics_backup_data.metrics_data[0], ctx, MEMFAULT_METRICS_CONTEXT_SIZE_BYTES); } memfault_unlock(); s_mflt_metrics_backup_data.metrics_crc32 = esp_crc32_le( 0, s_mflt_metrics_backup_data.metrics_data, sizeof(s_mflt_metrics_backup_data.metrics_data)); s_mflt_metrics_backup_data.metrics_magic = MEMFAULT_DEEP_SLEEP_MAGIC; ESP_LOGD(TAG, "Metrics backup CRC32: %08" PRIu32 "", s_mflt_metrics_backup_data.metrics_crc32); } static void prv_backup_logs(void) { if (!memfault_log_booted()) { ESP_LOGW(TAG, "Log storage not booted, skipping backup"); return; } sMfltLogSaveState state = memfault_log_get_state(); if (state.context_len == MEMFAULT_LOG_STATE_SIZE_BYTES && state.storage_len == CONFIG_MEMFAULT_LOG_STORAGE_RAM_SIZE) { // Lock when copying the data, to mitigate chances of modification memfault_lock(); { memcpy(&s_mflt_metrics_backup_data.log_data[0], state.context, state.context_len); memmove(&s_mflt_metrics_backup_data.log_data[MEMFAULT_LOG_STATE_SIZE_BYTES], state.storage, state.storage_len); } memfault_unlock(); } else { ESP_LOGW(TAG, "Event storage state size mismatch: %u != %u or %u != %u", MEMFAULT_LOG_STATE_SIZE_BYTES, state.context_len, CONFIG_MEMFAULT_LOG_STORAGE_RAM_SIZE, state.storage_len); } s_mflt_metrics_backup_data.log_magic = MEMFAULT_DEEP_SLEEP_MAGIC; s_mflt_metrics_backup_data.log_crc32 = esp_crc32_le(0, s_mflt_metrics_backup_data.log_data, sizeof(s_mflt_metrics_backup_data.log_data)); ESP_LOGD(TAG, "👉 Logs after this point are not backed up"); ESP_LOGD(TAG, "Log buf usage: %u/%u", memfault_log_get_unsent_count().bytes, CONFIG_MEMFAULT_LOG_STORAGE_RAM_SIZE); ESP_LOGD(TAG, "Log backup CRC32: %08" PRIu32 "", s_mflt_metrics_backup_data.log_crc32); } static void prv_check_and_trigger_heartbeat(void) { #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_HEARTBEAT_ON_SLEEP) // Check if it's time to trigger a heartbeat. We do this by checking if the // current time-since-boot exceeds the configured heartbeat interval // (CONFIG)MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS); if so, call // memfault_metrics_heartbeat_debug_trigger(), and record the time the // heartbeat actually fired. // // If we assume a strictly periodic deep sleep cycle, we will end up with // uniform heartbeat intervals, aligned to deep sleep sleep times, of no // shorter than the minimum configured interval. const uint64_t current_time = memfault_platform_get_time_since_boot_ms(); const uint64_t heartbeat_interval = CONFIG_MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS * 1000; // ms const uint64_t time_since_last_heartbeat = current_time - s_mflt_metrics_backup_data.heartbeat_last_time; if (time_since_last_heartbeat >= heartbeat_interval) { // trigger the heartbeat collection ESP_LOGD(TAG, "Triggering heartbeat, time since last: %" PRIu32 "", (uint32_t)time_since_last_heartbeat); memfault_reboot_tracking_reset_crash_count(); memfault_metrics_heartbeat_debug_trigger(); // record the time the heartbeat should have fired s_mflt_metrics_backup_data.heartbeat_last_time = current_time; } #endif // CONFIG_MEMFAULT_DEEP_SLEEP_HEARTBEAT_ON_SLEEP } static void prv_check_and_trigger_upload(void) { #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_ON_SLEEP) // Check if it's time to trigger an upload. We do this by checking if the // current time-since-boot exceeds the configured upload interval // (CONFIG)MEMFAULT_DEEP_SLEEP_UPLOAD_INTERVAL_SECS); if so, call // memfault_platform_trigger_upload(), and record the time the upload // actually fired. // // If we assume a strictly periodic deep sleep cycle, we will end up with // uniform upload intervals, aligned to deep sleep sleep times, of no // shorter than the minimum configured interval. const uint64_t current_time = memfault_platform_get_time_since_boot_ms(); const uint64_t upload_interval = CONFIG_MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_INTERVAL_SECS * 1000; // ms const uint64_t time_since_last_upload = current_time - s_mflt_metrics_backup_data.upload_last_time; if (time_since_last_upload >= upload_interval) { // trigger the upload ESP_LOGD(TAG, "Triggering upload, time since last: %" PRIu32 "", (uint32_t)time_since_last_upload); #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_TRIGGER_LOGS) memfault_log_trigger_collection(); #endif memfault_esp_port_http_client_post_data(); s_mflt_metrics_backup_data.upload_last_time = current_time; } #endif // CONFIG_MEMFAULT_DEEP_SLEEP_HTTP_UPLOAD_ON_SLEEP } void memfault_platform_deep_sleep_save_state(void) { #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_ENABLE_DEBUG_LOG) esp_log_level_set(TAG, ESP_LOG_DEBUG); #endif // This is called when the device is going to sleep ESP_LOGD(TAG, "🥱 Deep sleep state saving"); // Check if it's time to trigger a heartbeat or upload. Since these checks // rely on saved state, we need to first confirm if the saved state is valid. // It will be invalid on the first deep sleep cycle, initialize it then. if (s_mflt_metrics_backup_data.last_time_magic != MEMFAULT_DEEP_SLEEP_MAGIC) { // Assume this is the first time we are going to deep sleep, so // initialize the last time values to 0. s_mflt_metrics_backup_data.heartbeat_last_time = 0; s_mflt_metrics_backup_data.upload_last_time = 0; s_mflt_metrics_backup_data.last_time_magic = MEMFAULT_DEEP_SLEEP_MAGIC; } prv_check_and_trigger_heartbeat(); prv_check_and_trigger_upload(); // Save the event storage state prv_backup_event_storage(); // Save the metrics state. first trigger a collection of metrics- this will // include any delta computations, like network_rx/tx, etc., but does not // trigger any metric timer tallying- metrics timers can run through deep // sleep cycles. memfault_metrics_heartbeat_collect(); #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_METRICS) MEMFAULT_METRIC_TIMER_STOP(active_time_ms); MEMFAULT_METRIC_TIMER_START(deep_sleep_time_ms); #endif prv_backup_metrics(); prv_backup_logs(); ESP_LOGD(TAG, "RTC time ms at sleep: %" PRIu32 "", (uint32_t)(esp_rtc_get_time_us() / 1000)); // Note: this log will roll over at around 50 days of uptime; avoiding PRIu64 // to support newlib nano. ESP_LOGD(TAG, "Time-since-boot ms at sleep: %" PRIu32 "", (uint32_t)memfault_platform_get_time_since_boot_ms()); // delay for a bit to allow logs to be sent esp_rom_delay_us(250 * 1000); } static bool prv_woke_up_from_deep_sleep(void) { // This API changed in ESP-IDF v6 #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0) esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); bool result = (wakeup_reason != ESP_SLEEP_WAKEUP_UNDEFINED) && (wakeup_reason != ESP_SLEEP_WAKEUP_ALL); #else uint32_t wakeup_reason = esp_sleep_get_wakeup_causes(); bool result = (wakeup_reason & (BIT(ESP_SLEEP_WAKEUP_UNDEFINED) | BIT(ESP_SLEEP_WAKEUP_ALL))) == 0; #endif if (result) { ESP_LOGD(TAG, "🌅 Woke up from deep sleep, reason: 0x%x", (int)wakeup_reason); } else { ESP_LOGD(TAG, "Woke up from non-deep sleep, reason: 0x%x", (int)wakeup_reason); } return result; } bool memfault_event_storage_restore_state(sMfltEventStorageSaveState *state) { if (!prv_woke_up_from_deep_sleep()) { return false; } // check magic number first if (s_mflt_metrics_backup_data.event_storage_magic != MEMFAULT_DEEP_SLEEP_MAGIC) { ESP_LOGW(TAG, "No event storage backup data"); return false; } // check crc32 uint32_t crc32 = esp_crc32_le(0, s_mflt_metrics_backup_data.event_storage_data, sizeof(s_mflt_metrics_backup_data.event_storage_data)); if (crc32 != s_mflt_metrics_backup_data.event_storage_crc32) { ESP_LOGW(TAG, "Event storage backup CRC32 mismatch: %08" PRIu32 " != %08" PRIu32 "", crc32, s_mflt_metrics_backup_data.event_storage_crc32); return false; } // copy the data back to the state state->context = &s_mflt_metrics_backup_data.event_storage_data[0]; state->context_len = MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES; state->storage = &s_mflt_metrics_backup_data.event_storage_data[MEMFAULT_EVENT_STORAGE_STATE_SIZE_BYTES]; state->storage_len = CONFIG_MEMFAULT_EVENT_STORAGE_RAM_SIZE; // clear the magic number; we only want to restore the backup once s_mflt_metrics_backup_data.event_storage_magic = 0; return true; } bool memfault_metrics_restore_state(void *state) { if (!prv_woke_up_from_deep_sleep()) { return false; } // check magic number first if (s_mflt_metrics_backup_data.metrics_magic != MEMFAULT_DEEP_SLEEP_MAGIC) { ESP_LOGW(TAG, "No metrics backup data"); return false; } // check crc32 uint32_t crc32 = esp_crc32_le(0, s_mflt_metrics_backup_data.metrics_data, sizeof(s_mflt_metrics_backup_data.metrics_data)); if (crc32 != s_mflt_metrics_backup_data.metrics_crc32) { ESP_LOGW(TAG, "Metrics backup CRC32 mismatch: %08" PRIu32 " != %08" PRIu32 "", crc32, s_mflt_metrics_backup_data.metrics_crc32); return false; } // copy the data back to the state memcpy(state, &s_mflt_metrics_backup_data.metrics_data[0], MEMFAULT_METRICS_CONTEXT_SIZE_BYTES); // clear the magic number; we only want to restore the backup once s_mflt_metrics_backup_data.metrics_magic = 0; #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_METRICS) // there will be a few ms of error in the sleep time metric, due to bootup time. MEMFAULT_METRIC_TIMER_STOP(deep_sleep_time_ms); MEMFAULT_METRIC_TIMER_START(active_time_ms); MEMFAULT_METRIC_ADD(deep_sleep_wakeup_count, 1); #endif return true; } extern bool memfault_log_restore_state(sMfltLogSaveState *state) { if (!prv_woke_up_from_deep_sleep()) { return false; } // check magic number first if (s_mflt_metrics_backup_data.log_magic != MEMFAULT_DEEP_SLEEP_MAGIC) { ESP_LOGW(TAG, "No log backup data"); return false; } // check crc32 uint32_t crc32 = esp_crc32_le(0, s_mflt_metrics_backup_data.log_data, sizeof(s_mflt_metrics_backup_data.log_data)); if (crc32 != s_mflt_metrics_backup_data.log_crc32) { ESP_LOGW(TAG, "Log backup CRC32 mismatch: %08" PRIu32 " != %08" PRIu32 "", crc32, s_mflt_metrics_backup_data.log_crc32); return false; } // copy the data back to the state state->context = &s_mflt_metrics_backup_data.log_data[0]; state->context_len = MEMFAULT_LOG_STATE_SIZE_BYTES; state->storage = &s_mflt_metrics_backup_data.log_data[MEMFAULT_LOG_STATE_SIZE_BYTES]; state->storage_len = CONFIG_MEMFAULT_LOG_STORAGE_RAM_SIZE; // clear the magic number; we only want to restore the backup once s_mflt_metrics_backup_data.log_magic = 0; return true; } void memfault_platform_deep_sleep_restore_state(void) { #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_ENABLE_DEBUG_LOG) esp_log_level_set(TAG, ESP_LOG_DEBUG); #endif // Check if wakeup was from deep sleep. The current implementation doesn't // need to run any specific code on wakeup, so annotate only. if (prv_woke_up_from_deep_sleep()) { ESP_LOGD(TAG, "RTC time ms at wakeup: %" PRIu32 "", (uint32_t)(esp_rtc_get_time_us() / 1000)); ESP_LOGD(TAG, "Time-since-boot ms at wakeup: %" PRIu32 "", (uint32_t)memfault_platform_get_time_since_boot_ms()); } else { // Not a deep sleep wakeup, clear the saved state prv_clear_backup_data(); } // Always start the active time metric timer, to ensure we track active time // on non-deep-sleep wakeups as well as deep sleep wakeups. MEMFAULT_METRIC_TIMER_START(active_time_ms); } ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_demo_cli_cmds.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! ESP32 CLI implementation for demo application // 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" #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) #include "driver/gptimer.h" #elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) #include "driver/gptimer.h" #include "esp_private/esp_clk.h" #include "soc/timer_periph.h" #else #include "driver/periph_ctrl.h" #include "driver/timer.h" #include "esp32/clk.h" #endif #include #include #include "memfault/components.h" #include "memfault/esp_port/cli.h" #include "memfault/esp_port/http_client.h" static void IRAM_ATTR prv_recursive_crash(int depth) { if (depth == 15) { MEMFAULT_ASSERT_RECORD(depth); } if (depth == 15) { // unreachable; this is to silence -Winfinite-recursion return; } // an array to create some stack depth variability int dummy_array[depth + 1]; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(dummy_array); i++) { dummy_array[i] = (depth << 24) | i; } dummy_array[depth] = depth + 1; prv_recursive_crash(dummy_array[depth]); } void prv_check1(const void *buf) { MEMFAULT_ASSERT_RECORD(sizeof(buf)); } void prv_check2(const void *buf) { uint8_t buf2[200] = { 0 }; prv_check1(buf2); } void prv_check3(const void *buf) { uint8_t buf3[300] = { 0 }; prv_check2(buf3); } void prv_check4(void) { uint8_t buf4[400] = { 0 }; prv_check3(buf4); } #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) static bool IRAM_ATTR prv_timer_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) { (void)timer, (void)edata, (void)user_ctx; // Crash from ISR: ESP_ERROR_CHECK(-1); return true; } static gptimer_handle_t s_gptimer = NULL; static void prv_timer_init(void) { gptimer_config_t timer_config = { .clk_src = GPTIMER_CLK_SRC_DEFAULT, .direction = GPTIMER_COUNT_UP, .resolution_hz = 1000000, // 1MHz, 1 tick=ums }; ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &s_gptimer)); gptimer_event_callbacks_t cbs = { .on_alarm = prv_timer_cb, }; ESP_ERROR_CHECK(gptimer_register_event_callbacks(s_gptimer, &cbs, NULL)); ESP_ERROR_CHECK(gptimer_enable(s_gptimer)); } static void prv_timer_start(uint32_t timer_interval_ms) { gptimer_alarm_config_t alarm_config = { .alarm_count = timer_interval_ms * 1000, }; ESP_ERROR_CHECK(gptimer_set_alarm_action(s_gptimer, &alarm_config)); ESP_ERROR_CHECK(gptimer_start(s_gptimer)); } #else // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) #define TIMER_DIVIDER (16ULL) // Hardware timer clock divider #define TIMER_SCALE_TICKS_PER_MS(_baseFrequency) \ (((_baseFrequency) / TIMER_DIVIDER) / 1000ULL) // convert counter value to milliseconds static void IRAM_ATTR prv_timer_group0_isr(void *para) { // Always clear the interrupt: #if CONFIG_IDF_TARGET_ESP32 TIMERG0.int_clr_timers.t0 = 1; #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 TIMERG0.int_clr_timers.t0_int_clr = 1; #endif // Crash from ISR: ESP_ERROR_CHECK(-1); } static void prv_timer_init(void) { const timer_config_t config = { .divider = TIMER_DIVIDER, .counter_dir = TIMER_COUNT_UP, .counter_en = TIMER_PAUSE, .alarm_en = TIMER_ALARM_EN, .intr_type = TIMER_INTR_LEVEL, .auto_reload = false, }; timer_init(TIMER_GROUP_0, TIMER_0, &config); timer_enable_intr(TIMER_GROUP_0, TIMER_0); timer_isr_register(TIMER_GROUP_0, TIMER_0, prv_timer_group0_isr, NULL, ESP_INTR_FLAG_IRAM, NULL); } static void prv_timer_start(uint32_t timer_interval_ms) { uint32_t clock_hz = esp_clk_apb_freq(); timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL); // cast timer interval to uint64_t which will promote the other operand and avoid overflow during // multiplication timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, (uint64_t)timer_interval_ms * TIMER_SCALE_TICKS_PER_MS(clock_hz)); timer_set_alarm(TIMER_GROUP_0, TIMER_0, TIMER_ALARM_EN); timer_start(TIMER_GROUP_0, TIMER_0); } #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) static int prv_esp32_assert_example(int argc, char **argv) { // default to assert() int assert_type = 1; if (argc >= 2) { assert_type = atoi(argv[1]); } if (assert_type == 1) { assert(0); } else if (assert_type == 2) { MEMFAULT_ASSERT(0); } else if (assert_type == 3) { ESP_ERROR_CHECK(-1); } return 0; } static int prv_esp32_crash_example(int argc, char **argv) { int crash_type = 0; if (argc >= 2) { crash_type = atoi(argv[1]); } if (crash_type == 0) { ESP_ERROR_CHECK(10); } else if (crash_type == 2) { // Crash in timer ISR: prv_timer_start(10); } else if (crash_type == 3) { prv_recursive_crash(0); } else if (crash_type == 4) { prv_check4(); } return 0; } static int prv_esp32_leak_cmd(int argc, char **argv) { int leak_size = 0; if (argc >= 2) { leak_size = atoi(argv[1]); } if (leak_size == 0) { MEMFAULT_LOG_INFO("Usage: leak "); return 1; } MEMFAULT_LOG_INFO("Allocating %d bytes", leak_size); void *ptr = malloc(leak_size); if (ptr == NULL) { MEMFAULT_LOG_ERROR("Failed to allocate %d bytes", leak_size); return 1; } MEMFAULT_LOG_INFO("Allocated %d bytes at %p", leak_size, ptr); return 0; } static int prv_esp32_reboot_cmd(int argc, char **argv) { bool hard_reboot = false; int reboot_code = kMfltRebootReason_UserReset; // Parse optional arguments: for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "hard") == 0) { hard_reboot = true; } else if (strcmp(argv[i], "soft") == 0) { hard_reboot = false; } else { // Try to parse as reboot code (hex or decimal) char *endptr; long parsed_code = strtol(argv[i], &endptr, 0); // base 0 auto-detects hex (0x) or decimal if (*endptr != '\0') { MEMFAULT_LOG_ERROR("Invalid argument: %s. Expected 'hard', 'soft', or reboot code", argv[i]); return 1; } reboot_code = (int)parsed_code; } } MEMFAULT_LOG_INFO("Rebooting system: code (%d) type (%s)", reboot_code, hard_reboot ? "hard" : "soft"); MEMFAULT_REBOOT_MARK_RESET_IMMINENT(reboot_code); if (hard_reboot) { memfault_platform_reboot(); } else { esp_restart(); } return 0; } static int prv_esp32_memfault_heartbeat(int argc, char **argv) { memfault_metrics_heartbeat_debug_trigger(); return 0; } static int prv_esp32_memfault_heartbeat_dump(int argc, char **argv) { memfault_metrics_heartbeat_debug_print(); return 0; } static int prv_memfault_demo_cli_cmd_time(int argc, char **argv) { sMemfaultCurrentTime time; if (!memfault_platform_time_get_current(&time)) { MEMFAULT_LOG_INFO("No time available"); return 0; } if (time.type == kMemfaultCurrentTimeType_UnixEpochTimeSec) { // convert to broken-down time struct tm broken_down_time; time_t unix_time = (time_t)time.info.unix_timestamp_secs; localtime_r(&unix_time, &broken_down_time); MEMFAULT_LOG_INFO("Current time: %llu | %04d-%02d-%02d %02d:%02d:%02d", time.info.unix_timestamp_secs, broken_down_time.tm_year + 1900, broken_down_time.tm_mon + 1, broken_down_time.tm_mday, broken_down_time.tm_hour, broken_down_time.tm_min, broken_down_time.tm_sec); } else { MEMFAULT_LOG_INFO("Unknown time type: %d", time.type); } return 0; } static int prv_event_storage_used(int argc, char **argv) { MEMFAULT_LOG_INFO("Event storage used: %zu/%zu bytes", memfault_event_storage_bytes_used(), CONFIG_MEMFAULT_EVENT_STORAGE_RAM_SIZE); return 0; } static bool prv_netif_connected_check(const char *op) { if (memfault_esp_port_netif_connected()) { return true; } MEMFAULT_LOG_ERROR("Must be connected to Network to %s. For WiFi, use 'join '", op); return false; } #if MEMFAULT_ESP_HTTP_CLIENT_ENABLE typedef struct { bool perform_ota; } sMemfaultOtaUserCtx; static bool prv_handle_ota_upload_available(void *user_ctx) { sMemfaultOtaUserCtx *ctx = (sMemfaultOtaUserCtx *)user_ctx; MEMFAULT_LOG_DEBUG("OTA Update Available"); if (ctx->perform_ota) { MEMFAULT_LOG_INFO("Starting OTA download ..."); } return ctx->perform_ota; } static bool prv_handle_ota_download_complete(void *user_ctx) { MEMFAULT_LOG_INFO("OTA Update Complete, Rebooting System"); esp_restart(); return true; } static int prv_memfault_ota(sMemfaultOtaUserCtx *ctx) { if (!prv_netif_connected_check("perform an OTA")) { return -1; } sMemfaultOtaUpdateHandler handler = { .user_ctx = ctx, .handle_update_available = prv_handle_ota_upload_available, .handle_download_complete = prv_handle_ota_download_complete, }; MEMFAULT_LOG_INFO("Checking for OTA Update"); int rv = memfault_esp_port_ota_update(&handler); if (rv == 0) { MEMFAULT_LOG_INFO("Up to date!"); } else if (rv == 1) { MEMFAULT_LOG_INFO("Update available!"); } else if (rv < 0) { MEMFAULT_LOG_ERROR("OTA update failed, rv=%d", rv); } // Should return 0 on success. Change update available rv to 0 return (rv == 1) ? 0 : rv; } static int prv_memfault_ota_perform(int argc, char **argv) { sMemfaultOtaUserCtx user_ctx = { .perform_ota = true, }; return prv_memfault_ota(&user_ctx); } static int prv_memfault_ota_check(int argc, char **argv) { sMemfaultOtaUserCtx user_ctx = { .perform_ota = false, }; return prv_memfault_ota(&user_ctx); } static int prv_post_memfault_data(int argc, char **argv) { return memfault_esp_port_http_client_post_data(); } #endif /* MEMFAULT_ESP_HTTP_CLIENT_ENABLE */ static int prv_chunk_data_export(int argc, char **argv) { memfault_data_export_dump_chunks(); return 0; } void memfault_register_cli(void) { prv_timer_init(); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "assert", .help = "Trigger an assert; optional arg of 1=assert(), 2=MEMFAULT_ASSERT(), 3=ESP_ERROR_CHECK(-1)", .hint = "", .func = prv_esp32_assert_example, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "crash", .help = "Trigger a crash", .hint = "", .func = memfault_demo_cli_cmd_crash, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "esp_crash", .help = "Trigger a timer isr crash", .hint = "", .func = prv_esp32_crash_example, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "leak", .help = "Allocate the specified number of bytes without freeing", .hint = "", .func = prv_esp32_leak_cmd, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "reboot", .help = "Reboot the system", .hint = "[hard|soft] [code]", .func = prv_esp32_reboot_cmd, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "test_log", .help = "Writes test logs to log buffer", .hint = NULL, .func = memfault_demo_cli_cmd_test_log, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "trigger_logs", .help = "Trigger capture of current log buffer contents", .hint = NULL, .func = memfault_demo_cli_cmd_trigger_logs, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "test_trace", .help = "Capture an example trace event with an optional custom log message", .hint = "", .func = memfault_demo_cli_cmd_trace_event_capture, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "clear_core", .help = "Clear an existing coredump", .hint = NULL, .func = memfault_demo_cli_cmd_clear_core, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "get_core", .help = "Get coredump info", .hint = NULL, .func = memfault_demo_cli_cmd_get_core, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "coredump_size", .help = "Print the coredump storage capacity and the capacity required", .hint = NULL, .func = &memfault_demo_cli_cmd_coredump_size, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "get_device_info", .help = "Display device information", .hint = NULL, .func = memfault_demo_cli_cmd_get_device_info, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "export", .help = "Can be used to dump chunks to console or post via GDB", .hint = NULL, .func = prv_chunk_data_export, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "heartbeat", .help = "trigger an immediate capture of all heartbeat metrics", .hint = NULL, .func = prv_esp32_memfault_heartbeat, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "heartbeat_dump", .help = "Dump current Memfault metrics heartbeat state", .hint = NULL, .func = prv_esp32_memfault_heartbeat_dump, })); // command to dump the current time ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "time", .help = "Dump the current time", .hint = NULL, .func = prv_memfault_demo_cli_cmd_time, })); // dump the memfault_event_storage_bytes_used() result ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "event_storage_used", .help = "Print the number of bytes used in event storage", .hint = NULL, .func = prv_event_storage_used, })); #if MEMFAULT_ESP_HTTP_CLIENT_ENABLE ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "post_chunks", .help = "Post Memfault data to cloud", .hint = NULL, .func = prv_post_memfault_data, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "memfault_ota_check", .help = "Checks Memfault to see if a new OTA is available", .hint = NULL, .func = prv_memfault_ota_check, })); ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "memfault_ota_perform", .help = "Performs an OTA is an updates is available from Memfault", .hint = NULL, .func = prv_memfault_ota_perform, })); #endif /* MEMFAULT_ESP_HTTP_CLIENT_ENABLE */ #if defined(CONFIG_MEMFAULT_CLI_SELF_TEST) ESP_ERROR_CHECK(esp_console_cmd_register(&(esp_console_cmd_t){ .command = "memfault_self_test", .help = "Performs on-device tests to validate integration with Memfault", .hint = "", .func = memfault_demo_cli_cmd_self_test, })); #endif } ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_device_info.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Reference implementation of Memfault device info API platform dependencies for the ESP32 #include #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]; // NOTE: Some versions of the esp-idf use locking when reading mac info // so this isn't safe to call from an interrupt static void prv_get_device_serial(char *buf, size_t buf_len) { // Use the ESP32's MAC address as unique device id: uint8_t mac[6]; esp_read_mac(mac, ESP_MAC_WIFI_STA); size_t curr_idx = 0; for (size_t i = 0; i < sizeof(mac); i++) { size_t space_left = buf_len - curr_idx; int bytes_written = snprintf(&buf[curr_idx], space_left, "%02X", (int)mac[i]); if (bytes_written < space_left) { curr_idx += bytes_written; } else { // we are out of space, return what we got, it's NULL terminated return; } } } void memfault_esp_port_get_device_info(struct MemfaultDeviceInfo *info) { // Initialize the device information data structure if the s_device_serial is // not set. Note that the first call to this function should be in a // non-interrupt context, to safely load the mac address. char *device_serial = s_device_serial; if (s_device_serial[0] == '\0') { // if in isr, don't attempt to read mac address. just set the device serial // to "unknown" and continue if (memfault_arch_is_inside_isr()) { device_serial = "unknown"; } else { prv_get_device_serial(s_device_serial, sizeof(s_device_serial)); } } *info = (struct MemfaultDeviceInfo){ .device_serial = device_serial, .hardware_version = CONFIG_MEMFAULT_DEVICE_INFO_HARDWARE_VERSION, .software_version = CONFIG_MEMFAULT_DEVICE_INFO_SOFTWARE_VERSION, .software_type = CONFIG_MEMFAULT_DEVICE_INFO_SOFTWARE_TYPE, }; } #if defined(CONFIG_MEMFAULT_DEFAULT_GET_DEVICE_INFO) void memfault_platform_get_device_info(struct MemfaultDeviceInfo *info) { memfault_esp_port_get_device_info(info); } #endif ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_http_client.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Example implementation of platform dependencies on the ESP32 for the Memfault HTTP APIs #include "memfault/config.h" #if MEMFAULT_ESP_HTTP_CLIENT_ENABLE #include #include #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/version.h" #ifndef MEMFAULT_HTTP_DEBUG #define MEMFAULT_HTTP_DEBUG (0) #endif //! Default buffer size for the URL-encoded device info parameters. This may //! need to be set higher by the user if there are particularly long device info //! strings #ifndef MEMFAULT_DEVICE_INFO_URL_ENCODED_MAX_LEN #define MEMFAULT_DEVICE_INFO_URL_ENCODED_MAX_LEN (48) #endif #define MEMFAULT_HTTP_USER_AGENT "MemfaultSDK/" MEMFAULT_SDK_VERSION_STR #if CONFIG_MEMFAULT_HTTP_MAX_REQUEST_SIZE > 0 && \ (CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN < (CONFIG_MEMFAULT_HTTP_MAX_REQUEST_SIZE + 1024)) #warning \ "MBEDTLS_SSL_IN_CONTENT_LEN < CONFIG_MEMFAULT_HTTP_MAX_REQUEST_SIZE + 1kB recommended room for TLS overhead" #endif MEMFAULT_STATIC_ASSERT( sizeof(CONFIG_MEMFAULT_PROJECT_KEY) > 1, "Memfault Project Key not configured. Please visit https://mflt.io/project-key " "and add CONFIG_MEMFAULT_PROJECT_KEY=\"YOUR_KEY\" to sdkconfig.defaults"); sMfltHttpClientConfig g_mflt_http_client_config = { .api_key = CONFIG_MEMFAULT_PROJECT_KEY }; // Track the number of bytes sent in each data post operation size_t g_chunk_bytes_sent; #if MEMFAULT_HTTP_DEBUG static esp_err_t prv_http_event_handler(esp_http_client_event_t *evt) { switch (evt->event_id) { case HTTP_EVENT_ERROR: memfault_platform_log(kMemfaultPlatformLogLevel_Error, "HTTP_EVENT_ERROR"); break; case HTTP_EVENT_ON_CONNECTED: memfault_platform_log(kMemfaultPlatformLogLevel_Info, "HTTP_EVENT_ON_CONNECTED"); break; case HTTP_EVENT_HEADER_SENT: memfault_platform_log(kMemfaultPlatformLogLevel_Info, "HTTP_EVENT_HEADER_SENT"); break; case HTTP_EVENT_ON_HEADER: memfault_platform_log(kMemfaultPlatformLogLevel_Info, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value); break; case HTTP_EVENT_ON_DATA: memfault_platform_log(kMemfaultPlatformLogLevel_Info, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len); if (!esp_http_client_is_chunked_response(evt->client)) { // Write out data // memfault_platform_log(kMemfaultPlatformLogLevel_Info, "%.*s", evt->data_len, // (char*)evt->data); } break; case HTTP_EVENT_ON_FINISH: memfault_platform_log(kMemfaultPlatformLogLevel_Info, "HTTP_EVENT_ON_FINISH"); break; case HTTP_EVENT_DISCONNECTED: memfault_platform_log(kMemfaultPlatformLogLevel_Info, "HTTP_EVENT_DISCONNECTED"); break; } return ESP_OK; } #endif // MEMFAULT_HTTP_DEBUG static int prv_post_chunks(esp_http_client_handle_t client, void *buffer, size_t buf_len, size_t *chunk_bytes_sent) { *chunk_bytes_sent = 0; // drain all the chunks we have while (1) { // NOTE: Ideally we would be able to enable multi packet chunking which would allow a chunk to // span multiple calls to memfault_packetizer_get_next(). Unfortunately the esp-idf does not // have a POST mechanism that can use a callback so our POST size is limited by the size of the // buffer we can allocate. size_t read_size = buf_len; const bool more_data = memfault_esp_port_get_chunk(buffer, &read_size); if (!more_data) { break; } *chunk_bytes_sent += read_size; esp_http_client_set_post_field(client, buffer, read_size); esp_http_client_set_header(client, "Content-Type", "application/octet-stream"); esp_err_t err = esp_http_client_perform(client); if (ESP_OK != err) { return MEMFAULT_PLATFORM_SPECIFIC_ERROR(err); } } return 0; } static char s_mflt_base_url_buffer[MEMFAULT_HTTP_URL_BUFFER_SIZE]; sMfltHttpClient *memfault_platform_http_client_create(void) { memfault_http_build_url(s_mflt_base_url_buffer, ""); const esp_http_client_config_t config = { #if MEMFAULT_HTTP_DEBUG .event_handler = prv_http_event_handler, #endif .url = s_mflt_base_url_buffer, .timeout_ms = CONFIG_MEMFAULT_HTTP_CLIENT_TIMEOUT_MS, .cert_pem = g_mflt_http_client_config.disable_tls ? NULL : MEMFAULT_ROOT_CERTS_PEM, .user_agent = MEMFAULT_HTTP_USER_AGENT, }; esp_http_client_handle_t client = esp_http_client_init(&config); if (!client) { return NULL; } esp_http_client_set_header(client, MEMFAULT_HTTP_PROJECT_KEY_HEADER, g_mflt_http_client_config.api_key); return (sMfltHttpClient *)client; } int memfault_platform_http_client_destroy(sMfltHttpClient *_client) { esp_http_client_handle_t client = (esp_http_client_handle_t)_client; esp_err_t err = esp_http_client_cleanup(client); if (err == ESP_OK) { return 0; } return MEMFAULT_PLATFORM_SPECIFIC_ERROR(err); } typedef struct MfltHttpResponse { uint16_t status; } sMfltHttpResponse; int memfault_platform_http_response_get_status(const sMfltHttpResponse *response, uint32_t *status_out) { MEMFAULT_ASSERT(response); if (status_out) { *status_out = response->status; } return 0; } //! Check the 3 device info fields that aren't escaped. Return -1 if any need //! escaping static int prv_deviceinfo_needs_url_escaping(sMemfaultDeviceInfo *device_info) { const struct params_s { const char *name; const char *value; } params[] = { { .name = "device_serial", .value = device_info->device_serial, }, { .name = "hardware_version", .value = device_info->hardware_version, }, { .name = "software_type", .value = device_info->software_type, }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(params); i++) { if (memfault_http_needs_escape(params[i].value, strlen(params[i].value))) { MEMFAULT_LOG_ERROR( "OTA URL query param '%s' contains reserved characters and needs escaping: %s", params[i].name, params[i].value); return -1; } } return 0; } static int prv_build_latest_release_url(char *buf, size_t buf_len) { sMemfaultDeviceInfo device_info; memfault_http_get_device_info(&device_info); // if any of the device info fields need escaping, abort if (prv_deviceinfo_needs_url_escaping(&device_info)) { return -1; } // URL encode software_version before appending it to the URL; most likely to // have reserved characters char urlencoded_software_version[MEMFAULT_DEVICE_INFO_URL_ENCODED_MAX_LEN]; int rv = memfault_http_urlencode(device_info.software_version, strlen(device_info.software_version), urlencoded_software_version, sizeof(urlencoded_software_version)); if (rv != 0) { MEMFAULT_LOG_ERROR("Failed to URL encode software version"); return -1; } return snprintf(buf, buf_len, "%s://%s/api/v0/releases/latest/" "url?device_serial=%s&hardware_version=%s&software_type=%s¤t_version=%s", MEMFAULT_HTTP_GET_SCHEME(), MEMFAULT_HTTP_GET_DEVICE_API_HOST(), device_info.device_serial, device_info.hardware_version, device_info.software_type, urlencoded_software_version); } int memfault_esp_port_ota_get_release_url(char **download_url_out) { sMfltHttpClient *http_client = memfault_http_client_create(); char *url = NULL; char *download_url = NULL; int status_code = -1; int rv = -1; if (http_client == NULL) { MEMFAULT_LOG_ERROR("OTA: failed to create HTTP client"); return rv; } // call once with no buffer to figure out space we need to allocate to hold url int url_len = prv_build_latest_release_url(NULL, 0); if (url_len < 0) { MEMFAULT_LOG_ERROR("Failed to build OTA URL"); goto cleanup; } const size_t url_buf_len = url_len + 1 /* for '\0' */; url = calloc(1, url_buf_len); if (url == NULL) { MEMFAULT_LOG_ERROR("Unable to allocate url buffer (%dB)", (int)url_buf_len); goto cleanup; } if (prv_build_latest_release_url(url, url_buf_len) != url_len) { goto cleanup; } esp_http_client_handle_t client = (esp_http_client_handle_t)http_client; // NB: For esp-idf versions > v3.3 will set the Host automatically as part // of esp_http_client_set_url() so this call isn't strictly necessary. // // https://github.com/espressif/esp-idf/commit/a8755055467f3e6ab44dd802f0254ed0281059cc // https://github.com/espressif/esp-idf/commit/d154723a840f04f3c216df576456830c884e7abd esp_http_client_set_header(client, "Host", MEMFAULT_HTTP_GET_DEVICE_API_HOST()); esp_http_client_set_url(client, url); esp_http_client_set_method(client, HTTP_METHOD_GET); // to keep the parsing simple, we will request the download url as plaintext esp_http_client_set_header(client, "Accept", "text/plain"); const esp_err_t err = esp_http_client_open(client, 0); if (ESP_OK != err) { rv = MEMFAULT_PLATFORM_SPECIFIC_ERROR(err); goto cleanup; } const int content_length = esp_http_client_fetch_headers(client); if (content_length < 0) { rv = MEMFAULT_PLATFORM_SPECIFIC_ERROR(content_length); goto cleanup; } download_url = calloc(1, content_length + 1); if (download_url == NULL) { MEMFAULT_LOG_ERROR("Unable to allocate download url buffer (%dB)", (int)download_url); goto cleanup; } int bytes_read = 0; while (bytes_read != content_length) { int len = esp_http_client_read(client, &download_url[bytes_read], content_length - bytes_read); if (len < 0) { rv = MEMFAULT_PLATFORM_SPECIFIC_ERROR(len); goto cleanup; } bytes_read += len; } status_code = esp_http_client_get_status_code(client); if (status_code != 200 && status_code != 204) { MEMFAULT_LOG_ERROR("OTA Query Failure. Status Code: %d", status_code); MEMFAULT_LOG_INFO("Response Body: %s", download_url); goto cleanup; } // Lookup to see if a release is available was successful! rv = 0; cleanup: free(url); memfault_http_client_destroy(http_client); if (status_code == 200) { *download_url_out = download_url; } else { // on failure or if no update is available (204) there is no download url to return free(download_url); *download_url_out = NULL; } return rv; } int memfault_esp_port_ota_update(const sMemfaultOtaUpdateHandler *handler) { char *download_url = NULL; int rv; if ((handler == NULL) || (handler->handle_update_available == NULL) || (handler->handle_download_complete == NULL)) { rv = MemfaultInternalReturnCode_InvalidInput; goto cleanup; } rv = memfault_esp_port_ota_get_release_url(&download_url); if ((rv != 0) || (download_url == NULL)) { goto cleanup; } printf("Download URL: %s\n", download_url); const bool perform_ota = handler->handle_update_available(handler->user_ctx); if (!perform_ota) { // Client decided to abort the OTA but we still set the return code to 1 to indicate a new // update is available. rv = 1; goto cleanup; } #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) esp_https_ota_config_t config = { .http_config = &(esp_http_client_config_t){ .url = download_url, .timeout_ms = CONFIG_MEMFAULT_HTTP_CLIENT_TIMEOUT_MS, .cert_pem = MEMFAULT_ROOT_CERTS_PEM, .user_agent = MEMFAULT_HTTP_USER_AGENT, }, .http_client_init_cb = NULL, .bulk_flash_erase = false, // The following 2 config options were made optional in ESP-IDF v6 #if (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 5, 0)) || \ defined(CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD) #if defined(CONFIG_MEMFAULT_HTTP_PARTIAL_DOWNLOAD_ENABLE) .partial_http_download = true, #else .partial_http_download = false, #endif .max_http_request_size = CONFIG_MEMFAULT_HTTP_MAX_REQUEST_SIZE, #endif }; #else esp_http_client_config_t config = { .url = download_url, .timeout_ms = CONFIG_MEMFAULT_HTTP_CLIENT_TIMEOUT_MS, .cert_pem = MEMFAULT_ROOT_CERTS_PEM, }; #endif const esp_err_t err = esp_https_ota(&config); if (err != ESP_OK) { rv = MEMFAULT_PLATFORM_SPECIFIC_ERROR(err); goto cleanup; } const bool success = handler->handle_download_complete(handler->user_ctx); rv = success ? 1 : -1; cleanup: free(download_url); if (handler->handle_ota_done) { handler->handle_ota_done(rv, handler->user_ctx); } return rv; } int memfault_platform_http_client_post_data(sMfltHttpClient *_client, MemfaultHttpClientResponseCallback callback, void *ctx) { if (!memfault_esp_port_data_available()) { return 0; // no new chunks to send } MEMFAULT_LOG_DEBUG("Posting Memfault Data"); size_t buffer_size = 0; void *buffer = memfault_http_client_allocate_chunk_buffer(&buffer_size); if (buffer == NULL || buffer_size == 0) { MEMFAULT_LOG_ERROR("Unable to allocate POST buffer"); return -1; } esp_http_client_handle_t client = (esp_http_client_handle_t)_client; char url[MEMFAULT_HTTP_URL_BUFFER_SIZE]; memfault_http_build_url(url, MEMFAULT_HTTP_CHUNKS_API_SUBPATH); esp_http_client_set_url(client, url); esp_http_client_set_method(client, HTTP_METHOD_POST); esp_http_client_set_header(client, "Accept", "application/json"); int rv = prv_post_chunks(client, buffer, buffer_size, &g_chunk_bytes_sent); memfault_http_client_release_chunk_buffer(buffer); if (rv != 0) { MEMFAULT_LOG_ERROR("%s failed: %d", __func__, (int)rv); return rv; } const sMfltHttpResponse response = { .status = (uint32_t)esp_http_client_get_status_code(client), }; if (callback) { callback(&response, ctx); } if ((response.status == 200) || (response.status == 202)) { return 0; } else { // Use #define MEMFAULT_HTTP_DEBUG=1 to enable response payload debug log return -1; } } int memfault_platform_http_client_wait_until_requests_completed(sMfltHttpClient *client, uint32_t timeout_ms) { // No-op because memfault_platform_http_client_post_data() is synchronous return 0; } bool memfault_esp_port_wifi_connected(void) { wifi_ap_record_t ap_info; const bool connected = esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK; return connected; } bool memfault_esp_port_netif_connected(void) { // 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) esp_netif_next_unsafe #else esp_netif_next #endif (netif)) != NULL) { if (!esp_netif_is_netif_up(netif)) { continue; } esp_netif_ip_info_t ip_info; esp_err_t err = esp_netif_get_ip_info(netif, &ip_info); if ((err == ESP_OK) && (ip_info.ip.addr != 0)) { return true; } } // didn't find a netif with a non-zero IP address, return not connected return false; } // Similar to memfault_platform_http_client_post_data() but just posts // whatever is pending, if anything. int memfault_esp_port_http_client_post_data(void) { if (!memfault_esp_port_netif_connected()) { MEMFAULT_LOG_INFO("%s: Network unavailable", __func__); return -1; } // Check for data available first as nothing else matters if not. if (!memfault_esp_port_data_available()) { MEMFAULT_LOG_INFO("No new data found"); return 0; } sMfltHttpClient *http_client = memfault_http_client_create(); if (!http_client) { MEMFAULT_LOG_ERROR("Failed to create HTTP client"); #if defined(CONFIG_MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS) // count as a sync failure- if we're here, we have a valid netif connection // but were unable to establish the TLS connection memfault_metrics_connectivity_record_memfault_sync_failure(); #endif return MemfaultInternalReturnCode_Error; } const eMfltPostDataStatus rv = (eMfltPostDataStatus)memfault_http_client_post_data(http_client); switch (rv) { case kMfltPostDataStatus_Success: MEMFAULT_LOG_INFO("Data posted successfully, %d bytes sent", (int)g_chunk_bytes_sent); #if defined(CONFIG_MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS) memfault_metrics_connectivity_record_memfault_sync_success(); #endif break; case kMfltPostDataStatus_NoDataFound: MEMFAULT_LOG_INFO("Done: no data to send"); break; default: MEMFAULT_LOG_ERROR("Failed to post data, err=%d", rv); #if defined(CONFIG_MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS) memfault_metrics_connectivity_record_memfault_sync_failure(); #endif break; } const uint32_t timeout_ms = 30 * 1000; memfault_http_client_wait_until_requests_completed(http_client, timeout_ms); memfault_http_client_destroy(http_client); return (int)rv; } #endif /* MEMFAULT_ESP_HTTP_CLIENT_ENABLE */ ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_http_client_buffer.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Default implementation for buffer allocation while POSTing memfault chunk data #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/esp_port/http_client.h" #ifndef MEMFAULT_HTTP_CLIENT_MAX_BUFFER_SIZE #define MEMFAULT_HTTP_CLIENT_MAX_BUFFER_SIZE (16 * 1024) #endif #if MEMFAULT_HTTP_CLIENT_MAX_BUFFER_SIZE < MEMFAULT_HTTP_CLIENT_MIN_BUFFER_SIZE #error "MEMFAULT_HTTP_CLIENT_MAX_BUFFER_SIZE must be greater than 1024 bytes" #endif MEMFAULT_WEAK void *memfault_http_client_allocate_chunk_buffer(size_t *buffer_size) { // The more data we can pack into one http request, the more efficient things will // be from a network perspective. Let's start by trying to use a 16kB buffer and slim // things down if there isn't that much space available size_t try_alloc_size = MEMFAULT_HTTP_CLIENT_MAX_BUFFER_SIZE; const uint32_t min_alloc_size = MEMFAULT_HTTP_CLIENT_MIN_BUFFER_SIZE; void *buffer = NULL; while (try_alloc_size > min_alloc_size) { buffer = malloc(try_alloc_size); if (buffer != NULL) { *buffer_size = try_alloc_size; break; } try_alloc_size /= 2; } return buffer; } MEMFAULT_WEAK void memfault_http_client_release_chunk_buffer(void *buffer) { free(buffer); } ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_http_periodic_upload.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief Provides a built-in HTTP periodic upload task #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/core/reboot_tracking.h" #include "memfault/esp_port/http_client.h" #define PERIODIC_UPLOAD_TASK_NAME "mflt_periodic_upload" #if !defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS) // Runtime configurable static bool s_mflt_upload_logs; #endif #if defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA) // Set default callbacks. Ideally we'd use a weak symbol here instead of a // Kconfig, but that's complicated due to ESP-IDF linking components as static // libraries. #if !defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA_CUSTOM_CBS) static bool prv_handle_ota_upload_available(void *user_ctx) { MEMFAULT_LOG_INFO("Starting OTA download ..."); return true; } static bool prv_handle_ota_download_complete(void *user_ctx) { MEMFAULT_LOG_INFO("OTA Update Complete, Rebooting System"); MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_FirmwareUpdate); esp_restart(); return true; } sMemfaultOtaUpdateHandler g_memfault_ota_update_handler = { .user_ctx = NULL, .handle_update_available = prv_handle_ota_upload_available, .handle_download_complete = prv_handle_ota_download_complete, .handle_ota_done = NULL, }; #endif // !CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA_CUSTOM_CBS static void prv_do_periodic_ota(void) { memfault_esp_port_ota_update(&g_memfault_ota_update_handler); } #endif // CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA // Periodically post any Memfault data that has not yet been posted. static void prv_periodic_upload_task(void *args) { const uint32_t interval_secs = CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_INTERVAL_SECS; // Randomize the first post to spread out the reporting of // information from a fleet of devices that all reboot at once. // For very low values of CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_INTERVAL_SECS // cut minimum delay of first post for better testing/demoing experience // For intervals below 60s, delay first post by 5s + random portion of interval // For larger intervals, delay first post by 60s + random portion of interval const uint32_t duration_secs_minimum = interval_secs >= 60 ? 60 : 5; const uint32_t duration_secs = duration_secs_minimum + (esp_random() % interval_secs); const TickType_t initial_delay_ms_ticks = (1000 * duration_secs) / portTICK_PERIOD_MS; const TickType_t interval_ms_ticks = (1000 * interval_secs) / portTICK_PERIOD_MS; MEMFAULT_LOG_INFO("Periodic background upload scheduled, initial delay=%" PRIu32 "s period=%" PRIu32 "s", duration_secs, interval_secs); vTaskDelay(initial_delay_ms_ticks); while (true) { if (memfault_esp_port_netif_connected()) { #if defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS) memfault_log_trigger_collection(); #else if (s_mflt_upload_logs) { memfault_log_trigger_collection(); } #endif int err = memfault_esp_port_http_client_post_data(); if (err) { MEMFAULT_LOG_ERROR("Post data failed: %d", err); } #if defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA) prv_do_periodic_ota(); #endif } // sleep vTaskDelay(interval_ms_ticks); } } #if !defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS) void memfault_esp_port_http_periodic_upload_logs(bool enable) { s_mflt_upload_logs = enable; } #endif void memfault_esp_port_http_periodic_upload_start(void) { #if defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_TASK_STATIC) static StackType_t s_periodic_upload_stack[CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_STACK_SIZE] = { 0 }; static StaticTask_t s_periodic_upload_task = { 0 }; TaskHandle_t result = xTaskCreateStatic(prv_periodic_upload_task, PERIODIC_UPLOAD_TASK_NAME, CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_STACK_SIZE, NULL, CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_PRIORITY, s_periodic_upload_stack, &s_periodic_upload_task); if (result == NULL) { MEMFAULT_LOG_ERROR("Periodic upload static task creation failed"); } #else BaseType_t result = xTaskCreate(prv_periodic_upload_task, PERIODIC_UPLOAD_TASK_NAME, CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_STACK_SIZE, NULL, CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_PRIORITY, NULL); if (result != pdTRUE) { MEMFAULT_LOG_ERROR("Periodic upload dynamic task creation failed"); } #endif } ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_metrics.c ================================================ //! @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 #include #include #include "esp_err.h" #include "esp_event.h" #include "esp_heap_caps.h" #include "esp_idf_version.h" #include "esp_log.h" #include "esp_timer.h" #include "esp_wifi.h" #include "esp_wifi_types.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_tracking.h" #include "memfault/esp_port/metrics.h" #include "memfault/metrics/connectivity.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/connectivity.h" #include "memfault/metrics/platform/timer.h" #include "sdkconfig.h" #if defined(CONFIG_MEMFAULT_LWIP_METRICS) #include "memfault/ports/lwip/metrics.h" #endif // CONFIG_MEMFAULT_LWIP_METRICS #if defined(CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS) #include "memfault/ports/freertos/metrics.h" #endif // CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS #if defined(CONFIG_MEMFAULT_MBEDTLS_METRICS) #include "memfault/ports/mbedtls/metrics.h" #endif // CONFIG_MEMFAULT_MBEDTLS_METRICS #if defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) #include "driver/temperature_sensor.h" #include "soc/clk_tree_defs.h" #endif // CONFIG_MEMFAULT_METRICS_CPU_TEMP #if defined(CONFIG_MEMFAULT_METRICS_CHIP_ENABLE) #include "hal/efuse_hal.h" #endif // CONFIG_MEMFAULT_METRICS_CHIP_ENABLE #if defined(CONFIG_MEMFAULT_METRICS_FLASH_ENABLE) #include "esp_flash.h" #include "esp_spi_flash_counters.h" #endif // CONFIG_MEMFAULT_METRICS_FLASH_ENABLE #if defined(CONFIG_MEMFAULT_ESP_WIFI_METRICS) int32_t s_min_rssi; // 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 #if defined(CONFIG_MEMFAULT_METRICS_HEARTBEAT_TIMER_ENABLE) 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); } #endif // CONFIG_MEMFAULT_METRICS_HEARTBEAT_TIMER_ENABLE //! The netif traffic reporting feature is only available in ESP-IDF v5.4 and later: //! https://github.com/espressif/esp-idf/commit/ad9787bb4d88378c71b84c44a65a1be7930fa504 #if defined(CONFIG_MEMFAULT_METRICS_NETWORK_IO) && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)) static void prv_network_io_metric_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { ip_event_tx_rx_t *event = (ip_event_tx_rx_t *)event_data; if (event->dir == ESP_NETIF_TX) { MEMFAULT_METRIC_ADD(network_tx_bytes, event->len); } else if (event->dir == ESP_NETIF_RX) { MEMFAULT_METRIC_ADD(network_rx_bytes, event->len); } } static void prv_enable_network_io_tap(void) { static bool netif_tap_enabled = false; if (!netif_tap_enabled) { esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); if (netif == NULL) { MEMFAULT_LOG_ERROR("Failed to get wifi netif handle, network io stats not available"); } else { // Register the event handler for the RX/TX events esp_err_t err = esp_netif_tx_rx_event_enable(netif); if (err != ESP_OK) { MEMFAULT_LOG_ERROR("Failed to enable TX/RX events, err %d", err); } else { ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_TX_RX, &prv_network_io_metric_event_handler, NULL)); netif_tap_enabled = true; } } } } #endif // CONFIG_MEMFAULT_METRICS_NETWORK_IO #if defined(CONFIG_MEMFAULT_METRICS_BOOT_TIME) // for esp-idf 5.4+, use esp_log_timestamp.h, otherwise use esp_log.h #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) #include "esp_log_timestamp.h" #else #include "esp_log.h" #endif void __wrap_esp_startup_start_app(void); void __real_esp_startup_start_app(void); static uint32_t s_memfault_boot_time_ms = 0; void __wrap_esp_startup_start_app(void) { s_memfault_boot_time_ms = esp_log_early_timestamp(); __real_esp_startup_start_app(); } static void prv_boot_time_metric_handler(void) { // Record the boot time once on the first heartbeat if (s_memfault_boot_time_ms == 0) { return; } MEMFAULT_METRIC_SET_UNSIGNED(boot_time_ms, s_memfault_boot_time_ms); s_memfault_boot_time_ms = 0; } #endif // CONFIG_MEMFAULT_METRICS_BOOT_TIME #if defined(CONFIG_MEMFAULT_ESP_WIFI_METRICS) static void prv_wifi_metric_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { wifi_event_bss_rssi_low_t *rssi_data; switch (event_id) { case WIFI_EVENT_STA_START: #if defined(CONFIG_MEMFAULT_ESP_WIFI_CONNECTIVITY_TIME_METRICS) memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_Started); #endif #if defined(CONFIG_MEMFAULT_METRICS_NETWORK_IO) && \ (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)) prv_enable_network_io_tap(); #endif break; case WIFI_EVENT_STA_CONNECTED: // 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)); MEMFAULT_METRIC_TIMER_START(wifi_connected_time_ms); #if defined(CONFIG_MEMFAULT_ESP_WIFI_CONNECTIVITY_TIME_METRICS) memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_Connected); #endif break; case WIFI_EVENT_STA_DISCONNECTED: MEMFAULT_METRIC_ADD(wifi_disconnect_count, 1); MEMFAULT_METRIC_TIMER_STOP(wifi_connected_time_ms); #if defined(CONFIG_MEMFAULT_ESP_WIFI_CONNECTIVITY_TIME_METRICS) memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_ConnectionLost); #endif break; 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; default: break; } } static void prv_register_event_handler(void) { ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_loop_create_default()); ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &prv_wifi_metric_event_handler, NULL)); } static void prv_collect_oui(wifi_ap_record_t *ap_info) { static uint8_t s_memfault_ap_bssid[sizeof(ap_info->bssid)]; // only set the metric if the AP MAC changed if (memcmp(s_memfault_ap_bssid, ap_info->bssid, sizeof(s_memfault_ap_bssid)) != 0) { char oui[9]; snprintf(oui, sizeof(oui), "%02x:%02x:%02x", ap_info->bssid[0], ap_info->bssid[1], ap_info->bssid[2]); MEMFAULT_METRIC_SET_STRING(wifi_ap_oui, oui); memcpy(s_memfault_ap_bssid, ap_info->bssid, sizeof(s_memfault_ap_bssid)); } } // Maximum allowable string length is limited by the metric using this, static const char *auth_mode_strings[] = { // clang-format off [WIFI_AUTH_OPEN] = "OPEN", [WIFI_AUTH_WEP] = "WEP", [WIFI_AUTH_WPA_PSK] = "WPA-PSK", [WIFI_AUTH_WPA2_PSK] = "WPA2-PSK", [WIFI_AUTH_WPA_WPA2_PSK] = "WPA-WPA2-PSK", [WIFI_AUTH_WPA2_ENTERPRISE] = "WPA2-ENTERPRISE", [WIFI_AUTH_WPA3_PSK] = "WPA3-PSK", [WIFI_AUTH_WPA2_WPA3_PSK] = "WPA2-WPA3-PSK", [WIFI_AUTH_WAPI_PSK] = "WAPI-PSK", [WIFI_AUTH_WPA3_ENT_192] = "WPA3-ENT-192", // other auth modes are not supported, and will show "UNKNOWN" // clang-format on }; static const char *prv_esp_idf_authmode_to_str(wifi_auth_mode_t auth_mode) { if (auth_mode >= MEMFAULT_ARRAY_SIZE(auth_mode_strings)) { return "UNKNOWN"; } const char *authmode = auth_mode_strings[auth_mode]; // The auth mode enum is not contiguous, due to different entries supported on // different ESP-IDF versions, so we need to check for NULL. We care most // about the most popular auth modes, so we don't need to be exhaustive. if (authmode == NULL) { return "UNKNOWN"; } return authmode; } static void prv_collect_wifi_version(wifi_ap_record_t *ap_info) { // Map the phy_ bits to a Wi-Fi Version integer const char *wifi_version = "unknown"; if (ap_info->phy_11n) { wifi_version = "802.11n"; // phy_11ac and phy_11a were added in ESP-IDF 5.3 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) } else if (ap_info->phy_11ac) { wifi_version = "802.11ac"; } else if (ap_info->phy_11a) { wifi_version = "802.11a"; #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) // phy_11ax was added in ESP-IDF 5.1 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) } else if (ap_info->phy_11ax) { wifi_version = "802.11ax"; #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) } else if (ap_info->phy_11g) { wifi_version = "802.11g"; } else if (ap_info->phy_11b) { wifi_version = "802.11b"; } MEMFAULT_METRIC_SET_STRING(wifi_standard_version, wifi_version); } static void prv_collect_wifi_metrics(void) { 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 // initialized yet at time of heartbeat. In that case, the RSSI threshold // will be set reset on the first connection, and any errors here can be // safely ignored. (void)esp_wifi_set_rssi_threshold(MAXIMUM_RSSI); s_min_rssi = 0; } wifi_ap_record_t ap_info; esp_err_t err = esp_wifi_sta_get_ap_info(&ap_info); if (err != ESP_OK) { // Other metrics require this data, exit early if we can't get it return; } // Primary channel MEMFAULT_METRIC_SET_UNSIGNED(wifi_primary_channel, ap_info.primary); const char *auth_mode_str = prv_esp_idf_authmode_to_str(ap_info.authmode); MEMFAULT_METRIC_SET_STRING(wifi_security_type, auth_mode_str); prv_collect_wifi_version(&ap_info); prv_collect_oui(&ap_info); } #endif // CONFIG_MEMFAULT_ESP_WIFI_METRICS #if defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) static void prv_collect_memory_usage_metrics(void) { multi_heap_info_t heap_info = { 0 }; heap_caps_get_info(&heap_info, MALLOC_CAP_DEFAULT); MEMFAULT_METRIC_SET_UNSIGNED(heap_free_bytes, heap_info.total_free_bytes); MEMFAULT_METRIC_SET_UNSIGNED(heap_largest_free_block_bytes, heap_info.largest_free_block); MEMFAULT_METRIC_SET_UNSIGNED(heap_allocated_blocks_count, heap_info.allocated_blocks); // Metrics below use lifetime minimum free bytes. see caveat here: // https://docs.espressif.com/projects/esp-idf/en/v5.1.1/esp32/api-reference/system/mem_alloc.html#_CPPv431heap_caps_get_minimum_free_size8uint32_t MEMFAULT_METRIC_SET_UNSIGNED(heap_min_free_bytes, heap_info.minimum_free_bytes); uint32_t total_heap_size = heap_info.total_free_bytes + heap_info.total_allocated_bytes; // Range is 0-10000 for 0.00-100.00% // Multiply by 100 to get us 2 decimals of precision via the scale factor and then by 100 again // for percentage conversion. MEMFAULT_METRIC_SET_UNSIGNED( memory_pct_max, ((uint32_t)((total_heap_size - heap_info.minimum_free_bytes) * 10000.0f) / total_heap_size)); } #endif // CONFIG_MEMFAULT_METRICS_MEMORY_USAGE #if defined(CONFIG_MEMFAULT_METRICS_HEARTBEAT_TIMER_ENABLE) 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", }; #if defined(CONFIG_MEMFAULT_AUTOMATIC_INIT) // This is only needed if CONFIG_MEMFAULT_AUTOMATIC_INIT is enabled. Normally // esp_timer_init() is called during the system init sequence, before // app_main(), but if memfault_boot() is running in the system init stage, it // can potentially run before esp_timer_init() has been called. // // 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(); #endif 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; } #endif // defined(CONFIG_MEMFAULT_METRICS_HEARTBEAT_TIMER_ENABLE) #if defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) static void prv_collect_temperature_metric(void) { // See documentation here: // https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/temp_sensor.html static temperature_sensor_handle_t temp_handle = NULL; temperature_sensor_config_t temp_sensor = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80); esp_err_t err; do { if (!temp_handle) { // temperature_sensor_install() always emits an ESP_LOGI() message, // disable it to avoid cluttering the logs esp_log_level_set("temperature_sensor", ESP_LOG_WARN); err = temperature_sensor_install(&temp_sensor, &temp_handle); if (err != ESP_OK) { MEMFAULT_LOG_ERROR("Failed to install temperature sensor: %d", err); break; } } // Enable temperature sensor err = temperature_sensor_enable(temp_handle); if (err != ESP_OK) { MEMFAULT_LOG_ERROR("Failed to enable temperature sensor: %d", err); break; } // Get converted sensor data float tsens_out; err = temperature_sensor_get_celsius(temp_handle, &tsens_out); if (err != ESP_OK) { MEMFAULT_LOG_ERROR("Failed to get temperature sensor data: %d", err); break; } else { MEMFAULT_METRIC_SET_SIGNED(thermal_cpu_c, (int32_t)(tsens_out * 10.0f)); } } while (0); // Disable the temperature sensor if it is not needed and save the power (void)temperature_sensor_disable(temp_handle); } #endif // CONFIG_MEMFAULT_METRICS_CPU_TEMP #if defined(CONFIG_MEMFAULT_METRICS_CHIP_ENABLE) static void prv_collect_chip_metrics(void) { static bool did_collect = false; if (did_collect) { return; } uint32_t efuse_chip_id = efuse_hal_chip_revision(); // Chip version in format: Major * 100 + Minor // e.g. Major = 3, Minor = 0 -> 300 // Convert to string with leading CONFIG_IDF_TARGET, eg "esp32s3-3.3" char chip_id_str[sizeof(CONFIG_IDF_TARGET) + sizeof("-00.00")]; const uint8_t major = efuse_chip_id / 100; const uint8_t minor = efuse_chip_id % 100; snprintf(chip_id_str, sizeof(chip_id_str), CONFIG_IDF_TARGET "-%u.%u", major, minor); MEMFAULT_METRIC_SET_STRING(esp_chip_revision, chip_id_str); // We only need to collect this one time on boot did_collect = true; } #endif // CONFIG_MEMFAULT_METRICS_CHIP_ENABLE #if defined(CONFIG_MEMFAULT_METRICS_FLASH_ENABLE) sMfltFlashCounters memfault_platform_metrics_get_flash_counters( sMfltFlashCounters *last_counter_values) { // this API was renamed in v5.1 #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) const esp_flash_counters_t *esp_flash_counters = esp_flash_get_counters(); #else const spi_flash_counters_t *esp_flash_counters = spi_flash_get_counters(); #endif // Compute delta from last heartbeat. Depends on less than 4GB erased or // written in a single heartbeat period. uint32_t flash_erase_bytes = esp_flash_counters->erase.bytes; uint32_t flash_erase_delta = flash_erase_bytes - last_counter_values->erase_bytes; last_counter_values->erase_bytes = flash_erase_bytes; uint32_t flash_write_bytes = esp_flash_counters->write.bytes; uint32_t flash_write_delta = flash_write_bytes - last_counter_values->write_bytes; last_counter_values->write_bytes = flash_write_bytes; return (sMfltFlashCounters){ .erase_bytes = flash_erase_delta, .write_bytes = flash_write_delta, }; } static void prv_collect_flash_metrics(void) { // collect chip info only one time on boot static bool did_collect_chip_info = false; if (!did_collect_chip_info) { uint32_t flash_chip_id; esp_err_t err = esp_flash_read_id(NULL, &flash_chip_id); if (err == ESP_OK) { // flash_chip_id is 24 bits. convert to hex. char flash_chip_id_str[7]; snprintf(flash_chip_id_str, sizeof(flash_chip_id_str), "%06" PRIx32, flash_chip_id); // Set the chip id metric MEMFAULT_METRIC_SET_STRING(flash_spi_manufacturer_id, flash_chip_id_str); } uint32_t flash_size; err = esp_flash_get_physical_size(NULL, &flash_size); if (err == ESP_OK) { MEMFAULT_METRIC_SET_UNSIGNED(flash_spi_total_size_bytes, flash_size); } did_collect_chip_info = true; } static sMfltFlashCounters last_counter_values = { 0 }; sMfltFlashCounters flash_counters = memfault_platform_metrics_get_flash_counters(&last_counter_values); // Set the metrics MEMFAULT_METRIC_SET_UNSIGNED(flash_spi_erase_bytes, flash_counters.erase_bytes); MEMFAULT_METRIC_SET_UNSIGNED(flash_spi_write_bytes, flash_counters.write_bytes); } #endif // CONFIG_MEMFAULT_METRICS_FLASH_ENABLE void memfault_metrics_heartbeat_collect_sdk_data(void) { #if defined(CONFIG_MEMFAULT_LWIP_METRICS) memfault_lwip_heartbeat_collect_data(); #endif // CONFIG_MEMFAULT_LWIP_METRICS #if defined(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 defined(CONFIG_MEMFAULT_MBEDTLS_METRICS) memfault_mbedtls_heartbeat_collect_data(); #endif // CONFIG_MEMFAULT_MBEDTLS_METRICS #if defined(CONFIG_MEMFAULT_ESP_WIFI_METRICS) prv_collect_wifi_metrics(); #endif // CONFIG_MEMFAULT_ESP_WIFI_METRICS #if defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) prv_collect_memory_usage_metrics(); #endif // CONFIG_MEMFAULT_METRICS_MEMORY_USAGE #if defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) prv_collect_temperature_metric(); #endif // CONFIG_MEMFAULT_METRICS_CPU_TEMP #if defined(CONFIG_MEMFAULT_METRICS_CHIP_ENABLE) prv_collect_chip_metrics(); #endif // CONFIG_MEMFAULT_METRICS_CHIP_ENABLE #if defined(CONFIG_MEMFAULT_METRICS_BOOT_TIME) prv_boot_time_metric_handler(); #endif // CONFIG_MEMFAULT_METRICS_BOOT_TIME #if defined(CONFIG_MEMFAULT_METRICS_FLASH_ENABLE) prv_collect_flash_metrics(); #endif // CONFIG_MEMFAULT_METRICS_FLASH_ENABLE } #if defined(CONFIG_MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT) //! Register handlers for WiFi events void memfault_platform_metrics_connectivity_boot(void) { #if defined(CONFIG_MEMFAULT_ESP_WIFI_METRICS) prv_register_event_handler(); #endif // CONFIG_MEMFAULT_ESP_WIFI_METRICS } #endif // CONFIG_MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT #if defined(CONFIG_MEMFAULT_WRAP_EVENT_LOOP_CREATE_DEFAULT) esp_err_t __real_esp_event_loop_create_default(void); esp_err_t __wrap_esp_event_loop_create_default(void) { esp_err_t result = __real_esp_event_loop_create_default(); return result == ESP_ERR_INVALID_STATE ? ESP_OK : result; } // Ensure the substituted function signature matches the original function _Static_assert(__builtin_types_compatible_p(__typeof__(&esp_event_loop_create_default), __typeof__(&__wrap_esp_event_loop_create_default)) && __builtin_types_compatible_p(__typeof__(&esp_event_loop_create_default), __typeof__(&__real_esp_event_loop_create_default)), "Error: check esp_event_loop_create_default function signature"); #endif // defined(CONFIG_MEMFAULT_WRAP_EVENT_LOOP_CREATE_DEFAULT) ================================================ FILE: ports/esp_idf/memfault/common/memfault_platform_system_time.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "esp_log.h" #include "memfault/components.h" bool memfault_platform_time_get_current(sMemfaultCurrentTime *mflt_time) { time_t time_now = time(NULL); if (time_now == -1) { return false; } // convert to broken down time struct tm tm_time; if (gmtime_r(&time_now, &tm_time) == NULL) { return false; } // confirm the year is reasonable (>=2024, <=2100) if ((tm_time.tm_year < 124) || (tm_time.tm_year > 200)) { return false; } // load the timestamp and return true for a valid timestamp *mflt_time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = (uint64_t)time_now, }, }; return true; } ================================================ FILE: ports/esp_idf/memfault/common/memfault_self_test_platform.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include // Espressif's esp-idf project uses a different include directory by default. #if defined(ESP_PLATFORM) #include "sdkconfig.h" #define MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #endif #if defined(MEMFAULT_USE_ESP32_FREERTOS_INCLUDE) #include "freertos/FreeRTOS.h" #include "freertos/task.h" #else // MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #include "FreeRTOS.h" #include "task.h" #endif // MEMFAULT_USE_ESP32_FREERTOS_INCLUDE void memfault_self_test_platform_delay(uint32_t delay_ms) { vTaskDelay(delay_ms / portTICK_PERIOD_MS); } #if defined(CONFIG_MEMFAULT_CLI_SELF_TEST_COREDUMP_STORAGE) static portMUX_TYPE lock = portMUX_INITIALIZER_UNLOCKED; bool memfault_self_test_platform_disable_irqs(void) { // First prevent any ISRs local to this core from running by entering critical section portENTER_CRITICAL(&lock); // Next reset and stall the other core if we're not in single core mode // Reset required, see // esp-idf/components/esp_system/port/soc//system_internal.c:esp_restart_noos for details #if !defined(CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE) int core_id = esp_cpu_get_core_id(); for (uint32_t i = 0; i < SOC_CPU_CORES_NUM; i++) { if (i != core_id) { esp_cpu_reset(i); esp_cpu_stall(i); } } #endif // !defined(CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE) return true; } bool memfault_self_test_platform_enable_irqs(void) { // First unstall the other core before exiting the critical section #if !defined(CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE) int core_id = esp_cpu_get_core_id(); for (uint32_t i = 0; i < SOC_CPU_CORES_NUM; i++) { if (i != core_id) { esp_cpu_unstall(i); } } #endif // !defined(CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE) // Exit the critical section entered by memfault_self_test_platform_disable_irqs portEXIT_CRITICAL(&lock); return true; } #endif // Workaround to allow strong definition of memfault_self_test_platform functions to be linked when // building with esp-idf void memfault_esp_idf_include_self_test_impl(void) { } ================================================ FILE: ports/esp_idf/memfault/config/memfault_esp_idf_port_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // // ESP-IDF specific Memfault configs. This file has the following purposes: // 1. provide a way to set Memfault configs (default_config.h overrides) from // ESP-IDF Kconfig flags // 2. remove the requirement for a user to provide "memfault_platform_config.h" // themselves, if they don't need to override any default options #ifdef __cplusplus extern "C" { #endif #include "sdkconfig.h" #if defined(CONFIG_MEMFAULT_METRICS_SYNC_SUCCESS) #define MEMFAULT_METRICS_SYNC_SUCCESS 1 #endif #if defined(CONFIG_MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS) #define MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS 1 #endif #if defined(CONFIG_MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME) #define MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME 1 #endif #if defined(CONFIG_MEMFAULT_METRICS_BATTERY) #define MEMFAULT_METRICS_BATTERY_ENABLE 1 #endif #if defined(CONFIG_MEMFAULT_CLI_SELF_TEST) #define MEMFAULT_DEMO_CLI_SELF_TEST 1 #define MEMFAULT_SELF_TEST_COREDUMP_STORAGE_DISABLE_MSG \ "Set CONFIG_MEMFAULT_CLI_SELF_TEST_COREDUMP_STORAGE in your prj.conf" #endif #if defined(CONFIG_MEMFAULT_CLI_SELF_TEST_COREDUMP_STORAGE) #define MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE 1 #endif #if defined(CONFIG_MEMFAULT_ESP_WIFI_CONNECTIVITY_TIME_METRICS) #define MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME 1 #endif #if defined(CONFIG_MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT) #define MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT 1 #endif #if defined(CONFIG_MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE) #define MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE 1 #endif #if defined(CONFIG_MEMFAULT_COMPACT_LOG_ENABLE) #define MEMFAULT_COMPACT_LOG_ENABLE 1 #endif #if defined(CONFIG_MEMFAULT_HEAP_STATS) #define MEMFAULT_COREDUMP_COLLECT_HEAP_STATS 1 #define MEMFAULT_HEAP_STATS_MAX_COUNT CONFIG_MEMFAULT_HEAP_STATS_MAX_COUNT // Synchronization is done outside of the heap stats module #define MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE 0 // In-use block tallying is done manually #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 #if defined(CONFIG_MEMFAULT_SYSTEM_TIME) #define MEMFAULT_EVENT_STORAGE_STUB_SYSTEM_TIME 0 #endif #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_SUPPORT) #define MEMFAULT_EVENT_STORAGE_RESTORE_STATE 1 #define MEMFAULT_METRICS_RESTORE_STATE 1 #define MEMFAULT_LOG_RESTORE_STATE 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 // Kconfiglib will set this symbol to y even if SOC_CPU_CORES_NUM is not defined in older esp-idf // versions. Instead, use the FreeRTOS-SMP configNUM_CORES to determine support #if defined(CONFIG_MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT) #if defined(configNUM_CORES) && configNUM_CORES > 1 #define MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT 1 #endif #endif // Defaults to 1, so only disable if Kconfig setting is unset #if !defined(CONFIG_MEMFAULT_METRICS_LOGS_ENABLE) #define MEMFAULT_METRICS_LOGS_ENABLE 0 #endif #define MEMFAULT_COREDUMP_CPU_COUNT CONFIG_MEMFAULT_COREDUMP_CPU_COUNT #define MEMFAULT_PLATFORM_TASK_STACK_SIZE_TO_COLLECT \ CONFIG_MEMFAULT_PLATFORM_TASK_STACK_SIZE_TO_COLLECT // Pick up any user configuration overrides. This should be kept at the bottom // of this file #if CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL #if __has_include(CONFIG_MEMFAULT_PLATFORM_CONFIG_FILE) #include CONFIG_MEMFAULT_PLATFORM_CONFIG_FILE #endif #else #include CONFIG_MEMFAULT_PLATFORM_CONFIG_FILE #endif /* CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL */ #if defined(MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) #error \ "MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS is deprecated. Use CONFIG_MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS instead." #endif #define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS CONFIG_MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/config/memfault_esp_metrics_heartbeat_config.def ================================================ /// @file // Required to pull in Kconfig-generated definitions #include "sdkconfig.h" // Required for ESP-IDF version macros #include "esp_idf_version.h" #if defined(CONFIG_MEMFAULT_LWIP_METRICS) #include "memfault_lwip_metrics_heartbeat_config.def" #endif // CONFIG_MEMFAULT_LWIP_METRICS #if defined(CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS) #include "memfault_metrics_heartbeat_freertos_config.def" #endif // CONFIG_MEMFAULT_FREERTOS_TASK_RUNTIME_STATS #if defined(CONFIG_MEMFAULT_MBEDTLS_METRICS) #include "memfault_mbedtls_metrics_heartbeat_config.def" #endif // CONFIG_MEMFAULT_MBEDTLS_METRICS #if defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) MEMFAULT_METRICS_KEY_DEFINE(heap_free_bytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(heap_largest_free_block_bytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(heap_allocated_blocks_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(heap_min_free_bytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_pct_max, kMemfaultMetricType_Unsigned, 100) #endif // CONFIG_MEMFAULT_METRICS_MEMORY_USAGE #if defined(CONFIG_MEMFAULT_ESP_WIFI_METRICS) MEMFAULT_METRICS_KEY_DEFINE(wifi_connected_time_ms, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(wifi_sta_min_rssi, kMemfaultMetricType_Signed) MEMFAULT_METRICS_KEY_DEFINE(wifi_primary_channel, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_STRING_KEY_DEFINE(wifi_security_type, sizeof("WPA3-EXT-PSK-MIXED-MODE") - 1) MEMFAULT_METRICS_STRING_KEY_DEFINE(wifi_standard_version, sizeof("802.11ax") - 1) 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 #if defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) // One decimal place of precision MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(thermal_cpu_c, kMemfaultMetricType_Signed, 10) #endif // CONFIG_MEMFAULT_METRICS_CPU_TEMP #if defined(CONFIG_MEMFAULT_METRICS_CHIP_ENABLE) // SPI Flash chip ID is captured as 24 bits in hex MEMFAULT_METRICS_STRING_KEY_DEFINE(flash_spi_manufacturer_id, sizeof("012345") - 1) MEMFAULT_METRICS_KEY_DEFINE(flash_spi_total_size_bytes, kMemfaultMetricType_Unsigned) // ESP32 Chip Revision is captured as "esp32s3-1.2" etc. MEMFAULT_METRICS_STRING_KEY_DEFINE(esp_chip_revision, sizeof(CONFIG_IDF_TARGET) + sizeof("-00.00") - 1) #endif // CONFIG_MEMFAULT_METRICS_CHIP_ENABLE #if defined(CONFIG_MEMFAULT_METRICS_NETWORK_IO) && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)) MEMFAULT_METRICS_KEY_DEFINE(network_rx_bytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(network_tx_bytes, kMemfaultMetricType_Unsigned) #endif // CONFIG_MEMFAULT_METRICS_NETWORK_IO #if defined(CONFIG_MEMFAULT_METRICS_BOOT_TIME) MEMFAULT_METRICS_KEY_DEFINE(boot_time_ms, kMemfaultMetricType_Unsigned) #endif // CONFIG_MEMFAULT_METRICS_BOOT_TIME #if defined(CONFIG_MEMFAULT_METRICS_FLASH_ENABLE) MEMFAULT_METRICS_KEY_DEFINE(flash_spi_write_bytes, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(flash_spi_erase_bytes, kMemfaultMetricType_Unsigned) #endif // CONFIG_MEMFAULT_METRICS_FLASH_ENABLE #if defined(CONFIG_MEMFAULT_DEEP_SLEEP_METRICS) MEMFAULT_METRICS_KEY_DEFINE(deep_sleep_time_ms, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(deep_sleep_wakeup_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(active_time_ms, kMemfaultMetricType_Timer) #endif // CONFIG_MEMFAULT_DEEP_SLEEP_METRICS #if defined(CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL) #if __has_include("memfault_metrics_heartbeat_config.def") #include "memfault_metrics_heartbeat_config.def" #endif #else #include "memfault_metrics_heartbeat_config.def" #endif /* CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL */ ================================================ FILE: ports/esp_idf/memfault/config/memfault_platform_freertos_error_log.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Implement a special MEMFAULT_FREERTOS_REGISTRY_FULL_ERROR_LOG() macro used //! when logging from the FreeRTOS task creation function. #ifdef __cplusplus extern "C" { #endif #include "esp_log.h" // Use the ESP-IDF logging 'ESP_DRAM_LOGx' log variant instead of the default // 'MEMFAULT_LOG_ERROR' macro. This should be safe to call in interrupt context // or when the logging subsystem has not been initialized (early init). #define MEMFAULT_FREERTOS_REGISTRY_FULL_ERROR_LOG(...) ESP_DRAM_LOGE("mflt", __VA_ARGS__) #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/config/memfault_trace_reason_esp_idf_port_config.def ================================================ #include "sdkconfig.h" #if CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL # if __has_include("memfault_trace_reason_user_config.def") # include "memfault_trace_reason_user_config.def" # endif #else #include "memfault_trace_reason_user_config.def" #endif /* CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL */ ================================================ FILE: ports/esp_idf/memfault/include/memfault/esp_port/cli.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! ESP32 Memfault Demo Cli Port #ifdef __cplusplus extern "C" { #endif //! Call on boot to enabled the mflt demo cli //! See https://mflt.io/demo-cli for more info void memfault_register_cli(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/include/memfault/esp_port/core.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include #ifdef __cplusplus extern "C" { #endif //! Returns whether or not there is Memfault data to send //! //! This function is called by memfault_http_client_post_data() when data is being pushed to the //! memfault service. //! //! There is a weak implementation defined by default which checks for locally collected memfault //! data. If multiple MCUs are forwarding data to the ESP32 for transport, this function can //! be overridden to check the sources for data as well. //! //! @return true if there is memfault data available to send, false otherwise bool memfault_esp_port_data_available(void); //! Fills buffer with a Memfault "chunk" when there is data available //! //! This function is called by memfault_http_client_post_data() when data is being pushed to the //! memfault service. //! //! There is a weak implementation defined by default which checks for locally collected memfault //! data. If multiple MCUs are forwarding data to the ESP32 for transport, this function can //! be overridden to check the sources for data as well. //! //! @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 //! with the amount of data, in bytes, that was copied into the buffer. //! //! @return true if the buffer was filled, false otherwise bool memfault_esp_port_get_chunk(void *buf, size_t *buf_len); //! Record vprintf log lines into the Memfault log buffer. //! //! Normally installed with esp_log_set_vprintf() as part of the Memfault //! integration memfault_boot() function, this function is exposed if the //! esp_log_set_vprintf() call is disabled with //! CONFIG_MEMFAULT_LOG_USE_VPRINTF_HOOK=n, and the user still wants to record //! logs into the Memfault logging system. //! //! @param[in] fmt Format string //! @param[in] args Variable argument list //! //! @return 0 on success, non-zero error code on failure int memfault_esp_port_vprintf_log_hook(const char *fmt, va_list args); //! Initializes the Memfault system, and should be called one time by the application during boot. void memfault_boot(void); //! Collect the reboot reason event and serialize it into event storage. //! //! This is normally called automatically during memfault_boot() after Memfault //! event storage has been initialized. If //! CONFIG_MEMFAULT_RECORD_REBOOT_ON_BOOT is disabled (because device info is not //! available at boot), the application must call this function once device info //! is ready, but only after memfault_boot() has already run. //! //! This function should not be called on Deep Sleep wakeups. memfault_boot() //! already suppresses reboot-event collection in that case. void memfault_esp_port_collect_reset_info(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/include/memfault/esp_port/coredump.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "esp_idf_version.h" #include "memfault/panics/platform/coredump.h" #include "sdkconfig.h" #ifdef __cplusplus extern "C" { #endif //! MEMFAULT_ESP_PORT_NUM_REGIONS is the maximum number of regions that will be //! filled by memfault_esp_port_coredump_get_regions(). //! Task WDT region is configurable, and only supported on esp-idf v5.3+ #if defined(CONFIG_MEMFAULT_COREDUMP_CAPTURE_TASK_WATCHDOG) && \ (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4)) #define MEMFAULT_ESP_PORT_TASK_WATCHDOG_REGION_COUNT 1 #else #define MEMFAULT_ESP_PORT_TASK_WATCHDOG_REGION_COUNT 0 #endif #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 3) // Memory regions used for esp-idf >= 4.4.3 // Active stack (1) + task/timer and bss/sbss/common regions (6) + freertos tasks //(MEMFAULT_PLATFORM_MAX_TASK_REGIONS) + task_tcbs(1) + task_watermarks(1) + // bss(1) + data(1) + heap(1) #define MEMFAULT_ESP_PORT_NUM_REGIONS \ (1 + 6 + MEMFAULT_PLATFORM_MAX_TASK_REGIONS + 1 + 1 + 1 + 1 + 1 + \ MEMFAULT_ESP_PORT_TASK_WATCHDOG_REGION_COUNT) #else // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 3) // Memory regions for esp-idf < 4.4.3 // Active stack (1) + bss(1) + data(1) + heap(1) #define MEMFAULT_ESP_PORT_NUM_REGIONS \ (1 + 1 + 1 + 1 + MEMFAULT_ESP_PORT_TASK_WATCHDOG_REGION_COUNT) #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 3) //! ESP-IDF specific coredump region collection function. //! //! Returns an array of the regions to capture when the system crashes //! //! @param crash_info Information pertaining to the crash. The user of the SDK can decide //! whether or not to use this info when generating coredump regions to collect. Some //! example ideas can be found in the comments for the struct above //! @param num_regions The number of regions in the list returned const sMfltCoredumpRegion *memfault_esp_port_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions); #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/include/memfault/esp_port/deep_sleep.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif //! This function should be called just before entering deep sleep //! (esp_deep_sleep_start()). It saves Memfault system state to RTC memory so it //! can be restored after waking up from deep sleep. void memfault_platform_deep_sleep_save_state(void); //! This function should be called after waking up from deep sleep, before //! calling memfault_boot(). It restores state saved in //! memfault_platform_deep_sleep_save_state(). void memfault_platform_deep_sleep_restore_state(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/include/memfault/esp_port/device_info.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/components.h" #ifdef __cplusplus extern "C" { #endif //! Default ESP-IDF implementation of the memfault platform device info API. //! Uses WiFi MAC address for device serial, other values are set from Kconfig. //! //! @note the first time this function is called must be from a non-interrupt //! context (i.e. when it's called from 'memfault_boot()' during system //! initialization), because it accesses the eFuse to read the MAC address. void memfault_esp_port_get_device_info(struct MemfaultDeviceInfo *info); #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/include/memfault/esp_port/http_client.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! esp-idf port specific functions related to http #include #include #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_HTTP_CLIENT_MIN_BUFFER_SIZE 1024 //! Called to get a buffer to use for POSTing data to the Memfault cloud //! //! @note The default implementation just calls malloc but is defined //! as weak so a end user can easily override the implementation //! //! @param buffer_size[out] Filled with the size of the buffer allocated. The expectation is that //! the buffer will be >= MEMFAULT_HTTP_CLIENT_MIN_BUFFER_SIZE. The larger the buffer, the less //! amount of POST requests that will be needed to publish data //! //! @return Allocated buffer or NULL on failure void *memfault_http_client_allocate_chunk_buffer(size_t *buffer_size); //! Called to release the buffer that was being to POST data to the Memfault cloud //! //! @note The default implementation just calls malloc but defined //! as weak so an end user can easily override the implementation void memfault_http_client_release_chunk_buffer(void *buffer); typedef struct { //! Optional: Context for use by the caller void *user_ctx; //! Called if a new ota update is available //! @return true to continue, false to abort the OTA download bool (*handle_update_available)(void *user_ctx); //! Called once the entire ota payload has been saved to flash //! //! This is typically where any final shutdown handler logic would be performed //! and esp_restart() would be called bool (*handle_download_complete)(void *user_ctx); //! Called when the OTA process has completed (if the handle_download_complete //! callback returns, i.e. doesn't reboot the system). //! //! @param status 0 if the OTA process completed successfully, else error code void (*handle_ota_done)(int status, void *user_ctx); } sMemfaultOtaUpdateHandler; //! Handler which can be used to run OTA update using Memfault's Release Mgmt Infra //! For more details see: //! https://mflt.io/release-mgmt //! //! @param handler Context with info necessary to perform an OTA. See struct //! definition for more details. //! //! @note This function is blocking. 'handler' callbacks will be invoked prior to the function //! returning. //! //! @return //! < 0 Error while trying to figure out if an update was available //! 0 Check completed successfully - No new update available //! 1 New update is available and handlers were invoked int memfault_esp_port_ota_update(const sMemfaultOtaUpdateHandler *handler); //! POSTs all collected diagnostic data to Memfault Cloud //! //! @note This function should only be called when connected to WiFi //! //! @return 0 on success, else error code int memfault_esp_port_http_client_post_data(void); //! @return true if connected to WiFi, false otherwise bool memfault_esp_port_wifi_connected(void); //! @return true if any available netif is connected (link up and ip address is //! set), false otherwise bool memfault_esp_port_netif_connected(void); //! Returns the URL to download the latest OTA release for current HTTP(S) client configuration //! //! Uses current HTTP(S) client configuration to construct an HTTP(S) GET /releases/latest/url //! request. The response contains a URL which should be used to download the release. //! //! This function can be used to query a Memfault release on behalf of a downstream device. //! Before calling this function, set the appropriate .api_key and .get_device_info fields for //! the downstream device. //! //! @param[out] download_url Pointer to store download url C string into. The caller is //! responsible for freeing this string. Parameter is set to NULL on failure. //! @return 0 if request succeeded, otherwise error code int memfault_esp_port_ota_get_release_url(char **download_url); #if defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD) //! Start a thread to periodically post chunks to Memfault via HTTPS void memfault_esp_port_http_periodic_upload_start(void); #if !defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS) //! Enable or disable periodic log upload at runtime void memfault_esp_port_http_periodic_upload_logs(bool enable); #endif #if defined(CONFIG_MEMFAULT_HTTP_PERIODIC_UPLOAD_OTA_CUSTOM_CBS) //! Set the OTA update handler callbacks for the periodic upload task extern sMemfaultOtaUpdateHandler g_memfault_ota_update_handler; #endif #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/include/memfault/esp_port/metrics.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include #include "memfault/metrics/platform/timer.h" #include "sdkconfig.h" #ifdef __cplusplus extern "C" { #endif //! Called each time a heartbeat interval expires to invoke the handler //! //! The Memfault port implements a default implementation of this in //! ports/esp_idf/memfault/common/memfault_platform_metrics.c which //! runs the callback on the esp timer task. //! //! Since the duty cycle is low (once / hour) and the work performed is //! copying a small amount of data in RAM, it's recommended to use the default //! implementation. However, the function is defined as weak so an end-user //! can override this behavior by implementing the function in their main application. //! //! @param handler The callback to invoke to serialize heartbeat metrics void memfault_esp_metric_timer_dispatch(MemfaultPlatformTimerCallback handler); typedef struct sMfltFlashCounters { uint32_t erase_bytes; uint32_t write_bytes; } sMfltFlashCounters; //! Collects the current flash counters and returns the difference from the last //! time this function was called. //! //! @param last_counter_values The last values of the flash counters. This is //! updated internally and should not be modified by the caller, but must be //! initialized to 0. //! @return The difference between the current and last values of the flash //! counters. //! //! Example usage: //! @code //! // static storage lifetime for the last counter values //! static sMfltFlashCounters last_counter_values = { 0 }; //! sMfltFlashCounters flash_counters = //! memfault_platform_metrics_get_flash_counters(&last_counter_values); //! MEMFAULT_LOG_INFO("Flash erase bytes: %u", flash_counters.erase_bytes); //! MEMFAULT_LOG_INFO("Flash write bytes: %u", flash_counters.write_bytes); //! @endcode sMfltFlashCounters memfault_platform_metrics_get_flash_counters( sMfltFlashCounters *last_counter_values); #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/include/memfault/esp_port/spi_flash.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! The esp-idf spi flash was largely refactored between the v3.x and v4.0 release. This header //! maintains a think wrapper around the two implementations so the Memfault coredump collection //! code is compatible with either major version of the esp-idf. //! //! For more details about the spi flash driver check out: //! https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/spi_flash.html#overview #include #ifdef __cplusplus extern "C" { #endif int memfault_esp_spi_flash_coredump_begin(void); int memfault_esp_spi_flash_erase_range(size_t start_address, size_t size); int memfault_esp_spi_flash_write(size_t dest_addr, const void *src, size_t size); int memfault_esp_spi_flash_read(size_t src_addr, void *dest, size_t size); #ifdef __cplusplus } #endif ================================================ FILE: ports/esp_idf/memfault/v4.x/Memfault-esp-idf-compat.cmake ================================================ function(mflt_esp32_component_get_target var component_dir) set(${var} ${COMPONENT_LIB} PARENT_SCOPE) endfunction() set(MEMFAULT_ESP_IDF_VERSION_SPECIFIC_REQUIRES esp32 espcoredump esp_timer esp_wifi ) ================================================ FILE: ports/esp_idf/memfault/v4.x/memfault_esp_spi_flash.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "esp_flash.h" #include "esp_flash_internal.h" #include "esp_spi_flash.h" #include "memfault/esp_port/spi_flash.h" #ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL #error "The v4.x esp-idf port expects CONFIG_SPI_FLASH_USE_LEGACY_IMPL=n" #endif 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 esp_flash_app_disable_protect(true); } int memfault_esp_spi_flash_erase_range(size_t start_address, size_t size) { return esp_flash_erase_region(esp_flash_default_chip, start_address, size); } int memfault_esp_spi_flash_write(size_t dest_addr, const void *src, size_t size) { return esp_flash_write(esp_flash_default_chip, src, dest_addr, size); } int memfault_esp_spi_flash_read(size_t src_addr, void *dest, size_t size) { return esp_flash_read(esp_flash_default_chip, dest, src_addr, size); } ================================================ FILE: ports/esp_idf/memfault/v5.x/Memfault-esp-idf-compat.cmake ================================================ function(mflt_esp32_component_get_target var component_dir) set(${var} ${COMPONENT_LIB} PARENT_SCOPE) endfunction() set(MEMFAULT_ESP_IDF_VERSION_SPECIFIC_REQUIRES espcoredump esp_partition esp_timer esp_wifi ) ================================================ FILE: ports/esp_idf/memfault/v5.x/memfault_esp_spi_flash.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "esp_flash.h" #include "esp_flash_internal.h" #include "esp_private/spi_flash_os.h" #include "memfault/core/math.h" #include "memfault/esp_port/spi_flash.h" #include "spi_flash_mmap.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 esp_flash_app_disable_protect(true); } int memfault_esp_spi_flash_erase_range(size_t start_address, size_t size) { return esp_flash_erase_region(esp_flash_default_chip, start_address, size); } int memfault_esp_spi_flash_write(size_t dest_addr, const void *src, size_t size) { #if defined(CONFIG_SPI_FLASH_VERIFY_WRITE) uint8_t temp_buf[128]; uintptr_t src_end = (uintptr_t)src + size; // Use a temp buffer to avoid issues where the source data changes // during the write verification for (const uint8_t *src_pointer = src; (uintptr_t)src_pointer < src_end; src_pointer += sizeof(temp_buf)) { // Chunk the source buffer into temp_buf segments or remaining data, whichever is smaller size_t chunk_size = MEMFAULT_MIN(sizeof(temp_buf), (src_end - (uintptr_t)src_pointer)); memcpy(temp_buf, src_pointer, chunk_size); // Write the temp buffer to flash, bail early if we hit an error int err = esp_flash_write(esp_flash_default_chip, temp_buf, (uintptr_t)dest_addr + ((uintptr_t)src_pointer - (uintptr_t)src), chunk_size); if (err != 0) { return err; } } return 0; #else return esp_flash_write(esp_flash_default_chip, src, dest_addr, size); #endif // defined(CONFIG_SPI_FLASH_VERIFY_WRITE) } int memfault_esp_spi_flash_read(size_t src_addr, void *dest, size_t size) { return esp_flash_read(esp_flash_default_chip, dest, src_addr, size); } ================================================ FILE: ports/esp_idf/memfault/v6.x/Memfault-esp-idf-compat.cmake ================================================ function(mflt_esp32_component_get_target var component_dir) set(${var} ${COMPONENT_LIB} PARENT_SCOPE) endfunction() set(MEMFAULT_ESP_IDF_VERSION_SPECIFIC_REQUIRES espcoredump esp_partition esp_timer esp_wifi ) ================================================ FILE: ports/esp_idf/memfault/v6.x/memfault_esp_spi_flash.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "esp_flash.h" // In ESP-IDF >v6, this include moved location. #if __has_include("esp_private/esp_flash_internal.h") #include "esp_private/esp_flash_internal.h" #else #include "esp_flash_internal.h" #endif #include "esp_private/spi_flash_os.h" #include "memfault/core/math.h" #include "memfault/esp_port/spi_flash.h" #include "spi_flash_mmap.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 esp_flash_app_disable_protect(true); } int memfault_esp_spi_flash_erase_range(size_t start_address, size_t size) { return esp_flash_erase_region(esp_flash_default_chip, start_address, size); } int memfault_esp_spi_flash_write(size_t dest_addr, const void *src, size_t size) { #if defined(CONFIG_SPI_FLASH_VERIFY_WRITE) uint8_t temp_buf[128]; uintptr_t src_end = (uintptr_t)src + size; // Use a temp buffer to avoid issues where the source data changes // during the write verification for (const uint8_t *src_pointer = src; (uintptr_t)src_pointer < src_end; src_pointer += sizeof(temp_buf)) { // Chunk the source buffer into temp_buf segments or remaining data, whichever is smaller size_t chunk_size = MEMFAULT_MIN(sizeof(temp_buf), (src_end - (uintptr_t)src_pointer)); memcpy(temp_buf, src_pointer, chunk_size); // Write the temp buffer to flash, bail early if we hit an error int err = esp_flash_write(esp_flash_default_chip, temp_buf, (uintptr_t)dest_addr + ((uintptr_t)src_pointer - (uintptr_t)src), chunk_size); if (err != 0) { return err; } } return 0; #else return esp_flash_write(esp_flash_default_chip, src, dest_addr, size); #endif // defined(CONFIG_SPI_FLASH_VERIFY_WRITE) } int memfault_esp_spi_flash_read(size_t src_addr, void *dest, size_t size) { return esp_flash_read(esp_flash_default_chip, dest, src_addr, size); } ================================================ FILE: ports/esp_idf/memfault.cmake ================================================ # Typically, the platform specific dependencies (e.g. memfault_platform_get_device_info()) that are # not part of the esp_idf default port are added to the "main" component. However, the user # of the port may also explicitly setting the MEMFAULT_PLATFORM_PORT_COMPONENTS prior to including this # cmake helper: # # set(MEMFAULT_PLATFORM_PORT_COMPONENTS ) if(NOT DEFINED MEMFAULT_PLATFORM_PORT_COMPONENTS) set(MEMFAULT_PLATFORM_PORT_COMPONENTS main) message(STATUS "MEMFAULT_PLATFORM_PORT_COMPONENTS not provided, using default ('${MEMFAULT_PLATFORM_PORT_COMPONENTS}')") endif() # Some esp32 ports (i.e amazon-freertos) do not make use of the esp_http_client component so we # expose a way to disable it entirely if(NOT DEFINED MEMFAULT_ESP_HTTP_CLIENT_ENABLE) set(MEMFAULT_ESP_HTTP_CLIENT_ENABLE 1) endif() # Note: We want to forward some settings to the "memfault" idf component but that takes # place at a later stage in the build process not included directly in this path so we # pass settings using environment variables set(ENV{MEMFAULT_PLATFORM_PORT_COMPONENTS} ${MEMFAULT_PLATFORM_PORT_COMPONENTS}) set(ENV{MEMFAULT_ESP_HTTP_CLIENT_ENABLE} ${MEMFAULT_ESP_HTTP_CLIENT_ENABLE}) set(ENV{MEMFAULT_PLATFORM_EXTRA_INCLUDES} ${MEMFAULT_PLATFORM_EXTRA_INCLUDES}) # This will inform esp-idf to pick up the memfault component which includes the # memfault-firmware-sdk as well as the esp-idf porting layer that is not application specific set(EXTRA_COMPONENT_DIRS ${EXTRA_COMPONENT_DIRS} ${CMAKE_CURRENT_LIST_DIR}) ================================================ FILE: ports/freertos/README.md ================================================ # FreeRTOS Specific Port ## Overview This directory contains an implementation of Memfault dependency functions when a platform is built on top of [FreeRTOS](https://www.freertos.org/). ## Usage 1. Add `$MEMFAULT_FIRMWARE_SDK/ports/freertos/include` to your projects include path 2. Prior to using the Memfault SDK in your code, initialize the FreeRTOS port. ```c #include "memfault/ports/freertos.h" void main(void) { memfault_freertos_port_boot(); } ``` ## Heap Tracking The memfault-firmware-sdk has a built in utility for tracking heap allocations to facilitate debug of out of memory bugs. To enable, add the following to your `memfault_platform_config.h` file: ```c #define MEMFAULT_FREERTOS_PORT_HEAP_STATS_ENABLE 1 #define MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE 0 #define MEMFAULT_COREDUMP_COLLECT_HEAP_STATS 1 ``` ================================================ FILE: ports/freertos/config/memfault_metrics_heartbeat_freertos_config.def ================================================ #include "memfault/ports/freertos/metrics.h" // This definition is used to track if this file has been included yet #define MEMFAULT_METRICS_HEARTBEAT_FREERTOS_CONFIG_DEF 1 #define CPU_USAGE_PCT_SCALE_VALUE (100) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(cpu_usage_pct, kMemfaultMetricType_Unsigned, CPU_USAGE_PCT_SCALE_VALUE) #if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(cpu1_usage_pct, kMemfaultMetricType_Unsigned, CPU_USAGE_PCT_SCALE_VALUE) #endif #if MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES MEMFAULT_METRICS_KEY_DEFINE(timer_task_stack_free_bytes, kMemfaultMetricType_Unsigned) #endif #if MEMFAULT_METRICS_THREADS_DEFAULTS MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_idle_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_tmr_svc_pct_max, kMemfaultMetricType_Unsigned, MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #endif ================================================ FILE: ports/freertos/src/memfault_core_freertos.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port of dependency functions for Memfault core subsystem using FreeRTOS. #include "FreeRTOS.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/overrides.h" #include "memfault/ports/freertos.h" #include "semphr.h" #include "task.h" #if configUSE_MUTEXES == 0 #error "configUSE_MUTEXES must be enabled to use Memfault FreeRTOS port" #endif #if configUSE_RECURSIVE_MUTEXES == 0 #error "configUSE_RECURSIVE_MUTEXES must be enabled to use Memfault FreeRTOS port" #endif uint64_t memfault_platform_get_time_since_boot_ms(void) { static uint64_t s_elapsed_ticks = 0; static uint32_t s_last_tick_count = 0; taskENTER_CRITICAL(); { const uint32_t curr_tick_count = xTaskGetTickCount(); // NB: Since we are doing unsigned arithmetic here, this operation will still work when // xTaskGetTickCount() has overflowed and wrapped around s_elapsed_ticks += (curr_tick_count - s_last_tick_count); s_last_tick_count = curr_tick_count; } taskEXIT_CRITICAL(); // Convert ticks to milliseconds using multiply-and-shift, to avoid performing // a 64-bit divide. If configTICK_RATE_HZ==1000, compilers with optimization // level -Og or greater will optimize this out, and return the tick count // directly. return (s_elapsed_ticks * ((1000ull << 16) / configTICK_RATE_HZ)) >> 16; } static SemaphoreHandle_t s_memfault_lock; SemaphoreHandle_t prv_init_memfault_mutex(void) { #if MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION != 0 static StaticSemaphore_t s_memfault_lock_context; return xSemaphoreCreateRecursiveMutexStatic(&s_memfault_lock_context); #else return xSemaphoreCreateRecursiveMutex(); #endif } void memfault_lock(void) { xSemaphoreTakeRecursive(s_memfault_lock, portMAX_DELAY); } void memfault_unlock(void) { xSemaphoreGiveRecursive(s_memfault_lock); } void memfault_freertos_port_boot(void) { s_memfault_lock = prv_init_memfault_mutex(); } ================================================ FILE: ports/freertos/src/memfault_freertos_ram_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @note This file enables collecting a portion of all the FreeRTOS task state in a minimal RAM //! footprint. If you are able to collect all of RAM in your coredump, there is no need to use the //! utilities in this file //! //! To utilize this implementation to capture a portion of all the FreeRTOS tasks: //! //! 1) Update linker script to place FreeRTOS at a fixed address: //! //! .bss (NOLOAD) : //! { //! _sbss = . ; //! __bss_start__ = _sbss; //! __memfault_capture_bss_start = .; //! /* Place all objects from the FreeRTOS timers and tasks modules here. //! Note that some build systems will use 'timers.o' as the object //! file name, and some may use variations of 'timers.c.o' or //! 'timers.obj' etc. This pattern should capture all of them. */ //! *tasks*.o*(.bss COMMON .bss*) //! *timers*.o*(.bss COMMON .bss*) //! __memfault_capture_bss_end = .; //! //! 2) Add this file to your build and update FreeRTOSConfig.h to //! include "memfault/ports/freertos_trace.h" //! //! 3) Implement memfault_platform_sanitize_address_range(). This routine is used //! to run an extra sanity check in case any task context state is corrupted, i.e //! //! #include "memfault/ports/freertos_coredump.h" //! #include "memfault/core/math.h" //! // ... //! size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { //! // Note: This will differ depending on the memory map of the MCU //! const uint32_t ram_start = 0x20000000; //! const uint32_t ram_end = 0x20000000 + 64 * 1024; //! if ((uint32_t)start_addr >= ram_start && (uint32_t)start_addr < ram_end) { //! return MEMFAULT_MIN(desired_size, ram_end - (uint32_t)start_addr); //! } //! //! // invalid address //! return 0; //! } //! //! 4) Update memfault_platform_coredump_get_regions() to include FreeRTOS state //! and new regions: //! //! const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( //! const sCoredumpCrashInfo *crash_info, size_t *num_regions) { //! int region_idx = 0; //! //! const size_t active_stack_size_to_collect = 512; //! //! // first, capture the active stack //! s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( //! crash_info->stack_address, //! memfault_platform_sanitize_address_range(crash_info->stack_address, //! active_stack_size_to_collect)); //! region_idx++; //! //! extern uint32_t __memfault_capture_bss_start; //! extern uint32_t __memfault_capture_bss_end; //! const size_t memfault_region_size = (uint32_t)&__memfault_capture_bss_end - //! (uint32_t)&__memfault_capture_bss_start; //! //! s_coredump_regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( //! &__memfault_capture_bss_start, memfault_region_size); //! region_idx++; //! //! region_idx += memfault_freertos_get_task_regions(&s_coredump_regions[region_idx], //! MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx); //! //! *num_regions = region_idx; //! return &s_coredump_regions[0]; //! } #include #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/panics/coredump.h" #include "memfault/ports/freertos_coredump.h" // Espressif's esp-idf project uses a different include directory by default. #if defined(ESP_PLATFORM) #include "sdkconfig.h" #define MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #endif #if defined(MEMFAULT_USE_ESP32_FREERTOS_INCLUDE) #include "freertos/FreeRTOS.h" #include "freertos/task.h" #else // MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #include "FreeRTOS.h" #include "task.h" #endif // MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #if !defined(MEMFAULT_FREERTOS_TRACE_ENABLED) #error "'#include "memfault/ports/freertos_trace.h"' must be added to FreeRTOSConfig.h \ Note: For PlatformIO projects, memfault/ports/freertos_trace.h must be added with -include in your .ini file. \ See 'https://github.com/memfault/platformio-esp32-espidf' for an example" #endif // This feature is opt-in, since it can fail if the TCB data structures are // corrupt, and results in a lost coredump. #if !defined(MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) #define MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE 0 #endif #if MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE && !INCLUDE_uxTaskGetStackHighWaterMark #warning MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE requires uxTaskGetStackHighWaterMark() API.\ Add '#define INCLUDE_uxTaskGetStackHighWaterMark 1' to FreeRTOSConfig.h to enable it,\ or set '#define MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE 0' in\ memfault_platform_config.h to disable this warning. #undef MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE #define MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE 0 #endif #if !defined(MEMFAULT_FREERTOS_WARN_STACK_HIGH_ADDRESS_UNAVAILABLE) #if !configRECORD_STACK_HIGH_ADDRESS && \ !(configUSE_TRACE_FACILITY && INCLUDE_uxTaskGetStackHighWaterMark) #warning To see FreeRTOS thread stack sizes in coredumps, Add\ '#define configRECORD_STACK_HIGH_ADDRESS 1' to FreeRTOSConfig.h. Otherwise, set\ '#define MEMFAULT_FREERTOS_WARN_STACK_HIGH_ADDRESS_UNAVAILABLE 0' in\ memfault_platform_config.h to disable this warning. #endif #endif #if MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE #define MEMFAULT_THREAD_STACK_0_BYTES_UNUSED 0xffffffff static struct MfltTaskWatermarks { uint32_t bytes_unused; } s_memfault_task_watermarks_v2[MEMFAULT_PLATFORM_MAX_TRACKED_TASKS]; #endif // If the MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE value is set to 0, apply a default // to MEMFAULT_FREERTOS_TCB_SIZE here. This value can be overridden by setting // MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE in memfault_platform_config.h. See // include/memfault/default_config.h for more information. #if MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE #define MEMFAULT_FREERTOS_TCB_SIZE MEMFAULT_PLATFORM_FREERTOS_TCB_SIZE #else #if tskKERNEL_VERSION_MAJOR <= 8 //! Note: What we want here is sizeof(TCB_t) but that is a private declaration in FreeRTOS //! tasks.c. Since the static declaration doesn't exist for FreeRTOS kernel <= 8, fallback to a //! generous size that will include the entire TCB. A user of the SDK can tune the size by //! adding MEMFAULT_FREERTOS_TCB_SIZE to memfault_platform_config.h #define MEMFAULT_FREERTOS_TCB_SIZE 200 #else #define MEMFAULT_FREERTOS_TCB_SIZE sizeof(StaticTask_t) #endif #endif //! Allow overriding the MEMFAULT_LOG_ERROR() call when the task registry is //! full and a new task is created. Some systems are unable to issue logs from //! the task creation calling context (i.e the task is created before the //! logging system is initialized). //! //! For example, for ESP-IDF, it might be appropriate to instead use: //! #define MEMFAULT_FREERTOS_REGISTRY_FULL_ERROR_LOG(...) ESP_EARLY_LOGE("mflt", __VA_ARGS__) //! //! The overriding is done in a file included from here, in case additional //! headers are needed #if defined(MEMFAULT_FREERTOS_REGISTRY_FULL_ERROR_LOG_INCLUDE) #include MEMFAULT_FREERTOS_REGISTRY_FULL_ERROR_LOG_INCLUDE #else #define MEMFAULT_FREERTOS_REGISTRY_FULL_ERROR_LOG MEMFAULT_LOG_ERROR #endif //! This array holds the tracked TCB references static void *s_task_tcbs[MEMFAULT_PLATFORM_MAX_TRACKED_TASKS]; #define EMPTY_SLOT 0 static bool prv_find_slot(size_t *idx, void *desired_tcb) { for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_task_tcbs); i++) { if (s_task_tcbs[i] == desired_tcb) { *idx = i; return true; } } return false; } // We're not locking around the 'memfault_freertos_trace_task_create()' / // 'memfault_freertos_trace_task_delete()' operations, since they are expected // to be called as part of the FreeRTOS kernel trace hooks ('traceTASK_CREATE()' // / 'traceTASK_DELETE()'), which are already serialized with a kernel lock or // port critical section void memfault_freertos_trace_task_create(void *tcb) { size_t idx = 0; const bool slot_found = prv_find_slot(&idx, EMPTY_SLOT); if (slot_found) { s_task_tcbs[idx] = tcb; } if (!slot_found) { MEMFAULT_FREERTOS_REGISTRY_FULL_ERROR_LOG( "Task registry full (" MEMFAULT_EXPAND_AND_QUOTE(MEMFAULT_PLATFORM_MAX_TRACKED_TASKS) ")"); } } void memfault_freertos_trace_task_delete(void *tcb) { size_t idx = 0; if (!prv_find_slot(&idx, tcb)) { // A TCB not currently in the registry return; } s_task_tcbs[idx] = EMPTY_SLOT; } #if MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE static uint32_t prv_stack_bytes_unused(void *tcb_address) { // Note: ideally we would sanitize the pxTCB->pxStack value here (which is // used when computing high watermark) to prevent a memory error. We could // potentially scan in decrementing addresses from pxTopOfStack (full // descending stack), looking for the first n run of 0xa5 bytes, and use that // as the presumed "high watermark". We could then sanitize that access, so // we'd be less likely to trip a memory error. For now, just rely on the // earlier sanity check on pxTopOfStack being a valid address, and assume // pxStack hasn't been corrupted in that case. // Note: uxTaskGetStackHighWaterMark is very simple, and just scans the // stack for the first non 0xa5 byte. It's safe to call it from the fault // handler context (no locks, no memory allocation, etc) // The value returned here is the "unused space", despite the API name. // uxTaskGetStackHighWaterMark() returns a value in units of // sizeof(StackType_t), so convert it to bytes. uint32_t bytes_unused = uxTaskGetStackHighWaterMark((TaskHandle_t)tcb_address) * sizeof(StackType_t); if (bytes_unused == 0) { // In the case of a fully exhausted stack, return -1. This is converted back // to 0 in the backend. We can't use 0 here, because that's also the // initialization value of the s_memfault_task_watermarks_v2 data structure, // and if this routine isn't called before coredump saving for some reason, // we don't want to incorrectly show the stacks as fully used ("0 bytes // unused"). bytes_unused = MEMFAULT_THREAD_STACK_0_BYTES_UNUSED; } return bytes_unused; } #endif size_t memfault_freertos_get_task_regions(sMfltCoredumpRegion *regions, size_t num_regions) { if (regions == NULL || num_regions == 0) { return 0; } size_t region_idx = 0; // First we will try to store all the task TCBs. This way if we run out of space // while storing tasks we will still be able to recover the state of all the threads for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_task_tcbs) && region_idx < num_regions; i++) { void *tcb_address = s_task_tcbs[i]; if (tcb_address == EMPTY_SLOT) { continue; } const size_t tcb_size = memfault_platform_sanitize_address_range(tcb_address, MEMFAULT_FREERTOS_TCB_SIZE); if (tcb_size == 0) { // An invalid address, scrub the TCB from the list so we don't try to dereference // it when grabbing stacks below and move on. s_task_tcbs[i] = EMPTY_SLOT; continue; } regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(tcb_address, tcb_size); region_idx++; } // Now we store the region of the stack where context is saved. This way // we can unwind the stacks for threads that are not actively running for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_task_tcbs) && region_idx < num_regions; i++) { void *tcb_address = s_task_tcbs[i]; if (tcb_address == EMPTY_SLOT) { continue; } // pxTopOfStack is always the first entry in the FreeRTOS TCB void *top_of_stack = (void *)(*(uintptr_t *)tcb_address); const size_t stack_size = memfault_platform_sanitize_address_range( top_of_stack, MEMFAULT_PLATFORM_TASK_STACK_SIZE_TO_COLLECT); if (stack_size == 0) { continue; } #if MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE s_memfault_task_watermarks_v2[i].bytes_unused = prv_stack_bytes_unused(tcb_address); #endif regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(top_of_stack, stack_size); region_idx++; if (region_idx == num_regions) { return region_idx; } } #if MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE // Store the task TCBs and watermarks, if there's free regions if (region_idx < num_regions) { regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&s_task_tcbs[0], sizeof(s_task_tcbs)); region_idx++; regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( &s_memfault_task_watermarks_v2[0], sizeof(s_memfault_task_watermarks_v2)); region_idx++; } #endif return region_idx; } ================================================ FILE: ports/freertos/src/memfault_metrics_freertos.c ================================================ //! @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 "FreeRTOS.h" #include "memfault/core/compiler.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/timer.h" #include "memfault/ports/freertos.h" #include "timers.h" // Define portTICK_PERIOD_MS is using an older version of FreeRTOS for compatibility // portTICK_PERIOD_MS was added in FreeRTOS v8.0.0 #ifndef portTICK_PERIOD_MS #define portTICK_PERIOD_MS portTICK_RATE_MS #endif #define SEC_TO_FREERTOS_TICKS(period_sec) \ ((uint64_t)(((uint64_t)period_sec * 1000ULL) / (uint64_t)portTICK_PERIOD_MS)) static MemfaultPlatformTimerCallback *s_metric_timer_cb = NULL; static void prv_metric_timer_callback(MEMFAULT_UNUSED TimerHandle_t handle) { s_metric_timer_cb(); } static TimerHandle_t prv_metric_timer_init(const char *const pcTimerName, TickType_t xTimerPeriodInTicks, UBaseType_t uxAutoReload, void *pvTimerID, TimerCallbackFunction_t pxCallbackFunction) { #if MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION != 0 static StaticTimer_t s_metric_timer_context; return xTimerCreateStatic(pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, &s_metric_timer_context); #else return xTimerCreate(pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction); #endif } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback callback) { // Validate MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS does not overflow when converting to ticks // Assumes a tick rate <= 1000 Hz and MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS used as period MEMFAULT_STATIC_ASSERT( MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS <= (uint32_t)(UINT32_MAX / 1000UL), "Period too large and will cause overflow"); TimerHandle_t timer = prv_metric_timer_init("metric_timer", SEC_TO_FREERTOS_TICKS(period_sec), pdTRUE, /* auto reload */ (void *)NULL, prv_metric_timer_callback); if (timer == 0) { return false; } s_metric_timer_cb = callback; xTimerStart(timer, 0); return true; } ================================================ FILE: ports/freertos/src/memfault_panics_freertos.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Hooks for collecting coredumps from failure paths in FreeRTOS kernel #include "FreeRTOS.h" #include "memfault/core/compiler.h" #include "memfault/panics/assert.h" #include "task.h" //! Collect a coredump when a FreeRTOS assert takes place //! //! Implementation for assert function present in FreeRTOSConfig.h: //! #define configASSERT(x) if ((x) == 0) vAssertCalled( __FILE__, __LINE__ ) void vAssertCalled(MEMFAULT_UNUSED const char *file, MEMFAULT_UNUSED int line) { MEMFAULT_ASSERT(0); } //! Collects a coredump when a stack overflow takes place in FreeRTOS kernel //! //! @note: Depends on FreeRTOS kernel being compiled with //! configCHECK_FOR_STACK_OVERFLOW != 0 //! //! If this hook conflicts with an existing application-defined //! vApplicationStackOverflowHook(), define //! MEMFAULT_FREERTOS_DISABLE_STACK_OVERFLOW_HOOK in memfault_platform_config.h //! to suppress this definition. When suppressed, you must either provide your //! own vApplicationStackOverflowHook() implementation or set //! configCHECK_FOR_STACK_OVERFLOW=0 in FreeRTOSConfig.h to avoid a link error. #if !defined(MEMFAULT_FREERTOS_DISABLE_STACK_OVERFLOW_HOOK) void vApplicationStackOverflowHook(MEMFAULT_UNUSED TaskHandle_t xTask, MEMFAULT_UNUSED char *pcTaskName) { MEMFAULT_ASSERT_WITH_REASON(0, kMfltRebootReason_StackOverflow); } #endif ================================================ FILE: ports/freertos/src/memfault_sdk_metrics_freertos.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief #include #include "memfault/metrics/metrics.h" #include "memfault/ports/freertos/metrics.h" #include "memfault/ports/freertos/thread_metrics.h" #if !defined(MEMFAULT_METRICS_HEARTBEAT_FREERTOS_CONFIG_DEF) #error \ "Please include memfault_metrics_heartbeat_freertos_config.def in your memfault_metrics_heartbeat_config.def file" #endif #ifdef MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #include "esp_idf_version.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/timers.h" #else #include "FreeRTOS.h" #include "task.h" #include "timers.h" #endif #if MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS // Sanity check that the required configs are set #if !configGENERATE_RUN_TIME_STATS || !configUSE_TRACE_FACILITY #error "configGENERATE_RUN_TIME_STATS and configUSE_TRACE_FACILITY must be enabled" #endif // On FreeRTOS v11 and later, there's an additional config option required #if (tskKERNEL_VERSION_MAJOR >= 11) && !INCLUDE_xTaskGetIdleTaskHandle #error "INCLUDE_xTaskGetIdleTaskHandle must be enabled" #endif // Older versions of FreeRTOS do not have this type, default to uint32_t #ifndef configRUN_TIME_COUNTER_TYPE #define configRUN_TIME_COUNTER_TYPE uint32_t #endif // xTaskGetIdleRunTimeCounter was renamed to ulTaskGetIdleRunTimeCounter in // FreeRTOS v10.3.0. Support an explicit override in case we can't determine // which version is being used. #if defined(MEMFAULT_USE_NEW_FREERTOS_IDLETASK_RUNTIME_API) #if MEMFAULT_USE_NEW_FREERTOS_IDLETASK_RUNTIME_API == 1 #define MEMFAULT_FREERTOS_TASK_GET_IDLE_RUN_TIME_COUNTER ulTaskGetIdleRunTimeCounter #else #define MEMFAULT_FREERTOS_TASK_GET_IDLE_RUN_TIME_COUNTER xTaskGetIdleRunTimeCounter #endif #else // If kernel version is >= 10.3.0, use ulTaskGetIdleRunTimeCounter #if (tskKERNEL_VERSION_MAJOR > 10) || \ ((tskKERNEL_VERSION_MAJOR == 10) && (tskKERNEL_VERSION_MINOR >= 3)) #define MEMFAULT_FREERTOS_TASK_GET_IDLE_RUN_TIME_COUNTER ulTaskGetIdleRunTimeCounter #else #define MEMFAULT_FREERTOS_TASK_GET_IDLE_RUN_TIME_COUNTER xTaskGetIdleRunTimeCounter #endif #endif // Defines the precision used to calculate changes in cpu_usage_pct // Changes in this value require changing the scale_value used with cpu_usage_pct metrics // according to this equation: 100 = PRECISION_CONSTANT / scale_value // See ports/freertos/config/memfault_metrics_heartbeat_freertos_config.def #define PRECISION_CONSTANT (10000ULL) // A fudge factor used to improve rounding behavior at low/high end #define FUDGE_FACTOR (PRECISION_CONSTANT / 2ULL) static configRUN_TIME_COUNTER_TYPE prv_calculate_delta_runtime( configRUN_TIME_COUNTER_TYPE prev_runtime_ticks, configRUN_TIME_COUNTER_TYPE curr_runtime_ticks) { return curr_runtime_ticks - prev_runtime_ticks; } // NB: We calculate a percentage that may need to be scaled according to PRECISION constant: // - 1 percent: 1000 ticks/s * 60 s/min * 60 min/hr * (1/100)hrs -> 36,000 ticks // - 1 permyriad: 1000 ticks/s * 60 s/min * 60 min/hr * (1/10000)hrs -> 360 ticks // The PRECISION_CONSTANT allows handling higher tick rates at higher values (i.e. percent vs // permyriad) static int32_t prv_calc_runtime_percent(configRUN_TIME_COUNTER_TYPE prev_task_runtime, configRUN_TIME_COUNTER_TYPE curr_task_runtime, configRUN_TIME_COUNTER_TYPE curr_total_runtime, configRUN_TIME_COUNTER_TYPE prev_total_runtime) { uint64_t delta_runtime = prv_calculate_delta_runtime(prev_task_runtime, curr_task_runtime); uint64_t delta_total_runtime = prv_calculate_delta_runtime(prev_total_runtime, curr_total_runtime); // Guard against divide by zero if (delta_total_runtime == 0) { return -1; } // Make sure to not overflow when computing the percentage below if (delta_runtime >= UINT64_MAX - (UINT64_MAX / PRECISION_CONSTANT)) { // Scale both parts of the fraction by 10000 to avoid overflow. // Include a fudge factor of 5000 to prevent rounding errors. delta_runtime = ((UINT64_MAX - FUDGE_FACTOR) < delta_runtime) ? (UINT64_MAX) : (delta_runtime + FUDGE_FACTOR); delta_runtime /= PRECISION_CONSTANT; delta_total_runtime /= PRECISION_CONSTANT; } // Clamp delta_runtime to be less than delta_total_runtime // This can happen due to differences in when the total runtime vs a task's runtime are updated if (delta_runtime > delta_total_runtime) { delta_runtime = delta_total_runtime; } // Now compute percentage const int32_t percentage = (int32_t)((delta_runtime * PRECISION_CONSTANT) / delta_total_runtime); return percentage; } static configRUN_TIME_COUNTER_TYPE prv_get_total_runtime(void) { configRUN_TIME_COUNTER_TYPE total_runtime; #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE portALT_GET_RUN_TIME_COUNTER_VALUE(total_runtime); #else total_runtime = portGET_RUN_TIME_COUNTER_VALUE(); #endif return total_runtime; } #endif // MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS #if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE static configRUN_TIME_COUNTER_TYPE prv_get_idle_counter_for_core(uint32_t core) { TaskHandle_t idle_task_handle = #ifdef MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) xTaskGetIdleTaskHandleForCore(core) #else xTaskGetIdleTaskHandleForCPU(core) #endif #else xTaskGetIdleTaskHandleForCPU(core) #endif ; TaskStatus_t idle_task_status; // This could be a bit cleaner if we could use `ulTaskGetRunTimeCounter`, but that API // is not available in FreeRTOS < 10.4.4. vTaskGetInfo(idle_task_handle, &idle_task_status, pdTRUE, eRunning); return idle_task_status.ulRunTimeCounter; } #endif #if MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES // FreeRTOS v9.0.0 made the xTimerGetTimerDaemonTaskHandle() API always // available: // https://github.com/FreeRTOS/FreeRTOS-Kernel/commit/6568ba6eb08a5ed0adf5141291d31f2144a79d08#diff-343560675a0f127a21d52dd3379ff23b01fe59fe87db61669efd3c903621fc22R427-R433 #if (tskKERNEL_VERSION_MAJOR < 9) && (!INCLUDE_xTimerGetTimerDaemonTaskHandle) #error \ "MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES requires FreeRTOS v9.0.0 or later, or INCLUDE_xTimerGetTimerDaemonTaskHandle=1 in FreeRTOSConfig.h" #endif static void prv_record_timer_stack_free_bytes(void) { TaskHandle_t timer_task_handle = xTimerGetTimerDaemonTaskHandle(); UBaseType_t free_space = uxTaskGetStackHighWaterMark(timer_task_handle); // uxTaskGetStackHighWaterMark() returns units of "words", so convert to bytes MEMFAULT_METRIC_SET_UNSIGNED(timer_task_stack_free_bytes, free_space * sizeof(StackType_t)); } #endif void memfault_freertos_port_task_runtime_metrics(void) { #if MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS static configRUN_TIME_COUNTER_TYPE s_prev_idle_runtime = 0; // Only collect separate cpu1 runtime if multi-core split #if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT static configRUN_TIME_COUNTER_TYPE s_prev_idle1_runtime = 0; #endif static configRUN_TIME_COUNTER_TYPE s_prev_total_runtime = 0; configRUN_TIME_COUNTER_TYPE idle_runtime = #if MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE prv_get_idle_counter_for_core(0); #else MEMFAULT_FREERTOS_TASK_GET_IDLE_RUN_TIME_COUNTER(); #endif #if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT configRUN_TIME_COUNTER_TYPE idle1_runtime = prv_get_idle_counter_for_core(1); #endif configRUN_TIME_COUNTER_TYPE total_runtime = prv_get_total_runtime(); int32_t idle_runtime_percent = prv_calc_runtime_percent(s_prev_idle_runtime, idle_runtime, total_runtime, s_prev_total_runtime); #if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT int32_t idle1_runtime_percent = prv_calc_runtime_percent(s_prev_idle1_runtime, idle1_runtime, total_runtime, s_prev_total_runtime); #endif s_prev_idle_runtime = idle_runtime; #if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT s_prev_idle1_runtime = idle1_runtime; #endif s_prev_total_runtime = total_runtime; if (idle_runtime_percent >= 0) { MEMFAULT_METRIC_SET_UNSIGNED(cpu_usage_pct, (uint32_t)(PRECISION_CONSTANT - (uint32_t)idle_runtime_percent)); } #if MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT if (idle1_runtime_percent >= 0) { MEMFAULT_METRIC_SET_UNSIGNED(cpu1_usage_pct, (uint32_t)(PRECISION_CONSTANT - (uint32_t)idle1_runtime_percent)); } #endif #endif // MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS #if MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES prv_record_timer_stack_free_bytes(); #endif #if MEMFAULT_FREERTOS_COLLECT_THREAD_METRICS memfault_freertos_port_thread_metrics(); #endif } ================================================ FILE: ports/freertos/src/memfault_sdk_metrics_thread.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief #include "memfault/ports/freertos/metrics.h" #if MEMFAULT_FREERTOS_COLLECT_THREAD_METRICS #include #include #include "memfault/metrics/metrics.h" #include "memfault/ports/freertos/thread_metrics.h" #if !defined(MEMFAULT_METRICS_HEARTBEAT_FREERTOS_CONFIG_DEF) #error \ "Please include memfault_metrics_heartbeat_freertos_config.def in your memfault_metrics_heartbeat_config.def file" #endif #ifdef MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #include "freertos/FreeRTOS.h" #include "freertos/task.h" #else #include "FreeRTOS.h" #include "task.h" #endif #if !configRECORD_STACK_HIGH_ADDRESS && \ !(configUSE_TRACE_FACILITY && INCLUDE_uxTaskGetStackHighWaterMark) #error \ "FreeRTOSConfig.h must enable either configRECORD_STACK_HIGH_ADDRESS=1 or both configUSE_TRACE_FACILITY=1 and INCLUDE_uxTaskGetStackHighWaterMark=1" #endif #define DEBUG_ 0 #if DEBUG_ #include #define DEBUG_PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define DEBUG_PRINTF(fmt, ...) #endif #if MEMFAULT_METRICS_THREADS_DEFAULTS_INDEX // There is a bug in older versions of GCC when using -std=gnu11 that prevents // the use of compound designated initializers. #if (__STDC_VERSION__ < 201100L) || (__GNUC__ >= 5) MEMFAULT_WEAK MEMFAULT_METRICS_DEFINE_THREAD_METRICS( #if MEMFAULT_METRICS_THREADS_DEFAULTS { .thread_name = "IDLE", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle_pct_max), }, { .thread_name = "Tmr Svc", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_tmr_svc_pct_max), } #endif // MEMFAULT_METRICS_THREADS_DEFAULTS ); #else // (__STDC_VERSION__ < 201100L) || (__GNUC__ >= 5) MEMFAULT_WEAK const sMfltFreeRTOSTaskMetricsIndex g_memfault_thread_metrics_index[] = { { 0 }, }; #endif // (__STDC_VERSION__ < 201100L) || (__GNUC__ >= 5) #endif // MEMFAULT_METRICS_THREADS_DEFAULTS_INDEX #if MEMFAULT_FREERTOS_VERSION_GTE(10, 0, 0) || defined(MEMFAULT_FREERTOS_PXENDOFSTACK_AVAILABLE) // `MEMFAULT_FREERTOS_PXENDOFSTACK_AVAILABLE` is an escape hatch for kernels // outside the version range above. Define it in your platform config (for // example, `memfault_platform_config.h`) only after verifying the internal // `TCB_t` layout matches `struct MfltFreeRTOSTCB` below: `pcTaskName` must // precede `pxEndOfStack`, and there must be no additional fields between them // other than the SMP / ESP-IDF-specific fields handled here. // Unfortunately we must break the opaque pointer to the TCB and access the // pxEndOfStack field directly. The simplest approach is to access the task // name address in the TCB, then offset to the location of the pxEndOfStack // field, which has a reliable location in the versions of FreeRTOS we // support. // https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/V10.0.0/FreeRTOS/Source/tasks.c#L281-L285 // https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/V10.4.6/tasks.c#L267-L271 // https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/6f63da20b3c9e1408e7c8007d3427b75878cbd64/tasks.c#L267-L271 // https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/V10.6.2/tasks.c#L271-L275 struct MfltFreeRTOSTCB { char pcTaskName[configMAX_TASK_NAME_LEN]; // Two cases require additional adjustment: // 1. vanilla FreeRTOS SMP versions (i.e V11.0.0+) that enable // configUSE_TASK_PREEMPTION_DISABLE: // https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/V11.0.0/tasks.c#L373 // 2. ESP-IDF fork, with configNUMBER_OF_CORES > 1: // https://github.com/espressif/esp-idf/blob/v5.3/components/freertos/FreeRTOS-Kernel/tasks.c#L408 #if MEMFAULT_FREERTOS_VERSION_GTE(11, 0, 0) || defined(ESP_PLATFORM) #if (configUSE_TASK_PREEMPTION_DISABLE == 1) || (configNUMBER_OF_CORES > 1) BaseType_t xPreemptionDisable_or_xCoreID; #elif defined(MEMFAULT_USE_ESP32_FREERTOS_INCLUDE) // ESP-IDF TCB_t unconditionally included the xCoreID until v5.3.0: // https://github.com/espressif/esp-idf/commit/439c7c4261837b4563f278b095c77accb266a8c1 #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) BaseType_t xCoreID; #endif #endif #endif // MEMFAULT_FREERTOS_VERSION_GTE(11, 0, 0) || defined(ESP_PLATFORM) uint32_t pxEndOfStack; }; #else #error \ "Unsupported FreeRTOS version, please contact https://mflt.io/contact-support for assistance" #endif static uint32_t prv_get_stack_usage_pct_for_thread(const TaskHandle_t task_handle) { // Read task info to get the stack unused bytes and base address TaskStatus_t task_status; vTaskGetInfo(task_handle, &task_status, pdTRUE, eRunning); const uint32_t stack_unused_bytes = task_status.usStackHighWaterMark * sizeof(StackType_t); const uint32_t stack_base = (uint32_t)task_status.pxStackBase; // Access the TCB structure from the task name address to get the stack_end char *task_name = (char *)task_status.pcTaskName; struct MfltFreeRTOSTCB *tcb = (struct MfltFreeRTOSTCB *)(task_name); const uint32_t stack_end = tcb->pxEndOfStack; // Calculate stack size and usage percentage const uint32_t stack_size = stack_end - stack_base + sizeof(StackType_t); const uint32_t stack_used_bytes = stack_size - stack_unused_bytes; const uint32_t stack_pct = (100 * MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR * stack_used_bytes) / stack_size; DEBUG_PRINTF("Task: %s\n", task_status.pcTaskName); DEBUG_PRINTF(" Stack End: 0x%08lx\n", stack_end); DEBUG_PRINTF(" Stack Base: 0x%08lx\n", stack_base); DEBUG_PRINTF(" Stack Size: %lu\n", stack_size); DEBUG_PRINTF(" Stack Used Bytes: %lu\n", stack_used_bytes); DEBUG_PRINTF(" Stack Unused Bytes: %lu\n", stack_unused_bytes); DEBUG_PRINTF(" Stack Usage: %lu.%02lu%%\n", stack_pct / 100, stack_pct % 100); return stack_pct; } void memfault_freertos_port_thread_metrics(void) { const sMfltFreeRTOSTaskMetricsIndex *thread_metrics = g_memfault_thread_metrics_index; while (thread_metrics->thread_name) { DEBUG_PRINTF("Thread: %s\n", thread_metrics->thread_name); TaskHandle_t task_handle = NULL; if (thread_metrics->get_task_handle) { task_handle = thread_metrics->get_task_handle(); } if (task_handle == NULL) { task_handle = xTaskGetHandle(thread_metrics->thread_name); } if (task_handle != NULL) { memfault_metrics_heartbeat_set_unsigned(thread_metrics->stack_usage_metric_key, prv_get_stack_usage_pct_for_thread(task_handle)); } else { DEBUG_PRINTF(" Thread not found\n"); } thread_metrics++; } } #endif // MEMFAULT_FREERTOS_COLLECT_THREAD_METRICS ================================================ FILE: ports/freertos/src/memfault_self_test_platform.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "FreeRTOS.h" #include "memfault/core/self_test.h" #include "task.h" void memfault_self_test_platform_delay(uint32_t delay_ms) { vTaskDelay(delay_ms / portTICK_PERIOD_MS); } bool memfault_self_test_platform_disable_irqs(void) { taskENTER_CRITICAL(); return true; } bool memfault_self_test_platform_enable_irqs(void) { taskEXIT_CRITICAL(); return true; } ================================================ FILE: ports/include/.mtbsearch.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! This file is required so that this path is still included when mtbsearch generates cyqbuild.mk //! //! For issues debugging the include path check the generate output in the build/ directory //! of a MTB application. //! //! Here's snippet for searching for memfault related include paths, run in the same directory as //! inclist.rsp: `cat inclist.rsp | tr " " "\n" | rg -e memfault-firmware-sdk` #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/ble/mds.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! API for the Bluetooth Low Energy Memfault Diagnostic GATT Service (MDS) //! //! This service can be used for forwarding diagnostic data collected by firmware through a BLE //! gateway. The service is designed such that the gateway can be agnostic to the data being //! forwarded and the URI it should be forwarded to. //! //! Memfault Diagnostic Service: (UUID 54220000-f6a5-4007-a371-722f4ebd8436) //! //! MDS Supported Features Characteristic: (UUID 54220001-f6a5-4007-a371-722f4ebd8436) //! read - (length: variable*) //! Octet 0: //! bit 0-7: rsvd for future use //! ... //! //! MDS Device Identifier Characteristic: (UUID 54220002-f6a5-4007-a371-722f4ebd8436) //! read - (length: variable*) returns the device identifier populated in the //! memfault_platform_get_device_info() dependency function //! //! MDS Data URI Characteristic: (UUID 54220003-f6a5-4007-a371-722f4ebd8436) //! read - (length: variable*) returns the URI diagnostic data should be forwarded to //! //! MDS Authorization Characteristic: (UUID 54220004-f6a5-4007-a371-722f4ebd8436) //! read - (length: variable*) returns the configured authorization to use such as //! "Memfault-Project-Key:YOUR_PROJECT_KEY" //! //! MDS Data Export Characteristic: (UUID 54220005-f6a5-4007-a371-722f4ebd8436) //! notify - must be subscribed to in order for any collected SDK data to be streamed //! write - (length: 1 byte) controls data export operation mode //! 0x00 - Streaming Disabled //! 0x01 - Streaming of any collected SDK data enabled //! Other: reserved for future use //! //! When subscribed to and when streaming mode has been enabled via a GATT Write with response, //! packets will be sized to fit within negotiated MTU. //! The format of a packet streamed is as follows: //! byte 0: //! bits 0-4: Sequence Number A sequentially increasing counter that can be used to detect //! errors in the BLE stack. SN is reset to 0 anytime a disconnect takes place. //! Errors client can check for: //! - Packets are getting repeated when Last Sequence Number == Current Sequence Number //! - Packets are getting dropped when abs(Last Sequence Number - Current Sequence Number) //! != 1 //! bit 5-7: Reserved for future use //! //! byte 1-N: opaque payload listener is responsible for forwarding to the URI specified in the //! Data URI //! Characteristic using the authorization scheme specified in the Authorization //! Characteristic. //! //! Client Characteristic Configuration Descriptor: (UUID 00002902-0000-1000-8000-00805f9b34fb) //! //! * If length exceeds negotiated MTU size, it is assumed that client has implemented support for //! long attribute reads. (iOS & Android both support this). If long attribute reads are not //! supported for some reason, an MTU needs to be negotiated which exceeds the length of the //! Project Key and Device Identifier. //! //! Note 1: If additional characteristics are ever added to the service, they should be appended //! to the table to minimize impact for clients who have not (correctly) implemented the "Service //! Changed" characteristic in the Generic Attribute Profile Service. //! //! Note 2: For production applications and enhanced security and privacy, it is recommended //! the memfault_mds_access_enabled() be implemented. See function comments below for more details. //! //! Note 3: This GATT service is currently under development and subject to updates and //! enhancements in the future with no current guarantees for backward compatibility. Client //! implementations should read the MDS Supported Features Characteristic and only attempt to use //! the service as it is currently defined if the value is 0x00. #ifdef __cplusplus extern "C" { #endif #include #include #include "memfault/components.h" //! Initializes the MDS Gatt Service //! //! @return A reference to the service which was initialized void *mds_boot(void); //! Controls whether or not client can access diagnostic data //! //! This function exists so an end user can restrict access to the data available from the //! diagnostic service. The default behavior is to always return true (data available on all //! connections). //! //! @note We recommend implement this dependency function for production applications //! and only allowing access to the diagnostic data when the connected peer has been //! authenticated and the connection is encrypted. //! //! @return true if client can access diagnostic data, false otherwise. If call return false //! here, BLE service will return ATT_ERROR_READ_NOT_PERMITTED / ATT_ERROR_WRITE_NOT_PERMITTED //! for any read or write operation to the GATT service, respectively extern bool mds_access_enabled(uint16_t connection_handle); #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/buffered_coredump_storage.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Background: Many storage implementations have alignment requirements for write / program //! operations. Reasons vary but include: //! - A Flash storage is covered by ECC bits meaning a block can only get written once since //! the ECC bits are in flash too. //! - Memory region can only program in units of words, double-words, etc. //! //! This is a single-file header style utility that can be included in a flash coredump storage //! implementation to buffer write operations so they are guaranteed be aligned along and sized in //! MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE units. //! //! The implementation takes advantage of the property that the Memfault SDK will always use //! sequential writes when flushing to coredump storage with the exception of the header which is //! written at offset 0 of storage as the last step. //! //! To use: //! //! 1. Include this file in your storage port //! 2. Implement buffered writer: //! bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *block) { //! const addr = your_storage_base_addr + block->offset; //! your_storage_write(addr, block->data, MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE); //! } //! //! Usage Notes: //! - The size of coredump storage itself must be >= MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE //! - If a coredump is not exactly MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE units in size, a //! MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE size buffer will still be written but unused bytes //! will be set to zero. #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/panics/platform/coredump.h" #ifndef MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE #define MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE 32 #endif #ifdef __cplusplus extern "C" { #endif typedef struct CoredumpWorkingBuffer sCoredumpWorkingBuffer; //! Callback invoked when a 'MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE' is ready to be written //! //! @param blk The block of data to write. The size to write is always //! MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE. //! //! @return true if write was successful, false otherwise bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk); // // Buffered Storage Implementation, single-file header style so it can easily be picked up // by a coredump storage port by simply including this file. // struct CoredumpWorkingBuffer { // data to write uint8_t data[MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE]; // offset within storage to be written to uint32_t write_offset; // Internal tracking of how many bytes were written uint32_t bytes_written; }; MEMFAULT_ALIGNED(8) static sCoredumpWorkingBuffer s_working_buffer_header; MEMFAULT_ALIGNED(8) static sCoredumpWorkingBuffer s_working_buffer; static sCoredumpWorkingBuffer *prv_get_working_buf(uint32_t offset) { return (offset == 0) ? &s_working_buffer_header : &s_working_buffer; } static bool prv_write_blk(sCoredumpWorkingBuffer *block) { if (!memfault_platform_coredump_storage_buffered_write(block)) { return false; } *block = (sCoredumpWorkingBuffer){ 0 }; return true; } static bool prv_try_flush(void) { sCoredumpWorkingBuffer *hdr_block = &s_working_buffer_header; sCoredumpWorkingBuffer *data_block = &s_working_buffer; if (hdr_block->bytes_written == MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) { // this is the final write for the coredump (header) // it's possible the last data blob in the coredump isn't a multiple // of our write size so let's flush whatever is queued up. (Unused bytes // are just zero'd since the working buffer is memset to 0 before each use) if (data_block->bytes_written != 0) { if (!prv_write_blk(data_block)) { return false; } } // write the header if (!prv_write_blk(hdr_block)) { return false; } } if (data_block->bytes_written == MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) { if (!prv_write_blk(data_block)) { return false; } } return true; } static bool memfault_coredump_storage_buffered_write(uint32_t offset, const void *data, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); if (((offset + data_len) > info.size) || ((info.size % MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) != 0) || (info.size < MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE)) { return false; // out of bounds write } const uint8_t *datap = (const uint8_t *)data; uint32_t start_addr = offset; uint32_t page_aligned_start_address = (start_addr / MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) * MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE; // we have to copy data into a temporary buffer because we can only issue aligned writes if (page_aligned_start_address != start_addr) { sCoredumpWorkingBuffer *working_buffer = prv_get_working_buf(page_aligned_start_address); uint32_t bytes_to_write = MEMFAULT_MIN( (page_aligned_start_address + MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) - start_addr, data_len); uint32_t write_offset = start_addr - page_aligned_start_address; memmove(&working_buffer->data[write_offset], datap, bytes_to_write); working_buffer->bytes_written += bytes_to_write; working_buffer->write_offset = page_aligned_start_address; if (!prv_try_flush()) { return false; } start_addr += bytes_to_write; data_len -= bytes_to_write; datap += bytes_to_write; } // now that we have copied data into a temporary buffer and are aligned by the expected // write size, let's flush complete blocks to flash for (uint32_t i = 0; i < data_len; i += MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) { const uint32_t size = MEMFAULT_MIN(MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE, data_len - i); sCoredumpWorkingBuffer *working_buffer = prv_get_working_buf(start_addr + i); memmove(&working_buffer->data, &datap[i], size); working_buffer->bytes_written += size; working_buffer->write_offset = start_addr + i; if (!prv_try_flush()) { return false; } } return true; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { return memfault_coredump_storage_buffered_write(offset, data, data_len); } #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/freertos/metrics.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief #if defined(ESP_PLATFORM) #include "sdkconfig.h" #define MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #endif #include "memfault/config.h" #ifdef MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #include "freertos/FreeRTOS.h" #include "freertos/task.h" #else #include "FreeRTOS.h" #include "task.h" #endif // The FreeRTOS task.h header provides these version specifiers starting with // FreeRTOS V8.0.0. #define MEMFAULT_FREERTOS_VERSION_GTE(major, minor, build) \ (tskKERNEL_VERSION_MAJOR > major) || \ (tskKERNEL_VERSION_MAJOR == major && tskKERNEL_VERSION_MINOR > minor) || \ (tskKERNEL_VERSION_MAJOR == major && tskKERNEL_VERSION_MINOR == minor && \ tskKERNEL_VERSION_BUILD >= build) // Auto enable run time stats if the FreeRTOS version is >= 10.2.0 and and the // FreeRTOS config options are set appropriately #if configGENERATE_RUN_TIME_STATS && configUSE_TRACE_FACILITY && \ MEMFAULT_FREERTOS_VERSION_GTE(10, 2, 0) // For now only support ESP-IDF multi-core FreeRTOS until the // FreeRTOS kernel mainlines support for SMP. #if defined(ESP_PLATFORM) && configNUM_CORES == 2 #define MEMFAULT_FREERTOS_RUN_TIME_STATS_SINGLE_CORE 0 #define MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE 1 #else #define MEMFAULT_FREERTOS_RUN_TIME_STATS_SINGLE_CORE 1 #define MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE 0 #endif #else // Not available #define MEMFAULT_FREERTOS_RUN_TIME_STATS_SINGLE_CORE 0 #define MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE 0 #endif // The user can opt-out by setting MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS to 0 // in memfault_platform_config.h #if !defined(MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS) #define MEMFAULT_FREERTOS_COLLECT_RUN_TIME_STATS \ (MEMFAULT_FREERTOS_RUN_TIME_STATS_MULTI_CORE || MEMFAULT_FREERTOS_RUN_TIME_STATS_SINGLE_CORE) #endif // Disable multi-core split metrics if not defined #if !defined(MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT) #define MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT 0 #endif // Provide build-time error if single core + runtime stats split #if MEMFAULT_FREERTOS_RUN_TIME_STATS_SINGLE_CORE && MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT #error \ "Multi-core split stats not supported for single-core devices, disable MEMFAULT_FREERTOS_RUNTIME_STATS_MULTI_CORE_SPLIT" #endif #if !defined(MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES) #define MEMFAULT_FREERTOS_COLLECT_TIMER_STACK_FREE_BYTES 1 #endif #if !defined(MEMFAULT_FREERTOS_COLLECT_THREAD_METRICS) #define MEMFAULT_FREERTOS_COLLECT_THREAD_METRICS 1 #endif #if !defined(MEMFAULT_METRICS_THREADS_DEFAULTS) #define MEMFAULT_METRICS_THREADS_DEFAULTS 1 #endif #if !defined(MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #define MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR 100 #endif // The default thread metrics are defined as weak so they can be overridden by // the user. Some build systems may not support strong symbols overriding weak // ones in the Memfault library, so we provide a way to disable the default // thread metrics definition entirely. #if !defined(MEMFAULT_METRICS_THREADS_DEFAULTS_INDEX) #define MEMFAULT_METRICS_THREADS_DEFAULTS_INDEX 1 #endif #ifdef __cplusplus extern "C" { #endif // __cplusplus void memfault_freertos_port_task_runtime_metrics(void); #ifdef __cplusplus } #endif // __cplusplus ================================================ FILE: ports/include/memfault/ports/freertos/thread_metrics.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief #include "memfault/config.h" #include "memfault/metrics/metrics.h" #include "memfault/ports/freertos/metrics.h" #if defined(ESP_PLATFORM) #include "sdkconfig.h" #define MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #endif #ifdef MEMFAULT_USE_ESP32_FREERTOS_INCLUDE #include "freertos/task.h" #else #include "task.h" #endif #ifdef __cplusplus extern "C" { #endif // __cplusplus typedef struct MfltFreeRTOSTaskMetricsIndex { // The name of the task to monitor. Must _exactly_ match the target task. // FreeRTOS task names are limited to configMAX_TASK_NAME_LEN bytes in length, // be sure to test the string matches exactly for each registered task. const char *thread_name; // The task can optionally be tagged with the task handle, by providing a // callback. This helps in cases where there are tasks with ambiguous names. // If the callback is unpopulated or returns NULL, thread_name is used to // identify the task. If thread_name is NULL, no data is recorded. TaskHandle_t (*get_task_handle)(void); // The Memfault Heartbeat Metric key for stack usage. Should be defined as // follows: // MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory__pct_max, // kMemfaultMetricType_Unsigned, // MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR); MemfaultMetricId stack_usage_metric_key; } sMfltFreeRTOSTaskMetricsIndex; //! This data structure can be initialized by the user to specify which threads //! should be recorded in the thread metrics. A weak definition is provided that //! records the idle + sysworkq threads only. //! //! The array must contain a blank entry at the end to terminate the list. //! An example initialization looks like this: //! //! MEMFAULT_METRICS_DEFINE_THREAD_METRICS ( //! { //! .thread_name = "idle", //! .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle_pct_max), //! }, //! { //! .thread_name = "sysworkq", //! .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_sysworkq_pct_max), //! } //! ); extern const sMfltFreeRTOSTaskMetricsIndex g_memfault_thread_metrics_index[]; #define MEMFAULT_METRICS_DEFINE_THREAD_METRICS(...) \ const sMfltFreeRTOSTaskMetricsIndex g_memfault_thread_metrics_index[] = { __VA_ARGS__, { 0 } } //! Perform tallying of thread metrics for registered threads. Called internally //! by memfault_freertos_port_task_runtime_metrics(). void memfault_freertos_port_thread_metrics(void); #ifdef __cplusplus } #endif // __cplusplus ================================================ FILE: ports/include/memfault/ports/freertos.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "FreeRTOSConfig.h" #include "memfault/config.h" #ifdef __cplusplus extern "C" { #endif //! By default, The Memfault FreeRTOS port determines what memory allocation scheme to //! use based on the configSUPPORT_STATIC_ALLOCATION & configSUPPORT_DYNAMIC_ALLOCATION defines //! within FreeRTOSConfig.h. //! //! Alternatively, MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION can be added to CFLAGS //! or "memfault_platform_config.h" to override this default behavior #if !defined(MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION) #if configSUPPORT_STATIC_ALLOCATION == 1 #define MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION 1 #elif configSUPPORT_DYNAMIC_ALLOCATION == 1 #define MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION 0 #else // If a user hasn't explicitly set an allocation scheme, fallback to using FreeRTOS default // (dynamic allocation) #define MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION 0 #endif #endif /* MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION */ //! Should be called prior to making any Memfault SDK calls void memfault_freertos_port_boot(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/freertos_coredump.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "memfault/config.h" #include "memfault/panics/platform/coredump.h" #ifdef __cplusplus extern "C" { #endif //! For each task tracked we will need to collect the TCB + a region of the stack #define MEMFAULT_PLATFORM_MAX_TASK_REGIONS \ (MEMFAULT_PLATFORM_MAX_TRACKED_TASKS * (1 /* TCB */ + 1 /* stack */)) //! Helper to collect minimal RAM needed for backtraces of non-running FreeRTOS tasks //! //! @param[out] regions Populated with the regions that need to be collected in order //! for task and stack state to be recovered for non-running FreeRTOS tasks //! @param[in] num_regions The number of entries in the 'regions' array //! //! @return The number of entries that were populated in the 'regions' argument. Will always //! be <= num_regions size_t memfault_freertos_get_task_regions(sMfltCoredumpRegion *regions, size_t num_regions); #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/freertos_trace.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! This file needs to be included from your platforms FreeRTOSConfig.h to take advantage of //! Memfault's hooks into the FreeRTOS tracing utilities #ifdef __cplusplus extern "C" { #endif #include "memfault/config.h" // FreeRTOSConfig.h is often included in assembly files so wrap function declarations for // convenience to prevent compilation errors #if !defined(__ASSEMBLER__) && !defined(__IAR_SYSTEMS_ASM__) void memfault_freertos_trace_task_create(void *tcb); void memfault_freertos_trace_task_delete(void *tcb); #include "memfault/core/heap_stats.h" #endif // // We ifndef the trace macros so it's possible for an end user to use a custom definition that // calls Memfault's implementation as well as their own // #ifndef traceTASK_CREATE #define traceTASK_CREATE(pxNewTcb) memfault_freertos_trace_task_create(pxNewTcb) #endif #ifndef traceTASK_DELETE #define traceTASK_DELETE(pxTaskToDelete) memfault_freertos_trace_task_delete(pxTaskToDelete) #endif #if MEMFAULT_FREERTOS_PORT_HEAP_STATS_ENABLE #if MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE != 0 // FreeRTOS has its own locking mechanism (suspends tasks) so don't attempt // to use the memfault_lock implementation as well #error \ "MEMFAULT_COREDUMP_HEAP_STATS_LOCK_ENABLE must be 0 when using MEMFAULT_FREERTOS_PORT_HEAP_STATS_ENABLE" #endif #ifndef traceFREE #define traceFREE(pv, xBlockSize) MEMFAULT_HEAP_STATS_FREE(pv) #endif #ifndef traceMALLOC #define traceMALLOC(pvReturn, xWantedSize) MEMFAULT_HEAP_STATS_MALLOC(pvReturn, xWantedSize) #endif #endif /* MEMFAULT_FREERTOS_PORT_HEAP_STATS_ENABLE */ //! A define that is used to assert that this file has been included from FreeRTOSConfig.h #define MEMFAULT_FREERTOS_TRACE_ENABLED 1 #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/lwip/metrics.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #ifdef __cplusplus extern "C" { #endif // _cplusplus //! Collects a round TCP/UDP metrics from LwIP void memfault_lwip_heartbeat_collect_data(void); #ifdef __cplusplus } #endif // _cplusplus ================================================ FILE: ports/include/memfault/ports/mbedtls/metrics.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #ifdef __cplusplus extern "C" { #endif // __cplusplus #include //! Structure to hold mbedTLS metrics, for runtime access typedef struct MemfaultMbedtlsMetricData { int32_t mem_used_bytes; uint32_t mem_max_bytes; } sMemfaultMbedtlsMetricData; //! Fetch the current MbedTLS metric data void memfault_mbedtls_heartbeat_get_data(sMemfaultMbedtlsMetricData *metrics); //! Collects a heartbeat of mbedTLS metrics before resetting the values void memfault_mbedtls_heartbeat_collect_data(void); //! Clears backing metric values, only for testing purposes void memfault_mbedtls_test_clear_values(void); #ifdef __cplusplus } #endif // __cplusplus ================================================ FILE: ports/include/memfault/ports/reboot_reason.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Ports to facilitate the integration of Memfault reboot reason tracking //! For more details about the integration, see https://mflt.io/reboot-reasons #include "memfault/core/reboot_tracking.h" #ifdef __cplusplus extern "C" { #endif //! Reads platform reset reason registers and converts to format suitable for reporting to Memfault //! //! @note Reboot reasons are recorded and reported to the Memfault UI and //! serve as a leading indicator to issues being seen in the field. //! @note For MCUs where reset reason register information is "sticky" (persists across resets), //! the platform port will clear the register. This way the next boot will be guaranteed to reflect //! the correct information for the reboot that just took place. //! @note By default, ports print the MCU reset information on bootup using the MEMFAULT_LOG //! infrastructure. This can optionally be disabled by adding -DMEMFAULT_ENABLE_REBOOT_DIAG_DUMP=0 //! to your list of compiler flags //! //! @param[out] info Populated with platform specific reset reason info. Notably, reset_reason_reg //! is populated with the reset reason register value and reset_reason is populated with a //! eMemfaultRebootReason which will be displayed in the Memfault UI void memfault_reboot_reason_get(sResetBootupInfo *info); #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/stm32cube/l4/flash.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Utilities that can be used alongside STM32L4 coredump storage flash port #include #ifdef __cplusplus extern "C" { #endif //! Check FLASH->ECCR for ECC error and clear error if it's within address range //! //! @note The STM32L4 ECCs every double-word write. There is a low probability that //! a write fails due to a power glitch / brown out or other phenomena. If the error is not //! correctable (> 1 bit), an NMI will be generated and the NMI_Handler will be invoked. //! From that handler, one can programmatically clear the error by writing zeros to //! the failing address. See https://mflt.io/stm32l4-ecc-error-recovery for more context //! //! @param[in] start_addr The start address of the range to clear ECC errors in //! @param[in] end_addr The end address pf the range to clear ECC errors in //! @param[out] corrupted_address The address that was corrupted or zero if no //! error was found //! //! @return true if no error was detected or error was successfully cleared, //! false otherwise bool memfault_stm32cubel4_flash_clear_ecc_error(uint32_t start_addr, uint32_t end_addr, uint32_t *corrupted_address); #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/stm32cube/wb/flash.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Utilities that can be used alongside STM32WB coredump storage flash port #include #ifdef __cplusplus extern "C" { #endif //! Check FLASH->ECCR for ECC error and clear error if it's within address range //! //! @note The STM32WB ECCs every double-word write. There is a low probability that //! a write fails due to a power glitch / brown out or other phenomena. If the error is not //! correctable (> 1 bit), an NMI will be generated and the NMI_Handler will be invoked. //! From that handler, one can programmatically clear the error by writing zeros to //! the failing address. See https://mflt.io/stm32-ecc-error-recovery for more context //! //! @param[in] start_addr The start address of the range to clear ECC errors in //! @param[in] end_addr The end address of the range to clear ECC errors in //! @param[out] corrupted_address The address that was corrupted or zero if no //! error was found //! //! @return true if no error was detected or error was successfully cleared, //! false otherwise bool memfault_stm32cubewb_flash_clear_ecc_errors(uint32_t start_addr, uint32_t end_addr, uint32_t *corrupted_address); #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/threadx_coredump.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! ThreadX coredump helper: capture per-thread TCBs, stacks, and a stack-usage //! watermark sidecar into coredump regions. //! //! Usage from memfault_platform_coredump_get_regions(): //! //! #include "memfault/ports/threadx_coredump.h" //! //! // ... after capturing active stack and kernel BSS ... //! region_idx += memfault_threadx_get_thread_regions( //! &s_coredump_regions[region_idx], //! MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx); #include #include "memfault/config.h" #include "memfault/panics/platform/coredump.h" #ifdef __cplusplus extern "C" { #endif //! Maximum number of ThreadX threads whose TCB + stack + watermark are captured. //! Override in memfault_platform_config.h if the application has more threads. #ifndef MEMFAULT_THREADX_MAX_THREADS #define MEMFAULT_THREADX_MAX_THREADS 16 #endif //! Upper bound on regions returned by memfault_threadx_get_thread_regions(): //! MEMFAULT_THREADX_MAX_THREADS TCB regions //! MEMFAULT_THREADX_MAX_THREADS stack regions //! 1 watermark sidecar region #define MEMFAULT_THREADX_MAX_TASK_REGIONS (MEMFAULT_THREADX_MAX_THREADS * 2 + 1) //! Walk the ThreadX created-thread circular list and populate @p regions with: //! - Each thread's TX_THREAD control block //! - Each thread's full stack (validated via memfault_platform_sanitize_address_range) //! - A stack-usage watermark sidecar array (one entry per thread) //! //! The walk is safe against corrupted TCBs: each pointer is sanitized and each //! TCB's tx_thread_id field is checked before any stack fields are accessed. //! //! @param regions Output array to populate with coredump regions. //! @param num_regions Size of the @p regions array. //! @return Number of entries written to @p regions (always <= @p num_regions). size_t memfault_threadx_get_thread_regions(sMfltCoredumpRegion *regions, size_t num_regions); #ifdef __cplusplus } #endif ================================================ FILE: ports/include/memfault/ports/watchdog.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! An abstraction to facilitate implementing a debuggable watchdog subsystem using the ideas //! discussed in https://mflt.io/root-cause-watchdogs #ifdef __cplusplus extern "C" { #endif #include #include "memfault/config.h" //! Starts a software watchdog with a timeout of MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS //! //! When the software watchdog expires, a platform specific ISR handler will //! be invoked. We recommend wiring this handler directly up to the memfault-firmware-sdk by //! adding the following define to your build system. //! -DMEMFAULT_EXC_HANDLER_WATCHDOG=YourIRQ_Handler //! //! @return 0 on success, else error code int memfault_software_watchdog_enable(void); //! Stops the software watchdog //! //! @return 0 on success, else error code int memfault_software_watchdog_disable(void); //! A call to this function restarts the software watchdog countdown //! //! @return 0 on success, else error code int memfault_software_watchdog_feed(void); //! Resets and changes the software watchdog timeout //! //! @return 0 on success, else error code int memfault_software_watchdog_update_timeout(uint32_t timeout_ms); #ifdef __cplusplus } #endif ================================================ FILE: ports/lwip/config/memfault_lwip_metrics_heartbeat_config.def ================================================ #include "lwip/stats.h" #if TCP_STATS MEMFAULT_METRICS_KEY_DEFINE(tcp_tx_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(tcp_rx_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(tcp_drop_count, kMemfaultMetricType_Unsigned) #endif // TCP_STATS #if UDP_STATS MEMFAULT_METRICS_KEY_DEFINE(udp_tx_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(udp_rx_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(udp_drop_count, kMemfaultMetricType_Unsigned) #endif // UDP_STATS ================================================ FILE: ports/lwip/memfault_lwip_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "lwip/stats.h" #include "memfault/metrics/metrics.h" #include "memfault/ports/lwip/metrics.h" // Macro used to calculate appropriate diff for the protocol stats, update the metric values, and // reset static values. // // Instead of resetting the counter values to 0, a difference is calculated to prevent concurrency // issues. We take the previous counter value and subtract from the current value. This way only // reads are performed on the stat counters in this context. The difference calculations rely on two // things: // 1. The counters are monotonically increasing // 2. The values will never wrap more than once before a heartbeat #define SET_PROTOCOL_METRICS(proto_struct, proto_name, proto_metric_prefix) \ do { \ uint32_t tx_diff = proto_struct.xmit - s_##proto_name##_tx_prev; \ uint32_t rx_diff = proto_struct.recv - s_##proto_name##_rx_prev; \ uint32_t drop_diff = proto_struct.drop - s_##proto_name##_drop_prev; \ \ MEMFAULT_METRIC_SET_UNSIGNED(proto_metric_prefix##_tx_count, tx_diff); \ MEMFAULT_METRIC_SET_UNSIGNED(proto_metric_prefix##_rx_count, rx_diff); \ MEMFAULT_METRIC_SET_UNSIGNED(proto_metric_prefix##_drop_count, drop_diff); \ \ s_##proto_name##_tx_prev += tx_diff; \ s_##proto_name##_rx_prev += rx_diff; \ s_##proto_name##_drop_prev += drop_diff; \ } while (0) void memfault_lwip_heartbeat_collect_data(void) { #if TCP_STATS static uint32_t s_tcp_tx_prev = 0; static uint32_t s_tcp_rx_prev = 0; static uint32_t s_tcp_drop_prev = 0; SET_PROTOCOL_METRICS(lwip_stats.tcp, tcp, tcp); #endif // TCP_STATS #if UDP_STATS static uint32_t s_udp_tx_prev = 0; static uint32_t s_udp_rx_prev = 0; static uint32_t s_udp_drop_prev = 0; SET_PROTOCOL_METRICS(lwip_stats.udp, udp, udp); #endif // UDP_STATS } ================================================ FILE: ports/mbedtls/config/memfault_mbedtls_metrics_heartbeat_config.def ================================================ MEMFAULT_METRICS_KEY_DEFINE(mbedtls_mem_used_bytes, kMemfaultMetricType_Signed) MEMFAULT_METRICS_KEY_DEFINE(mbedtls_mem_max_bytes, kMemfaultMetricType_Unsigned) ================================================ FILE: ports/mbedtls/memfault_mbedtls_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "memfault/core/math.h" #include "memfault/metrics/metrics.h" #include "memfault/ports/mbedtls/metrics.h" #include "memfault/util/align.h" // We create a metadata type who's size is equal to the maximum alignment of the architecture // This allows us to safely prefix the allocated memory with our metadata and return an aligned // offset value to the caller typedef union { uint32_t requested_size; uMemfaultMaxAlignType padding; } uAllocMetadata; #define ALLOC_METADATA_OVERHEAD (sizeof(uAllocMetadata)) // Due to the metadata structure, we require the largest datatype is >= than the largest alignment // requirement MEMFAULT_STATIC_ASSERT(ALLOC_METADATA_OVERHEAD >= MEMFAULT_MAX_ALIGN_SIZE, "sizeof(uAllocMetadata) must be >= to maximum alignment size"); // Additionally we require that the sizeof our metadata structure be a multiple of the alignment MEMFAULT_STATIC_ASSERT(ALLOC_METADATA_OVERHEAD % MEMFAULT_MAX_ALIGN_SIZE == 0, "uAllocMetadata must be a multiple of the maximum alignment"); extern void *__real_mbedtls_calloc(size_t n, size_t size); extern void __real_mbedtls_free(void *ptr); static sMemfaultMbedtlsMetricData s_mbedtls_metrics = { 0 }; // This wrapper adds allocations with a metadata structure at the beginning of the allocation. // The memory allocated is large enough to account for overhead of the metadata structure, // including its padding. The metadata structure must be equal in size to the maximum // alignment. This is because a fixed offset is used to transform the pointer from the wrapped // pointer containing the metadata structure and the pointer returned to the caller. void *__wrap_mbedtls_calloc(size_t n, size_t size) { // Pointer to return to the caller void *aligned_ptr = NULL; // Requested allocation size size_t requested_size = n * size; // Size required by maximum alignment (includes stat and alignment padding) size_t total_size = requested_size + ALLOC_METADATA_OVERHEAD; void *wrapper_ptr = __real_mbedtls_calloc(1, total_size); if (wrapper_ptr) { // Offset wrapped pointer by metadata overhead and round up to next aligned multiple aligned_ptr = (void *)((uintptr_t)wrapper_ptr + ALLOC_METADATA_OVERHEAD); // Get metadata pointer and update the fields uAllocMetadata *metadata_ptr = (uAllocMetadata *)wrapper_ptr; metadata_ptr->requested_size = requested_size; // Update metric values s_mbedtls_metrics.mem_used_bytes += requested_size; if (s_mbedtls_metrics.mem_used_bytes > (int32_t)s_mbedtls_metrics.mem_max_bytes) { s_mbedtls_metrics.mem_max_bytes = (uint32_t)s_mbedtls_metrics.mem_used_bytes; } } return aligned_ptr; } // This wrapper performs the opposite operations of the calloc wrapper. The memory allocation // is arranged such that we can safely subtract the provided pointer by the size of the // metadata structure to access the metadata stored to update the memory metrics and restore // the originally allocated pointer. void __wrap_mbedtls_free(void *ptr) { if (ptr == NULL) { return; } // Get pointer to stat structure uAllocMetadata *metadata_ptr = (uAllocMetadata *)ptr - 1; // Determine original pointer from stat offset void *orig_ptr = (void *)metadata_ptr; // Update metric s_mbedtls_metrics.mem_used_bytes -= metadata_ptr->requested_size; // Free original pointer __real_mbedtls_free(orig_ptr); } void memfault_mbedtls_heartbeat_get_data(sMemfaultMbedtlsMetricData *metrics) { *metrics = s_mbedtls_metrics; } void memfault_mbedtls_heartbeat_collect_data(void) { MEMFAULT_METRIC_SET_SIGNED(mbedtls_mem_used_bytes, s_mbedtls_metrics.mem_used_bytes); MEMFAULT_METRIC_SET_UNSIGNED(mbedtls_mem_max_bytes, s_mbedtls_metrics.mem_max_bytes); } void memfault_mbedtls_test_clear_values(void) { s_mbedtls_metrics.mem_used_bytes = 0; s_mbedtls_metrics.mem_max_bytes = 0; } ================================================ FILE: ports/mbedtls/memfault_platform_http_client.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Implements platform dependency functions required to utilize Memfault's http client //! for posting data collected via HTTPS with a stack making using of lwIP & Mbed TLS #include #include #include #include "mbedtls/debug.h" #include "mbedtls/entropy.h" #include "mbedtls/hmac_drbg.h" #include "mbedtls/net_sockets.h" #include "mbedtls/ssl.h" #include "mbedtls/ssl_cache.h" #include "mbedtls/x509.h" #include "memfault/components.h" //! lwIP is by far the most popular port used with Mbed TLS with embedded stacks so by default //! include a port which implements a minimal mbedtls/net_sockets.h interface. #ifndef MEMFAULT_PORT_MBEDTLS_USE_LWIP #define MEMFAULT_PORT_MBEDTLS_USE_LWIP 1 #endif #if MEMFAULT_PORT_MBEDTLS_USE_LWIP #include "lwip/debug.h" #include "lwip/netdb.h" #include "lwip/opt.h" #include "lwip/sockets.h" #include "lwip/stats.h" #include "lwip/tcp.h" void mbedtls_net_init(mbedtls_net_context *ctx) { ctx->fd = -1; } int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, const char *port, int proto) { struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE, }; struct addrinfo *res = NULL; int ret = getaddrinfo(MEMFAULT_HTTP_CHUNKS_API_HOST, port, &hints, &res); if ((ret != 0) || (res == NULL)) { MEMFAULT_LOG_ERROR("Unable to resolve IP for %s - %d", MEMFAULT_HTTP_CHUNKS_API_HOST, (int)ret); return ret; } ctx->fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (ctx->fd < 0) { MEMFAULT_LOG_ERROR("Unable to open socket"); return -1; } ret = connect(ctx->fd, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); return ret; } int mbedtls_net_send(void *ctx, unsigned char const *buf, size_t len) { mbedtls_net_context *net_ctx = (mbedtls_net_context *)ctx; return lwip_send(net_ctx->fd, buf, len, 0); } int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len) { mbedtls_net_context *net_ctx = (mbedtls_net_context *)ctx; return lwip_recv(net_ctx->fd, (void *)buf, len, 0); } void mbedtls_net_close(mbedtls_net_context *ctx) { if (ctx->fd == -1) { return; } close(ctx->fd); ctx->fd = -1; } void mbedtls_net_free(mbedtls_net_context *ctx) { mbedtls_net_close(ctx); } #endif /* MEMFAULT_PORT_MBEDTLS_USE_LWIP */ struct MfltHttpClient { bool active; mbedtls_net_context net_ctx; mbedtls_entropy_context entropy; mbedtls_hmac_drbg_context hmac_drbg; mbedtls_ssl_context ssl; mbedtls_ssl_config conf; uint32_t flags; mbedtls_x509_crt cacert; }; typedef struct MfltHttpResponse { // HTTP status code or negative error code int status_code; } sMfltHttpResponse; static sMfltHttpClient s_client; static void prv_teardown_mbedtls(sMfltHttpClient *client) { mbedtls_net_free(&client->net_ctx); mbedtls_x509_crt_free(&client->cacert); mbedtls_ssl_free(&client->ssl); mbedtls_ssl_config_free(&client->conf); mbedtls_hmac_drbg_free(&client->hmac_drbg); mbedtls_entropy_free(&client->entropy); } sMfltHttpClient *memfault_platform_http_client_create(void) { char port[10] = { 0 }; if (s_client.active) { MEMFAULT_LOG_ERROR("Memfault HTTP client already in use"); return NULL; } mbedtls_ssl_init(&s_client.ssl); mbedtls_ssl_config_init(&s_client.conf); mbedtls_hmac_drbg_init(&s_client.hmac_drbg); mbedtls_x509_crt_init(&s_client.cacert); mbedtls_net_init(&s_client.net_ctx); mbedtls_entropy_init(&s_client.entropy); const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); #define HMAC_PERS "memfault" int ret = mbedtls_hmac_drbg_seed(&s_client.hmac_drbg, md_info, mbedtls_entropy_func, &s_client.entropy, (const unsigned char *)HMAC_PERS, sizeof(HMAC_PERS) - 1); if (ret != 0) { MEMFAULT_LOG_ERROR("mbedtls_hmac_drbg_seed returned -0x%x", (unsigned int)-ret); goto cleanup; } ret = mbedtls_x509_crt_parse(&s_client.cacert, (const unsigned char *)MEMFAULT_ROOT_CERTS_PEM, sizeof(MEMFAULT_ROOT_CERTS_PEM)); if (ret != 0) { MEMFAULT_LOG_ERROR("mbedtls_x509_crt_parse returned -0x%x", (unsigned int)-ret); goto cleanup; } // Perform DNS lookup for Memfault server and open connection snprintf(port, sizeof(port), "%d", MEMFAULT_HTTP_GET_CHUNKS_API_PORT()); ret = mbedtls_net_connect(&s_client.net_ctx, MEMFAULT_HTTP_CHUNKS_API_HOST, port, MBEDTLS_NET_PROTO_TCP); if (ret != 0) { MEMFAULT_LOG_ERROR("mbedtls_net_connect returned -0x%x", (unsigned int)-ret); } ret = mbedtls_ssl_config_defaults(&s_client.conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret != 0) { MEMFAULT_LOG_ERROR("mbedtls_ssl_config_defaults returned -0x%x", (unsigned int)-ret); goto cleanup; } mbedtls_ssl_conf_authmode(&s_client.conf, MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_ca_chain(&s_client.conf, &s_client.cacert, NULL); mbedtls_ssl_conf_rng(&s_client.conf, mbedtls_hmac_drbg_random, &s_client.hmac_drbg); ret = mbedtls_ssl_setup(&s_client.ssl, &s_client.conf); if (ret != 0) { MEMFAULT_LOG_ERROR("mbedtls_ssl_setup returned -0x%x", (unsigned int)-ret); } ret = mbedtls_ssl_set_hostname(&s_client.ssl, MEMFAULT_HTTP_CHUNKS_API_HOST); if (ret) { MEMFAULT_LOG_ERROR("mbedtls_ssl_set_hostname returned -0x%x", (unsigned int)-ret); goto cleanup; } mbedtls_ssl_set_bio(&s_client.ssl, &s_client.net_ctx, mbedtls_net_send, mbedtls_net_recv, NULL); do { ret = mbedtls_ssl_handshake(&s_client.ssl); if (ret == 0) { break; } if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) { continue; } else { // all other errors are fatal MEMFAULT_LOG_ERROR("mbedtls_ssl_handshake returned -0x%x\n", (unsigned int)-ret); goto cleanup; } } while (1); s_client.active = true; return &s_client; cleanup: prv_teardown_mbedtls(&s_client); return NULL; } int memfault_platform_http_client_destroy(sMfltHttpClient *client) { if (!client || !client->active) { return -1; } // teardown the connection prv_teardown_mbedtls(client); s_client.active = false; return 0; } int memfault_platform_http_client_wait_until_requests_completed( MEMFAULT_UNUSED sMfltHttpClient *client, MEMFAULT_UNUSED uint32_t timeout_ms) { // No-op because memfault_platform_http_client_post_data() is synchronous return 0; } static bool prv_try_send(sMfltHttpClient *client, const uint8_t *buf, size_t buf_len) { size_t idx = 0; while (idx != buf_len) { int rv = mbedtls_ssl_write(&client->ssl, (const unsigned char *)buf, buf_len); if (rv >= 0) { idx += (size_t)rv; continue; } MEMFAULT_LOG_ERROR("Data Send Error: bytes_sent=%d, ret=0x%x", (int)idx, (unsigned int)rv); return false; } return true; } static bool prv_send_data(const void *data, size_t data_len, void *ctx) { sMfltHttpClient *client = (sMfltHttpClient *)ctx; return prv_try_send(client, data, data_len); } static bool prv_read_socket_data(sMfltHttpClient *client, void *buf, size_t *buf_len) { int rv = mbedtls_ssl_read(&client->ssl, buf, *buf_len); if ((rv == MBEDTLS_ERR_SSL_WANT_READ) || (rv == MBEDTLS_ERR_SSL_WANT_WRITE)) { *buf_len = 0; return true; } if ((rv < 0) || (rv == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)) { return false; } *buf_len = (size_t)rv; return true; } static int prv_wait_for_http_response(sMfltHttpClient *client) { sMemfaultHttpResponseContext ctx = { 0 }; while (1) { // We don't expect any response that needs to be parsed so // just use an arbitrarily small receive buffer char buf[32]; size_t bytes_read = sizeof(buf); if (!prv_read_socket_data(client, buf, &bytes_read)) { return -1; } bool done = memfault_http_parse_response(&ctx, buf, bytes_read); if (done) { MEMFAULT_LOG_DEBUG("Response Complete: Parse Status %d HTTP Status %d!", (int)ctx.parse_error, ctx.http_status_code); MEMFAULT_LOG_DEBUG("Body: %s", ctx.http_body); return ctx.http_status_code; } } } int memfault_platform_http_client_post_data(sMfltHttpClient *client, MemfaultHttpClientResponseCallback callback, void *ctx) { if (!client->active) { return -1; } const sPacketizerConfig cfg = { // let a single msg span many "memfault_packetizer_get_next" calls .enable_multi_packet_chunk = true, }; // will be populated with size of entire message queued for sending sPacketizerMetadata metadata; const bool data_available = memfault_packetizer_begin(&cfg, &metadata); if (!data_available) { MEMFAULT_LOG_DEBUG("No more data to send"); return kMfltPostDataStatus_NoDataFound; } memfault_http_start_chunk_post(prv_send_data, client, metadata.single_chunk_message_length); // Drain all the data that is available to be sent while (1) { // Arbitrarily sized send buffer. uint8_t buf[128]; size_t buf_len = sizeof(buf); eMemfaultPacketizerStatus status = memfault_packetizer_get_next(buf, &buf_len); if (status == kMemfaultPacketizerStatus_NoMoreData) { break; } if (!prv_try_send(client, buf, buf_len)) { // unexpected failure, abort in-flight transaction memfault_packetizer_abort(); return -1; } if (status == kMemfaultPacketizerStatus_EndOfChunk) { break; } } // we've sent a chunk, drain status sMfltHttpResponse response = { .status_code = prv_wait_for_http_response(client), }; if (callback) { callback(&response, ctx); } return response.status_code != 202 ? response.status_code : 0; } int memfault_platform_http_response_get_status(const sMfltHttpResponse *response, uint32_t *status_out) { MEMFAULT_SDK_ASSERT(response != NULL); *status_out = (uint32_t)response->status_code; return 0; } ================================================ FILE: ports/mynewt/README.md ================================================ ## Overview To use the memfault-firmware-sdk with mynewt add following lines to **project.yml**. ```yaml repository.memfault-firmware-sdk: type: github vers: 0.0.0 user: memfault repo: memfault-firmware-sdk ``` Adding this dependency will allow you to pull in Memfault features ```yaml pkg.deps: - "@memfault-firmware-sdk/ports/mynewt" ``` You will also need to enable the following options in the **syscfg.vals:** section of your `syscfg.yml` file: ```yaml syscfg.vals: [...] MEMFAULT_ENABLE: 1 MEMFAULT_COREDUMP_CB: 1 OS_COREDUMP: 1 OS_COREDUMP_CB: 1 ``` Other syscfgs you can configure can be found in [syscfg.yml](syscfg.yml) Finally, on boot up initialize the Memfault subsystem from your `main` routine: ```c #include "memfault/components.h" int main(int argc, char **argv) { // ... memfault_platform_boot(); // ... } ``` ## Using the GNU Build Id for Indexing We recommend enabling the generation of a unique identifier for your project. See https://mflt.io/symbol-file-build-ids for more details. To enable, add the following compilation flags to your `pkg.yml`: ``` pkg.lflags: - -Wl,--build-id pkg.cflags: - -DMEMFAULT_USE_GNU_BUILD_ID=1 ``` And add the following **after** the `.text` in your targets linker script: ``` .note.gnu.build-id : { __start_gnu_build_id_start = .; KEEP(*(.note.gnu.build-id)) } > FLASH ``` ## Enabling Memfault Demo Shell Commands The Memfault demo shell commands can be included in the mynewt shell (useful for testing various Memfault features). To enable: 1. set `MEMFAULT_CLI: 1` in the target's `syscfg.yml`. 2. enable the shell commands by calling `mflt_shell_init()` at the appropriate point in the application initialization (eg after `sysinit()`). ================================================ FILE: ports/mynewt/include/memfault_metrics_heartbeat_mynewt_config.def ================================================ //! @file # if __has_include("memfault_metrics_heartbeat_config.def") # include "memfault_metrics_heartbeat_config.def" # endif ================================================ FILE: ports/mynewt/include/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 //! Override the default names for configuration files so application developer //! can easily customize without having to modify the port #define MEMFAULT_TRACE_REASON_USER_DEFS_FILE "memfault_trace_reason_mynewt_config.def" #define MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE "memfault_metrics_heartbeat_mynewt_config.def" #ifdef __cplusplus } #endif ================================================ FILE: ports/mynewt/include/memfault_platform_log_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif #include "console/console.h" #define _MEMFAULT_LOG_IMPL(fmt, ...) console_printf(fmt "\n", ##__VA_ARGS__); #define MEMFAULT_LOG_DEBUG(fmt, ...) _MEMFAULT_LOG_IMPL(" " fmt, ##__VA_ARGS__) #define MEMFAULT_LOG_INFO(fmt, ...) _MEMFAULT_LOG_IMPL(" " fmt, ##__VA_ARGS__) #define MEMFAULT_LOG_WARN(fmt, ...) _MEMFAULT_LOG_IMPL(" " fmt, ##__VA_ARGS__) #define MEMFAULT_LOG_ERROR(fmt, ...) _MEMFAULT_LOG_IMPL(" " fmt, ##__VA_ARGS__) #define MEMFAULT_LOG_RAW(fmt, ...) console_printf(fmt "\n", ##__VA_ARGS__) #ifdef __cplusplus } #endif ================================================ FILE: ports/mynewt/include/memfault_shell.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif int mflt_shell_init(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/mynewt/include/memfault_trace_reason_mynewt_config.def ================================================ //! @file # if __has_include("memfault_trace_reason_user_config.def") # include "memfault_trace_reason_user_config.def" # endif ================================================ FILE: ports/mynewt/pkg.yml ================================================ pkg.name: ports/mynewt pkg.description: Memfault integration package (https://memfault.com/) pkg.author: "Memfault Inc " pkg.homepage: "https://github.com/memfault/memfault-firmware-sdk/" pkg.keywords: pkg.type: sdk pkg.deps: - "@apache-mynewt-core/kernel/os" - "@apache-mynewt-core/sys/shell" - "@apache-mynewt-core/sys/flash_map" - "@apache-mynewt-core/hw/hal" - "@apache-mynewt-core/mgmt/imgmgr" - "@mcuboot/boot/bootutil" pkg.ign_dirs: pkg.include_dirs: - "../../components/include" - "../../components/log/include" - "../include" pkg.src_dirs: - "src" - "../../components/" ================================================ FILE: ports/mynewt/src/memfault_platform_flash_backed_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Implements platform dependencies for storing Memfault coredumps in mynewt coredump storage. #include #include #include #include "flash_map/flash_map.h" #include "hal/hal_bsp.h" #include "img_mgmt/img_mgmt.h" #include "memfault/components.h" #include "syscfg/syscfg.h" #include "sysflash/sysflash.h" #if MYNEWT_VAL(MEMFAULT_ENABLE) const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { static sMfltCoredumpRegion s_coredump_regions[MYNEWT_VAL(MEMFAULT_COREDUMP_MAX_AREA_COUNT)]; #if MYNEWT_VAL(MEMFAULT_COREDUMP_CAPTURE_MINIMAL) const size_t active_stack_size_to_collect = MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT; s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( crash_info->stack_address, memfault_platform_sanitize_address_range( crash_info->stack_address, active_stack_size_to_collect)); *num_regions = 1; #else int area_cnt = 0; const struct hal_bsp_mem_dump *mem = hal_bsp_core_dump(&area_cnt); const size_t areas_to_collect = MEMFAULT_MIN(MEMFAULT_ARRAY_SIZE(s_coredump_regions), area_cnt); for (size_t i = 0; i < areas_to_collect; i++) { s_coredump_regions[i] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(mem[i].hbmd_start, mem[i].hbmd_size); } *num_regions = areas_to_collect; #endif return s_coredump_regions; } static int prv_flash_open(const struct flash_area **fa) { if (flash_area_open(MYNEWT_VAL(COREDUMP_FLASH_AREA), fa)) { return -OS_ERROR; } // Don't overwrite an image that has any flags set (pending, active, or confirmed). const int slot = flash_area_id_to_image_slot(MYNEWT_VAL(COREDUMP_FLASH_AREA)); if (slot != -1 && img_mgmt_slot_in_use(slot)) { return -OS_ERROR; } return OS_OK; } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { const struct flash_area *fa; if (prv_flash_open(&fa)) { assert(0); } *info = (sMfltCoredumpStorageInfo){ .size = fa->fa_size, .sector_size = 0, // Not needed for port }; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { const struct flash_area *fa; if (prv_flash_open(&fa)) { return false; } if ((offset + read_len) > fa->fa_size) { return false; } if (flash_area_read(fa, offset, data, read_len)) { return false; } return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { const struct flash_area *fa; if (prv_flash_open(&fa)) { return false; } if ((offset + erase_size) > fa->fa_size) { return false; } if (flash_area_erase(fa, offset, erase_size)) { return false; } return true; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { const struct flash_area *fa; if (prv_flash_open(&fa)) { return false; } if ((offset + data_len) > fa->fa_size) { return false; } if (flash_area_write(fa, offset, data, data_len)) { return false; } return true; } void memfault_platform_coredump_storage_clear(void) { const struct flash_area *fa; if (prv_flash_open(&fa)) { assert(0); } // Erasing whole area takes too much time and causes ble connection to time out. Instead only // clear the first byte to mark the segment as invalid. const uint8_t clear_byte = 0x0; memfault_platform_coredump_storage_write(0, &clear_byte, sizeof(clear_byte)); } #endif /* MYNEWT_VAL(MEMFAULT_ENABLE) */ ================================================ FILE: ports/mynewt/src/memfault_platform_port.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port of Memfault dependency functions to mynewt targets #include #include //! Keep os.h above bsp.h; some bsp definitions require the MYNEWT_VAL definition #include "bsp/bsp.h" #include "hal/hal_bsp.h" #include "hal/hal_system.h" #include "memfault/components.h" #include "memfault/ports/reboot_reason.h" #include "os/os.h" #include "sysinit/sysinit.h" #if MYNEWT_VAL(MEMFAULT_ENABLE) void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { *info = (sMemfaultDeviceInfo){ // Note: serial number will be recovered from route used when posting // data to the Memfault cloud and does not need to be populated here .device_serial = NULL, .software_type = MYNEWT_VAL(MEMFAULT_DEVICE_INFO_SOFTWARE_TYPE), .software_version = MYNEWT_VAL(MEMFAULT_DEVICE_INFO_SOFTWARE_VERS), .hardware_version = MYNEWT_VAL(MEMFAULT_DEVICE_INFO_HARDWARE_VERS), }; } //! Last function called after a coredump is saved. Should perform //! any final cleanup and then reset the device void memfault_platform_reboot(void) { os_system_reset(); MEMFAULT_UNREACHABLE; } // This will cause events logged by the SDK to be timestamped on the device rather than when they // arrive on the server bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { #if MYNEWT_VAL(MEMFAULT_USE_DEVICE_TIMESTAMP) != 0 struct os_timeval tv; const int rc = os_gettimeofday(&tv, NULL); if (rc != 0) { return false; } *time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = tv.tv_sec }, }; return true; #else return false; #endif } size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { int area_cnt; const struct hal_bsp_mem_dump *mem_regions = hal_bsp_core_dump(&area_cnt); for (size_t i = 0; i < area_cnt; i++) { const uint32_t lower_addr = (uint32_t)mem_regions[i].hbmd_start; const uint32_t upper_addr = lower_addr + mem_regions[i].hbmd_size; if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); } } return 0; } #if MYNEWT_VAL(MEMFAULT_METRICS_COMPONENT_ENABLE) != 0 static struct os_callout s_heartbeat_timer_cb; static uint32_t s_heartbeat_period_ticks; static void prv_heartbeat_timer_cb(struct os_event *ev) { MemfaultPlatformTimerCallback *cb = (MemfaultPlatformTimerCallback *)ev->ev_arg; cb(); os_callout_reset(&s_heartbeat_timer_cb, s_heartbeat_period_ticks); } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback callback) { os_callout_init(&s_heartbeat_timer_cb, os_eventq_dflt_get(), prv_heartbeat_timer_cb, callback); s_heartbeat_period_ticks = period_sec * OS_TICKS_PER_SEC; const int rc = os_callout_reset(&s_heartbeat_timer_cb, s_heartbeat_period_ticks); return (rc == 0); } #endif uint64_t memfault_platform_get_time_since_boot_ms(void) { return os_get_uptime_usec() / 1000; } int memfault_platform_boot(void) { memfault_build_info_dump(); memfault_device_info_dump(); memfault_platform_reboot_tracking_boot(); static uint8_t s_event_storage[MYNEWT_VAL(MEMFAULT_EVENT_STORAGE_SIZE)]; const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(evt_storage); memfault_reboot_tracking_collect_reset_info(evt_storage); #if MYNEWT_VAL(MEMFAULT_METRICS_COMPONENT_ENABLE) != 0 sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); #endif MEMFAULT_LOG_INFO("Memfault Initialized!"); return 0; } static eMemfaultRebootReason prv_get_reboot_reason(enum hal_reset_reason reset_cause) { switch (reset_cause) { case HAL_RESET_POR: return kMfltRebootReason_PowerOnReset; case HAL_RESET_PIN: return kMfltRebootReason_PinReset; case HAL_RESET_WATCHDOG: return kMfltRebootReason_HardwareWatchdog; case HAL_RESET_SOFT: return kMfltRebootReason_SoftwareReset; case HAL_RESET_BROWNOUT: return kMfltRebootReason_BrownOutReset; case HAL_RESET_REQUESTED: return kMfltRebootReason_UserReset; case HAL_RESET_SYS_OFF_INT: return kMfltRebootReason_DeepSleep; case HAL_RESET_DFU: return kMfltRebootReason_FirmwareUpdate; default: return kMfltRebootReason_Unknown; } } void memfault_reboot_reason_get(sResetBootupInfo *info) { const enum hal_reset_reason reset_cause = hal_reset_cause(); *info = (sResetBootupInfo){ // The mynewt hal does not expose the raw mcu register value so // leave unpopulated for now .reset_reason_reg = 0, .reset_reason = prv_get_reboot_reason(reset_cause) }; } static bssnz_t uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; void memfault_platform_reboot_tracking_boot(void) { sResetBootupInfo reset_info = { 0 }; memfault_reboot_reason_get(&reset_info); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); } #if MYNEWT_VAL(MEMFAULT_COREDUMP_CB) static eMemfaultRebootReason s_reboot_reason = kMfltRebootReason_UnknownError; #if MYNEWT_VAL(MEMFAULT_ASSERT_CB) void os_assert_cb(void) { s_reboot_reason = kMfltRebootReason_Assert; } #endif static eMemfaultRebootReason prv_resolve_reason_from_active_isr(void) { // ARM Cortex-M have a standard set of exception numbers used for faults. // // The bottom byte of the XPSR register tells us which interrupt we are running from. // See https://mflt.io/cortex-m-exc-numbering uint32_t vect_active = __get_xPSR() & 0xff; switch (vect_active) { case 2: return kMfltRebootReason_Nmi; case 3: return kMfltRebootReason_HardFault; case 4: return kMfltRebootReason_MemFault; case 5: return kMfltRebootReason_BusFault; case 6: return kMfltRebootReason_UsageFault; default: return kMfltRebootReason_HardFault; } } void os_coredump_cb(void *tf) { if (s_reboot_reason == kMfltRebootReason_UnknownError) { s_reboot_reason = prv_resolve_reason_from_active_isr(); } memfault_fault_handler(tf, s_reboot_reason); } #endif /* MYNEWT_VAL(MEMFAULT_COREDUMP_CB) */ #endif /* MYNEWT_VAL(MEMFAULT_ENABLE) */ ================================================ FILE: ports/mynewt/src/memfault_shell.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Integrate the Memfault demo shell with the mynewt console #include "memfault_shell.h" #include #include #include "console/console.h" #include "hal/hal_gpio.h" #include "memfault/components.h" #include "os/mynewt.h" #if MYNEWT_VAL(MEMFAULT_CLI) #include "parse/parse.h" #include "shell/shell.h" #if MYNEWT_VAL(BSP_NRF52) || MYNEWT_VAL(BSP_NRF52840) #include #elif MYNEWT_VAL(BSP_NRF51) #include #elif MYNEWT_VAL(BSP_NRF5340) #include #elif MYNEWT_VAL(BSP_NRF5340_NET) #include #elif MYNEWT_VAL(BSP_APOLLO2) #include #else #error "Unsupported arch" #endif static int mflt_shell_cmd(int argc, char **argv); static struct shell_cmd mflt_shell_cmd_struct = { .sc_cmd = "mflt", .sc_cmd_func = mflt_shell_cmd, }; static int mflt_shell_err_unknown_arg(char *cmd_name) { console_printf("Error: unknown argument \"%s\"\n", cmd_name); return SYS_EINVAL; } static int mflt_shell_help(void) { console_printf("%s cmd\n", mflt_shell_cmd_struct.sc_cmd); console_printf("cmd:\n"); console_printf("\tlogging - Tests memfault logging api\n"); console_printf( "\tcoredump_storage - Runs a sanity test to confirm coredump port is working as expected\n"); console_printf("\theartbeat - Triggers an immediate heartbeat capture (instead of waiting for " "timer to expire\n"); console_printf("\ttrace - Test trace event\n"); console_printf("\treboot - Trigger a user initiated reboot and confirm reason is persisted\n"); console_printf("\tassert - Triggers memfault assert where a coredump should be captured\n"); console_printf("\tfault - Triggers memfault fault where a coredump should be captured \n"); console_printf("\thang - Triggers memfault hang where a coredump should be captured \n"); console_printf("\texport - Dump Memfault data collected to console\n"); return SYS_EOK; } // // Test Platform Ports // static int test_logging(int argc, char *argv[]) { MEMFAULT_LOG_DEBUG("Debug log!"); MEMFAULT_LOG_INFO("Info log!"); MEMFAULT_LOG_WARN("Warning log!"); MEMFAULT_LOG_ERROR("Error log!"); return 0; } // Runs a sanity test to confirm coredump port is working as expected static int test_coredump_storage(int argc, char *argv[]) { // Note: Coredump saving runs from an ISR prior to reboot so should // be safe to call with interrupts disabled. int sr; __HAL_DISABLE_INTERRUPTS(sr); memfault_coredump_storage_debug_test_begin(); __HAL_ENABLE_INTERRUPTS(sr); memfault_coredump_storage_debug_test_finish(); return 0; } // // Test core SDK functionality // // Triggers an immediate heartbeat capture (instead of waiting for timer // to expire) static int test_heartbeat(int argc, char *argv[]) { memfault_metrics_heartbeat_debug_trigger(); return 0; } static int test_trace(int argc, char *argv[]) { MEMFAULT_TRACE_EVENT_WITH_LOG(Unknown, "A test error trace!"); return 0; } //! Trigger a user initiated reboot and confirm reason is persisted static int test_reboot(int argc, char *argv[]) { MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_UserReset); memfault_platform_reboot(); } // // Test different crash types where a coredump should be captured // static int test_assert(int argc, char *argv[]) { MEMFAULT_ASSERT(0); return -1; // should never get here } static int test_fault(int argc, char *argv[]) { void (*bad_func)(void) = (void *)0xEEEEDEAD; bad_func(); return -1; // should never get here } static int test_hang(int argc, char *argv[]) { while (1) { } return -1; // should never get here } // Dump Memfault data collected to console static int test_export(int argc, char *argv[]) { memfault_data_export_dump_chunks(); return 0; } static int mflt_shell_cmd(int argc, char **argv) { if (argc < 2) { return mflt_shell_help(); } struct subcommand { const char *name; int (*func)(int argc, char **argv); }; static const struct subcommand subcommands[] = { { "logging", test_logging }, { "coredump_storage", test_coredump_storage }, { "heartbeat", test_heartbeat }, { "trace", test_trace }, { "reboot", test_reboot }, { "assert", test_assert }, { "fault", test_fault }, { "hang", test_hang }, { "export", test_export }, }; for (int i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); i++) { if (strcmp(argv[1], subcommands[i].name) == 0) { return subcommands[i].func(argc - 1, argv + 1); } } return mflt_shell_err_unknown_arg(argv[1]); } int mflt_shell_init(void) { int rc = shell_cmd_register(&mflt_shell_cmd_struct); SYSINIT_PANIC_ASSERT(rc == 0); return rc; } #endif ================================================ FILE: ports/mynewt/syscfg.yml ================================================ syscfg.defs: MEMFAULT_ENABLE: description: Enables Memfault SDK features value: 0 MEMFAULT_CLI: description: Enables Memfault CLI test commands value: 0 restriction: - 'MEMFAULT_ENABLE if 1' MEMFAULT_COREDUMP_CB: description: Enable custom memfault coredump handling cb value: 0 restriction: - 'OS_COREDUMP_CB if 1' MEMFAULT_ASSERT_CB: description: > Enable custom assert handling callback to correctly capture that a reboot was triggered via an assert. value: 0 restriction: - 'MEMFAULT_COREDUMP_CB if 1' - 'OS_ASSERT_CB if 1' MEMFAULT_DEVICE_INFO_SOFTWARE_TYPE: description: A name to represent the firmware running on the MCU. value: '"app-fw"' MEMFAULT_DEVICE_INFO_SOFTWARE_VERS: description: The version of the "software_type" currently running. value: '"1.0.0"' MEMFAULT_DEVICE_INFO_HARDWARE_VERS: description: The revision of hardware for the device. This value must remain the same for a unique device. value: '"mp"' MEMFAULT_EVENT_STORAGE_SIZE: description: Storage size for Memfault events value: 1024 MEMFAULT_COREDUMP_CAPTURE_MINIMAL: description: > Enables a light coredump capture which includes active stack and fault register state. The hal_bsp_core_dump() API is used to discover valid memory regions to collect data from. value: 1 MEMFAULT_METRICS_COMPONENT_ENABLE: description: Enable metrics subsystem, https://mflt.io/embedded-metrics value: 0 MEMFAULT_COREDUMP_MAX_AREA_COUNT: description: Maximum number of RAM areas to include in a coredump capture value: 3 MEMFAULT_USE_DEVICE_TIMESTAMP: description: > Use a timestamp from device when recording events. When disabled, events will be timestamped when it arrives on the server side. value: 0 ================================================ FILE: ports/nrf5_sdk/memfault_platform_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! A port of the memfault timer dependency functions which utilizes the nRF5 SDK app_timer module //! //! Options: //! MEMFAULT_PLATFORM_BOOT_TIMER_CUSTOM (default = 0) //! When set to 1, user must define memfault_platform_get_time_since_boot_ms. #include "app_timer.h" #include "memfault/core/debug_log.h" #include "memfault/core/platform/core.h" #include "memfault/metrics/platform/timer.h" #include "nrf_log.h" #if !MEMFAULT_PLATFORM_BOOT_TIMER_CUSTOM #define MEMFAULT_PLATFORM_BOOT_TIMER_CUSTOM 0 #endif APP_TIMER_DEF(m_mflt_metric_log_timer); static MemfaultPlatformTimerCallback *s_registered_cb = NULL; static uint32_t s_minutes_elapsed; static uint32_t s_interval_minutes; #if !MEMFAULT_PLATFORM_BOOT_TIMER_CUSTOM typedef struct { uint32_t last_tick_count; // Worst case on the nrf52 we will have 32768 ticks per second // // 2^64 / (32768 * 86400 * 365 * 1000) = 17851 years ... so // not practical tick count overflow concern uint64_t time_since_boot_ticks; } sMfltPlatformUptimeCtx; static sMfltPlatformUptimeCtx s_uptime_ctx; uint64_t memfault_platform_get_time_since_boot_ms(void) { uint32_t ticks_current = app_timer_cnt_get(); uint32_t ticks_diff = app_timer_cnt_diff_compute(ticks_current, s_uptime_ctx.last_tick_count); const uint64_t curr_tick_count = s_uptime_ctx.time_since_boot_ticks + ticks_diff; const uint32_t ticks_per_sec = APP_TIMER_CLOCK_FREQ / (APP_TIMER_CONFIG_RTC_FREQUENCY + 1); const uint64_t curr_tick_time_ms = (1000 * curr_tick_count) / ticks_per_sec; return curr_tick_time_ms; } static void prv_update_boot_time(void) { const uint32_t ticks_current = app_timer_cnt_get(); const uint32_t ticks_diff = app_timer_cnt_diff_compute(ticks_current, s_uptime_ctx.last_tick_count); s_uptime_ctx.time_since_boot_ticks += ticks_diff; s_uptime_ctx.last_tick_count = ticks_current; } #else static void prv_update_boot_time(void) { } #endif /* MEMFAULT_PLATFORM_BOOT_TIMER_CUSTOM */ static void prv_mflt_metric_timer(void *p_context) { prv_update_boot_time(); // The customer can configure the PRESCALAR off them but by default it's a 24 bit counter running // at 32kHz so it overflows every 511 seconds. When a period is passed that is larger than this, // use a 1 minute base timer and fire on the nearest requested minute: if (s_interval_minutes != 0) { ++s_minutes_elapsed; if ((s_minutes_elapsed % s_interval_minutes) != 0) { return; } } if (s_registered_cb != NULL) { s_registered_cb(); } } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback callback) { if (s_registered_cb != NULL) { MEMFAULT_LOG_ERROR("%s should only be called once", __func__); return false; } uint32_t err_code = app_timer_create(&m_mflt_metric_log_timer, APP_TIMER_MODE_REPEATED, prv_mflt_metric_timer); APP_ERROR_CHECK(err_code); s_registered_cb = callback; uint32_t period_ticks = APP_TIMER_TICKS(period_sec * 1000); if (period_ticks > APP_TIMER_MAX_CNT_VAL) { // Assume period_s is a multiple of 60, otherwise round to the nearest minute: s_interval_minutes = ROUNDED_DIV(period_sec, 60); s_minutes_elapsed = 0; period_ticks = APP_TIMER_TICKS(60 * 1000); } else { s_interval_minutes = 0; } err_code = app_timer_start(m_mflt_metric_log_timer, period_ticks, NULL); APP_ERROR_CHECK(err_code); return true; } ================================================ FILE: ports/nrf5_sdk/nrf5_coredump_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Implements APIs for collecting RAM regions on nRF5 as part of a coredump //! //! Options (To override, update memfault_platform_config.h) //! MEMFAULT_PLATFORM_COREDUMP_CUSTOM_REGIONS (default = 0) //! When set to 1, user must define memfault_platform_coredump_get_regions() and //! declare their own regions to collect. //! //! MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY (default = 1) //! This mode will collect just the stack that was active at the time of //! crash. To capture more data, set this to 0 in memfault_platform_config.h. //! The regions to be captured are specified by adding the following to the //! project's .ld file: //! //! __MemfaultCoredumpRamStart = ORIGIN(RAM); //! __MfltCoredumpRamEnd = ORIGIN(RAM) + LENGTH(RAM); //! #include "memfault/core/math.h" #include "memfault/panics/platform/coredump.h" #include "sdk_common.h" #ifndef MEMFAULT_PLATFORM_COREDUMP_CUSTOM_REGIONS #define MEMFAULT_PLATFORM_COREDUMP_CUSTOM_REGIONS 0 #endif //! Truncates the region if it's outside the bounds of RAM size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { // All NRF MCUs RAM starts at this address. No define is exposed in the SDK for it however const uint32_t ram_start = 0x20000000; #ifdef NRF51 const uint32_t ram_size = (NRF_FICR->SIZERAMBLOCKS) * NRF_FICR->NUMRAMBLOCK; #else const uint32_t ram_size = NRF_FICR->INFO.RAM * 1024; #endif const uint32_t ram_end = ram_start + ram_size; if ((uintptr_t)start_addr >= ram_start && (uintptr_t)start_addr < ram_end) { return MEMFAULT_MIN(desired_size, ram_end - (uintptr_t)start_addr); } return 0; } #if !MEMFAULT_PLATFORM_COREDUMP_CUSTOM_REGIONS #ifndef MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY #define MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY 1 #endif #ifndef MEMFAULT_COREDUMP_RAM_REGION_START_ADDR extern uint32_t __MemfaultCoredumpRamStart[]; #define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) #endif #ifndef MEMFAULT_COREDUMP_RAM_REGION_END_ADDR extern uint32_t __MfltCoredumpRamEnd[]; #define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) #endif const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { // Let's collect the callstack at the time of crash static sMfltCoredumpRegion s_coredump_regions[1]; #if (MEMFAULT_PLATFORM_COREDUMP_CAPTURE_STACK_ONLY == 1) const void *stack_start_addr = crash_info->stack_address; // Capture only the interrupt stack. Use only if there is not enough storage to capture all of // RAM. s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( stack_start_addr, (uintptr_t)STACK_TOP - (uintptr_t)stack_start_addr); #else // Capture all of RAM. Recommended: it enables broader post-mortem analyses, // but has larger storage requirements. s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( MEMFAULT_COREDUMP_RAM_REGION_START_ADDR, MEMFAULT_COREDUMP_RAM_REGION_END_ADDR - MEMFAULT_COREDUMP_RAM_REGION_START_ADDR); #endif *num_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions); return s_coredump_regions; } #endif // MEMFAULT_PLATFORM_COREDUMP_CUSTOM_REGIONS ================================================ FILE: ports/nrf5_sdk/nrf5_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Implements platform dependency functions required for saving coredumps to //! internal flash when using the NRF5 SDK. //! //! To use, update your linker script (.ld file) to expose information about the //! location to use. For example, using a 256K region of the nRF52840 (1MB //! flash) would look something like this: //! //! MEMORY //! { //! FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0x7B000 //! MEMFAULT_CORES (rw) : ORIGIN = 0xA1000, LENGTH = 0x40000 //! /* Note: most production apps would have a bootloader somewhere in this range! */ //! RSVD (rwx) : ORIGIN = 0xE1000, LENGTH = 0x1F000 //! RAM (rwx) : ORIGIN = 0x200057b8, LENGTH = 0x3a848 //! } //! //! __MemfaultCoreStorageStart = ORIGIN(MEMFAULT_CORES); //! __MemfaultCoreStorageEnd = ORIGIN(MEMFAULT_CORES) + LENGTH(MEMFAULT_CORES); //! //! See also nrf5_coredump_regions.c for information on which areas are captured //! during a coredump. #include #include "app_util.h" #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "nrf_log.h" #include "nrf_nvmc.h" #include "nrf_sdh.h" #include "nrf_sdh_soc.h" #include "nrf_soc.h" #ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR extern uint32_t __MemfaultCoreStorageStart[]; #define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_END_ADDR extern uint32_t __MemfaultCoreStorageEnd[]; #define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) #endif typedef enum { kMemfaultCoredumpClearState_Idle = 0, kMemfaultCoredumpClearState_ClearRequested, kMemfaultCoredumpClearState_ClearInProgress, } eMemfaultCoredumpClearState; static eMemfaultCoredumpClearState s_coredump_clear_state; static void prv_handle_flash_op_complete(bool success) { if (s_coredump_clear_state == kMemfaultCoredumpClearState_Idle) { return; // not a flash op we care about } if (s_coredump_clear_state == kMemfaultCoredumpClearState_ClearInProgress && success) { // The erase is complete! s_coredump_clear_state = kMemfaultCoredumpClearState_Idle; return; } if (!success) { NRF_LOG_WARNING("Coredump clear failed, retrying ..."); } // we either haven't kicked off a clear operation yet or our previous attempt // was not successful and we need to retry memfault_platform_coredump_storage_clear(); } static void prv_coredump_handle_soc_update(uint32_t sys_evt, void *p_context) { switch (sys_evt) { case NRF_EVT_FLASH_OPERATION_SUCCESS: case NRF_EVT_FLASH_OPERATION_ERROR: { const bool success = (sys_evt == NRF_EVT_FLASH_OPERATION_SUCCESS); prv_handle_flash_op_complete(success); break; } default: break; } } NRF_SDH_SOC_OBSERVER(m_soc_evt_observer, 0, prv_coredump_handle_soc_update, NULL); void memfault_platform_coredump_storage_clear(void) { // static because sd_flash_write may take place asynchronously const static uint32_t invalidate = 0x0; // If the soft device is enabled we need to route our flash operation through it (Code saving // backtraces hits the flash directly since the soft device is out of the picture at the time we // crash!) if (nrf_sdh_is_enabled()) { // NB: When the SoftDevice is active, flash operations get scheduled and an asynchronous // event is emitted when the operation completes. Therefore, we need to add some tracking // to make sure the coredump clear request completes. // // More details can be found in Section 8 "Flash memory API" of the SoftDevice Spec // https://infocenter.nordicsemi.com/pdf/S140_SDS_v2.1.pdf s_coredump_clear_state = kMemfaultCoredumpClearState_ClearRequested; const uint32_t rv = sd_flash_write((void *)MEMFAULT_COREDUMP_STORAGE_START_ADDR, &invalidate, 1); if (rv == NRF_SUCCESS) { s_coredump_clear_state = kMemfaultCoredumpClearState_ClearInProgress; } else if (rv == NRF_ERROR_BUSY) { // An earlier flash command is still in progress. We will retry when // prv_handle_flash_op_complete() is invoked when the in-flight flash op completes. NRF_LOG_INFO("Coredump clear deferred, flash busy"); } else { // NB: Any error except for NRF_ERROR_BUSY is indicative of a configuration error of some // sort. NRF_LOG_ERROR("Unexpected error clearing coredump! %d", rv); } } else { nrf_nvmc_write_word(MEMFAULT_COREDUMP_STORAGE_START_ADDR, invalidate); } } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { const size_t size = MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR; *info = (sMfltCoredumpStorageInfo){ .size = size, .sector_size = NRF_FICR->CODEPAGESIZE, }; } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } //! Don't return any new data while a clear operation is in progress //! //! This prevents reading the same coredump again while an erase is in flight. //! //! We implement this in memfault_coredump_read() so the logic is only run //! while the system is running. bool memfault_coredump_read(uint32_t offset, void *data, size_t read_len) { if (s_coredump_clear_state != kMemfaultCoredumpClearState_Idle) { // Return false here to indicate that there is no new data to read return false; } return memfault_platform_coredump_storage_read(offset, data, read_len); } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if (!prv_op_within_flash_bounds(offset, data_len)) { return false; } uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; const uint32_t word_size = 4; if (((address % word_size) == 0) && (((uint32_t)data % word_size) == 0)) { const size_t num_words = data_len / word_size; nrf_nvmc_write_words(address, data, num_words); data_len = data_len % word_size; const size_t bytes_written = num_words * word_size; address += num_words * word_size; const uint8_t *data_rem = data; data_rem += bytes_written; nrf_nvmc_write_bytes(address, data_rem, data_len); } else { nrf_nvmc_write_bytes(address, data, data_len); } return true; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } const uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; memcpy(data, (void *)address, read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } const size_t sector_size = NRF_FICR->CODEPAGESIZE; if ((offset % sector_size) != 0) { return false; } for (size_t sector = offset; sector < erase_size; sector += sector_size) { const uint32_t address = MEMFAULT_COREDUMP_STORAGE_START_ADDR + sector; nrf_nvmc_page_erase(address); } return true; } ================================================ FILE: ports/nrf5_sdk/resetreas_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset Reason" (RESETREAS) Register. //! //! More details can be found in the "Reset Reason" (RESETREAS)" //! section of the nRF528xx product specification document for you //! specific chip. #include #include #include #include #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #include "memfault/ports/reboot_reason.h" #include "nrf_power.h" #include "nrf_sdh.h" #include "nrf_soc.h" #include "nrf_stack_guard.h" #ifndef MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_ENABLE_REBOOT_DIAG_DUMP 1 #endif #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif // Private helper functions deal with the details of the soft device // modality. static uint32_t prv_reset_reason_get(void) { if (nrf_sdh_is_enabled()) { uint32_t reset_reas = 0; sd_power_reset_reason_get(&reset_reas); return reset_reas; } return NRF_POWER->RESETREAS; } #if MEMFAULT_REBOOT_REASON_CLEAR static void prv_reset_reason_clear(uint32_t reset_reas_clear_mask) { if (nrf_sdh_is_enabled()) { sd_power_reset_reason_clr(reset_reas_clear_mask); } else { NRF_POWER->RESETREAS |= reset_reas_clear_mask; } } #endif // Called by the user application. void memfault_platform_reboot_tracking_boot(void) { // NOTE: Since the NRF52 SDK .ld files are based on the CMSIS ARM Cortex-M linker scripts, we use // the bottom of the main stack to hold the 64 byte reboot reason. // // For a detailed explanation about reboot reason storage options check out the guide at: // https://mflt.io/reboot-reason-storage uint32_t reboot_tracking_start_addr = (uint32_t)STACK_BASE; #if NRF_STACK_GUARD_ENABLED reboot_tracking_start_addr += STACK_GUARD_SIZE; #endif sResetBootupInfo reset_reason = { 0 }; memfault_reboot_reason_get(&reset_reason); memfault_reboot_tracking_boot((void *)reboot_tracking_start_addr, &reset_reason); } // Map chip-specific reset reasons to Memfault reboot reasons. void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); // Consume the reset reason register leaving it cleared in HW. // RESETREAS is part of the always on power domain so it's sticky until a full reset occurs // Therefore, we clear the bits which were set so that don't get logged in future reboots as well const uint32_t reset_reason_reg = prv_reset_reason_get(); #if MEMFAULT_REBOOT_REASON_CLEAR prv_reset_reason_clear(reset_reason_reg); #endif MEMFAULT_PRINT_RESET_INFO("Reset Reason, RESETREAS=0x%" PRIx32, reset_reason_reg); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); // Assume "no bits set" implies POR. eMemfaultRebootReason reset_reason = kMfltRebootReason_PowerOnReset; // These appear to be common. Also, the assumption is // that only one bit per actual reset event can be set. if (reset_reason_reg & NRF_POWER_RESETREAS_RESETPIN_MASK) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else if (reset_reason_reg & NRF_POWER_RESETREAS_DOG_MASK) { MEMFAULT_PRINT_RESET_INFO(" Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_reason_reg & NRF_POWER_RESETREAS_SREQ_MASK) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_reason_reg & NRF_POWER_RESETREAS_LOCKUP_MASK) { MEMFAULT_PRINT_RESET_INFO(" Lockup"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_reason_reg & NRF_POWER_RESETREAS_OFF_MASK) { MEMFAULT_PRINT_RESET_INFO(" GPIO Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; } else if (reset_reason_reg & NRF_POWER_RESETREAS_DIF_MASK) { MEMFAULT_PRINT_RESET_INFO(" Debug Interface Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; } // The following are decided based on some #define logic provided by Nordic // whereby they use the _Msk suffixed #defines to create (or not) an entry // in an enum with an NRF_ prefix and a _MASK suffix. // E.g. POWER_X_Msk --> NRF_POWER_X_MASK. #if defined(POWER_RESETREAS_LPCOMP_Msk) else if (reset_reason_reg & NRF_POWER_RESETREAS_LPCOMP_MASK) { MEMFAULT_PRINT_RESET_INFO(" LPCOMP Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; } #endif #if defined(POWER_RESETREAS_NFC_Msk) else if (reset_reason_reg & NRF_POWER_RESETREAS_NFC_MASK) { MEMFAULT_PRINT_RESET_INFO(" NFC Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; } #endif #if defined(POWER_RESETREAS_VBUS_Msk) else if (reset_reason_reg & NRF_POWER_RESETREAS_VBUS_MASK) { MEMFAULT_PRINT_RESET_INFO(" VBUS Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; } #endif *info = (sResetBootupInfo){ .reset_reason_reg = reset_reason_reg, .reset_reason = reset_reason, }; } ================================================ FILE: ports/nrf5_sdk/software_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A Software Watchdog port for nRF5 platform which makes use of the RTC Peripheral. //! //! Note: RTC2 is used since RTC0 is used by the SoftDevice & RTC1 is used by the app_timer module. //! If you are already making use of RTC2 for other purposes you may need to fork this port and //! modify accordingly. //! //! Note: While the nRF5 WDT peripheral does have an interrupt that can be enabled when the //! watchdog expires, the delay until a reboot is only two 32kHz clock cycles and is _not_ //! configurable. This does not provide enough time to save a full coredump so we make use of the //! RTC peripheral instead as our software watchdog. //! //! Note: If you are making use of the nRF5 WDT peripheral, this module will auto configure a //! software watchdog timeout that is 125ms shorter than the hardware watchdog timeout. When the //! software watchdog fires, one final feed of the hardware watchdog will be made and a coredump //! will be captured. A different timeout can be configured by adding the following to your //! memfault_platform_config.h: //! #define MEMFAULT_NRF5_WATCHDOG_SW_AUTOCONFIG 0 //! #define MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS //! //! Recommended setup: //! //! 1. Add the following to your project's sdk_config.h to enable the RTC2 Peripheral //! #define NRFX_RTC_ENABLED 1 //! #define NRFX_RTC2_ENABLED 1 //! #define RTC_ENABLED 1 // only needed if these legacy defines are in your sdk_config.h //! #define RTC2_ENABLED 1 // only needed if these legacy defines are in your sdk_config.h //! 2. Add following C files to build system: //! ${NRF5_SDK_ROOT}/modules/nrfx/drivers/src/nrfx_rtc.c //! ${MEMFAULT_SDK_ROOT}/ports/nrf5_sdk/software_watchdog.c //! 3. In the same place you enable the hardware watchdog (via wdt_enable) //! initialize this module: //! #include "memfault/ports/watchdog.h" //! ... //! memfault_software_watchdog_enable(); //! 4. In the same place you feed the hardware watchdog (via wdt_channel_feed, wdt_feed), //! feed this software watchdog: //! #include "memfault/ports/watchdog.h" //! ... //! memfault_software_watchdog_feed(); //! #include "app_error.h" #include "memfault/components.h" #include "memfault/ports/watchdog.h" #include "nrfx_rtc.h" #if NRFX_WDT_ENABLED #include #endif // // Configuration sanity check -- if legacy defines exist in sdk_config.h they will get remapped to // the expected defines via integration/nrfx/legacy/apply_old_config.h. We use the same logic from // that header to sanity check the configuration. // #if defined(RTC_ENABLED) #if !RTC_ENABLED #error "Set RTC_ENABLED 1 to use the Software Watchdog port" #endif #elif !NRFX_RTC_ENABLED #error "Set NRFX_RTC_ENABLED 1 to use the Software Watchdog port" #endif #if defined(RTC2_ENABLED) #if !RTC2_ENABLED #error "Set RTC2_ENABLED 1 to use the Software Watchdog port" #endif #elif !NRFX_RTC2_ENABLED #error "Set NRFX_RTC2_ENABLED 1 to use the Software Watchdog port" #endif //! Note: We set PRESCALE to 2^12-1 which results in a 125ms resolution per RTC tick #define MEMFAULT_PRESCALE_RESOLUTION_MS 125 #ifndef MEMFAULT_NRF5_WATCHDOG_SW_AUTOCONFIG //! Auto configure software watchdog timeout relative to hardware watchdog #define MEMFAULT_NRF5_WATCHDOG_SW_AUTOCONFIG NRFX_WDT_ENABLED #endif #if MEMFAULT_NRF5_WATCHDOG_SW_AUTOCONFIG #if NRFX_WDT_CONFIG_RELOAD_VALUE < MEMFAULT_PRESCALE_RESOLUTION_MS #error "NRFX_WDT_CONFIG_RELOAD_VALUE must be > MEMFAULT_PRESCALE_RESOLUTION_MS (125ms)" #endif #define MEMFAULT_NRF5_WATCHDOG_SW_TIMEOUT_MS \ (NRFX_WDT_CONFIG_RELOAD_VALUE - MEMFAULT_PRESCALE_RESOLUTION_MS) #else #if (NRFX_WDT_ENABLED && \ (NRFX_WDT_CONFIG_RELOAD_VALUE < (MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS * 1000))) #error \ "Set MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS must be less than hardware watchdog timeout (NRFX_WDT_CONFIG_RELOAD_VALUE)" #endif #define MEMFAULT_NRF5_WATCHDOG_SW_TIMEOUT_MS (MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS * 1000) #endif /* MEMFAULT_NRF5_WATCHDOG_SW_AUTOCONFIG */ #ifndef MEMFAULT_NRF5_WATCHDOG_SW_CHANNEL #define MEMFAULT_NRF5_WATCHDOG_SW_CHANNEL 0 #endif //! The nRF5 MCU has 3 RTC peripheral instances. RTC0 is used for scheduling by the SoftDevice, //! RTC1 is used for the app_timer library. And static const nrfx_rtc_t s_rtc = NRFX_RTC_INSTANCE(2); static void prv_software_watchdog_timeout(nrfx_rtc_int_type_t int_type) { #if NRFX_WDT_ENABLED nrfx_wdt_feed(); #endif //! Note: We can't call MEMFAULT_EXC_HANDLER_WATCHDOG directly here because the IRQ is handled by //! the nRF5 SDK so we will trap into the assert handler instead. //! //! Stack at this point for reference: //! irq_handler (p_reg=0x40024000, instance_id=0, channel_count=4) //! RTC2_IRQHandler () MEMFAULT_SOFTWARE_WATCHDOG(); } int memfault_software_watchdog_enable(void) { const nrfx_rtc_config_t config = { // Use highest configurable priority interrupt so we can catch hangs from low priority ISRs .interrupt_priority = 0, // This will make our timer resolution equal to (32768 / 2^12) = 125ms .prescaler = 0xfff, // NB: tick_latency is only used if reliable == true .tick_latency = 0xff, .reliable = false, }; ret_code_t err_code = nrfx_rtc_init(&s_rtc, &config, &prv_software_watchdog_timeout); if (err_code != NRF_SUCCESS) { MEMFAULT_LOG_ERROR("Failed to configure rtc software watchdog: rv=%d", (int)err_code); return err_code; } nrfx_rtc_tick_disable(&s_rtc); nrfx_rtc_counter_clear(&s_rtc); nrfx_rtc_enable(&s_rtc); memfault_software_watchdog_update_timeout(MEMFAULT_NRF5_WATCHDOG_SW_TIMEOUT_MS); return 0; } int memfault_software_watchdog_disable(void) { nrfx_rtc_disable(&s_rtc); return 0; } int memfault_software_watchdog_feed(void) { nrfx_rtc_counter_clear(&s_rtc); return 0; } int memfault_software_watchdog_update_timeout(uint32_t timeout_ms) { const uint32_t counter_val = (timeout_ms / MEMFAULT_PRESCALE_RESOLUTION_MS); MEMFAULT_LOG_INFO("Software Watchdog configured to %dms", (int)(counter_val * MEMFAULT_PRESCALE_RESOLUTION_MS)); // We configure a counter interrupt to fire when the desired timeout is hit. // When we "feed" the watchdog, we reset the counter back to 0. Therefore, the interrupt // will only trigger if we fail to feed the watchdog in the specified window. nrfx_rtc_cc_set(&s_rtc, MEMFAULT_NRF5_WATCHDOG_SW_CHANNEL, counter_val, true /* enable_irq */); return 0; } ================================================ FILE: ports/nxp/rt1021/src_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the "System Reset //! Controller" (SRC)'s "Reset Status Register" (SRC_SRSR). //! //! More details can be found in the chapter "21.8.3 SRC Reset Status Register //! (SRC_SRSR)" in the RT1021 Reference Manual #include #include "MIMXRT1021.h" #include "memfault/components.h" #include "memfault/ports/reboot_reason.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { const uint32_t reset_cause = SRC->SRSR & (SRC_SRSR_W1C_BITS_MASK | SRC_SRSR_TEMPSENSE_RST_B_MASK); eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; // clear the SRC Reset Status Register by writing a 1 to each bit #if MEMFAULT_REBOOT_REASON_CLEAR SRC->SRSR = SRC_SRSR_W1C_BITS_MASK; #endif MEMFAULT_PRINT_RESET_INFO("Reset Reason, SRC_SRSR=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Cause: "); if (reset_cause & SRC_SRSR_JTAG_SW_RST_MASK) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & SRC_SRSR_TEMPSENSE_RST_B_MASK) { MEMFAULT_PRINT_RESET_INFO(" Temp Sensor"); reset_reason = kMfltRebootReason_UnknownError; } else if (reset_cause & SRC_SRSR_WDOG3_RST_B_MASK) { MEMFAULT_PRINT_RESET_INFO(" HW Watchdog3"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & SRC_SRSR_WDOG_RST_B_MASK) { MEMFAULT_PRINT_RESET_INFO(" HW Watchdog"); // reset_cause can be use to disambiguate the watchdog types reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & SRC_SRSR_JTAG_RST_B_MASK) { MEMFAULT_PRINT_RESET_INFO(" Debugger"); reset_reason = kMfltRebootReason_DebuggerHalted; } else if (reset_cause & SRC_SRSR_IPP_USER_RESET_B_MASK) { MEMFAULT_PRINT_RESET_INFO(" Button"); reset_reason = kMfltRebootReason_ButtonReset; } else if (reset_cause & SRC_SRSR_CSU_RESET_B_MASK) { // Central Security Unit triggered the reset (see Security Reference // Manual) MEMFAULT_PRINT_RESET_INFO(" CSU"); reset_reason = kMfltRebootReason_UnknownError; } else if (reset_cause & SRC_SRSR_LOCKUP_SYSRESETREQ_MASK) { MEMFAULT_PRINT_RESET_INFO(" Lockup"); reset_reason = kMfltRebootReason_Lockup; } else if (reset_cause & SRC_SRSR_IPP_RESET_B_MASK) { // TODO this might be equivalent to POR... MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else { MEMFAULT_PRINT_RESET_INFO(" Unknown"); } *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/nxp/rw61x/pmu_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Extract system reboot reason from the PMU.SYS_RST_STATUS register. #include #include "memfault/components.h" #include "memfault/ports/reboot_reason.h" #if defined(RW610) #include "RW610.h" #else // Default to the RW612 header file. If a user needs the RW610 header file, // they can define RW610 in their project settings or in // memfault_platform_config.h #include "RW612.h" #endif #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { // Undocumented: on reset, the ROM bootloader backs up PMU->SYS_RST_STATUS to // RF_SYSCON->WO_SCRATCH_REG[3] and clears the PMU->SYS_RST_STATUS register. // https://github.com/zephyrproject-rtos/hal_nxp/blob/8c354a918c1272b40ad9b4ffecac1d89125efbe6/mcux/mcux-sdk/devices/RW610/drivers/fsl_power.h#L259-L264 const uint32_t reset_cause = RF_SYSCON->WO_SCRATCH_REG[3]; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; // Clear the SRC Reset Status Register by writing a 1 to each bit. This is // done automatically by the ROM bootloader on reset already, but it doesn't // hurt to do it again. #if MEMFAULT_REBOOT_REASON_CLEAR SRC->SYS_RST_CLR = reset_cause; #endif MEMFAULT_PRINT_RESET_INFO("Reset Reason, SYS_RST_STATUS=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Cause: "); if (reset_cause & PMU_SYS_RST_STATUS_CM33_SYSRESETREQ_MASK) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & PMU_SYS_RST_STATUS_CM33_LOCKUP_MASK) { MEMFAULT_PRINT_RESET_INFO(" Lockup"); reset_reason = kMfltRebootReason_Lockup; } else if (reset_cause & PMU_SYS_RST_STATUS_WDT_RST_MASK) { MEMFAULT_PRINT_RESET_INFO(" HW Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & PMU_SYS_RST_STATUS_AP_SYSRESETREQ_MASK) { MEMFAULT_PRINT_RESET_INFO(" Debugger"); reset_reason = kMfltRebootReason_DebuggerHalted; } else if (reset_cause & PMU_SYS_RST_STATUS_CODE_WDT_RST_MASK) { MEMFAULT_PRINT_RESET_INFO(" SW Watchdog"); reset_reason = kMfltRebootReason_SoftwareWatchdog; } else if (reset_cause & PMU_SYS_RST_STATUS_ITRC_CHIP_RST_MASK) { MEMFAULT_PRINT_RESET_INFO(" Tamper"); reset_reason = kMfltRebootReason_SecurityViolation; } else if (reset_cause & PMU_SYS_RST_STATUS_SW_RESETB_SCANTEST_MASK) { MEMFAULT_PRINT_RESET_INFO(" Resetb"); // Unfortunately this doesn't seem to be set on pin reset reset_reason = kMfltRebootReason_PinReset; } else { // Power On Reset and Pin Reset are unfortunately not distinguished in the // reset cause register. Default to Power On Reset. MEMFAULT_PRINT_RESET_INFO(" POR"); reset_reason = kMfltRebootReason_PowerOnReset; } *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/panics/src/memfault_platform_ram_backed_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A port for the platform dependencies needed to use the coredump feature from the "panics" //! component by saving the Memfault coredump data in a "noinit" region of RAM. //! //! This can be linked in directly by adding the .c file to the build system or can be //! copied into your repo and modified to collect different RAM regions. //! //! By default, it will collect the top of the stack which was running at the time of the //! crash. This allows for a reasonable backtrace to be collected while using very little RAM. //! //! Place the "noinit" region in an area of RAM that will persist across bootup. //! The region must: //! - not be placed in .bss //! - not be an area of RAM used by any of your bootloaders //! For example, with GNU GCC, this can be achieved by adding something like the following to //! your linker script: //! MEMORY //! { //! [...] //! COREDUMP_NOINIT (rw) : ORIGIN = , LENGTH = 1024 //! } //! SECTIONS //! { //! [...] //! .coredump_noinit (NOLOAD): { KEEP(*(*.noinit.mflt_coredump)) } > COREDUMP_NOINIT //! } #include "memfault/config.h" #if MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_RAM #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/panics/platform/coredump.h" #if !MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_CUSTOM #if ((MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE % 4) != 0) #error "MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE must be a multiple of 4" #endif MEMFAULT_STATIC_ASSERT(sizeof(uint32_t) == 4, "port expects sizeof(uint32_t) == 4"); MEMFAULT_PUT_IN_SECTION(MEMFAULT_PLATFORM_COREDUMP_NOINIT_SECTION_NAME) static uint32_t s_ram_backed_coredump_region[MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE / 4]; #define MEMFAULT_PLATFORM_COREDUMP_RAM_START_ADDR ((uint8_t *)&s_ram_backed_coredump_region[0]) #endif /* MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_CUSTOM */ #if !MEMFAULT_PLATFORM_COREDUMP_STORAGE_REGIONS_CUSTOM //! Collect the active stack as part of the coredump capture. //! User can implement their own version to override the implementation MEMFAULT_WEAK const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { static sMfltCoredumpRegion s_coredump_regions[1]; const size_t stack_size = memfault_platform_sanitize_address_range( crash_info->stack_address, MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT); s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(crash_info->stack_address, stack_size); *num_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions); return &s_coredump_regions[0]; } #endif void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE, }; } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } const uint8_t *storage_ptr = MEMFAULT_PLATFORM_COREDUMP_RAM_START_ADDR; const uint8_t *read_ptr = &storage_ptr[offset]; memcpy(data, read_ptr, read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } uint8_t *storage_ptr = MEMFAULT_PLATFORM_COREDUMP_RAM_START_ADDR; void *erase_ptr = &storage_ptr[offset]; memset(erase_ptr, 0x0, erase_size); return true; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if (!prv_op_within_flash_bounds(offset, data_len)) { return false; } uint8_t *storage_ptr = MEMFAULT_PLATFORM_COREDUMP_RAM_START_ADDR; uint8_t *write_ptr = (uint8_t *)&storage_ptr[offset]; memcpy(write_ptr, data, data_len); return true; } void memfault_platform_coredump_storage_clear(void) { const uint8_t clear_byte = 0x0; memfault_platform_coredump_storage_write(0, &clear_byte, sizeof(clear_byte)); } #endif /* MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_RAM */ ================================================ FILE: ports/particle/README.md ================================================ # Memfault Library for use with Particle Device OS Ship Firmware with Confidence. More details about the Memfault platform itself, how it works, and step-by-step integration guides [can be found here](https://mflt.io/particle-getting-started). The library is compatible with all versions of [Device OS](https://github.com/particle-iot/device-os) greater than or equal to Device OS 3.0 :exclamation: Note: Use the [particle-firmware-library](https://github.com/memfault/particle-firmware-library) repository to add memfault support to an application. This repository is updated as part of the release process for the [memfault-firmware-sdk](https://github.com/memfault/memfault-firmware-sdk). ## Welcome to your library! To get started, add the library to your particle application: ```bash $ git submodule add https://github.com/memfault/particle-firmware-library lib/memfault ``` ## Integration Steps 1. Add the following to your application ```c #include "memfault.h" Memfault memfault; void loop() { // ... memfault.process(); // ... } ``` 2. Create a Memfault Project Key at https://goto.memfault.com/create-key/particle and copy it to your clipboard. 3. Navigate to the Integrations tab in the Particle Cloud UI and create a new "Custom Template" webhook. Be sure to replace `MEMFAULT_PROJECT_KEY` below with the one copied in step 2. ```json { "event": "memfault-chunks", "responseTopic": "", "url": "https://chunks.memfault.com/api/v0/chunks/{{PARTICLE_DEVICE_ID}}", "requestType": "POST", "noDefaults": false, "rejectUnauthorized": true, "headers": { "Memfault-Project-Key": "MEMFAULT_PROJECT_KEY", "Content-Type": "application/octet-stream", "Content-Encoding": "base64" }, "body": "{{{PARTICLE_EVENT_VALUE}}}" } ``` ## Example Usage See the [examples/memfault_test/](examples/memfault_test) folder for a complete application demonstrating how to use Memfault. ## Questions Don't hesitate to contact us for help! You can reach us through ================================================ FILE: ports/particle/examples/memfault_test/project.properties ================================================ name=memfault_test-fw ================================================ FILE: ports/particle/examples/memfault_test/src/application.cpp ================================================ //! @file //! //! @brief //! //! A simple test app which can be used for testing the Particle Memfault library #include "application.h" #include "Particle.h" #include "logging.h" #include "memfault.h" #include "spark_wiring_json.h" LOG_SOURCE_CATEGORY("memfault_app") SYSTEM_MODE(MANUAL); //! Note: Must be bumped everytime modifications are made //! https://mflt.io/particle-versioning #define EXAMPLE_APP_VERSION 1 PRODUCT_ID(PLATFORM_ID); PRODUCT_VERSION(EXAMPLE_APP_VERSION) // Enable USB logging and enable full verbosity for debug purposes static SerialLogHandler s_usb_log_handler(115200, LOG_LEVEL_ALL); // Note: For test purposes, we defer initializing the Memfault library to // give time for the USB serial to be initialized and console logs to be up and running // // For a production application, we recommend constructing Memfault on bootup by // adding the following to your application: // // static Memfault s_memfault(your_product_id, your_product_version); // void loop() { // [...] // memfault.process(); // } static Memfault *s_memfault = NULL; #if Wiring_WiFi #error "Support for WiFi transport not implemented yet" #endif void setup() { Particle.connect(); } //! Note: This function loops forever void loop() { if (s_memfault == NULL) { // insert a delay to give Device OS USB init time to complete delay(1000); s_memfault = new Memfault(EXAMPLE_APP_VERSION); } s_memfault->process(); Particle.process(); } JSONValue getValue(const JSONValue &obj, const char *name) { JSONObjectIterator it(obj); while (it.next()) { if (it.name() == name) { return it.value(); } } return JSONValue(); } void ctrl_request_custom_handler(ctrl_request *req) { LOG(INFO, "Received Command: %.*s", req->request_size, req->request_data); auto d = JSONValue::parse(req->request_data, req->request_size); SPARK_ASSERT(d.isObject()); JSONValue data_ = std::move(d); const char *command = getValue(data_, "command").toString().data(); //! No support for argv / argc forwarding today const bool success = s_memfault->run_debug_cli_command(command, 0, NULL); system_ctrl_set_result(req, success ? SYSTEM_ERROR_NONE : SYSTEM_ERROR_NOT_SUPPORTED, nullptr, nullptr, nullptr); } ================================================ FILE: ports/particle/examples/memfault_test/src/memfault_particle_user_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Application specific configuration overrides for memfault library #ifdef __cplusplus extern "C" { #endif //! WARNING: This should only be set for debug purposes. For production fleets, the //! value must be >= 3600 to avoid being rate limited #define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS 60 //! This exposes debug commands that can be called for testing Memfault at the cost of using //! some extra code space. For production builds, it is recommended this flag be set to 0 #define MEMFAULT_PARTICLE_PORT_DEBUG_API_ENABLE 1 //! The software_type name to be displayed in the Memfault UI #define MEMFAULT_PARTICLE_PORT_SOFTWARE_TYPE "mflt-test-fw" //! The particle librt-dynalib.a library implements a custom __assert_func(), so //! disable the conflicting Memfault SDK implementation #define MEMFAULT_ASSERT_CSTDLIB_HOOK_ENABLED 0 #ifdef __cplusplus } #endif ================================================ FILE: ports/particle/examples/memfault_test/src/memfault_trace_reason_user_config.def ================================================ // Custom user trace reasons can be defined here, i.e // MEMFAULT_TRACE_REASON(I2cError) ================================================ FILE: ports/particle/src/memfault.cpp ================================================ //! @file //! #include #include #include #define PARTICLE_USE_UNSTABLE_API #include "deviceid_hal.h" #include "logging.h" #include "memfault.h" #include "system_version.h" LOG_SOURCE_CATEGORY("memfault_lib") #define PARTICLE_SYSTEM_VERSION_GET_MAJOR(version) (((version) >> 24) & 0xFF) #define PARTICLE_SYSTEM_VERSION_GET_MINOR(version) (((version) >> 16) & 0xFF) #if PARTICLE_SYSTEM_VERSION_GET_MAJOR(SYSTEM_VERSION) < 3 #error "Memfault Library is only compatible with Device OS 3.0 or greater" #endif #if ((PARTICLE_SYSTEM_VERSION_GET_MAJOR(SYSTEM_VERSION) != \ MEMFAULT_PARTICLE_SYSTEM_VERSION_MAJOR) || \ (PARTICLE_SYSTEM_VERSION_GET_MINOR(SYSTEM_VERSION) != \ MEMFAULT_PARTICLE_SYSTEM_VERSION_MINOR)) #warning "Memfault Library is targeting a different Device OS version than the one configured" #endif using namespace std::placeholders; // battery backed SRAM - preserved over a reboot static retained uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; static char s_hardware_version[32] = "Unset-Hw"; static char s_system_version[32] = MEMFAULT_EXPAND_AND_QUOTE(SYSTEM_VERSION_STRING); #if MEMFAULT_PARTICLE_PORT_PANIC_HANDLER_HOOK_ENABLE static void prv_memfault_panic_handler(const ePanicCode code, const void *extraInfo) { MEMFAULT_ASSERT_RECORD(code); } #endif // Here we catch SDK asserts and invoke the panic handler. We do not need to worry about getting // into reset loops from within the Memfault SDK since the panic handling code in Memfault does not // issue any asserts void memfault_sdk_assert_func_noreturn(void) { PANIC(AssertionFailure, "memfault"); MEMFAULT_UNREACHABLE; } void Memfault::process(void) { if (!m_connected) { // we are not connected, skip attempting to send data return; } static uint32_t s_last_check_time = 0; if ((millis() - s_last_check_time) < 1000) { return; // we just sent data, wait a little bit } s_last_check_time = millis(); if (!memfault_packetizer_data_available()) { return; // no data to send! } const size_t event_data_max_size = Particle.maxEventDataSize(); char *chunk_buf = (char *)malloc(event_data_max_size + 1 /* for '\0' */); if (chunk_buf == NULL) { MEMFAULT_LOG_ERROR("Unable to allocate buffer for memfault-chunks, size=%d", (int)event_data_max_size); return; } size_t chunk_bin_len = MEMFAULT_BASE64_MAX_DECODE_LEN(event_data_max_size); const bool data_available = memfault_packetizer_get_chunk(chunk_buf, &chunk_bin_len); if (data_available) { const size_t base64_len = MEMFAULT_BASE64_ENCODE_LEN(chunk_bin_len); MEMFAULT_SDK_ASSERT(base64_len <= event_data_max_size); memfault_base64_encode_inplace(chunk_buf, chunk_bin_len); chunk_buf[base64_len] = '\0'; if (!Particle.publish("memfault-chunks", (const char *)chunk_buf)) { MEMFAULT_LOG_ERROR("Failed to send Memfault Chunk, length=%d", base64_len); } } free(chunk_buf); } #if MEMFAULT_PARTICLE_PORT_DEBUG_API_ENABLE static int prv_test_logging(int argc, char *argv[]) { MEMFAULT_LOG_DEBUG("Debug log!"); MEMFAULT_LOG_INFO("Info log!"); MEMFAULT_LOG_WARN("Warning log!"); MEMFAULT_LOG_ERROR("Error log!"); return 0; } // Triggers an immediate heartbeat capture (instead of waiting for timer // to expire) static int prv_test_heartbeat(int argc, char *argv[]) { memfault_metrics_heartbeat_debug_trigger(); return 0; } static int prv_test_trace(int argc, char *argv[]) { MEMFAULT_TRACE_EVENT_WITH_LOG(MemfaultCli_Test, "A test error trace!"); return 0; } //! Trigger a user initiated reboot and confirm reason is persisted static int prv_test_reboot(int argc, char *argv[]) { MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_UserReset); memfault_platform_reboot(); } // // Test different crash types where a coredump should be captured // static int prv_test_assert(int argc, char *argv[]) { MEMFAULT_ASSERT(0); return -1; // should never get here } static int prv_test_memfault(int argc, char *argv[]) { void (*bad_func)(void) = (void (*)())0xEEEEDEAD; bad_func(); return -1; // should never get here } static int prv_test_nmi(int argc, char *argv[]) { SCB->ICSR |= SCB_ICSR_NMIPENDSET_Msk; return -1; // should never get here } static int prv_test_hang(int argc, char *argv[]) { while (1) { } return -1; // should never get here } static int prv_test_particle_panic(int argc, char *argv[]) { panic_(InvalidCase, NULL, HAL_Delay_Microseconds); return -1; // should never get here } static int prv_test_busfault(int argc, char *argv[]) { uint32_t *f = (uint32_t *)0x00000000; *f = 0xFFEECAFE; return -1; // should never get here } static int prv_test_spark_assert(int argc, char *argv[]) { SPARK_ASSERT(0); return -1; // should never get here } static int prv_test_coredump_storage(int argc, char *argv[]) { int32_t state = HAL_disable_irq(); { memfault_coredump_storage_debug_test_begin(); } HAL_enable_irq(state); return memfault_coredump_storage_debug_test_finish() ? 0 : -1; } static int prv_test_device_info(int argc, char *argv[]) { memfault_build_info_dump(); memfault_device_info_dump(); return 0; } // Dump Memfault data collected to console static int prv_test_export(int argc, char *argv[]) { memfault_data_export_dump_chunks(); return 0; } typedef struct MemfaultTestCommand { const char *name; int (*func)(int argc, char **argv); } sMemfaultTestCommand; static const sMemfaultTestCommand s_memfault_test_commands[] = { { "coredump_storage", prv_test_coredump_storage }, { "device_info", prv_test_device_info }, { "export", prv_test_export }, { "heartbeat", prv_test_heartbeat }, { "logging", prv_test_logging }, { "reboot", prv_test_reboot }, { "trace", prv_test_trace }, // Different types of crashes { "assert", prv_test_assert }, { "busfault", prv_test_busfault }, { "hang", prv_test_hang }, { "memfault", prv_test_memfault }, { "nmi", prv_test_nmi }, { "panic", prv_test_particle_panic }, { "spark_assert", prv_test_spark_assert }, }; int Memfault::run_debug_cli_command(const char *command, int argc, char **argv) { for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_memfault_test_commands); i++) { if (strcmp(command, s_memfault_test_commands[i].name) != 0) { continue; } const int rv = s_memfault_test_commands[i].func(argc, argv); return rv; } MEMFAULT_LOG_ERROR("Unknown CLI Command: %s", command); return -1; } #else int Memfault::run_debug_cli_command(const char *command, int argc, char **argv) { MEMFAULT_LOG_ERROR( "Debug API disabled, add MEMFAULT_PARTICLE_PORT_DEBUG_API_ENABLE 1 to memfault config"); return -1; } #endif /* MEMFAULT_PARTICLE_PORT_DEBUG_API_ENABLE */ void Memfault::handle_cloud_connectivity_event(system_event_t event, int param) { if (event != cloud_status) { MEMFAULT_LOG_ERROR("Unexpected cloud event type: %d", event); return; } m_connected = (param == cloud_status_connected); #if MEMFAULT_PARTICLE_PORT_CLOUD_METRICS_ENABLE switch (param) { case cloud_status_disconnected: MEMFAULT_METRIC_TIMER_STOP(Cloud_ConnectingTime); MEMFAULT_METRIC_TIMER_STOP(Cloud_ConnectedTime); MEMFAULT_METRIC_ADD(Cloud_DisconnectCount, 1); break; case cloud_status_connecting: MEMFAULT_METRIC_TIMER_START(Cloud_ConnectingTime); break; case cloud_status_connected: MEMFAULT_METRIC_TIMER_STOP(Cloud_ConnectingTime); MEMFAULT_METRIC_TIMER_START(Cloud_ConnectedTime); MEMFAULT_METRIC_ADD(Cloud_ConnectCount, 1); break; case cloud_status_disconnecting: MEMFAULT_METRIC_TIMER_STOP(Cloud_ConnectingTime); MEMFAULT_METRIC_TIMER_STOP(Cloud_ConnectedTime); break; default: break; } #endif } static void prv_memfault_reboot_reason_get(sResetBootupInfo *info) { const uint32_t s_last_mcu_reset_reason = System.resetReason(); eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; switch (s_last_mcu_reset_reason) { case RESET_REASON_UNKNOWN: reset_reason = kMfltRebootReason_Unknown; break; case RESET_REASON_PIN_RESET: reset_reason = kMfltRebootReason_PinReset; break; case RESET_REASON_WATCHDOG: reset_reason = kMfltRebootReason_HardwareWatchdog; break; case RESET_REASON_USER: reset_reason = kMfltRebootReason_UserReset; break; case RESET_REASON_POWER_DOWN: case RESET_REASON_POWER_MANAGEMENT: reset_reason = kMfltRebootReason_UserShutdown; break; case RESET_REASON_POWER_BROWNOUT: reset_reason = kMfltRebootReason_BrownOutReset; break; case RESET_REASON_UPDATE: reset_reason = kMfltRebootReason_FirmwareUpdate; break; case RESET_REASON_UPDATE_TIMEOUT: reset_reason = kMfltRebootReason_FirmwareUpdateError; break; case RESET_REASON_PANIC: reset_reason = kMfltRebootReason_KernelPanic; break; case RESET_REASON_SAFE_MODE: case RESET_REASON_DFU_MODE: reset_reason = kMfltRebootReason_SoftwareReset; break; default: reset_reason = kMfltRebootReason_Unknown; break; } *info = (sResetBootupInfo){ .reset_reason_reg = s_last_mcu_reset_reason, .reset_reason = reset_reason, }; } uint64_t memfault_platform_get_time_since_boot_ms(void) { return millis(); } static MemfaultPlatformTimerCallback *s_callback; static void prv_metric_timer_callback() { MEMFAULT_SDK_ASSERT(s_callback != NULL); #if MEMFAULT_PARTICLE_PORT_HEAP_METRICS_ENABLE runtime_info_t info = { 0 }; info.size = sizeof(info); HAL_Core_Runtime_Info(&info, NULL); MEMFAULT_METRIC_SET_UNSIGNED(Heap_TotalSize, info.total_init_heap); MEMFAULT_METRIC_SET_UNSIGNED(Heap_MinBytesFree, info.total_init_heap - info.max_used_heap); MEMFAULT_METRIC_SET_UNSIGNED(Heap_BytesFree, info.freeheap); MEMFAULT_METRIC_SET_UNSIGNED(Heap_MaxBlockSize, info.largest_free_block_heap); #endif s_callback(); } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback *callback) { static Timer *s_memfault_timer = NULL; if (s_memfault_timer == NULL) { s_callback = callback; s_memfault_timer = new Timer(period_sec * 1000, prv_metric_timer_callback); s_memfault_timer->start(); MEMFAULT_SDK_ASSERT(s_memfault_timer); } return (s_memfault_timer != NULL); } void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { *info = (sMemfaultDeviceInfo){ // NB: We intentionally leave this blank. When data is posted to memfault via // the particle webhook integration, the "PARTICLE_DEVICE_ID" // will be picked up .device_serial = NULL, .software_type = MEMFAULT_PARTICLE_PORT_SOFTWARE_TYPE, .software_version = s_system_version, .hardware_version = s_hardware_version, }; } void memfault_platform_reboot(void) { System.reset(RESET_NO_WAIT); MEMFAULT_UNREACHABLE; } #if MEMFAULT_PARTICLE_PORT_COREDUMP_TASK_COLLECTION_ENABLE // TODO: See if we get exact TCB size exposed from device os side #define MEMFAULT_FREERTOS_TCB_SIZE 200 static size_t prv_get_task_region(os_thread_dump_info_t *info, sMfltCoredumpRegion *regions, size_t num_regions, const bool tcb) { if ((info == NULL) || (regions == NULL) || (num_regions == 0)) { return 0; } size_t region_idx = 0; // TCB is the thread handle in the os structure void *tcb_address = info->thread; if (tcb) { const size_t tcb_size = memfault_platform_sanitize_address_range(tcb_address, MEMFAULT_FREERTOS_TCB_SIZE); if (tcb_size != 0) { regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(tcb_address, tcb_size); region_idx++; } } else { void *top_of_stack = (void *)(*(uintptr_t *)tcb_address); const size_t stack_size = memfault_platform_sanitize_address_range( top_of_stack, MEMFAULT_PLATFORM_TASK_STACK_SIZE_TO_COLLECT); if (stack_size != 0) { regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(top_of_stack, stack_size); region_idx++; } } return region_idx; } #endif /* MEMFAULT_PARTICLE_PORT_COREDUMP_TASK_COLLECTION_ENABLE */ size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { // Note: This is grabbed from the linker script and makes the assumption that // there is only 1 contiguous RAM region. While this is true for the NRF52840, // it does not hold for all MCUs so this may need to be adjusted. extern uint32_t _ram_start[]; extern uint32_t _ram_end[]; const uint32_t ram_start = (uint32_t)_ram_start; const uint32_t ram_end = (uint32_t)_ram_end; if ((uint32_t)start_addr >= ram_start && (uint32_t)start_addr < ram_end) { return MEMFAULT_MIN(desired_size, ram_end - (uint32_t)start_addr); } return 0; } const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { int region_idx = 0; static sMfltCoredumpRegion s_coredump_regions[16]; // first, capture the stack that was active at the time of crash const size_t active_stack_size_to_collect = 512; s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT( crash_info->stack_address, memfault_platform_sanitize_address_range( crash_info->stack_address, active_stack_size_to_collect)); #if MEMFAULT_PARTICLE_PORT_COREDUMP_TASK_COLLECTION_ENABLE const size_t num_coredump_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions); // capture the threads and stacks (if we have space!) // First we will try to store all the task TCBs. This way if we run out of // space while storing tasks we will still be able to recover the state of all // the threads os_thread_dump( OS_THREAD_INVALID_HANDLE, [](os_thread_dump_info_t *info, void *data) -> os_result_t { int *region_idx = (int *)data; *region_idx += prv_get_task_region(info, &s_coredump_regions[*region_idx], num_coredump_regions - *region_idx, true); return 0; }, ®ion_idx); // Now we store the region of the stack where context is saved. This way // we can unwind the stacks for threads that are not actively running os_thread_dump( OS_THREAD_INVALID_HANDLE, [](os_thread_dump_info_t *info, void *data) -> os_result_t { int *region_idx = (int *)data; *region_idx += prv_get_task_region(info, &s_coredump_regions[*region_idx], num_coredump_regions - *region_idx, false); return 0; }, ®ion_idx); #endif *num_regions = region_idx; return &s_coredump_regions[0]; } void memfault_platform_reboot_tracking_boot(void) { // Read reason why device reset and update Memfault tracking accordingly // // For a detailed explanation about reboot reason storage options check out the guide at: // https://mflt.io/reboot-reason-storage sResetBootupInfo reset_reason = { 0 }; prv_memfault_reboot_reason_get(&reset_reason); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_reason); } Memfault::Memfault(const uint16_t product_version, const char *build_metadata, const char *hardware_version) : m_connected(false) { System.enableFeature(FEATURE_RESET_INFO); // register for system events System.on(cloud_status, &Memfault::handle_cloud_connectivity_event, this); // Grab the revision of the particle board being used for a given product type. if (hardware_version == NULL) { // Use default hardware_version scheme uint32_t hw_version_id = 0; hal_get_device_hw_version(&hw_version_id, NULL); snprintf(s_hardware_version, sizeof(s_hardware_version), "%s-rev%d", MEMFAULT_EXPAND_AND_QUOTE(PLATFORM_NAME), (int)hw_version_id); } else { snprintf(s_hardware_version, sizeof(s_hardware_version), "%s", hardware_version); } // Uniquely identifies a firmware version running on a device. // // We concatenate the application firmware version (PRODUCT_VERSION) with the Device OS (System // Firmware Version) to uniquely identify the software version combination on a device. snprintf(s_system_version, sizeof(s_system_version), "v%d.os" MEMFAULT_EXPAND_AND_QUOTE(SYSTEM_VERSION_STRING), (int)product_version); // Append metadata version info to avoid the collisions where the same version maps to different // build artifacts const char *extra_version_info = build_metadata; char buildid_str[6] = { 0 }; if ((extra_version_info == NULL) && memfault_build_id_get_string(buildid_str, sizeof(buildid_str))) { extra_version_info = &buildid_str[0]; } if (extra_version_info != NULL) { const size_t curr_len = strlen(s_system_version); snprintf(&s_system_version[curr_len], sizeof(s_system_version) - curr_len, "+%s", extra_version_info); } #if MEMFAULT_PARTICLE_PORT_LOG_STORAGE_ENABLE // When set up this buffer will be captured as part of a coredump // so one can see messages that were logged leading up to a crash static uint8_t s_log_storage[MEMFAULT_PARTICLE_PORT_LOG_STORAGE_SIZE]; memfault_log_boot(&s_log_storage, sizeof(s_log_storage)); #endif memfault_build_info_dump(); memfault_device_info_dump(); memfault_platform_reboot_tracking_boot(); // Initialize event storage static uint8_t s_event_storage[MEMFAULT_PARTICLE_PORT_EVENT_STORAGE_SIZE]; // 1/2 day MEMFAULT_STATIC_ASSERT(MEMFAULT_PARTICLE_PORT_EVENT_STORAGE_SIZE >= 100, "MEMFAULT_PARTICLE_PORT_EVENT_STORAGE_SIZE must be >= 100"); const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); memfault_trace_event_boot(evt_storage); // Serialize reset information so it gets reported to memfault memfault_reboot_tracking_collect_reset_info(evt_storage); // Start heartbeat subsystem sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); // hook into all of the ISRs #if MEMFAULT_PARTICLE_PORT_FAULT_HANDLERS_ENABLE bool isrAttachOK = true; isrAttachOK &= attachInterruptDirect(HardFault_IRQn, MEMFAULT_EXC_HANDLER_HARD_FAULT); isrAttachOK &= attachInterruptDirect(MemoryManagement_IRQn, MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT); isrAttachOK &= attachInterruptDirect(BusFault_IRQn, MEMFAULT_EXC_HANDLER_BUS_FAULT); isrAttachOK &= attachInterruptDirect(UsageFault_IRQn, MEMFAULT_EXC_HANDLER_USAGE_FAULT); isrAttachOK &= attachInterruptDirect(NonMaskableInt_IRQn, MEMFAULT_EXC_HANDLER_NMI); SPARK_ASSERT(isrAttachOK); #endif #if MEMFAULT_PARTICLE_PORT_PANIC_HANDLER_HOOK_ENABLE panic_set_hook(prv_memfault_panic_handler, NULL); #endif MEMFAULT_LOG_INFO("Memfault Initialized!"); } ================================================ FILE: ports/particle/src/memfault.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Wraps the memfault-firmware-sdk up as a Particle library: //! https://docs.particle.io/tutorials/device-os/libraries/ #include "Particle.h" // Convenience include of all memfault-firmware-sdk headers. This allows // an end user to simply this file, "memfault.h", to get access to all Memfault APIs #include "memfault-firmware-sdk/components/include/memfault/components.h" class Memfault { public: //! Constructor for Memfault library //! //! @param product_version The particle version of the application running on the device //! See https://mflt.io/particle-versioning for more details //! @param build_metadata Additional build metadata (such as a git short SHA or timestamp) //! to append to the "software_version" reported to Memfault. //! @param hardware_version When set, overrides the default strategy for reporting the //! hardware_version using the PLATFORM_NAME compiled against explicit Memfault(const uint16_t product_version = 0, const char *build_metadata = NULL, const char *hardware_version = NULL); //! Should be called from your applications loop handler //! //! //! static Memfault s_memfault(your_product_version); //! void loop() { //! [...] //! memfault.process(); //! } void process(void); //! Underlying functionality is disabled by default, but can be enabled //! by adding the following to your memfault_particle_user_config.h //! //! #define MEMFAULT_PARTICLE_PORT_DEBUG_API_ENABLE 1 //! //! @return >0 on success or error code on failure int run_debug_cli_command(const char *command, int argc, char **argv); private: //! Hooks into system "cloud_status_ event for tracking m_connected and recording connectivity //! heartbeat metrics void handle_cloud_connectivity_event(system_event_t event, int param); //! true if a particle cloud connection is available, false otherwise bool m_connected; }; ================================================ FILE: ports/particle/src/memfault_particle_metrics_heartbeat_config.def ================================================ #if MEMFAULT_PARTICLE_PORT_HEAP_METRICS_ENABLE // The total size of the heap. This value should only change with Device OS updates MEMFAULT_METRICS_KEY_DEFINE(Heap_TotalSize, kMemfaultMetricType_Unsigned) // The least amount of bytes free the heap has ever hit MEMFAULT_METRICS_KEY_DEFINE(Heap_MinBytesFree, kMemfaultMetricType_Unsigned) // The bytes free in the heap when sampled at a heartbeat interval MEMFAULT_METRICS_KEY_DEFINE(Heap_BytesFree, kMemfaultMetricType_Unsigned) // The largest size block free in the heap (if there is a lot of heap space free, and a small max // block size, this is a good indicator that the heap is suffering from fragmentation) MEMFAULT_METRICS_KEY_DEFINE(Heap_MaxBlockSize, kMemfaultMetricType_Unsigned) #endif #if MEMFAULT_PARTICLE_PORT_CLOUD_METRICS_ENABLE MEMFAULT_METRICS_KEY_DEFINE(Cloud_ConnectingTime, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(Cloud_ConnectedTime, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(Cloud_ConnectCount, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(Cloud_DisconnectCount, kMemfaultMetricType_Unsigned) #endif // Pull in any user heartbeat event reasons that have been defined # if __has_include("memfault_metrics_heartbeat_config.def") # include "memfault_metrics_heartbeat_config.def" # endif ================================================ FILE: ports/particle/src/memfault_particle_trace_reason_user_config.def ================================================ // Pull in any user trace event reasons that have been defined # if __has_include("memfault_trace_reason_user_config.def") # include "memfault_trace_reason_user_config.def" # endif ================================================ FILE: ports/particle/src/memfault_platform_config.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Particle specific memfault platform configuration //! //! An end user can further customize by creating a "memfault_particle_user_config.h" in their //! application directory #pragma once #ifdef __cplusplus extern "C" { #endif // Allow end user to override configuration defaults by creating their own // memfault_particle_user_config.h in their app directory #if __has_include("memfault_particle_user_config.h") #include "memfault_particle_user_config.h" #endif // system_version.h is not currently safe to include in headers which get included by C files // because it uses member initializers that are only CPP compatible. When the block below is // wrapped accordingly we will be able to include system_version.h and access the SYSTEM_VERSION // define: // https://github.com/particle-iot/device-os/blob/develop/system/inc/system_version.h#L341-L350 #ifndef MEMFAULT_PARTICLE_PORT_CPP_ONLY_SYSTEM_VERSION #define MEMFAULT_PARTICLE_PORT_CPP_ONLY_SYSTEM_VERSION 1 #endif #if !MEMFAULT_PARTICLE_PORT_CPP_ONLY_SYSTEM_VERSION #include "system_version.h" #define MEMFAULT_PARTICLE_SYSTEM_VERSION SYSTEM_VERSION #else // Assume target is Device OS 3.2 #define MEMFAULT_PARTICLE_SYSTEM_VERSION 0x03020000 #endif #ifndef MEMFAULT_PARTICLE_SYSTEM_VERSION_MAJOR #define MEMFAULT_PARTICLE_SYSTEM_VERSION_MAJOR (((MEMFAULT_PARTICLE_SYSTEM_VERSION) >> 24) & 0xff) #endif #ifndef MEMFAULT_PARTICLE_SYSTEM_VERSION_MINOR #define MEMFAULT_PARTICLE_SYSTEM_VERSION_MINOR (((MEMFAULT_PARTICLE_SYSTEM_VERSION) >> 16) & 0xff) #endif // Override default names for configuration files so the default files can optionally be defined // from an end user application to create custom events #define MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE "memfault_particle_metrics_heartbeat_config.def" #define MEMFAULT_TRACE_REASON_USER_DEFS_FILE "memfault_particle_trace_reason_user_config.def" //! Returns true if current target Device OS ("System Version") is greater than the one specified //! //! Two checks: //! - First check if major version is greater than the one specified //! - Finally check if the major version matches and the minor version is greater #define MEMFAULT_PARTICLE_SYSTEM_VERSION_GT(major, minor) \ ((MEMFAULT_PARTICLE_SYSTEM_VERSION_MAJOR > (major)) || \ ((MEMFAULT_PARTICLE_SYSTEM_VERSION_MAJOR == (major)) && \ (MEMFAULT_PARTICLE_SYSTEM_VERSION_MINOR > (minor)))) #if MEMFAULT_PARTICLE_SYSTEM_VERSION_GT(3, 2) // Support for emitting a build ID shipped in Device OS 3.3 // https://github.com/particle-iot/device-os/pull/2391 #define MEMFAULT_USE_GNU_BUILD_ID 1 // Build Id info is emitted as part of the software_version so don't double encode #define MEMFAULT_EVENT_INCLUDE_BUILD_ID 0 #define MEMFAULT_COREDUMP_INCLUDE_BUILD_ID 0 #endif // Mapping to Memfault logging API can be found in memfault_platform_log_config.h #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 #ifndef MEMFAULT_PARTICLE_PORT_LOGGING_ENABLE #define MEMFAULT_PARTICLE_PORT_LOGGING_ENABLE 1 #endif // Allow events to be batched in a single message rather than sending one at a time #ifndef MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED #define MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED 1 #endif #ifndef MEMFAULT_EVENT_STORAGE_READ_BATCHING_MAX_BYTES // To send over particle transport our message is going to be base64 encoded // Gen 3 products support a 1024 (MAX_EVENT_DATA_LENGTH) data buffer. // We pick a size that accounts for chunk message overhead (8 bytes) and can be encoded in a // single MTU #define MEMFAULT_EVENT_STORAGE_READ_BATCHING_MAX_BYTES (768 - 8) #endif //! Enables support for the non-volatile event storage at compile time //! instead of dynamically at runtime //! //! Disabling this feature saves several hundred bytes of codespace and can be useful to enable for //! extremely constrained environments #define MEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED 1 // We namespace Memfault fault handlers so they do not conflict with any handlers users may have // implemented using the default CMSIS names #define MEMFAULT_EXC_HANDLER_HARD_FAULT Memfault_HardFault_Handler #define MEMFAULT_EXC_HANDLER_MEMORY_MANAGEMENT Memfault_MemoryManagement_Handler #define MEMFAULT_EXC_HANDLER_BUS_FAULT Memfault_BusFault_Handler #define MEMFAULT_EXC_HANDLER_USAGE_FAULT Memfault_UsageFault_Handler #define MEMFAULT_EXC_HANDLER_NMI Memfault_NMI_Handler #define MEMFAULT_EXC_HANDLER_WATCHDOG Memfault_Watchdog_Handler #ifndef MEMFAULT_PARTICLE_PORT_FAULT_HANDLERS_ENABLE #define MEMFAULT_PARTICLE_PORT_FAULT_HANDLERS_ENABLE 1 #endif #ifndef MEMFAULT_PARTICLE_PORT_PANIC_HANDLER_HOOK_ENABLE #if MEMFAULT_PARTICLE_SYSTEM_VERSION_GT(3, 2) // Support for hooking into the panic subsystem shipped in Device OS 3.3 // https://github.com/particle-iot/device-os/pull/2384 #define MEMFAULT_PARTICLE_PORT_PANIC_HANDLER_HOOK_ENABLE 1 #else #define MEMFAULT_PARTICLE_PORT_PANIC_HANDLER_HOOK_ENABLE 0 #endif #endif #ifndef MEMFAULT_PARTICLE_PORT_COREDUMP_TASK_COLLECTION_ENABLE #if MEMFAULT_PARTICLE_SYSTEM_VERSION_GT(3, 2) // Support for exposing RTOS TCBs shipped in Device OS 3.3 // https://github.com/particle-iot/device-os/pull/2394 #define MEMFAULT_PARTICLE_PORT_COREDUMP_TASK_COLLECTION_ENABLE 1 #else #define MEMFAULT_PARTICLE_PORT_COREDUMP_TASK_COLLECTION_ENABLE 0 #endif #endif #ifndef MEMFAULT_PARTICLE_PORT_EVENT_STORAGE_SIZE // Storage for events yet to be sent up (i.e heartbeats, reboot, & trace) // Events vary in size but typically average ~50 bytes. By default, use // a size that allows for ~1/2 day of data to be batched up #define MEMFAULT_PARTICLE_PORT_EVENT_STORAGE_SIZE 512 #endif #ifndef MEMFAULT_PARTICLE_PORT_LOG_STORAGE_ENABLE #define MEMFAULT_PARTICLE_PORT_LOG_STORAGE_ENABLE 0 #endif #if MEMFAULT_PARTICLE_PORT_LOG_STORAGE_ENABLE #define MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS 1 #endif #ifndef MEMFAULT_PARTICLE_PORT_LOG_STORAGE_SIZE #define MEMFAULT_PARTICLE_PORT_LOG_STORAGE_SIZE 1024 #endif #ifndef MEMFAULT_PARTICLE_PORT_DEBUG_API_ENABLE #define MEMFAULT_PARTICLE_PORT_DEBUG_API_ENABLE 0 #endif #ifndef MEMFAULT_PARTICLE_PORT_HEAP_METRICS_ENABLE #define MEMFAULT_PARTICLE_PORT_HEAP_METRICS_ENABLE 1 #endif #ifndef MEMFAULT_PARTICLE_PORT_CLOUD_METRICS_ENABLE #define MEMFAULT_PARTICLE_PORT_CLOUD_METRICS_ENABLE 1 #endif #ifndef MEMFAULT_PARTICLE_PORT_SOFTWARE_TYPE #define MEMFAULT_PARTICLE_PORT_SOFTWARE_TYPE "app-fw" #endif // // Coredump collection configuration // By default, the particle port makes use of the RAM backed coredump port // ports/panics/src/memfault_platform_ram_backed_coredump.c // // The memory blocks to collect in a coredump come from the particle port #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_REGIONS_CUSTOM 1 // Noinit section data gets placed in from a user application when using "retained" #define MEMFAULT_PLATFORM_COREDUMP_NOINIT_SECTION_NAME ".retained_user" #ifndef MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE #define MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE 1024 #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/particle/src/memfault_platform_log_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Port of the Memfault logging APIs to the particle subsystem #ifdef __cplusplus extern "C" { #endif #include "logging.h" #include "memfault-firmware-sdk/components/include/memfault/config.h" #if MEMFAULT_PARTICLE_PORT_LOGGING_ENABLE #define MEMFAULT_LOG_DEBUG(...) LOG_DEBUG_C(INFO, LOG_THIS_CATEGORY(), __VA_ARGS__) #define MEMFAULT_LOG_INFO(...) LOG_C(INFO, LOG_THIS_CATEGORY(), __VA_ARGS__) #define MEMFAULT_LOG_WARN(...) LOG_C(WARN, LOG_THIS_CATEGORY(), __VA_ARGS__) #define MEMFAULT_LOG_ERROR(...) LOG_C(ERROR, LOG_THIS_CATEGORY(), __VA_ARGS__) #define MEMFAULT_LOG_RAW(...) LOG_DEBUG_C(INFO, LOG_THIS_CATEGORY(), __VA_ARGS__) #else #define MEMFAULT_LOG_DEBUG(...) #define MEMFAULT_LOG_INFO(...) #define MEMFAULT_LOG_WARN(...) #define MEMFAULT_LOG_ERROR(...) #define MEMFAULT_LOG_RAW(...) #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/qp/README.md ================================================ # Quantum Leaps' QP™ Specific Port ## Overview This directory contains patches needed to integrate the Memfault SDK into Quantum Leaps' QP™. The patches have been verified against versions v6.0.0 up to v6.6.0 of QP/C and QP/C++. The instructions below assume you have an environment already setup for building and flashing a QP application. If you do not, see the [official site](https://www.state-machine.com) for instructions. ## Integrating SDK 1. Apply `qassert.h.patch` QP uses its `Q_ASSERT...` macros to ensure the QP API is used correctly and the system is behaving correctly. The `qassert.h.patch` change these macros such that failing assertions will trigger the Memfault SDK to capture a coredump. ``` $ cd $QP_ROOT/ $ patch include/qassert.h $MEMFAULT_SDK_ROOT/ports/qp/qassert.h.patch ``` 2. Apply `qf_pkg.h.patch` or `qf_pkg.hpp.patch` This patches a second set of assertion macros that QP uses internally, since version 6.3.2. _If you are using an older version than 6.3.2, you can skip this step._ In case you are using QP/C: ``` $ cd $QP_ROOT/ $ patch src/qf_pkg.h $MEMFAULT_SDK_ROOT/ports/qp/qf_pkg.h.patch ``` In case you are using QP/C++: ``` $ cd $QP_ROOT/ $ patch src/qf_pkg.hpp $MEMFAULT_SDK_ROOT/ports/qp/qf_pkg.hpp.patch ``` 3. Remove `Q_onAssert` After applying the `ports/qp/qassert.h.patch` and `ports/qp/qf_pkg.h.patch` files from the Memfault Firmware SDK, the `Q_onAssert` function will no longer be necessary. Instead, Memfault's assertion handling will be used. In case your application's code is using `Q_onAssert` directly, either replace the usages with the `Q_ASSERT...` macros or use Memfault's `MEMFAULT_ASSERT...` macros. 4. Implement device-specific dependencies. ``` void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { *info = (sMemfaultDeviceInfo) { .device_serial = "DEMOSERIAL", .software_type = "qp-main", .software_version = "1.15.0", .hardware_version = "stm32f407g-disc1", }; } ``` ``` sMfltHttpClientConfig g_mflt_http_client_config = { .api_key = "", }; ``` ## Demo An example integration and instructions can be found for the STM32F407 in `$MEMFAULT_SDK_ROOT/examples/qp/README.md` ================================================ FILE: ports/qp/qassert.h.patch ================================================ 41a42,43 > #include "memfault/panics/assert.h" > 121c123 < static char_t const Q_this_module_[] = name_; --- > // static char_t const Q_this_module_[] = name_; 136,137c138,139 < #define Q_ASSERT(test_) ((test_) \ < ? (void)0 : Q_onAssert(&Q_this_module_[0], (int_t)__LINE__)) --- > #define Q_ASSERT(test_) \ > MEMFAULT_ASSERT((test_)) 155,156c157,158 < #define Q_ASSERT_ID(id_, test_) ((test_) \ < ? (void)0 : Q_onAssert(&Q_this_module_[0], (int_t)(id_))) --- > #define Q_ASSERT_ID(id_, test_) \ > MEMFAULT_ASSERT_EXTRA((test_), (id_)) 196c198 < Q_onAssert(&Q_this_module_[0], (int_t)__LINE__) --- > MEMFAULT_ASSERT_RECORD(__LINE__) 212c214 < Q_onAssert(&Q_this_module_[0], (int_t)(id_)) --- > MEMFAULT_ASSERT_RECORD((id_)) ================================================ FILE: ports/qp/qf_pkg.h.patch ================================================ 42a43,44 > #include "memfault/panics/assert.h" > 99,104c101,102 < #define Q_ASSERT_CRIT_(id_, test_) do { \ < if ((test_)) {} else { \ < QF_CRIT_EXIT_(); \ < Q_onAssert(&Q_this_module_[0], (int_t)(id_)); \ < } \ < } while (0) --- > #define Q_ASSERT_CRIT_(id_, test_) \ > MEMFAULT_ASSERT_EXTRA((test_), (id_)) 108,111c106,107 < #define Q_ERROR_CRIT_(id_) do { \ < QF_CRIT_EXIT_(); \ < Q_onAssert(&Q_this_module_[0], (int_t)(id_)); \ < } while (0) --- > #define Q_ERROR_CRIT_(id_) \ > MEMFAULT_ASSERT_RECORD((id_)) ================================================ FILE: ports/qp/qf_pkg.hpp.patch ================================================ 41a42,43 > #include "memfault/panics/assert.h" > 94,99c96,97 < #define Q_ASSERT_CRIT_(id_, test_) do {\ < if ((test_)) {} else { \ < QF_CRIT_EXIT_(); \ < Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ < } \ < } while (false) --- > #define Q_ASSERT_CRIT_(id_, test_) \ > MEMFAULT_ASSERT_EXTRA((test_), (id_)) 103,106c101,102 < #define Q_ERROR_CRIT_(id_) do { \ < QF_CRIT_EXIT_(); \ < Q_onAssert(&Q_this_module_[0], static_cast(id_)); \ < } while (false) --- > #define Q_ERROR_CRIT_(id_) \ > MEMFAULT_ASSERT_RECORD((id_)) ================================================ FILE: ports/s32sdk/ftfc_flash_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions to use sectors of internal flash //! on the S32K1xx family for coredump capture. The implementation makes use of the //! Flash Memory Module (FTFC) peripheral. //! //! To use, update your linker script (.ld file) to expose information about the location to use. //! //! For example, using 8kB (4 sectors) of the S32K144 FlexNVM would //! look something like this: //! //! MEMORY //! { //! /* ... other regions ... */ //! m_flexnvm (RW) : ORIGIN = 0x10000000, LENGTH = 8K //! } //! __MemfaultCoreStorageStart = ORIGIN(m_flexnvm); //! __MemfaultCoreStorageEnd = ORIGIN(m_flexnvm) + LENGTH(m_flexnvm); //! //! Notes: //! - S32K1xx has program flash (PF) and data flash (DF). We recommend using the //! dataflash region (FlexNVM) to store coredump information but both can be written //! to with this port //! - __MemfaultCoreStorageStart & __MemfaultCoreStorageEnd must be aligned on sector //! boundaries #include #include "device_registers.h" #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "memfault/ports/buffered_coredump_storage.h" #ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR extern uint32_t __MemfaultCoreStorageStart[]; #define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_END_ADDR extern uint32_t __MemfaultCoreStorageEnd[]; #define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) #endif #define MEMFAULT_S32_PF_BASE 0x00000000U #define MEMFAULT_S32_PF_END (MEMFAULT_S32_PF_BASE + FEATURE_FLS_PF_BLOCK_SIZE) #define MEMFAULT_S32_DF_BASE 0x10000000U #define MEMFAULT_S32_DF_END (MEMFAULT_S32_DF_BASE + FEATURE_FLS_DF_BLOCK_SIZE) #define MEMFAULT_S32_PROG_PHRASE_LEN 8 // Error writing to flash - should never happen & likely detects a configuration error // Call the reboot handler which will halt the device if a debugger is attached and then reboot MEMFAULT_NO_OPT static void prv_coredump_writer_assert_and_reboot(int error_code) { memfault_platform_halt_if_debugging(); memfault_platform_reboot(); } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { const size_t size = MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR; *info = (sMfltCoredumpStorageInfo){ .size = size, // Two configurations: // Program Flash: 2kB or 4kB depending on configuration // FlexNVM: 2kB or 4kB depending on configuration .sector_size = FEATURE_FLS_PF_BLOCK_SECTOR_SIZE, }; } static bool prv_lookup_flash_info(uint32_t start_addr, uint32_t end_addr, uint32_t *sector_size, uint32_t *flash_addr) { if ((start_addr >= MEMFAULT_S32_PF_BASE) && (end_addr <= MEMFAULT_S32_PF_END)) { *sector_size = FEATURE_FLS_PF_BLOCK_SECTOR_SIZE; *flash_addr = start_addr; } else if ((start_addr >= MEMFAULT_S32_DF_BASE) && (end_addr <= MEMFAULT_S32_DF_END)) { // FlexNVM is memory mapped to start at 0x10000000 (MEMFAULT_S32_DF_BASE) but the flash // address base used to program that range is 0x800000U. We convert to the correct program // address below *sector_size = FEATURE_FLS_DF_BLOCK_SECTOR_SIZE; *flash_addr = start_addr + 0x800000U - MEMFAULT_S32_DF_BASE; } else { // not in a known flash range or spans across program and data flash which is unsupported return false; } return true; } #define MEMFAULT_FSTAT_ERR_MASK \ (FTFC_FSTAT_MGSTAT0(1) | FTFC_FSTAT_FPVIOL(1) | FTFC_FSTAT_ACCERR(1) | FTFC_FSTAT_RDCOLERR(1)) static void prv_flash_wait_for_ready(void) { // wait for any outstanding flash operation to complete while ((FTFC->FSTAT & FTFC_FSTAT_CCIF(1)) == 0) { } } static void prv_flash_clear_errors(void) { FTFC->FSTAT = MEMFAULT_FSTAT_ERR_MASK; } static void prv_flash_start_cmd(void) { FTFC->FSTAT |= FTFC_FSTAT_CCIF(1); } static bool prv_erase_sector(uint32_t flash_address) { prv_flash_wait_for_ready(); prv_flash_clear_errors(); FTFC->FCCOB[3] = 0x09; // "Flash Erase Command" - See 37.5.8.2 Flash commands of S32K-RM FTFC->FCCOB[2] = (flash_address >> 16) & 0xff; FTFC->FCCOB[1] = (flash_address >> 8) & 0xff; FTFC->FCCOB[0] = flash_address & 0xff; // launch the command prv_flash_start_cmd(); // wait for it to complete prv_flash_wait_for_ready(); return ((FTFC->FSTAT & MEMFAULT_FSTAT_ERR_MASK) == 0); } static void prv_erase_sector_assert_success(uint32_t flash_address) { if (!prv_erase_sector(flash_address)) { prv_coredump_writer_assert_and_reboot(FTFC->FSTAT); } } static void prv_write_double_word(uint32_t flash_address, uint8_t data[MEMFAULT_S32_PROG_PHRASE_LEN]) { prv_flash_wait_for_ready(); prv_flash_clear_errors(); FTFC->FCCOB[3] = 0x07; // "Program Phrase" Command - See 37.5.8.2 Flash commands of S32K-RM FTFC->FCCOB[2] = (flash_address >> 16) & 0xff; FTFC->FCCOB[1] = (flash_address >> 8) & 0xff; FTFC->FCCOB[0] = flash_address & 0xff; for (size_t i = 0; i < MEMFAULT_S32_PROG_PHRASE_LEN; i++) { FTFC->FCCOB[4 + i] = data[i]; } // launch the command prv_flash_start_cmd(); // wait for it to complete prv_flash_wait_for_ready(); // we are saving a coredump and the write has failed, so just reboot the device since // we were going to reboot anyway if ((FTFC->FSTAT & MEMFAULT_FSTAT_ERR_MASK) != 0) { prv_coredump_writer_assert_and_reboot(FTFC->FSTAT); } } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR + blk->write_offset; const uint32_t end_addr = start_addr + blk->write_offset + sizeof(blk->data); uint32_t flash_address; uint32_t sector_size; if (!prv_lookup_flash_info(start_addr, end_addr, §or_size, &flash_address)) { return false; } const size_t block_size = sizeof(blk->data); if ((block_size % MEMFAULT_S32_PROG_PHRASE_LEN) != 0) { // configuration error prv_coredump_writer_assert_and_reboot(block_size); } for (size_t i = 0; i < block_size; i += MEMFAULT_S32_PROG_PHRASE_LEN) { prv_write_double_word(flash_address + i, &blk->data[i]); } return true; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // The internal flash is memory mapped so we can just use memcpy! const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; memcpy(data, (void *)(start_addr + offset), read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; const uint32_t end_addr = start_addr + erase_size; uint32_t sector_size = 0; uint32_t flash_address = 0; if (!prv_lookup_flash_info(start_addr, end_addr, §or_size, &flash_address)) { return false; } if (((start_addr + offset) % erase_size) != 0 || (erase_size % sector_size) != 0) { return false; } for (size_t offset = 0; offset < erase_size; offset += sector_size) { prv_erase_sector_assert_success(flash_address + offset); } return true; } void memfault_platform_coredump_storage_clear(void) { const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; const uint32_t end_addr = MEMFAULT_COREDUMP_STORAGE_END_ADDR; uint32_t sector_size = 0; uint32_t flash_address = 0; if (!prv_lookup_flash_info(start_addr, end_addr, §or_size, &flash_address)) { return; } if (!prv_erase_sector(flash_address)) { MEMFAULT_LOG_ERROR("Failed to clear coredump storage, 0x%" PRIx32, (uint32_t)FTFC->FSTAT); } } ================================================ FILE: ports/s32sdk/lpit_software_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A Software Watchdog implementation backed by the //! S32K Low Power Interrupt Timer (LPIT) peripheral & the S32K SDK. //! //! More details about the S32K1xx series can be found in the S32K-RM: //! available from NXPs website //! //! By setting MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS to a timeout less than the hardware watchdog, we //! can guarantee a capture of a coredump when the system is in a wedged state. //! //! If your application is using low power modes, consider running the low power timers in //! with DOZE_EN. This way a hang in stop mode will also be caught. //! LPIT0->MSR |= LPIT_MCR_DOZE_EN(1) #include #include "device_registers.h" #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/ports/watchdog.h" // The Low Power Interrupt Timer Channel to use. The S32K has 4 channels (0-3). #ifndef MEMFAULT_SOFTWARE_WATCHDOG_SOURCE #define MEMFAULT_SOFTWARE_WATCHDOG_SOURCE 0 #endif // If the LPIT is driven by SIRC or FIRC we can automatically derive the clock frequency. If // SPLL_CLK or SOS_CLK are used, we need a define to be specified because we can't programmatically // resolve the external source clock frequency #ifndef MEMFAULT_SOFTWARE_WATCHDOG_SOURCE_CLOCK_FREQ #define MEMFAULT_SOFTWARE_WATCHDOG_SOURCE_CLOCK_FREQ 0 #endif #if MEMFAULT_SOFTWARE_WATCHDOG_SOURCE < 0 || MEMFAULT_SOFTWARE_WATCHDOG_SOURCE > 4 #error "MEMFAULT_SOFTWARE_WATCHDOG_SOURCE must be between 0 and 3" #endif #ifndef MEMFAULT_EXC_HANDLER_WATCHDOG #if MEMFAULT_SOFTWARE_WATCHDOG_SOURCE == 0 #error \ "Port expects following define to be set: -DMEMFAULT_EXC_HANDLER_WATCHDOG=LPIT0_Ch0_IRQHandler" #else #error \ "Port expects following define to be set: -DMEMFAULT_EXC_HANDLER_WATCHDOG=LPIT0_Ch_IRQHandler" #endif #endif static void prv_configure_irq(IRQn_Type irqn) { const uint32_t iser_reg_idx = irqn >> 5; const uint32_t iser_bit_idx = irqn % 32; S32_NVIC->ISER[iser_reg_idx] = (1UL << iser_bit_idx); // highest priority (0) so we can catch hangs in lower priority isrs S32_NVIC->IP[irqn] = 0; } static int prv_lpit_with_timeout(uint32_t timeout_ms) { const uint32_t pcc = PCC->PCCn[PCC_LPIT_INDEX]; const uint32_t pcs = (pcc & PCC_PCCn_PCS_MASK) >> PCC_PCCn_PCS_SHIFT; // refer to "Table 27-9. Peripheral module clocking" of S32K-RM for valid options const char *clock_name; uint32_t clock_div2 = 0; uint32_t src_clock_freq = 0; switch (pcs) { case 1: clock_name = "SOSCDIV2"; clock_div2 = (SCG->SOSCDIV & SCG_SOSCDIV_SOSCDIV2_MASK) >> SCG_SOSCDIV_SOSCDIV2_SHIFT; src_clock_freq = MEMFAULT_SOFTWARE_WATCHDOG_SOURCE_CLOCK_FREQ; break; case 2: clock_name = "SIRCDIV2"; clock_div2 = (SCG->SIRCDIV & SCG_SIRCDIV_SIRCDIV2_MASK) >> SCG_SIRCDIV_SIRCDIV2_SHIFT; src_clock_freq = ((SCG->SIRCCFG & SCG_SIRCCFG_RANGE_MASK) != 0) ? FEATURE_SCG_SIRC_HIGH_RANGE_FREQ : 2000000U; break; case 3: clock_name = "FIRCDIV2"; clock_div2 = (SCG->FIRCDIV & SCG_FIRCDIV_FIRCDIV2_MASK) >> SCG_FIRCDIV_FIRCDIV2_SHIFT; src_clock_freq = FEATURE_SCG_FIRC_FREQ0; break; case 6: clock_name = "SPLLDIV2"; clock_div2 = (SCG->SPLLDIV & SCG_SPLLDIV_SPLLDIV2_MASK) >> SCG_SPLLDIV_SPLLDIV2_SHIFT; src_clock_freq = MEMFAULT_SOFTWARE_WATCHDOG_SOURCE_CLOCK_FREQ; default: MEMFAULT_LOG_ERROR("Illegal clock source for LPIT. PCC=0x%" PRIx32 " PCS=0x%" PRIx32, pcc, pcs); return -1; } if (clock_div2 == 0) { MEMFAULT_LOG_ERROR("LPIT source clock (%s) not enabled", clock_name); return -2; } if (src_clock_freq == 0) { MEMFAULT_LOG_ERROR("-DMEMFAULT_SOFTWARE_WATCHDOG_SOURCE_CLOCK_FREQ= required"); return -3; } const uint32_t clock_freq_hz = src_clock_freq >> (clock_div2 - 1); MEMFAULT_LOG_DEBUG("Configuring SW Watchdog. Source=%s, Timeout=%dms, Src Clock=%dHz", clock_name, (int)timeout_ms, (int)clock_freq_hz); const uint64_t desired_tval = ((uint64_t)timeout_ms * clock_freq_hz) / 1000; if (desired_tval > UINT32_MAX) { const uint32_t max_seconds = ((uint64_t)UINT32_MAX * 1000) / clock_freq_hz; MEMFAULT_LOG_ERROR("Can't configure software watchdog of %d sec. Max=%" PRIu32 " sec", MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS, max_seconds); return -4; } if ((LPIT0->MCR & LPIT_MCR_M_CEN_MASK) == 0) { LPIT0->MCR |= LPIT_MCR_M_CEN(1); // per "48.5.2 Initialization" of S32K-RM, must wait 4 peripheral clock // cycles "to allow time for clock synchronization and reset de-assertion" // NB: this will wind up being more cycles than needed since each loop iteration is // more than one instruction but will still be very fast and satisfy the initialization req const uint32_t core_to_periph_max_ratio = 256; for (volatile uint32_t delay = 0; delay < core_to_periph_max_ratio * 4; delay++) { } } // disable the timer, we are about to configure it! LPIT0->TMR[MEMFAULT_SOFTWARE_WATCHDOG_SOURCE].TCTRL &= ~LPIT_TMR_TCTRL_T_EN(1); // Set up the countdown to match the desired watchdog timeout LPIT0->TMR[MEMFAULT_SOFTWARE_WATCHDOG_SOURCE].TVAL = (uint32_t)desired_tval; // Steps // 1. Clear any pending ISRs // 2. Enable Timer countdown interrupt // 3. Enable ISR in NVIC // 4. Use highest priority so we can catch hangs in ISRs // of lower priorities switch (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case 0: LPIT0->MSR |= LPIT_MSR_TIF0(0); LPIT0->MIER |= LPIT_MIER_TIE0(1); prv_configure_irq(LPIT0_Ch0_IRQn); break; case 1: LPIT0->MSR |= LPIT_MSR_TIF1(0); LPIT0->MIER |= LPIT_MIER_TIE1(1); prv_configure_irq(LPIT0_Ch1_IRQn); break; case 2: LPIT0->MSR |= LPIT_MSR_TIF2(0); LPIT0->MIER |= LPIT_MIER_TIE2(1); prv_configure_irq(LPIT0_Ch2_IRQn); break; case 3: LPIT0->MSR |= LPIT_MSR_TIF3(0); LPIT0->MIER |= LPIT_MIER_TIE3(1); prv_configure_irq(LPIT0_Ch3_IRQn); break; default: break; } LPIT0->TMR[MEMFAULT_SOFTWARE_WATCHDOG_SOURCE].TCTRL = LPIT_TMR_TCTRL_MODE(0) | // 32 bit count down mode LPIT_TMR_TCTRL_T_EN(1); return 0; } int memfault_software_watchdog_enable(void) { const uint32_t timeout_ms = MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS * 1000; return prv_lpit_with_timeout(timeout_ms); } int memfault_software_watchdog_disable(void) { LPIT0->TMR[MEMFAULT_SOFTWARE_WATCHDOG_SOURCE].TCTRL &= ~LPIT_TMR_TCTRL_T_EN(1); return 0; } int memfault_software_watchdog_feed(void) { // PER S32K-RM, "To abort the current timer cycle and start a timer period with a new value, the // timer channel must be disabled and enabled again" LPIT0->TMR[MEMFAULT_SOFTWARE_WATCHDOG_SOURCE].TCTRL &= ~LPIT_TMR_TCTRL_T_EN(1); LPIT0->TMR[MEMFAULT_SOFTWARE_WATCHDOG_SOURCE].TCTRL |= LPIT_TMR_TCTRL_T_EN(1); return 0; } int memfault_software_watchdog_update_timeout(uint32_t timeout_ms) { return prv_lpit_with_timeout(timeout_ms); } ================================================ FILE: ports/s32sdk/rcm_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset Control Module" (RCM)'s "System Reset Status" (SRS) Register. //! //! See "26.4.3 System Reset Status Register" of S32K-RM for more details #include "device_registers.h" #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #include "memfault/ports/reboot_reason.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); // NB: The S32 has two reset registers: // System Reset Status (SRS) which contains reset reasons for most recent boot // Sticky System Reset Status (SSRS) which contains all reasons for resets since last POR // // We'll just use the non-sticky variant for the port! const uint32_t reset_cause = RCM->SRS; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, SRS=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); // From the S32K-RM: // // The reset value of this register depends on the reset source: // POR (including LVD) - 0x82 // LVD (without POR) - 0x02 // Other reset - a bit is set if its corresponding reset source caused the reset if ((reset_cause & RCM_SRS_LVD(1)) && (reset_cause & RCM_SRS_POR(1))) { MEMFAULT_PRINT_RESET_INFO(" Low or High Voltage"); reset_reason = kMfltRebootReason_BrownOutReset; } else if (reset_cause & RCM_SRS_POR(1)) { MEMFAULT_PRINT_RESET_INFO(" POR"); reset_reason = kMfltRebootReason_PowerOnReset; } else if (reset_cause & RCM_SRS_MDM_AP(1)) { MEMFAULT_PRINT_RESET_INFO(" Debugger (AP)"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & RCM_SRS_SW(1)) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & RCM_SRS_JTAG(1)) { MEMFAULT_PRINT_RESET_INFO(" Debugger (JTAG)"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & RCM_SRS_PIN(1)) { MEMFAULT_PRINT_RESET_INFO(" Reset Pin"); reset_reason = kMfltRebootReason_ButtonReset; } else if (reset_cause & RCM_SRS_LOCKUP(1)) { MEMFAULT_PRINT_RESET_INFO(" Lockup"); reset_reason = kMfltRebootReason_Lockup; } else if (reset_cause & RCM_SRS_WDOG(1)) { MEMFAULT_PRINT_RESET_INFO(" Hardware Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & RCM_SRS_LOL(1)) { MEMFAULT_PRINT_RESET_INFO(" Loss of Lock in PLL/FLL"); reset_reason = kMfltRebootReason_ClockFailure; } else if (reset_cause & RCM_SRS_LOC(1)) { MEMFAULT_PRINT_RESET_INFO(" Loss of Clock"); reset_reason = kMfltRebootReason_ClockFailure; } *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/silabs/wiseconnect/siwx91x/siwx91x_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information on the SiWx91x family. //! //! The SiWx91x chips only support 2 distinct reset reasons: //! - Power On Reset //! - Pin Reset //! //! When neither is active, we assume a generic software reset. //! //! See "9.10.32 MCUAON_KHZ_CLK_SEL_POR_RESET_STATUS" and "9.10.46 //! MCU_FSM_WAKEUP_STATUS_REG" of the SiWx917 Family Reference Manual, which was //! the reference for this implementation. //! //! NOTE: this requires the user to load the global variable //! "memfault_siwx91x_fsm_wakeup_status_reg" with the contents of //! MCU_FSM_WAKEUP_STATUS_REG before calling SystemInit() on boot! Example: //! //! #include "si91x_device.h" //! void Reset_Handler(void) { //! // Load the global variable with the contents of MCU_FSM_WAKEUP_STATUS_REG //! extern uint32_t memfault_siwx91x_fsm_wakeup_status_reg; //! memfault_siwx91x_fsm_wakeup_status_reg = MCU_FSM->MCU_FSM_WAKEUP_STATUS_REG; //! // Call SystemInit() to initialize the system //! SystemInit(); //! // Call the main function //! main(); //! } #include #include "memfault/core/debug_log.h" #include "memfault/ports/reboot_reason.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif // These bitmasks don't exist in the WiseConnect SDK, define them here. // See "9.10.46 MCU_FSM_WAKEUP_STATUS_REG" in the siw917x-family-rm . #define MCU_FIRST_POWERUP_RESET_N_MASK (0x1 << 17) #define MCU_FIRST_POWERUP_POR_MASK (0x1 << 16) // This register value needs to be cached by the system before SystemInit() is // called: // https://github.com/SiliconLabs/wiseconnect/blob/v3.4.1/components/device/silabs/si91x/mcu/core/chip/src/system_si91x.c#L280-L281 uint32_t memfault_siwx91x_fsm_wakeup_status_reg; void memfault_reboot_reason_get(sResetBootupInfo *info) { eMemfaultRebootReason reset_reason; MEMFAULT_PRINT_RESET_INFO("Reset Reason Reg: %" PRIx32, memfault_siwx91x_fsm_wakeup_status_reg); char *reset_reason_str; (void)reset_reason_str; if (!(memfault_siwx91x_fsm_wakeup_status_reg & MCU_FIRST_POWERUP_POR_MASK)) { // If the POR bit was cleared, this is a POR reset_reason = kMfltRebootReason_PowerOnReset; reset_reason_str = "Power On Reset"; } else if (!(memfault_siwx91x_fsm_wakeup_status_reg & MCU_FIRST_POWERUP_RESET_N_MASK)) { // If the First Powerup Reset bit is cleared, this was a reset pin reset reset_reason = kMfltRebootReason_PinReset; reset_reason_str = "Pin Reset"; } else { // Otherwise, if both bits were set, this is a soft reset reset_reason_str = "Software Reset"; reset_reason = kMfltRebootReason_SoftwareReset; } MEMFAULT_PRINT_RESET_INFO("Reset Cause: %s", reset_reason_str); *info = (sResetBootupInfo){ .reset_reason_reg = memfault_siwx91x_fsm_wakeup_status_reg, .reset_reason = reset_reason, }; } ================================================ FILE: ports/stm32cube/README.md ================================================ # Memfault Port File for STM32CubeSDK These port files provide one or more of the following platform implementations for select STM32 chip families: - **Reboot Reason** - **Coredump Platform Storage** - **Watchdog Integration** The ports are designed to be used with the STM32CubeSDK CMSIS and HAL. ST maintains a list of publicly-available STM32CubeSDKs here: [https://github.com/STMicroelectronics/STM32Cube_MCU_Overall_Offer#stm32cube-mcu-packages](https://github.com/STMicroelectronics/STM32Cube_MCU_Overall_Offer/tree/73403531e57d5789650230379d407e0e9bf415fe#stm32cube-mcu-packages) See the individual port files for details. ================================================ FILE: ports/stm32cube/f4/flash_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions to use a sector of internal flash //! for coredump capture //! //! To use, update your linker script (.ld file) to expose information about the location to use. //! For example, using the last sector of the STM32F429I (2MB dual banked flash) would look //! something like this: //! //! MEMORY //! { //! /* ... other regions ... */ //! COREDUMP_STORAGE_FLASH (r) : ORIGIN = 0x81E0000, LENGTH = 128K //! } //! __MemfaultCoreStorageStart = ORIGIN(COREDUMP_STORAGE_FLASH); //! __MemfaultCoreStorageEnd = ORIGIN(COREDUMP_STORAGE_FLASH) + LENGTH(COREDUMP_STORAGE_FLASH); //! __MemfaultCoreStorageSectorNumber = 23; #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "stm32f4xx_hal.h" #ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR extern uint32_t __MemfaultCoreStorageStart[]; #define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_END_ADDR extern uint32_t __MemfaultCoreStorageEnd[]; #define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER //! The sector number to write to. This ID can be found in the "Embedded Flash memory" //! section of the reference manual for the STM32 family extern uint32_t __MemfaultCoreStorageSectorNumber[]; #define MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER \ ((uint32_t)__MemfaultCoreStorageSectorNumber) #endif // Error writing to flash - should never happen & likely detects a configuration error // Call the reboot handler which will halt the device if a debugger is attached and then reboot MEMFAULT_NO_OPT static void prv_coredump_writer_assert_and_reboot(int error_code) { memfault_platform_halt_if_debugging(); memfault_platform_reboot(); } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } void memfault_platform_coredump_storage_clear(void) { const uint32_t data = 0x0; memfault_platform_coredump_storage_write(0, &data, sizeof(data)); } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { const size_t size = MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR; *info = (sMfltCoredumpStorageInfo){ .size = size, .sector_size = size, }; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if (!prv_op_within_flash_bounds(offset, data_len)) { return false; } const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; HAL_FLASH_Unlock(); { const uint8_t *datap = data; for (size_t i = 0; i < data_len; i++) { const uint32_t res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, start_addr + i, datap[i]); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } } HAL_FLASH_Lock(); return true; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // The internal flash is memory mapped so we can just use memcpy! const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; memcpy(data, (void *)(start_addr + offset), read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } FLASH_EraseInitTypeDef s_erase_cfg = { .TypeErase = FLASH_TYPEERASE_SECTORS, // Only needs to be provided for Mass Erase .Banks = 0, .Sector = MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER, .NbSectors = 1, // See "Program/erase parallelism" in TRM. By using the lowest parallelism // the driver will work over the entire voltage range supported by the MCU // (1.8 - 3.6V). Exact time ranges for sector erases can be found in the // "Flash memory programming" section of the device datasheet .VoltageRange = FLASH_VOLTAGE_RANGE_1 }; uint32_t SectorError = 0; HAL_FLASH_Unlock(); { int res = HAL_FLASHEx_Erase(&s_erase_cfg, &SectorError); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } HAL_FLASH_Lock(); return true; } ================================================ FILE: ports/stm32cube/f4/rcc_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset and Clock Control" (RCC)'s "control & status register" (CSR) Register. //! //! More details can be found in the "RCC clock control & status register (RCC_CSR)" //! section of the STM32F4 family reference manual. #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #include "memfault/ports/reboot_reason.h" #include "stm32f4xx_ll_rcc.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); const uint32_t reset_cause = RCC->CSR; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, RCC_CSR=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET) { MEMFAULT_PRINT_RESET_INFO(" Standby Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; // clear the standby wakeup __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB); } else if (reset_cause & LL_RCC_CSR_LPWRRSTF) { MEMFAULT_PRINT_RESET_INFO(" Low Power"); reset_reason = kMfltRebootReason_DeepSleep; } else if (reset_cause & LL_RCC_CSR_SFTRSTF) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & LL_RCC_CSR_IWDGRSTF) { MEMFAULT_PRINT_RESET_INFO(" Independent Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & LL_RCC_CSR_WWDGRSTF) { MEMFAULT_PRINT_RESET_INFO(" Window Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & LL_RCC_CSR_PORRSTF) { MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); reset_reason = kMfltRebootReason_PowerOnReset; #if defined(RCC_CSR_BORRSTF) } else if (reset_cause & LL_RCC_CSR_BORRSTF) { MEMFAULT_PRINT_RESET_INFO(" Brown out"); reset_reason = kMfltRebootReason_BrownOutReset; #endif } else if (reset_cause & LL_RCC_CSR_PINRSTF) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else { MEMFAULT_PRINT_RESET_INFO(" Unknown"); } // we have read the reset information so clear the bits (since they are sticky across reboots) __HAL_RCC_CLEAR_RESET_FLAGS(); *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/stm32cube/f7/rcc_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the "Reset and //! Clock Control" (RCC)'s "Control & Status Register" (CSR). //! //! More details can be found in the "RCC clock control & status register //! (RCC_CSR)" section of the STM32F7 family reference manual. #include "memfault/components.h" #include "stm32f7xx_hal.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif //! Reset reason codes come from "5.3.21 RCC clock control & status register //! (RCC_CSR)" of the ST "RM0410" Reference Manual for (STM32F76xxx and //! STM32F77xxx). void memfault_reboot_reason_get(sResetBootupInfo *info) { const uint32_t reset_cause = RCC->CSR; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, RCC_CSR=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); // look for the first bit set in the reset cause register. // // pin reset is checked last; all other internally generated resets are wired // to the reset pin, see section 5.1.2 of the Reference Manual. if (reset_cause & RCC_CSR_SFTRSTF_Msk) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & RCC_CSR_PORRSTF_Msk) { MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); reset_reason = kMfltRebootReason_PowerOnReset; } else if (reset_cause & RCC_CSR_BORRSTF_Msk) { MEMFAULT_PRINT_RESET_INFO(" Brown out"); reset_reason = kMfltRebootReason_BrownOutReset; } else if (reset_cause & RCC_CSR_WWDGRSTF_Msk) { MEMFAULT_PRINT_RESET_INFO(" Window Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & RCC_CSR_IWDGRSTF_Msk) { MEMFAULT_PRINT_RESET_INFO(" Independent Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & RCC_CSR_LPWRRSTF_Msk) { MEMFAULT_PRINT_RESET_INFO(" Low Power"); reset_reason = kMfltRebootReason_LowPower; } else if (reset_cause & RCC_CSR_PINRSTF_Msk) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else { MEMFAULT_PRINT_RESET_INFO(" Unknown"); reset_reason = kMfltRebootReason_Unknown; } #if MEMFAULT_REBOOT_REASON_CLEAR // we have read the reset information so clear the bits (since they are sticky // across reboots) __HAL_RCC_CLEAR_RESET_FLAGS(); #endif *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/stm32cube/h5/rcc_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset and Clock Control" (RCC)'s "Reset Status Register" (RSR). //! //! More details can be found in the "RCC Reset Status Register (RCC_RSR)" //! section of the STM32H5 family reference manual. #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/ports/reboot_reason.h" #include "stm32h5xx_ll_rcc.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { const uint32_t reset_cause = RCC->RSR; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, RCC_RSR=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); if (reset_cause & RCC_RSR_PINRSTF) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else if (reset_cause & RCC_RSR_BORRSTF) { MEMFAULT_PRINT_RESET_INFO(" Brown out"); reset_reason = kMfltRebootReason_BrownOutReset; } else if (reset_cause & RCC_RSR_SFTRSTF) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & RCC_RSR_IWDGRSTF) { MEMFAULT_PRINT_RESET_INFO(" Independent Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & RCC_RSR_WWDGRSTF) { MEMFAULT_PRINT_RESET_INFO(" Window Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & RCC_RSR_LPWRRSTF) { MEMFAULT_PRINT_RESET_INFO(" Low Power"); reset_reason = kMfltRebootReason_LowPower; } #if MEMFAULT_REBOOT_REASON_CLEAR // we have read the reset information so clear the bits (since they are sticky across reboots) __HAL_RCC_CLEAR_RESET_FLAGS(); #endif *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/stm32cube/h7/lptim_software_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A Software Watchdog implementation backed by the //! STM32H7xx LPTIM peripheral & the STM32CubeH7 HAL API. //! //! The STM32H7 HAL can be cloned from https://github.com/STMicroelectronics/STM32CubeH7 //! or downloaded as a zip from https://www.st.com/en/embedded-software/stm32cubeh7.html //! //! The LPTIM is configured to use the LSI clock source so that the counter continues to run //! while the system is in low power modes (just like the hardware backed IWDG). By setting //! MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS to a timeout less than the hardware watchdog, we can //! guarantee a capture of a coredump when the system is in a wedged state. #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/ports/watchdog.h" #include "stm32h7xx_hal.h" #ifndef MEMFAULT_SOFTWARE_WATCHDOG_SOURCE #define MEMFAULT_SOFTWARE_WATCHDOG_SOURCE LPTIM2_BASE #endif #ifndef MEMFAULT_LPTIM_ENABLE_FREEZE_DBGMCU // By default, we automatically halt the software watchdog timer when // the core is halted in debug mode. This prevents a watchdog from firing // immediately upon resumption. #define MEMFAULT_LPTIM_ENABLE_FREEZE_DBGMCU 1 #endif #ifndef MEMFAULT_EXC_HANDLER_WATCHDOG #if MEMFAULT_SOFTWARE_WATCHDOG_SOURCE == LPTIM2_BASE #error \ "Port expects following define to be set: -DMEMFAULT_EXC_HANDLER_WATCHDOG=LPTIM2_IRQHandler" #else #error \ "Port expects following define to be set: -DMEMFAULT_EXC_HANDLER_WATCHDOG=LPTIM_IRQHandler" #endif #endif #if ((MEMFAULT_SOFTWARE_WATCHDOG_SOURCE != LPTIM1_BASE) && \ (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE != LPTIM2_BASE) && \ (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE != LPTIM3_BASE) && \ (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE != LPTIM4_BASE) && \ (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE != LPTIM5_BASE)) #error "MEMFAULT_SOFTWARE_WATCHDOG_SOURCE must be one of LPTIM[1-5]_BASE" #endif //! We wire the LPTIM -> LSI so the clock frequency will be 32kHz #define MEMFAULT_LPTIM_CLOCK_FREQ_HZ 32000U #define MEMFAULT_LPTIM_PRESCALER 128 #define MEMFAULT_LPTIM_MAX_COUNT 0xFFFF #define MEMFAULT_LPTIM_HZ (MEMFAULT_LPTIM_CLOCK_FREQ_HZ / MEMFAULT_LPTIM_PRESCALER) #define MEMFAULT_MS_PER_SEC 1000 #define MEMFAULT_LPTIM_MAX_TIMEOUT_SEC ((MEMFAULT_LPTIM_MAX_COUNT + 1) / MEMFAULT_LPTIM_HZ) #if MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS > MEMFAULT_LPTIM_MAX_TIMEOUT_SEC #error "MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS exceeds maximum value supported by hardware" #endif static LPTIM_HandleTypeDef s_lptim_cfg; static void prv_lptim_clock_config(RCC_PeriphCLKInitTypeDef *config) { switch ((uint32_t)MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case LPTIM1_BASE: *config = (RCC_PeriphCLKInitTypeDef){ .PeriphClockSelection = RCC_PERIPHCLK_LPTIM1, .Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSI }; break; case LPTIM2_BASE: *config = (RCC_PeriphCLKInitTypeDef){ .PeriphClockSelection = RCC_PERIPHCLK_LPTIM2, .Lptim2ClockSelection = RCC_LPTIM2CLKSOURCE_LSI }; break; case LPTIM3_BASE: *config = (RCC_PeriphCLKInitTypeDef){ .PeriphClockSelection = RCC_PERIPHCLK_LPTIM3, .Lptim345ClockSelection = RCC_LPTIM3CLKSOURCE_LSI }; break; case LPTIM4_BASE: *config = (RCC_PeriphCLKInitTypeDef){ .PeriphClockSelection = RCC_PERIPHCLK_LPTIM4, .Lptim345ClockSelection = RCC_LPTIM4CLKSOURCE_LSI }; break; case LPTIM5_BASE: *config = (RCC_PeriphCLKInitTypeDef){ .PeriphClockSelection = RCC_PERIPHCLK_LPTIM5, .Lptim345ClockSelection = RCC_LPTIM4CLKSOURCE_LSI }; break; default: *config = (RCC_PeriphCLKInitTypeDef){ 0 }; break; } } static void prv_lptim_clk_enable(void) { switch (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case LPTIM1_BASE: __HAL_RCC_LPTIM1_CLK_ENABLE(); __HAL_RCC_LPTIM1_FORCE_RESET(); __HAL_RCC_LPTIM1_RELEASE_RESET(); break; case LPTIM2_BASE: __HAL_RCC_LPTIM2_CLK_ENABLE(); __HAL_RCC_LPTIM2_FORCE_RESET(); __HAL_RCC_LPTIM2_RELEASE_RESET(); break; case LPTIM3_BASE: __HAL_RCC_LPTIM3_CLK_ENABLE(); __HAL_RCC_LPTIM3_FORCE_RESET(); __HAL_RCC_LPTIM3_RELEASE_RESET(); break; case LPTIM4_BASE: __HAL_RCC_LPTIM4_CLK_ENABLE(); __HAL_RCC_LPTIM4_FORCE_RESET(); __HAL_RCC_LPTIM4_RELEASE_RESET(); break; case LPTIM5_BASE: __HAL_RCC_LPTIM5_CLK_ENABLE(); __HAL_RCC_LPTIM5_FORCE_RESET(); __HAL_RCC_LPTIM5_RELEASE_RESET(); break; default: break; } } static void prv_lptim_clk_freeze_during_dbg(void) { #if MEMFAULT_LPTIM_ENABLE_FREEZE_DBGMCU switch (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case LPTIM1_BASE: __HAL_DBGMCU_FREEZE_LPTIM1(); break; case LPTIM2_BASE: __HAL_DBGMCU_FREEZE_LPTIM2(); break; case LPTIM3_BASE: __HAL_DBGMCU_FREEZE_LPTIM3(); break; case LPTIM4_BASE: __HAL_DBGMCU_FREEZE_LPTIM4(); break; case LPTIM5_BASE: __HAL_DBGMCU_FREEZE_LPTIM5(); break; default: break; } #endif } static void prv_lptim_irq_enable(void) { switch (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case LPTIM1_BASE: NVIC_ClearPendingIRQ(LPTIM1_IRQn); NVIC_EnableIRQ(LPTIM1_IRQn); break; case LPTIM2_BASE: NVIC_ClearPendingIRQ(LPTIM2_IRQn); NVIC_EnableIRQ(LPTIM2_IRQn); break; case LPTIM3_BASE: NVIC_ClearPendingIRQ(LPTIM3_IRQn); NVIC_EnableIRQ(LPTIM3_IRQn); break; case LPTIM4_BASE: NVIC_ClearPendingIRQ(LPTIM4_IRQn); NVIC_EnableIRQ(LPTIM4_IRQn); break; case LPTIM5_BASE: NVIC_ClearPendingIRQ(LPTIM5_IRQn); NVIC_EnableIRQ(LPTIM5_IRQn); break; default: break; } } int memfault_software_watchdog_enable(void) { // We will drive the Low Power Timer (LPTIM) from the Low-speed internal (LSI) oscillator // (~32kHz). This source will run while in low power modes (just like the IWDG hardware watchdog) const bool lsi_on = (RCC->CSR & RCC_CSR_LSION) == RCC_CSR_LSION; if (!lsi_on) { __HAL_RCC_LSI_ENABLE(); const uint32_t tickstart = HAL_GetTick(); while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == 0) { if ((HAL_GetTick() - tickstart) > LSI_TIMEOUT_VALUE) { return HAL_TIMEOUT; } } } // The LPTIM can be driven from multiple clock sources so we need to explicitly connect // it to the LSI clock that we just enabled. RCC_PeriphCLKInitTypeDef lptim_clock_config; prv_lptim_clock_config(&lptim_clock_config); HAL_StatusTypeDef rv = HAL_RCCEx_PeriphCLKConfig(&lptim_clock_config); if (rv != HAL_OK) { return rv; } // Enable the LPTIM clock and reset the peripheral prv_lptim_clk_enable(); prv_lptim_clk_freeze_during_dbg(); s_lptim_cfg = (LPTIM_HandleTypeDef){ .Instance = (LPTIM_TypeDef *)MEMFAULT_SOFTWARE_WATCHDOG_SOURCE, .Init = { .Clock = { .Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC, #if MEMFAULT_LPTIM_PRESCALER == 128 .Prescaler = LPTIM_PRESCALER_DIV128, #else #error "Unexpected Prescaler value" #endif }, .Trigger = { .Source = LPTIM_TRIGSOURCE_SOFTWARE, }, .OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH, .UpdateMode = LPTIM_UPDATE_IMMEDIATE, .CounterSource = LPTIM_COUNTERSOURCE_INTERNAL, // NB: not used in config but HAL expects valid values here .Input1Source = LPTIM_INPUT1SOURCE_GPIO, .Input2Source = LPTIM_INPUT2SOURCE_GPIO, }, .State = HAL_LPTIM_STATE_RESET, }; rv = HAL_LPTIM_Init(&s_lptim_cfg); if (rv != HAL_OK) { return rv; } prv_lptim_irq_enable(); const uint32_t desired_timeout_ms = MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS * MEMFAULT_MS_PER_SEC; memfault_software_watchdog_update_timeout(desired_timeout_ms); return 0; } int memfault_software_watchdog_feed(void) { if ((s_lptim_cfg.Instance->CR & LPTIM_CR_COUNTRST) != 0) { // A COUNTRST is already in progress, no work to do return 0; } __HAL_LPTIM_RESET_COUNTER(&s_lptim_cfg); return 0; } int memfault_software_watchdog_update_timeout(uint32_t timeout_ms) { if (timeout_ms > (MEMFAULT_LPTIM_MAX_TIMEOUT_SEC * MEMFAULT_MS_PER_SEC)) { return -1; } HAL_StatusTypeDef rv = HAL_LPTIM_Counter_Stop(&s_lptim_cfg); if (rv != HAL_OK) { return rv; } __HAL_LPTIM_CLEAR_FLAG(&s_lptim_cfg, LPTIM_IT_ARRM); __HAL_LPTIM_DISABLE_IT(&s_lptim_cfg, LPTIM_IT_ARRM); const uint32_t ticks = MEMFAULT_MIN((timeout_ms * MEMFAULT_LPTIM_HZ) / MEMFAULT_MS_PER_SEC, MEMFAULT_LPTIM_MAX_COUNT); rv = HAL_LPTIM_Counter_Start(&s_lptim_cfg, ticks); if (rv != HAL_OK) { return rv; } __HAL_LPTIM_ENABLE_IT(&s_lptim_cfg, LPTIM_IT_ARRM); return 0; } int memfault_software_watchdog_disable(void) { // Clear and disable interrupts __HAL_LPTIM_DISABLE_IT(&s_lptim_cfg, LPTIM_IT_ARRM); // Stop the counter return HAL_LPTIM_Counter_Stop(&s_lptim_cfg); } ================================================ FILE: ports/stm32cube/h7/rcc_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset and Clock Control" (RCC)'s "Reset Status Register" (RSR). //! //! More details can be found in the "RCC Reset Status Register (RCC_RSR)" //! section of the STM32H7 family reference manual. #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #include "memfault/ports/reboot_reason.h" #include "stm32h7xx_ll_rcc.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif //! Mappings come from "8.4.4 Reset source identification" of //! "STM32H742, STM32H743/753 and STM32H750" Reference Manual typedef enum ResetSource { kResetSource_PwrPor = (RCC_RSR_PORRSTF | RCC_RSR_PINRSTF | RCC_RSR_BORRSTF | RCC_RSR_D2RSTF | RCC_RSR_D1RSTF | RCC_RSR_CPURSTF), kResetSource_Pin = (RCC_RSR_PINRSTF | RCC_RSR_CPURSTF), kResetSource_PwrBor = (RCC_RSR_PINRSTF | RCC_RSR_BORRSTF | RCC_RSR_CPURSTF), kResetSource_Software = (RCC_RSR_SFTRSTF | RCC_RSR_PINRSTF | RCC_RSR_CPURSTF), kResetSource_Cpu = RCC_RSR_CPURSTF, kResetSource_Wwdg = (RCC_RSR_WWDG1RSTF | RCC_RSR_PINRSTF | RCC_RSR_CPURSTF), kResetSource_Iwdg = (RCC_RSR_IWDG1RSTF | RCC_RSR_PINRSTF | RCC_RSR_CPURSTF), kResetSource_D1Exit = RCC_RSR_D1RSTF, kResetSource_D2Exit = RCC_RSR_D2RSTF, kResetSource_LowPowerError = (RCC_RSR_LPWRRSTF | RCC_RSR_PINRSTF | RCC_RSR_CPURSTF), } eResetSource; void memfault_reboot_reason_get(sResetBootupInfo *info) { const uint32_t reset_cause = RCC->RSR; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, RCC_RSR=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); const uint32_t reset_mask_all = (RCC_RSR_IWDG1RSTF | RCC_RSR_CPURSTF | RCC_RSR_D1RSTF | RCC_RSR_D2RSTF | RCC_RSR_BORRSTF | RCC_RSR_PINRSTF | RCC_RSR_PORRSTF | RCC_RSR_SFTRSTF | RCC_RSR_WWDG1RSTF | RCC_RSR_LPWRRSTF); switch (reset_cause & reset_mask_all) { case kResetSource_PwrPor: MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); reset_reason = kMfltRebootReason_PowerOnReset; break; case kResetSource_Pin: MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; break; case kResetSource_PwrBor: MEMFAULT_PRINT_RESET_INFO(" Brown out"); reset_reason = kMfltRebootReason_BrownOutReset; break; case kResetSource_Software: MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; break; case kResetSource_Cpu: // A reset generated via RCC_AHB3RSTR MEMFAULT_PRINT_RESET_INFO(" Cpu"); reset_reason = kMfltRebootReason_SoftwareReset; break; case kResetSource_Wwdg: MEMFAULT_PRINT_RESET_INFO(" Window Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; break; case kResetSource_Iwdg: MEMFAULT_PRINT_RESET_INFO(" Independent Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; break; case kResetSource_D1Exit: MEMFAULT_PRINT_RESET_INFO(" D1 Low Power Exit"); reset_reason = kMfltRebootReason_LowPower; break; case kResetSource_D2Exit: MEMFAULT_PRINT_RESET_INFO(" D2 Low Power Exit"); reset_reason = kMfltRebootReason_LowPower; break; case kResetSource_LowPowerError: MEMFAULT_PRINT_RESET_INFO(" Illegal D1 DStandby / CStop"); reset_reason = kMfltRebootReason_UnknownError; break; default: MEMFAULT_PRINT_RESET_INFO(" Unknown"); break; } #if MEMFAULT_REBOOT_REASON_CLEAR // we have read the reset information so clear the bits (since they are sticky across reboots) __HAL_RCC_CLEAR_RESET_FLAGS(); #endif *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/stm32cube/l4/flash_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions to use a sectors of internal flash //! on the STM32L4 family for coredump capture. //! //! To use, update your linker script (.ld file) to expose information about the location to use. //! //! For example, using the last 64kB (32 sectors) of the STM32L475VGT (1MB dual banked flash) would //! look something like this: //! //! MEMORY //! { //! /* ... other regions ... */ //! COREDUMP_STORAGE_FLASH (rw) : ORIGIN = 0x80f0000, LENGTH = 64K //! } //! __MemfaultCoreStorageStart = ORIGIN(COREDUMP_STORAGE_FLASH); //! __MemfaultCoreStorageEnd = ORIGIN(COREDUMP_STORAGE_FLASH) + LENGTH(COREDUMP_STORAGE_FLASH); //! //! Notes: //! - STM32L4 internal flash is contiguous and every sector has the same sector size //! - __MemfaultCoreStorageStart & __MemfaultCoreStorageEnd must be aligned on sector //! boundaries #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "memfault/ports/buffered_coredump_storage.h" #include "memfault/ports/stm32cube/l4/flash.h" #include "stm32l4xx_hal.h" #ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR extern uint32_t __MemfaultCoreStorageStart[]; #define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_END_ADDR extern uint32_t __MemfaultCoreStorageEnd[]; #define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) #endif bool memfault_stm32cubel4_flash_clear_ecc_error(uint32_t start_addr, uint32_t end_addr, uint32_t *corrupted_address) { const bool eccd_error = __HAL_FLASH_GET_FLAG(FLASH_FLAG_ECCD); if (!eccd_error) { if (corrupted_address != NULL) { *corrupted_address = 0; // no error found } return true; } const uint32_t eccr = FLASH->ECCR; uint32_t corrupted_flash_address = FLASH_BASE + ((eccr & FLASH_ECCR_ADDR_ECC) >> FLASH_ECCR_ADDR_ECC_Pos); // if the STM32L4 is dual-banked check to see what bank has a bit corrupted #if defined(FLASH_OPTR_BFB2) if ((eccr & FLASH_ECCR_BK_ECC) != 0) { corrupted_flash_address += FLASH_BANK_SIZE; } #endif if (corrupted_address != NULL) { *corrupted_address = corrupted_flash_address; } if (corrupted_flash_address < start_addr || corrupted_flash_address > end_addr) { // There is a ECC error but it is in a range we do not want to zero out return false; } // NB: The STM32L4 allows for a double word to reprogrammed to 0x0. If the word // had an ECC error for some reason, this will also clear the ECC bits and the error // on the word uint32_t res; HAL_FLASH_Unlock(); { uint64_t clear_error = 0; res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, corrupted_flash_address, clear_error); } HAL_FLASH_Lock(); return res == HAL_OK; } void memfault_platform_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { memfault_stm32cubel4_flash_clear_ecc_error(MEMFAULT_COREDUMP_STORAGE_START_ADDR, MEMFAULT_COREDUMP_STORAGE_END_ADDR, NULL); } // Error writing to flash - should never happen & likely detects a configuration error // Call the reboot handler which will halt the device if a debugger is attached and then reboot MEMFAULT_NO_OPT static void prv_coredump_writer_assert_and_reboot(int error_code) { memfault_platform_halt_if_debugging(); memfault_platform_reboot(); } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { const size_t size = MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR; *info = (sMfltCoredumpStorageInfo){ .size = size, // The STM32L4 series has a fixed page size and a contiguous address layout .sector_size = FLASH_PAGE_SIZE, }; } // NOTE: The internal STM32L4 flash uses 8 ECC bits over 8 byte "memory words". Since the ECC // bits are also in NOR flash, this means 8 byte hunks can only be written once since changing // a value in the double word after the fact would cause the ECC to fail. // // In practice this means, writes must be issued 8 bytes at a time. The code below accomplishes // this by buffering writes and then flushing in 8 byte hunks. The Memfault coredump writer is // guaranteed to issue writes sequentially with the exception of the header which is at the // beginning of the coredump region and written last. bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; const uint32_t addr = start_addr + blk->write_offset; HAL_FLASH_Unlock(); { uint64_t data; for (size_t i = 0; i < MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE; i += sizeof(data)) { memcpy(&data, &blk->data[i], sizeof(data)); const uint32_t res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr + i, data); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } } HAL_FLASH_Lock(); *blk = (sCoredumpWorkingBuffer){ 0 }; return true; } void memfault_platform_coredump_storage_clear(void) { HAL_FLASH_Unlock(); { const uint64_t clear_val = 0x0; const uint32_t res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, MEMFAULT_COREDUMP_STORAGE_START_ADDR, clear_val); if (res != HAL_OK) { MEMFAULT_LOG_ERROR("Could not clear coredump storage, 0x%" PRIx32, res); } } HAL_FLASH_Lock(); } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // The internal flash is memory mapped so we can just use memcpy! const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; memcpy(data, (void *)(start_addr + offset), read_len); return true; } static bool prv_erase_from_bank(uint32_t Banks, uint32_t Page, uint32_t NbPages) { FLASH_EraseInitTypeDef s_erase_cfg = { .TypeErase = FLASH_TYPEERASE_PAGES, .Banks = Banks, .Page = Page, .NbPages = NbPages, }; uint32_t SectorError = 0; HAL_FLASH_Unlock(); { int res = HAL_FLASHEx_Erase(&s_erase_cfg, &SectorError); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } HAL_FLASH_Lock(); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } const size_t stm32_l4_flash_end_addr = (FLASH_END + 1); const size_t stm32_l4_flash_bank1_end_addr = (FLASH_BANK1_END + 1); // check that address is in the range of flash uint32_t erase_begin_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; const uint32_t end_addr = erase_begin_addr + erase_size; if (erase_begin_addr < FLASH_BASE || erase_begin_addr > stm32_l4_flash_end_addr) { return false; } // make sure region is aligned along page boundaries and // is whole page units in size if (((erase_begin_addr + offset) % FLASH_PAGE_SIZE) != 0 || (erase_size % FLASH_PAGE_SIZE) != 0) { return false; } if (erase_begin_addr < stm32_l4_flash_bank1_end_addr) { const uint32_t bank1_erase_end_addr = MEMFAULT_MIN(stm32_l4_flash_bank1_end_addr, end_addr); const size_t bank1_page_start = (erase_begin_addr - FLASH_BASE) / FLASH_PAGE_SIZE; const size_t bank1_num_pages = (bank1_erase_end_addr - erase_begin_addr) / FLASH_PAGE_SIZE; if (!prv_erase_from_bank(FLASH_BANK_1, bank1_page_start, bank1_num_pages)) { return false; } if (bank1_erase_end_addr == end_addr) { return true; } // there's erasing to do in bank 2 erase_begin_addr = bank1_erase_end_addr; } const size_t bank2_page_start = (erase_begin_addr - stm32_l4_flash_bank1_end_addr) / FLASH_PAGE_SIZE; const size_t bank2_num_pages = (end_addr - erase_begin_addr) / FLASH_PAGE_SIZE; return prv_erase_from_bank(FLASH_BANK_2, bank2_page_start, bank2_num_pages); } ================================================ FILE: ports/stm32cube/l4/lptim_software_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A Software Watchdog implementation backed by the //! STM32L4xx LPTIM peripheral & the STM32CubeL4 HAL API. //! //! The STM32L4 HAL can be cloned from https://github.com/STMicroelectronics/STM32CubeL4 //! or downloaded as a zip from https://www.st.com/en/embedded-software/stm32cubel4.html //! //! The LPTIM is configured to use the LSI clock source so that the counter continues to run //! while the system is in low power modes (just like the hardware backed IWDG). By setting //! MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS to a timeout less than the hardware watchdog, we can //! guarantee a capture of a coredump when the system is in a wedged state. #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/ports/watchdog.h" #include "stm32l4xx_hal.h" #ifndef MEMFAULT_SOFTWARE_WATCHDOG_SOURCE #define MEMFAULT_SOFTWARE_WATCHDOG_SOURCE LPTIM2_BASE #endif #ifndef MEMFAULT_LPTIM_ENABLE_FREEZE_DBGMCU // By default, we automatically halt the software watchdog timer when // the core is halted in debug mode. This prevents a watchdog from firing // immediately upon resumption. #define MEMFAULT_LPTIM_ENABLE_FREEZE_DBGMCU 1 #endif #ifndef MEMFAULT_EXC_HANDLER_WATCHDOG #if MEMFAULT_SOFTWARE_WATCHDOG_SOURCE == LPTIM2_BASE #error \ "Port expects following define to be set: -DMEMFAULT_EXC_HANDLER_WATCHDOG=LPTIM2_IRQHandler" #else #error \ "Port expects following define to be set: -DMEMFAULT_EXC_HANDLER_WATCHDOG=LPTIM_IRQHandler" #endif #endif #if ((MEMFAULT_SOFTWARE_WATCHDOG_SOURCE != LPTIM1_BASE) && \ (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE != LPTIM2_BASE)) #error "MEMFAULT_SOFTWARE_WATCHDOG_SOURCE must be one of LPTIM[1-2]_BASE" #endif //! We wire the LPTIM -> LSI so the clock frequency will be 32kHz #define MEMFAULT_LPTIM_CLOCK_FREQ_HZ 32000U #define MEMFAULT_LPTIM_PRESCALER 128 #define MEMFAULT_LPTIM_MAX_COUNT 0xFFFF #define MEMFAULT_LPTIM_HZ (MEMFAULT_LPTIM_CLOCK_FREQ_HZ / MEMFAULT_LPTIM_PRESCALER) #define MEMFAULT_MS_PER_SEC 1000 #define MEMFAULT_LPTIM_MAX_TIMEOUT_SEC ((MEMFAULT_LPTIM_MAX_COUNT + 1) / MEMFAULT_LPTIM_HZ) #if MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS > MEMFAULT_LPTIM_MAX_TIMEOUT_SEC #error "MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS exceeds maximum value supported by hardware" #endif static LPTIM_HandleTypeDef s_lptim_cfg; static void prv_lptim_clock_config(RCC_PeriphCLKInitTypeDef *config) { switch ((uint32_t)MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case LPTIM1_BASE: *config = (RCC_PeriphCLKInitTypeDef){ .PeriphClockSelection = RCC_PERIPHCLK_LPTIM1, .Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSI }; break; case LPTIM2_BASE: *config = (RCC_PeriphCLKInitTypeDef){ .PeriphClockSelection = RCC_PERIPHCLK_LPTIM2, .Lptim2ClockSelection = RCC_LPTIM2CLKSOURCE_LSI }; break; default: *config = (RCC_PeriphCLKInitTypeDef){ 0 }; break; } } static void prv_lptim_clk_enable(void) { switch (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case LPTIM1_BASE: __HAL_RCC_LPTIM1_CLK_ENABLE(); __HAL_RCC_LPTIM1_FORCE_RESET(); __HAL_RCC_LPTIM1_RELEASE_RESET(); break; case LPTIM2_BASE: __HAL_RCC_LPTIM2_CLK_ENABLE(); __HAL_RCC_LPTIM2_FORCE_RESET(); __HAL_RCC_LPTIM2_RELEASE_RESET(); break; default: break; } } static void prv_lptim_clk_freeze_during_dbg(void) { #if MEMFAULT_LPTIM_ENABLE_FREEZE_DBGMCU switch (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case LPTIM1_BASE: __HAL_DBGMCU_FREEZE_LPTIM1(); break; case LPTIM2_BASE: __HAL_DBGMCU_FREEZE_LPTIM2(); break; default: break; } #endif } static void prv_lptim_irq_enable(void) { switch (MEMFAULT_SOFTWARE_WATCHDOG_SOURCE) { case LPTIM1_BASE: NVIC_ClearPendingIRQ(LPTIM1_IRQn); NVIC_EnableIRQ(LPTIM1_IRQn); break; case LPTIM2_BASE: NVIC_ClearPendingIRQ(LPTIM2_IRQn); NVIC_EnableIRQ(LPTIM2_IRQn); break; default: break; } } int memfault_software_watchdog_enable(void) { // We will drive the Low Power Timer (LPTIM) from the Low-speed internal (LSI) oscillator // (~32kHz). This source will run while in low power modes (just like the IWDG hardware watchdog) const bool lsi_on = (RCC->CSR & RCC_CSR_LSION) == RCC_CSR_LSION; if (!lsi_on) { __HAL_RCC_LSI_ENABLE(); const uint32_t tickstart = HAL_GetTick(); while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == 0) { // Typically, we would use LSI_TIMEOUT_VALUE but only RCC_DBP_TIMEOUT_VALUE is exposed to // users if ((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE) { return HAL_TIMEOUT; } } } // The LPTIM can be driven from multiple clock sources so we need to explicitly connect // it to the LSI clock that we just enabled. RCC_PeriphCLKInitTypeDef lptim_clock_config; prv_lptim_clock_config(&lptim_clock_config); HAL_StatusTypeDef rv = HAL_RCCEx_PeriphCLKConfig(&lptim_clock_config); if (rv != HAL_OK) { return rv; } // Enable the LPTIM clock and reset the peripheral prv_lptim_clk_enable(); prv_lptim_clk_freeze_during_dbg(); s_lptim_cfg = (LPTIM_HandleTypeDef){ .Instance = (LPTIM_TypeDef *)MEMFAULT_SOFTWARE_WATCHDOG_SOURCE, .Init = { .Clock = { .Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC, #if MEMFAULT_LPTIM_PRESCALER == 128 .Prescaler = LPTIM_PRESCALER_DIV128, #else #error "Unexpected Prescaler value" #endif }, .Trigger = { .Source = LPTIM_TRIGSOURCE_SOFTWARE, }, .OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH, .UpdateMode = LPTIM_UPDATE_IMMEDIATE, .CounterSource = LPTIM_COUNTERSOURCE_INTERNAL, // NB: not used in config but HAL expects valid values here .Input1Source = LPTIM_INPUT1SOURCE_GPIO, .Input2Source = LPTIM_INPUT2SOURCE_GPIO, }, .State = HAL_LPTIM_STATE_RESET, }; rv = HAL_LPTIM_Init(&s_lptim_cfg); if (rv != HAL_OK) { return rv; } prv_lptim_irq_enable(); const uint32_t desired_timeout_ms = MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS * MEMFAULT_MS_PER_SEC; memfault_software_watchdog_update_timeout(desired_timeout_ms); return 0; } int memfault_software_watchdog_feed(void) { if (s_lptim_cfg.Instance == NULL) { return -2; // Not initialized, call memfault_software_watchdog_enable() first } #if defined(LPTIM_CR_COUNTRST) if ((s_lptim_cfg.Instance->CR & LPTIM_CR_COUNTRST) != 0) { // A COUNTRST is already in progress, no work to do return 0; } __HAL_LPTIM_RESET_COUNTER(&s_lptim_cfg); #else // Some L4 variants do not have the COUNTRST bit. Disabling and re-enabling the peripheral is the // next best option to reset the CNT register __HAL_LPTIM_DISABLE(&s_lptim_cfg); __HAL_LPTIM_ENABLE(&s_lptim_cfg); __HAL_LPTIM_START_CONTINUOUS(&s_lptim_cfg); #endif return 0; } int memfault_software_watchdog_update_timeout(uint32_t timeout_ms) { if (timeout_ms > (MEMFAULT_LPTIM_MAX_TIMEOUT_SEC * MEMFAULT_MS_PER_SEC)) { return -1; } if (s_lptim_cfg.Instance == NULL) { return -2; // Not initialized, call memfault_software_watchdog_enable() first } HAL_StatusTypeDef rv = HAL_LPTIM_Counter_Stop(&s_lptim_cfg); if (rv != HAL_OK) { return rv; } __HAL_LPTIM_CLEAR_FLAG(&s_lptim_cfg, LPTIM_IT_ARRM); __HAL_LPTIM_DISABLE_IT(&s_lptim_cfg, LPTIM_IT_ARRM); const uint32_t ticks = MEMFAULT_MIN((timeout_ms * MEMFAULT_LPTIM_HZ) / MEMFAULT_MS_PER_SEC, MEMFAULT_LPTIM_MAX_COUNT); rv = HAL_LPTIM_Counter_Start(&s_lptim_cfg, ticks); if (rv != HAL_OK) { return rv; } __HAL_LPTIM_ENABLE_IT(&s_lptim_cfg, LPTIM_IT_ARRM); return 0; } int memfault_software_watchdog_disable(void) { if (s_lptim_cfg.Instance == NULL) { return -2; // Not initialized, call memfault_software_watchdog_enable() first } // Clear and disable interrupts __HAL_LPTIM_DISABLE_IT(&s_lptim_cfg, LPTIM_IT_ARRM); // Stop the counter return HAL_LPTIM_Counter_Stop(&s_lptim_cfg); } ================================================ FILE: ports/stm32cube/l4/rcc_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset and Clock Control" (RCC)'s "control & status register" (CSR) Register. //! //! More details can be found in the "RCC clock control & status register (RCC_CSR)" //! section of the STM32L4 family reference manual. #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #include "memfault/ports/reboot_reason.h" #include "stm32l4xx_ll_rcc.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); const uint32_t reset_cause = RCC->CSR; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, RCC_CSR=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET) { MEMFAULT_PRINT_RESET_INFO(" Standby Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; // clear the standby wakeup __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB); } else if (reset_cause & LL_RCC_CSR_FWRSTF) { MEMFAULT_PRINT_RESET_INFO(" Firewall Reset"); reset_reason = kMfltRebootReason_UsageFault; } else if (reset_cause & LL_RCC_CSR_SFTRSTF) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & LL_RCC_CSR_IWDGRSTF) { MEMFAULT_PRINT_RESET_INFO(" Independent Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & LL_RCC_CSR_WWDGRSTF) { MEMFAULT_PRINT_RESET_INFO(" Window Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & LL_RCC_CSR_BORRSTF) { MEMFAULT_PRINT_RESET_INFO(" Brown out / POR"); // The STM32L4 doesn't have a way to disambiguate // a BOR vs POR reset_reason = kMfltRebootReason_PowerOnReset; } else if (reset_cause & LL_RCC_CSR_PINRSTF) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else if (reset_cause & LL_RCC_CSR_OBLRSTF) { MEMFAULT_PRINT_RESET_INFO(" Option Byte Loader"); // Unexpected reset type, we'll just classify as Unknown } else { MEMFAULT_PRINT_RESET_INFO(" Unknown"); } // we have read the reset information so clear the bits (since they are sticky across reboots) __HAL_RCC_CLEAR_RESET_FLAGS(); *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/stm32cube/u5/flash_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions to use a sector of //! internal flash for coredump capture //! //! To use, update your linker script (.ld file) to expose information about the //! location to use. For example, using the last 4 sectors of the STM32U535/545 //! (512K flash) would look like this: //! //! MEMORY //! { //! /* ... other regions ... */ //! COREDUMP_STORAGE_FLASH (r) : ORIGIN = 0x0807C000, LENGTH = 4 * 8K //! } //! __MemfaultCoreStorageStart = ORIGIN(COREDUMP_STORAGE_FLASH); //! __MemfaultCoreStorageEnd = ORIGIN(COREDUMP_STORAGE_FLASH) + LENGTH(COREDUMP_STORAGE_FLASH); #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "stm32u5xx_hal.h" #if defined(FLASH_TYPEPROGRAM_DOUBLEWORD) #define MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE (2 * 4) #elif defined(FLASH_TYPEPROGRAM_QUADWORD) #define MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE (4 * 4) #else #error "Unsupported FLASH_TYPEPROGRAM" #endif #include "memfault/ports/buffered_coredump_storage.h" #ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR extern uint32_t __MemfaultCoreStorageStart[]; #define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_END_ADDR extern uint32_t __MemfaultCoreStorageEnd[]; #define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_SIZE //! STM32U5 series chips have 8kB page size #define MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_SIZE (8 * 1024) #endif #define FLASH_BANK_1_START_ADDR 0x08000000 #if defined(STM32U585xx) #define FLASH_BANK_2_START_ADDR 0x08100000 #elif defined(STM32U5G9xx) #define FLASH_BANK_2_START_ADDR 0x08200000 #else #error Unsupported STM32U5 series chip, please contact https://mflt.io/contact-support #endif #ifndef MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER //! The sector number to write to. Computed based on the start address, and //! offset by the bank start offset for the chip #define MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER \ (((MEMFAULT_COREDUMP_STORAGE_START_ADDR >= FLASH_BANK_2_START_ADDR) ? \ (MEMFAULT_COREDUMP_STORAGE_START_ADDR - FLASH_BANK_2_START_ADDR) : \ (MEMFAULT_COREDUMP_STORAGE_START_ADDR - FLASH_BANK_1_START_ADDR)) / \ MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_SIZE) #endif // Error writing to flash - should never happen & likely detects a configuration error // Call the reboot handler which will halt the device if a debugger is attached and then reboot MEMFAULT_NO_OPT static void prv_coredump_writer_assert_and_reboot(int error_code) { (void)error_code; memfault_platform_halt_if_debugging(); memfault_platform_reboot(); } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } void memfault_platform_coredump_storage_clear(void) { memfault_platform_coredump_storage_erase(0, MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_SIZE); } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { const size_t size = MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR; *info = (sMfltCoredumpStorageInfo){ .size = size, .sector_size = MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_SIZE, }; } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { const uint32_t write_offset = blk->write_offset; if (!prv_op_within_flash_bounds(write_offset, sizeof(blk->data))) { return false; } HAL_FLASH_Unlock(); MEMFAULT_STATIC_ASSERT(sizeof(blk->data) == MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE, "Invalid write size"); const uint32_t res = HAL_FLASH_Program( #if defined(FLASH_TYPEPROGRAM_DOUBLEWORD) FLASH_TYPEPROGRAM_DOUBLEWORD, #elif defined(FLASH_TYPEPROGRAM_QUADWORD) FLASH_TYPEPROGRAM_QUADWORD, #endif write_offset + MEMFAULT_COREDUMP_STORAGE_START_ADDR, (uint32_t)blk->data); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } HAL_FLASH_Lock(); return true; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // The internal flash is memory mapped so we can just use memcpy! const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; memcpy(data, (void *)(start_addr + offset), read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } // Compute the flash bank the coredump storage is in const uint32_t bank = (MEMFAULT_COREDUMP_STORAGE_START_ADDR >= FLASH_BANK_2_START_ADDR) ? (FLASH_BANK_2) : (FLASH_BANK_1); FLASH_EraseInitTypeDef s_erase_cfg = { .TypeErase = FLASH_TYPEERASE_PAGES, .Banks = bank, .Page = MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER + (offset / MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_SIZE), .NbPages = erase_size / MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_SIZE, }; uint32_t SectorError = 0; HAL_FLASH_Unlock(); { int res = HAL_FLASHEx_Erase(&s_erase_cfg, &SectorError); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } HAL_FLASH_Lock(); return true; } ================================================ FILE: ports/stm32cube/u5/rcc_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset and Clock Control" (RCC)'s "control & status register" (CSR) Register. //! //! More details can be found in the "RCC control/status register (RCC_CSR)" //! section of the STM32U5 family reference manual, RM0456. #include "memfault/components.h" #include "stm32u5xx_ll_rcc.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); const uint32_t reset_cause = RCC->CSR; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, RCC_CSR=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); if (LL_RCC_IsActiveFlag_LPWRRST()) { MEMFAULT_PRINT_RESET_INFO(" Low Power"); reset_reason = kMfltRebootReason_DeepSleep; } else if (LL_RCC_IsActiveFlag_SFTRST()) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (LL_RCC_IsActiveFlag_IWDGRST()) { MEMFAULT_PRINT_RESET_INFO(" Independent Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (LL_RCC_IsActiveFlag_WWDGRST()) { MEMFAULT_PRINT_RESET_INFO(" Window Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } // Per the reference manual, RM0456 "11.8.50 RCC control/status register // (RCC_CSR)", the reset value for this register is 0x0c00_4400, which is both // PINRST and BORRST bits set. So, we need to check for this case first. else if (LL_RCC_IsActiveFlag_PINRST() && LL_RCC_IsActiveFlag_BORRST()) { MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); reset_reason = kMfltRebootReason_PowerOnReset; } else if (LL_RCC_IsActiveFlag_BORRST()) { MEMFAULT_PRINT_RESET_INFO(" Brown out"); reset_reason = kMfltRebootReason_BrownOutReset; } else if (LL_RCC_IsActiveFlag_PINRST()) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else { MEMFAULT_PRINT_RESET_INFO(" Unknown"); } // we have read the reset information so clear the bits (since they are sticky across reboots) LL_RCC_ClearResetFlags(); *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/stm32cube/wb/flash_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Reference implementation of platform dependency functions to use a sector of internal flash //! for coredump capture //! //! STM32WB55xx/STM32WB35xx Flash topology //! - Single Bank, Up to 1MB //! - Page Size: 4kB //! - double-word operations only (64 bits plus 8 ECC bits) //! //! Note: Wireless Coprocessor Binary is programmed to the top of internal flash so //! be sure to place coredump region before that. More details can be found in "Release_Notes.html" //! within STM32CubeWB SDK: //! https://github.com/STMicroelectronics/STM32CubeWB/tree/v1.10.1/Projects/STM32WB_Copro_Wireless_Binaries //! //! To use this port, update your linker script (.ld file) to reserve a region for //! coredump storage such as the COREDUMP_STORAGE_FLASH region below. //! //! MEMORY //! { //! FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 768K //! COREDUMP_STORAGE_FLASH (r) : ORIGIN = 0x080C0000, LENGTH = 128K //! BLE_FLASH (rx) : ORIGIN = 0x080E0000, LENGTH = 128K //! } //! __MemfaultCoreStorageStart = ORIGIN(COREDUMP_STORAGE_FLASH); //! __MemfaultCoreStorageEnd = ORIGIN(COREDUMP_STORAGE_FLASH) + LENGTH(COREDUMP_STORAGE_FLASH); #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "memfault/ports/buffered_coredump_storage.h" #include "memfault/ports/stm32cube/wb/flash.h" #include "stm32wbxx_hal.h" #ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR extern uint32_t __MemfaultCoreStorageStart[]; #define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) #endif #ifndef MEMFAULT_COREDUMP_STORAGE_END_ADDR extern uint32_t __MemfaultCoreStorageEnd[]; #define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) #endif bool memfault_stm32cubewb_flash_clear_ecc_errors(uint32_t start_addr, uint32_t end_addr, uint32_t *corrupted_address) { const bool eccd_error = __HAL_FLASH_GET_FLAG(FLASH_FLAG_ECCD); if (!eccd_error) { if (corrupted_address != NULL) { *corrupted_address = 0; // no error found } return true; } const uint32_t eccr = FLASH->ECCR; uint32_t corrupted_flash_address = FLASH_BASE + ((eccr & FLASH_ECCR_ADDR_ECC) >> FLASH_ECCR_ADDR_ECC_Pos); if (corrupted_address != NULL) { *corrupted_address = corrupted_flash_address; } if ((corrupted_flash_address < start_addr) || (corrupted_flash_address >= end_addr)) { // There is a ECC error but it is in a range we do not want to zero out return false; } // When a multi bit ECCD error is detected, it can be cleared by programming the corrupted // address to 0x0. uint32_t res; HAL_FLASH_Unlock(); { uint64_t clear_error = 0; res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, corrupted_flash_address, clear_error); } HAL_FLASH_Lock(); return res == HAL_OK; } void memfault_platform_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) { memfault_stm32cubewb_flash_clear_ecc_errors(MEMFAULT_COREDUMP_STORAGE_START_ADDR, MEMFAULT_COREDUMP_STORAGE_END_ADDR, NULL); } // Error writing to flash - should never happen & likely detects a configuration error. // Call the reboot handler which will halt the device if a debugger is attached and then reboot. MEMFAULT_NO_OPT static void prv_coredump_writer_assert_and_reboot(int error_code) { memfault_platform_halt_if_debugging(); memfault_platform_reboot(); } static bool prv_op_within_storage_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } void memfault_platform_coredump_storage_clear(void) { HAL_FLASH_Unlock(); { const uint64_t clear_val = 0x0; const uint32_t res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, MEMFAULT_COREDUMP_STORAGE_START_ADDR, clear_val); if (res != HAL_OK) { MEMFAULT_LOG_ERROR("Could not clear coredump storage, 0x%" PRIx32, res); } } HAL_FLASH_Lock(); } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { const size_t size = MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR; *info = (sMfltCoredumpStorageInfo){ .size = size, .sector_size = FLASH_PAGE_SIZE, }; } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; const uint32_t addr = start_addr + blk->write_offset; HAL_FLASH_Unlock(); { uint64_t data; for (size_t i = 0; i < MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE; i += sizeof(data)) { memcpy(&data, &blk->data[i], sizeof(data)); const uint32_t res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr + i, data); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } } HAL_FLASH_Lock(); *blk = (sCoredumpWorkingBuffer){ 0 }; return true; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_storage_bounds(offset, read_len)) { return false; } // The internal flash is memory mapped so we can just use memcpy! const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; memcpy(data, (void *)(start_addr + offset), read_len); return true; } static bool prv_erase_pages(uint32_t Page, uint32_t NbPages) { FLASH_EraseInitTypeDef s_erase_cfg = { .TypeErase = FLASH_TYPEERASE_PAGES, .Page = Page, .NbPages = NbPages, }; uint32_t SectorError = 0; HAL_FLASH_Unlock(); { const int res = HAL_FLASHEx_Erase(&s_erase_cfg, &SectorError); if (res != HAL_OK) { prv_coredump_writer_assert_and_reboot(res); } } HAL_FLASH_Lock(); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_storage_bounds(offset, erase_size)) { return false; } const size_t flash_end_addr = (FLASH_END_ADDR + 1); // check that address is in the range of flash const uint32_t erase_begin_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; const uint32_t end_addr = erase_begin_addr + erase_size; if (erase_begin_addr < FLASH_BASE || erase_begin_addr >= flash_end_addr) { return false; } // make sure region is aligned along page boundaries and // is whole page units in size if (((erase_begin_addr + offset) % FLASH_PAGE_SIZE) != 0 || (erase_size % FLASH_PAGE_SIZE) != 0) { return false; } const size_t page_start = (erase_begin_addr - FLASH_BASE) / FLASH_PAGE_SIZE; const size_t num_pages = (end_addr - erase_begin_addr) / FLASH_PAGE_SIZE; if (!prv_erase_pages(page_start, num_pages)) { return false; } return true; } ================================================ FILE: ports/stm32cube/wb/rcc_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Reset and Clock Control" (RCC)'s "control & status register" (CSR) Register. //! //! More details can be found in the "RCC clock control & status register (RCC_CSR)" //! section of the STM32WBxx family reference manual. #include "memfault/config.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #include "memfault/ports/reboot_reason.h" #include "stm32wbxx_ll_rcc.h" #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); const uint32_t reset_cause = RCC->CSR; eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; MEMFAULT_PRINT_RESET_INFO("Reset Reason, RCC_CSR=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); if (reset_cause & LL_RCC_CSR_SFTRSTF) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & LL_RCC_CSR_IWDGRSTF) { MEMFAULT_PRINT_RESET_INFO(" Independent Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & LL_RCC_CSR_WWDGRSTF) { MEMFAULT_PRINT_RESET_INFO(" Window Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & LL_RCC_CSR_BORRSTF) { MEMFAULT_PRINT_RESET_INFO(" Brown out / POR"); // The STM32WB doesn't have a way to disambiguate a BOR vs POR. In this scenario both BOR & PIN // RSTF bits will be set reset_reason = kMfltRebootReason_PowerOnReset; } else if (reset_cause & LL_RCC_CSR_PINRSTF) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else if (reset_cause & LL_RCC_CSR_OBLRSTF) { MEMFAULT_PRINT_RESET_INFO(" Option Byte Loader"); // Unexpected reset type, we'll just classify as Unknown } else { MEMFAULT_PRINT_RESET_INFO(" Unknown"); } #if MEMFAULT_REBOOT_REASON_CLEAR // we have read the reset information so clear the bits (since they are sticky across reboots) __HAL_RCC_CLEAR_RESET_FLAGS(); #endif *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/templates/README.md ================================================ # templates This folder contains template files to help you integrate the Memfault SDK into your project. The `memfault_xxx` files within this directory are intended to be copied to your `/third_party/memfault` directory and then customized to match your needs. ================================================ FILE: ports/templates/apache-2.0.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: ports/templates/memfault_metrics_heartbeat_config.def ================================================ //! Define custom system metrics to track. For example, // MEMFAULT_METRICS_KEY_DEFINE(main_task_stack_hwm, kMemfaultMetricType_Unsigned) // MEMFAULT_METRICS_KEY_DEFINE(ble_min_rssi, kMemfaultMetricType_Signed) // MEMFAULT_METRICS_KEY_DEFINE(mcu_sleep_time_ms, kMemfaultMetricType_Timer) ================================================ FILE: ports/templates/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Copyright 2026 Memfault, Inc //! //! Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at //! //! http://www.apache.org/licenses/LICENSE-2.0 //! //! Unless required by applicable law or agreed to in writing, software //! distributed under the License is distributed on an "AS IS" BASIS, //! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //! See the License for the specific language governing permissions and //! limitations under the License. //! //! @brief //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" #ifdef __cplusplus extern "C" { #endif // For example, decide if you want to use the Gnu Build ID. // #define MEMFAULT_USE_GNU_BUILD_ID 1 #ifdef __cplusplus } #endif ================================================ FILE: ports/templates/memfault_platform_log_config.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Copyright 2026 Memfault, Inc //! //! Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at //! //! http://www.apache.org/licenses/LICENSE-2.0 //! //! Unless required by applicable law or agreed to in writing, software //! distributed under the License is distributed on an "AS IS" BASIS, //! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //! See the License for the specific language governing permissions and //! limitations under the License. //! //! @brief //! Logging depends on how your configuration does logging. See //! https://docs.memfault.com/docs/mcu/self-serve/#logging-dependency #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/templates/memfault_platform_port.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Copyright 2026 Memfault, Inc //! //! Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at //! //! http://www.apache.org/licenses/LICENSE-2.0 //! //! Unless required by applicable law or agreed to in writing, software //! distributed under the License is distributed on an "AS IS" BASIS, //! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //! See the License for the specific language governing permissions and //! limitations under the License. //! //! Glue layer between the Memfault SDK and the underlying platform //! //! TODO: Fill in FIXMEs below for your platform #include #include "memfault/components.h" #include "memfault/ports/reboot_reason.h" void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { // !FIXME: Populate with platform device information // IMPORTANT: All strings returned in info must be constant // or static as they will be used _after_ the function returns // See https://mflt.io/version-nomenclature for more context *info = (sMemfaultDeviceInfo){ // An ID that uniquely identifies the device in your fleet // (i.e serial number, mac addr, chip id, etc) // Regular expression defining valid device serials: ^[-a-zA-Z0-9_]+$ .device_serial = "DEMOSERIAL", // A name to represent the firmware running on the MCU. // (i.e "ble-fw", "main-fw", or a codename for your project) .software_type = "app-fw", // The version of the "software_type" currently running. // "software_type" + "software_version" must uniquely represent // a single binary .software_version = "1.0.0", // The revision of hardware for the device. This value must remain // the same for a unique device. // (i.e evt, dvt, pvt, or rev1, rev2, etc) // Regular expression defining valid hardware versions: ^[-a-zA-Z0-9_\.\+]+$ .hardware_version = "dvt1", }; } //! Last function called after a coredump is saved. Should perform //! any final cleanup and then reset the device void memfault_platform_reboot(void) { // !FIXME: Perform any final system cleanup here // NOTE: For SoCs with a data cache (for example, Cortex-M7 and some // Cortex-M33-based SoCs), clean and invalidate the data cache before // resetting to ensure cached writes are committed to memory. If CMSIS // provides cache feature macros/APIs, guard this with __DCACHE_PRESENT // and SCB_CleanInvalidateDCache() availability: // // SCB_CleanInvalidateDCache(); // __DSB(); // __ISB(); // !FIXME: Reset System // NVIC_SystemReset() while (1) { } // unreachable } bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { // !FIXME: If the device tracks real time, update 'unix_timestamp_secs' with seconds since epoch // This will cause events logged by the SDK to be timestamped on the device rather than when they // arrive on the server // NOTE: do not log within this function: it is called from the logging subsystem. *time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = 0 }, }; // !FIXME: If device does not track time, return false, else return true if time is valid return false; } size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { static const struct { uint32_t start_addr; size_t length; } s_mcu_mem_regions[] = { // !FIXME: Update with list of valid memory banks to collect in a coredump { .start_addr = 0x00000000, .length = 0xFFFFFFFF }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_mcu_mem_regions); i++) { const uint32_t lower_addr = s_mcu_mem_regions[i].start_addr; const uint32_t upper_addr = lower_addr + s_mcu_mem_regions[i].length; if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); } } return 0; } MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_info") static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; MEMFAULT_WEAK void memfault_reboot_reason_get(sResetBootupInfo *info) { //! !FIXME: Read reboot reason register //! Fill in sResetBootupInfo with reboot reason and reboot register value *info = (sResetBootupInfo){ .reset_reason_reg = 0x0, .reset_reason = kMfltRebootReason_Unknown, }; } void memfault_platform_reboot_tracking_boot(void) { sResetBootupInfo reset_info = { 0 }; memfault_reboot_reason_get(&reset_info); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); // Optionally, print the stored reboot reason #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP sMfltRebootReason stored_reboot_reason; int rv = memfault_reboot_tracking_get_reboot_reason(&stored_reboot_reason); if (!rv) { MEMFAULT_LOG_INFO("Reset Cause Stored: 0x%04x", stored_reboot_reason.prior_stored_reason); } #endif // MEMFAULT_ENABLE_REBOOT_DIAG_DUMP } //! !FIXME: Remove if using FreeRTOS port. The FreeRTOS port will provide this definition MEMFAULT_WEAK bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback *callback) { //! !FIXME: Initiate a periodic timer/task/thread to call callback every period_sec (void)period_sec; (void)callback; return false; } //! !FIXME: Remove if using FreeRTOS port. The FreeRTOS port will provide this definition MEMFAULT_WEAK uint64_t memfault_platform_get_time_since_boot_ms(void) { //! !FIXME: Return the time since the device booted in milliseconds return 0; } MEMFAULT_PRINTF_LIKE_FUNC(2, 3) void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { //! !FIXME: Use this function to send logs to your application logging component, serial console, //! etc (void)level; (void)fmt; } MEMFAULT_PRINTF_LIKE_FUNC(1, 2) void memfault_platform_log_raw(const char *fmt, ...) { //! !FIXME: Use this function to send logs to your application logging component, serial console, //! etc (void)fmt; } //! !FIXME: This function _must_ be called by your main() routine prior //! to starting an RTOS or baremetal loop. int memfault_platform_boot(void) { // !FIXME: Add init to any platform specific ports here. // (This will be done in later steps in the getting started Guide) memfault_build_info_dump(); memfault_device_info_dump(); memfault_platform_reboot_tracking_boot(); // initialize the event storage buffer static uint8_t s_event_storage[1024]; const sMemfaultEventStorageImpl *evt_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); // configure trace events to store into the buffer memfault_trace_event_boot(evt_storage); // record the current reboot reason memfault_reboot_tracking_collect_reset_info(evt_storage); // configure the metrics component to store into the buffer sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(evt_storage, &boot_info); MEMFAULT_LOG_INFO("Memfault Initialized!"); return 0; } ================================================ FILE: ports/templates/memfault_trace_reason_user_config.def ================================================ //! Define custom error reasons that can be filtered & searched //! on in the Memfault UI, i.e // MEMFAULT_TRACE_REASON_DEFINE(critical_error) ================================================ FILE: ports/threadx/src/memfault_threadx_ram_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include #include "memfault/core/math.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" #include "memfault/ports/threadx_coredump.h" #include "tx_api.h" // ThreadX TCB validation ID - ASCII "THRD" (0x54485244). Defined in the // internal tx_thread.h header; we replicate it here to avoid depending on that // private header in a port file. #ifndef TX_THREAD_ID #define TX_THREAD_ID ((ULONG)0x54485244UL) #endif // Sentinel for a thread whose stack is completely exhausted (0 free bytes at // the bottom). Matches the FreeRTOS MEMFAULT_THREAD_STACK_0_BYTES_UNUSED // convention so both can be decoded the same way by the backend. #define MEMFAULT_THREADX_STACK_UNUSED_FULLY_EXHAUSTED UINT32_MAX // One entry per tracked thread. Indexed in the same order as the // created-thread list was walked when the coredump was captured. typedef struct { uint32_t tx_thread_ptr; // Address of the TX_THREAD control block uint32_t stack_bytes_unused; // 0 = could not compute; UINT32_MAX = fully exhausted } sMfltThreadXStackInfo; static sMfltThreadXStackInfo s_mflt_threadx_stack_info[MEMFAULT_THREADX_MAX_THREADS]; // ThreadX global thread-list head and count, defined in tx_thread_initialize.c extern TX_THREAD *_tx_thread_created_ptr; extern ULONG _tx_thread_created_count; // Scan the stack fill pattern from the bottom (stack_start, lowest address) // upward and return the number of consecutive bytes still holding the fill // value. Returns 0 if the sanitize check fails, or // MEMFAULT_THREADX_STACK_UNUSED_FULLY_EXHAUSTED if the stack is fully used. static uint32_t prv_scan_stack_bytes_unused(const TX_THREAD *thread) { // Validate the entire stack range before touching it const size_t sanitized = memfault_platform_sanitize_address_range(thread->tx_thread_stack_start, thread->tx_thread_stack_size); if (sanitized < thread->tx_thread_stack_size) { return 0; } // tx_thread_stack_fill_value exists only when both // TX_ENABLE_RANDOM_NUMBER_STACK_FILLING and TX_ENABLE_STACK_CHECKING are // defined. Otherwise ThreadX uses the fixed 0xEFEFEFEF pattern. #if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) const ULONG fill = thread->tx_thread_stack_fill_value; #else const ULONG fill = (ULONG)0xEFEFEFEFUL; #endif const ULONG *stack_bottom = (const ULONG *)thread->tx_thread_stack_start; const ULONG stack_size_words = thread->tx_thread_stack_size / sizeof(ULONG); ULONG unused_words = 0; for (ULONG i = 0; i < stack_size_words; i++) { if (stack_bottom[i] != fill) { break; } unused_words++; } if (unused_words == 0) { return MEMFAULT_THREADX_STACK_UNUSED_FULLY_EXHAUSTED; } return (uint32_t)(unused_words * sizeof(ULONG)); } size_t memfault_threadx_get_thread_regions(sMfltCoredumpRegion *regions, size_t num_regions) { if (regions == NULL || num_regions == 0) { return 0; } memset(s_mflt_threadx_stack_info, 0, sizeof(s_mflt_threadx_stack_info)); size_t region_idx = 0; size_t info_idx = 0; TX_THREAD *t = _tx_thread_created_ptr; for (ULONG i = 0; i < _tx_thread_created_count && t != NULL; i++) { // Guard 1: TCB pointer must fall entirely within valid memory. if (memfault_platform_sanitize_address_range(t, sizeof(TX_THREAD)) < sizeof(TX_THREAD)) { break; } // Guard 2: TCB magic must be intact - if it isn't, the rest of the TCB // (including all stack-related fields) cannot be trusted. if (t->tx_thread_id != TX_THREAD_ID) { break; } // Capture the TCB. if (region_idx < num_regions) { regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(t, sizeof(TX_THREAD)); } // Guard 3: stack region must be non-NULL and have a positive size. if (t->tx_thread_stack_start != NULL && t->tx_thread_stack_size > 0) { const size_t sanitized = memfault_platform_sanitize_address_range(t->tx_thread_stack_start, t->tx_thread_stack_size); if (sanitized > 0) { // Compute the watermark and record it in the sidecar. if (info_idx < MEMFAULT_ARRAY_SIZE(s_mflt_threadx_stack_info)) { s_mflt_threadx_stack_info[info_idx].tx_thread_ptr = (uint32_t)(uintptr_t)t; s_mflt_threadx_stack_info[info_idx].stack_bytes_unused = prv_scan_stack_bytes_unused(t); info_idx++; } // Capture the full stack region. if (region_idx < num_regions) { regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(t->tx_thread_stack_start, sanitized); } } } // Advance to next thread; detect circular-list wrap. TX_THREAD *next = t->tx_thread_created_next; if (next == _tx_thread_created_ptr) { break; } t = next; } // Append the watermark sidecar so the backend can read per-thread usage // without scanning fill patterns itself. if (info_idx > 0 && region_idx < num_regions) { regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&s_mflt_threadx_stack_info[0], sizeof(s_mflt_threadx_stack_info)); } return region_idx; } ================================================ FILE: ports/zephyr/CMakeLists.txt ================================================ if(CONFIG_MEMFAULT) 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) if(CONFIG_MEMFAULT_HTTP_ENABLE OR CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) # Use http utils for constructing both HTTP and CoAP requests list(APPEND MEMFAULT_COMPONENTS http) endif() if(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) message(WARNING "Memfault HTTP TLS is DISABLED - data will be sent unencrypted") endif() if (CONFIG_MEMFAULT_METRICS) list(APPEND MEMFAULT_COMPONENTS metrics) endif() if(CONFIG_RISCV) set(MEMFAULT_ARCH "ARCH_RISCV") elseif(CONFIG_XTENSA) set(MEMFAULT_ARCH "ARCH_XTENSA") elseif(CONFIG_ARCH_POSIX) set(MEMFAULT_ARCH "ARCH_POSIX") else() set(MEMFAULT_ARCH "ARCH_ARM_CORTEX_M") endif() include(${MEMFAULT_SDK_ROOT}/cmake/Memfault.cmake) memfault_library(${MEMFAULT_SDK_ROOT} MEMFAULT_COMPONENTS MEMFAULT_COMPONENTS_SRCS MEMFAULT_COMPONENTS_INC_FOLDERS ${MEMFAULT_ARCH}) # Add Memfault SDK sources to memfault library zephyr_interface_library_named(memfault) zephyr_library() zephyr_library_sources(${MEMFAULT_COMPONENTS_SRCS}) zephyr_include_directories( ${MEMFAULT_COMPONENTS_INC_FOLDERS} ${MEMFAULT_SDK_ROOT}/ports/include ${MEMFAULT_SDK_ROOT}/ports/zephyr/config include ) # Be sure to use the Zephyr override config file if config'd in. This file # will include the base config file. zephyr_compile_definitions() use # INTERFACE as the scope so users will pickup this define as expected. zephyr_compile_definitions(MEMFAULT_PLATFORM_CONFIG_FILE=\"memfault_zephyr_platform_config.h\") # We automatically collect some Zephyr kernel metrics from a custom Zephyr port def file. The # Zephyr port def file will pull in the user's file via include directive. zephyr_compile_definitions(MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE=\"memfault_metrics_heartbeat_zephyr_port_config.def\") # Zephyr specific port def for trace errors zephyr_compile_definitions(MEMFAULT_TRACE_REASON_USER_DEFS_FILE=\"memfault_trace_reason_zephyr_port_config.def\") # WORKAROUND: Add the macros defined in our compatibility header into library scope # This scope allows our library access to the macros but not applications or other zephyr modules # which is good for minimizing the workaround reach # 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) # Add subdirectories add_subdirectory(common) add_subdirectory(panics) add_subdirectory_ifdef(CONFIG_MEMFAULT_NRF_CONNECT_SDK ncs) # Link Memfault Library zephyr_library_link_libraries(memfault) target_link_libraries(memfault INTERFACE zephyr_interface) zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS) # The Zephyr includes moved from /include to /include/zephyr in Zephyr 3.2: # https://github.com/zephyrproject-rtos/zephyr/commit/53ef68d4598b2f9005c5da3fc0b860ca1999d350 # Add the old path for backwards compatibility. Note that Zephyr itself # supports the Kconfig option 'CONFIG_LEGACY_INCLUDE_PATH' to enable the same # compatibility behavior, but it's marked as deprecated and to be removed in # the future, so just apply the compatibility fix here. if(${KERNEL_VERSION_MAJOR}.${KERNEL_VERSION_MINOR}.${KERNEL_PATCHLEVEL} VERSION_GREATER_EQUAL "3.1.99" AND CONFIG_LEGACY_INCLUDE_PATH) zephyr_include_directories(${ZEPHYR_BASE}/include/zephyr) endif() if(CONFIG_MEMFAULT_DTS_IN_ELF) # Copy the zephyr.dts file into a section in the elf set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${CMAKE_OBJCOPY} --add-section .debug_memfault_dts=${CMAKE_BINARY_DIR}/zephyr/zephyr.dts --set-section-flags .debug_memfault_dts=noload,readonly ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.elf ) endif() if(CONFIG_MEMFAULT_COMPRESS_DEBUG_INFO) # run a post build command on the output .elf file to compress debug info set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${CMAKE_OBJCOPY} --compress-debug-sections ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.elf ) endif() if(CONFIG_MEMFAULT_BUILD_META_IN_ELF AND CONFIG_BUILD_OUTPUT_META) # run a post build command on the output .elf file to embed metadata set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${CMAKE_OBJCOPY} --add-section .memfault-zephyr-build-meta=${PROJECT_BINARY_DIR}/${KERNEL_META_NAME} ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.elf ) endif() # If enabled, apply a post-build step to generate a Memfault build id if(CONFIG_MEMFAULT_USE_MEMFAULT_BUILD_ID) get_filename_component( memfault_fw_build_id_script ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/fw_build_id.py ABSOLUTE ) # force our command to be at the front of the list so it runs before any # other operations on the .elf file, including binary/OTA image generation get_property( PROPERTY_EXTRA_POST_BUILD_COMMANDS GLOBAL PROPERTY extra_post_build_commands ) set_property( GLOBAL PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${memfault_fw_build_id_script} ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.elf # now append the rest of the commands ${PROPERTY_EXTRA_POST_BUILD_COMMANDS} ) endif() endif() ================================================ FILE: ports/zephyr/Kconfig ================================================ config MEMFAULT bool "Memfault Support" default n depends on CPU_CORTEX_M || RISCV || SOC_SERIES_ESP32 || SOC_SERIES_ESP32S3 || ARCH_POSIX select EXTRA_EXCEPTION_INFO if CPU_CORTEX_M select DEBUG_THREAD_INFO select REBOOT if ARCH_POSIX imply HWINFO # Use CPU_HAS_DCACHE to selectively enable CACHE_MANAGEMENT, which # Memfault uses to flush the data cache on fault handling prior to # system reboot. DCACHE is default=y unconditionally in Zephyr < 3.2.0, # so we cannot use the more accurate DCACHE dep here. imply CACHE_MANAGEMENT if CPU_HAS_DCACHE help Enable Zephyr Integration with the Memfault SDK At the moment a port is only provided for Cortex-M based targets # This symbol was renamed in Zephyr v4.4. To avoid triggering Kconfig lints that # check for missing symbols, provide a fallback definition here, so we can # maintain the dependency checks for PEM support on older Zephyr versions. config MBEDTLS_PEM_CERTIFICATE_FORMAT bool # This symbol was renamed from MBEDTLS_CFG_FILE to MBEDTLS_CONFIG_FILE in # newer Zephyr/NCS releases. Define the old symbol here as a hidden fallback # so compatibility checks can continue to reference it without triggering # missing-symbol Kconfig linting on versions where only the new name exists. config MBEDTLS_CFG_FILE string # This symbol was introduced in newer Zephyr/NCS releases (replacing # MBEDTLS_CFG_FILE). Define it here as a hidden fallback so the dependency # in MEMFAULT_TLS_CERTS_USE_PEM remains valid on older versions where the # symbol does not exist. config MBEDTLS_CONFIG_FILE string if MEMFAULT config MEMFAULT_CACHE_FAULT_REGS bool "Memfault Cache ARM fault registers" default y depends on CPU_CORTEX_M help Save a copy of the ARMv7's fault registers before Zephyr modifies them to provide a more accurate crash analysis in the Memfault Issues view. config MEMFAULT_USER_CONFIG_ENABLE bool "User configuration of the Memfault SDK" default y help When enabled, a user must add the three Memfault configuration files to their port: memfault_platform_config.h memfault_metrics_heartbeat_config.def memfault_trace_reason_user_config.def config MEMFAULT_USER_CONFIG_SILENT_FAIL bool "Continue with build even if user configuration of Memfault SDK is missing" default y help When enabled, __has_include is used to conditionally include the three Memfault configuration files to a port if they exist instead of failing to compile if they do not: memfault_platform_config.h memfault_metrics_heartbeat_config.def memfault_trace_reason_user_config.def config MEMFAULT_SOC_FAMILY_ESP32 bool "ESP32 support" # Note: the generic ESP32 family config changed from SOC_FAMILY_ESP32 to # SOC_FAMILY_ESPRESSIF_ESP32 in Zephyr 3.7.0. To provide backwards # compatible support without referencing unavailable Kconfig values, # generate a Memfault-specific option that works on Zephyr 3.7.0 and # also earlier releases. default y if SOC_FAMILY_ESPRESSIF_ESP32 || SOC_SERIES_ESP32 || SOC_SERIES_ESP32S2 || SOC_SERIES_ESP32S3 help Enable ESP32 support in the Memfault SDK # The "SOC_SERIES_NRF" symbols were renamed in Zephyr 4.4. Generate some # backwards-compatible symbols compatible with earlier Zephyr versions. config MEMFAULT_SOC_NRF bool default y if \ (SOC_SERIES = "nrf52" || SOC_SERIES = "nrf53" || SOC_SERIES = "nrf54h" || SOC_SERIES = "nrf54l" || SOC_SERIES = "nrf71" || SOC_SERIES = "nrf91" || SOC_SERIES = "nrf92") help Non-user configurable option indicating a compatible Nordic SOC is in use. config MEMFAULT_SOC_SERIES_NRF91 bool default y if (SOC_SERIES = "nrf91") help Non-user configurable option indicating a nRF91 series SOC is in use. # sub menu for coredump settings menu "Memfault Coredump Settings" # For backwards compatibility, this must be outside of the choice block below config MEMFAULT_COREDUMP_STORAGE_CUSTOM bool "Custom coredump storage implementation" help Disable any built-in coredump storage implementations. The user must provide their own implementation for coredump storage. choice MEMFAULT_COREDUMP_STORAGE prompt "Coredump storage backend" default MEMFAULT_RAM_BACKED_COREDUMP depends on !MEMFAULT_COREDUMP_STORAGE_CUSTOM help Choose the implementation used to store coredumps config MEMFAULT_RAM_BACKED_COREDUMP bool "RAM backed coredump storage" help Save a minimal coredump in noinit RAM. config MEMFAULT_COREDUMP_STORAGE_NRF_RRAM bool "Nordic RRAM backed coredump storage" depends on NRFX_RRAMC help Use an internal RRAM-backed coredump storage implementation for Nordic devices. Requires a partition labeled `memfault_coredump_partition`. config MEMFAULT_COREDUMP_STORAGE_NRF_MRAM bool "NRF MRAM backed coredump storage" depends on SOC_FLASH_NRF_MRAM depends on FLASH help Use an internal Nordic MRAM-backed coredump storage implementation. Requires a partition labeled `memfault_coredump_partition`. config MEMFAULT_COREDUMP_STORAGE_AMBIQ_MRAM bool "Ambiq MRAM backed coredump storage" depends on SOC_SERIES_APOLLO4X || SOC_SERIES_APOLLO5X help Use an internal Ambiq MRAM-backed coredump storage implementation. Requires a DTS partition labeled `memfault_coredump_partition`. config MEMFAULT_COREDUMP_STORAGE_STM32U5_FLASH bool "STM32U5 Flash backed coredump storage" depends on SOC_SERIES_STM32U5X help Use an internal STM32U5 Flash-backed coredump storage implementation using direct SOC flash API (bypasses Zephyr flash driver to avoid semaphore usage in ISR context). Requires a partition labeled `memfault_coredump_partition`. endchoice # MEMFAULT_COREDUMP_STORAGE if MEMFAULT_RAM_BACKED_COREDUMP config MEMFAULT_RAM_BACKED_COREDUMP_SIZE int "Memfault Ram Backed Coredump" default 8192 depends on MEMFAULT_RAM_BACKED_COREDUMP help The amount of RAM to allocate for saving coredumps config MEMFAULT_RAM_BACKED_COREDUMP_REGION string "Memfault Ram Backed Coredump Region" default ".noinit.mflt_coredump" depends on MEMFAULT_RAM_BACKED_COREDUMP help The region in memory where the coredump will be stored. This region should be marked as NOINIT in the linker script. endif # MEMFAULT_RAM_BACKED_COREDUMP config MEMFAULT_COREDUMP_COLLECT_DATA_REGIONS bool "Include data region in coredump" default y help When enabled, the data region will be captured as part of the coredump. If not enough space is available for all of the data, the coredump will be truncated. config MEMFAULT_COREDUMP_COLLECT_BSS_REGIONS bool "Include bss region in coredump" default n help When enabled, the bss region will be captured as part of the coredump. If not enough space is available for all of the data, the coredump will be truncated. config MEMFAULT_COREDUMP_COLLECT_STACK_REGIONS bool "Include active stack region in coredump" default y help When enabled, the stack region will be captured as part of the coredump. If disabled, no backtrace will be possible. config MEMFAULT_COREDUMP_COLLECT_KERNEL_REGION bool "Include kernel region in coredump" default y help When enabled, the kernel region will be captured as part of the coredump. The kernel region holds data structures required to parse Zephyr thread state from the coredump data, so thread awareness will not be possible if this region is omitted. config MEMFAULT_COREDUMP_COLLECT_TASKS_REGIONS bool "Include tasks region in coredump" default y depends on MEMFAULT_COREDUMP_COLLECT_KERNEL_REGION help When enabled, Zephyr thread stacks will be captured as part of the coredump. Disabling this will disable non-active-thread backtrace. config MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE bool "Compute thread stack usage on device" default y depends on THREAD_STACK_INFO && !STACK_GROWS_UP && INIT_STACKS help Adds thread stack usage computed during fault handling into a coredump. config MEMFAULT_COREDUMP_STACK_SIZE_TO_COLLECT int "Maximum amount of bytes to collect for task" default 256 help The larger the size, the more stack frames Memfault can recover for tasks. The default setting typically allows for 4 or more frames to be recovered. config MEMFAULT_COREDUMP_ACTIVE_TASK_STACK_SIZE_TO_COLLECT int "Maximum amount of bytes to collect for active task" default MEMFAULT_COREDUMP_STACK_SIZE_TO_COLLECT help The larger the size, the more stack frames Memfault can recover for the active task. The default setting typically allows for 4 or more frames to be recovered. config MEMFAULT_COREDUMP_FULL_THREAD_STACKS bool "Collect full thread stacks in coredumps" default n depends on THREAD_STACK_INFO help When enabled, Memfault will collect the full thread stack in coredumps. This will likely increase the size of coredumps, and there will no longer be a strict ceiling on the coredump task region sizes. This is provided as an alternative when capturing all of RAM is not viable but full thread stack analysis (watermarking, stack overflow tagging) is desired. config MEMFAULT_COREDUMP_MAX_TRACKED_TASKS int "Maximum amount of tasks to collect in a coredump" default 32 help The maximum amount of tasks Memfault will store state for in a coredump. config MEMFAULT_COREDUMP_COLLECT_MPU_STATE bool "Include MPU state in coredump" default y depends on CPU_CORTEX_M depends on ARM_MPU help When enabled, the MPU state will be captured as part of the coredump. This adds 8 + 8 * MEMFAULT_COREDUMP_MPU_REGIONS_TO_COLLECT bytes to the coredump. config MEMFAULT_COREDUMP_MPU_REGIONS_TO_COLLECT int "Maximum amount of MPU regions to collect in a coredump" default 8 range 1 16 depends on MEMFAULT_COREDUMP_COLLECT_MPU_STATE help The maximum amount of MPU regions Memfault will store state for in a coredump. config MEMFAULT_COREDUMP_NVIC_INTERRUPTS_TO_COLLECT int "Maximum amount of NVIC interrupts to collect in a coredump" default NUM_IRQS range 32 512 depends on CPU_CORTEX_M help The maximum amount of NVIC interrupts Memfault will store state for in a coredump. This is useful for debugging interrupt state at the time of the fault. Each additional 32 interrupts collected uses 44 bytes of coredump storage. endmenu # Memfault Coredump Settings config MEMFAULT_HEAP_STATS bool "Collect system heap stats with coredumps" # our wrapper conflicts with the esp32 heap_caps implementation default y if (!MEMFAULT_SOC_FAMILY_ESP32) select SYS_HEAP_RUNTIME_STATS depends on HEAP_MEM_POOL_SIZE > 0 help When enabled, system heap stats are captured as part of the coredump. The heap stats subsystem collects info on system heap allocation/deallocation (k_malloc/k_free). config MEMFAULT_SHELL bool "Memfault Shell" default y if SHELL depends on SHELL help CLI Utilities for interfacing with the Memfault SDK config MEMFAULT_SHELL_SELF_TEST bool "Execute self test via a shell command `mflt test self`" default n depends on MEMFAULT_SHELL help Adds a command, `mflt test self`, to run a suite of tests used to verify the device's integration with the Memfault SDK. By default, this will run tests to validate device info, component boot, coredump regions, coredump storage capacity, data export, and platform time. A reboot test is available by running `memfault_self_test reboot` if MEMFAULT_SHELL_SELF_TEST config MEMFAULT_SHELL_SELF_TEST_COREDUMP_STORAGE bool "Adds option to run coredump storage test [EXPERIMENTAL]" select EXPERIMENTAL help Enabling this option adds an experimental coredump storage test. The test exercises the coredump storage implementation by erasing, reading, and writing the entire coredump partition. This test may fail due to Flash cache issues on some platforms. The test requires disabling any watchdogs (hardware, software, etc) to allow the test to complete. This test may be run with `mflt test self coredump_storage`. endif # MEMFAULT_SHELL_SELF_TEST config MEMFAULT_PLATFORM_LOG_FALLBACK_TO_PRINTK bool "Fallback to printk for platform logging" default y if ! LOG help When enabled, the platform logging implementation will fallback to printk if Zephyr LOG is disabled. This is useful for testing when Zephyr LOG is not enabled (eg due to code space limitations). config MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE bool "Allows users to create custom reboot reasons outside of the predefined set" default n help Enable the ability to define custom reboot reasons. This is useful for tracking down the root cause of a reboot when it is not supported by the default set of reboot reasons. config MEMFAULT_COMPACT_LOG bool "Compact logging" default n select REQUIRES_STD_C11 select GNU_C_EXTENSIONS help When enabled, the Memfault SDK will use a compact representation for log messages written using the 'MEMFAULT_LOG_x' macros. Find more information here: https://mflt.io/compact-logs config MEMFAULT_LOGGING_ENABLE bool "Memfault Zephyr backend logging Enable [EXPERIMENTAL]" default n select LOG select LOG_OUTPUT help Adds support for routing Zephyr logging calls to the Memfault logging backend. if MEMFAULT_LOGGING_ENABLE config MEMFAULT_LOGGING_RAM_SIZE int "Set the size of the Memfault logging storage buffer" default 512 depends on MEMFAULT_LOGGING_ENABLE help The Memfault logs will be stored in a static logging storage buffer defined in memfault_logging.c. Adjust this value to ensure enough room for a reasonalbe number of log entries. config MEMFAULT_METRICS_LOGS_ENABLE bool "Metrics for log message counts" default y help When enabled, Memfault will collect metrics for log messages. This can be useful for tracking the number of logs generated by the system. endif # MEMFAULT_LOGGING_ENABLE config MEMFAULT_ROOT_CERT_STORAGE bool "Memfault TLS root certificate storage enabled" default y if MEMFAULT_HTTP_ENABLE && !MEMFAULT_HTTP_DISABLE_TLS help Enables TLS root certificate storage for Memfault data upload choice MEMFAULT_ROOT_CERT_STORAGE_CONTEXT bool "Implementation used to store Memfault Root certificates" default MEMFAULT_ROOT_CERT_STORAGE_NRF_MODEM if (MEMFAULT_SOC_SERIES_NRF91 && TRUSTED_EXECUTION_NONSECURE) || MEMFAULT_ROOT_CERT_STORAGE_NRF9160_MODEM default MEMFAULT_ROOT_CERT_STORAGE_TLS_CREDENTIAL_STORAGE if TLS_CREDENTIALS default MEMFAULT_ROOT_CERT_STORAGE_CUSTOM depends on MEMFAULT_ROOT_CERT_STORAGE config MEMFAULT_ROOT_CERT_STORAGE_NRF_MODEM bool "Uses the storage on nRF modems for storing root certificates (deprecated)" depends on MODEM_KEY_MGMT help Use MEMFAULT_ROOT_CERT_STORAGE_NRF_MODEM instead. config MEMFAULT_ROOT_CERT_STORAGE_TLS_CREDENTIAL_STORAGE bool "Uses Zephyr's tls_credential_add() API for storing root certificates" depends on TLS_CREDENTIALS depends on NET_SOCKETS_SOCKOPT_TLS config MEMFAULT_ROOT_CERT_STORAGE_CUSTOM bool "Provide a custom implementation of memfault_root_cert_storage_add()" endchoice config MEMFAULT_ROOT_CERT_STORAGE_NRF9160_MODEM bool "Uses the storage on nRF modems for storing root certificates (deprecated)" depends on MEMFAULT_ROOT_CERT_STORAGE select DEPRECATED help Use MEMFAULT_ROOT_CERT_STORAGE_NRF_MODEM instead. config MEMFAULT_ROOT_CERT_INSTALL_ON_MODEM_LIB_INIT bool "Install root certificates on modem library initialization" default y depends on MEMFAULT_ROOT_CERT_STORAGE_NRF_MODEM help When enabled, Memfault root certificates (from memfault/http/root_certs.h) will be installed automatically from an NRF_MODEM_LIB_ON_INIT callback, after modem library initialization. Disable this to manage root certificate installation manually. Note, installation must occur after modem library initialization and before the LTE connection is active. config MEMFAULT_HTTP_ENABLE bool "Support for using Memfault's HTTP APIs" default y if MEMFAULT_SOC_SERIES_NRF91 && TRUSTED_EXECUTION_NONSECURE depends on NETWORKING depends on NET_SOCKETS help Enables support for querying the Memfault API for OTA updates and posting Memfault chunks if MEMFAULT_HTTP_ENABLE config MEMFAULT_HTTP_MAX_POST_SIZE int "Set the Maximum HTTP Body Size Memfault will use in a POST request" default 0 help Some network drivers have bugs which limit the maximum amount of data that can be sent in a single HTTP request. When the value is 0 (default), no size restriction on HTTP post size will be enforced. For a non-zero value, this will be the maximum body length of a posted check. This size is allocated on the stack posting the data. config MEMFAULT_HTTP_MAX_MESSAGES_TO_SEND int "Set the maximum number of messages to send when uploading data to Memfault" default 100 range 1 10000 depends on MEMFAULT_HTTP_ENABLE help The maximum number of messages to send per execution when using the Memfault HTTP client to upload Memfault data, i.e. memfault_zephyr_port_post_data() and similar APIs. A Memfault message is a single logical data unit, eg coredump, metrics report, etc. config MEMFAULT_HTTP_PACKETIZER_BUFFER_SIZE int "Set the size of the HTTP packetizer buffer" # Use a larger default on nRF91x series, specifically to support modem # trace CDR export more efficiently. default 1024 if MEMFAULT_SOC_SERIES_NRF91 default 128 help This buffer is used when reading incremental chunk data when posting via HTTP. It does NOT set the size of the HTTP body (see MEMFAULT_HTTP_MAX_POST_SIZE), but is instead used when issuing reads to the underlying data source (e.g. coredump, logs, CDR, etc). Increasing the size of this buffer can improve upload performance when the data source is reading from a storage medium that benefits from larger sequential reads. Note that larger values will be dynamically allocated instead of stack allocated. config MEMFAULT_HTTP_PERIODIC_UPLOAD bool "Work job to periodically push new data to Memfault (deprecated)" select MEMFAULT_PERIODIC_UPLOAD help Use MEMFAULT_PERIODIC_UPLOAD instead. # Keep choice, due to choice override in NCS Memfault port choice MEMFAULT_HTTP_PERIODIC_UPLOAD_CONTEXT prompt "Work queue implementation to use for periodic posting (deprecated)" depends on MEMFAULT_HTTP_PERIODIC_UPLOAD default MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE if MEMFAULT_SOC_SERIES_NRF91 default MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_SYSTEM_WORKQUEUE config MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_SYSTEM_WORKQUEUE bool "Periodically push new data to Memfault from system workqueue (deprecated)" help Use MEMFAULT_PERIODIC_UPLOAD_USE_SYSTEM_WORKQUEUE instead. config MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE bool "Periodically push new data to Memfault using a dedicated workqueue (deprecated)" help Use MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE instead. endchoice # MEMFAULT_HTTP_PERIODIC_UPLOAD_CONTEXT config MEMFAULT_HTTP_DEDICATED_WORKQUEUE_STACK_SIZE int "Stack size for dedicated upload workqueue, in bytes (deprecated)" default 4096 if WIFI_NRF70 default 2048 depends on MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE help Use MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_STACK_SIZE instead. config MEMFAULT_HTTP_PERIODIC_UPLOAD_INTERVAL_SECS int "Interval with which the periodic upload task should run (deprecated)" default 3600 depends on MEMFAULT_HTTP_PERIODIC_UPLOAD help Use MEMFAULT_PERIODIC_UPLOAD_INTERVAL_SECS instead. config MEMFAULT_HTTP_PERIODIC_UPLOAD_LOGS bool "Upload log buffer in addition to other data (deprecated)" select MEMFAULT_PERIODIC_UPLOAD_LOGS depends on MEMFAULT_HTTP_PERIODIC_UPLOAD help Use MEMFAULT_PERIODIC_UPLOAD_LOGS instead. config MEMFAULT_HTTP_USES_MBEDTLS bool "Use mbedTLS for HTTP transport" default y if MBEDTLS && !NRF_MODEM_LIB && !MEMFAULT_HTTP_DISABLE_TLS help Configure Memfault HTTP for using mbedTLS- perform some sanity checks at compile time that it is configured correctly. Note that if MbedTLS is used for purposes other than securing the TCP/IP sockets, i.e. if TLS is offloaded to the underlying socket, this check is invalid, and should be explicitly set to 'n'. choice MEMFAULT_TLS_CERTS_FORMAT prompt "TLS certificates format" default MEMFAULT_TLS_CERTS_USE_DER help Choose the format of the TLS certificates used by Memfault config MEMFAULT_TLS_CERTS_USE_PEM bool "Use PEM format for TLS certificates" depends on (MBEDTLS_PEM_CERTIFICATE_FORMAT || (MBEDTLS_PEM_PARSE_C && MBEDTLS_PEM_WRITE_C)) || \ !(MBEDTLS && MBEDTLS_BUILTIN && \ (MBEDTLS_CFG_FILE = "config-tls-generic.h" || MBEDTLS_CONFIG_FILE = "config-tls-generic.h")) help When enabled, Memfault will use PEM format for TLS certificates. When disabled, DER format will be used. config MEMFAULT_TLS_CERTS_USE_DER bool "Use DER format for TLS certificates" depends on !NET_SOCKETS_OFFLOAD help When enabled, Memfault will use DER format for TLS certificates. When disabled, PEM format will be used. DER format may reduce runtime memory requirements (for example if using mbedTLS). endchoice # MEMFAULT_TLS_CERTS_FORMAT config MEMFAULT_PROJECT_KEY string "Memfault Project Key" help Token used to communicate with Memfault. Find it at https://mflt.io/project-key config MEMFAULT_HTTP_CLIENT_TIMEOUT_MS int "The HTTP client timeout in milliseconds" default 5000 help The Memfault HTTP client timeout in milliseconds. This is the maximum amount of time the HTTP client will wait for a response from the server. config MEMFAULT_HTTP_SOCKET_DISPATCH bool "Permit specifying a network interface for Memfault HTTP uploads" help Adds support for specifying a network interface to dispatch the socket used for Memfault data upload, via memfault_zephyr_port_http_set_interface_name(). This must be set before performing Memfault data uploads. config MEMFAULT_HTTP_DISABLE_TLS bool "Disable TLS for Memfault HTTP transport [WARNING: debugging only!]" help When enabled, Memfault HTTP client will use plain-text HTTP instead of HTTPS. All transport encryption is disabled. WARNING: This should ONLY be used for local debugging (e.g. with a local proxy server). Never enable this in production firmware. config MEMFAULT_HTTP_CHUNKS_API_HOST string "Override the Memfault chunks API host" depends on MEMFAULT_HTTP_DISABLE_TLS default "" help Override the default Memfault chunks API host. When plain HTTP mode is enabled, this should be set to a local proxy or debugging server host. Do not leave this empty when TLS is disabled: the default host (chunks.memfault.com) does not support plain HTTP and connections will fail. config MEMFAULT_HTTP_DEVICE_API_HOST string "Override the Memfault device API host" depends on MEMFAULT_HTTP_DISABLE_TLS default "" help Override the default Memfault device API host. When plain HTTP mode is enabled, this should be set to a local proxy or debugging server host. Do not leave this empty when TLS is disabled: the default host (device.memfault.com) does not support plain HTTP and connections will fail. endif # MEMFAULT_HTTP_ENABLE menuconfig MEMFAULT_PERIODIC_UPLOAD bool "Periodic Memfault check-in (telemetry upload + optionally FOTA)" default y if MEMFAULT_SOC_SERIES_NRF91 depends on MEMFAULT_HTTP_ENABLE || MEMFAULT_USE_NRF_CLOUD_COAP help Schedules a background job that periodically checks for new Memfault data and pushes it to Memfault (via HTTP or CoAP depending on transport configuration). if MEMFAULT_PERIODIC_UPLOAD choice MEMFAULT_PERIODIC_UPLOAD_CONTEXT prompt "Work queue implementation" default MEMFAULT_PERIODIC_UPLOAD_USE_SYSTEM_WORKQUEUE if MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_SYSTEM_WORKQUEUE default MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE if (MEMFAULT_SOC_SERIES_NRF91 || MEMFAULT_HTTP_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE || MEMFAULT_PERIODIC_FOTA_CHECK) default MEMFAULT_PERIODIC_UPLOAD_USE_SYSTEM_WORKQUEUE config MEMFAULT_PERIODIC_UPLOAD_USE_SYSTEM_WORKQUEUE bool "Use system workqueue" help When using this feature, make sure CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE is set appropriately for the upload stack to not cause a stack overflow. Using the system workqueue reduces RAM usage compared to a dedicated workqueue. Sending data from the system workqueue with LTE networks is known to cause issues and a dedicated workqueue maybe be required. config MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE bool "Use dedicated workqueue" help When using this feature, make sure CONFIG_MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_STACK_SIZE is set appropriately for the upload stack to not cause a stack overflow. A dedicated workqueue over the system workqueue is beneficial if uploads for the network stack in use can block for a long time (e.g. LTE) but comes at the cost of using extra RAM. endchoice # MEMFAULT_PERIODIC_UPLOAD_CONTEXT config MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_STACK_SIZE int "Stack size for dedicated upload workqueue, in bytes" default MEMFAULT_HTTP_DEDICATED_WORKQUEUE_STACK_SIZE if MEMFAULT_HTTP_PERIODIC_UPLOAD default 4096 if WIFI_NRF70 default 2048 depends on MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE config MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_PRIORITY int "Priority for dedicated upload workqueue" default 14 depends on MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE help The priority for the dedicated workqueue thread. The default is the default value for K_LOWEST_APPLICATION_THREAD_PRIO = NUM_PREEMPT_PRIORITIES - 1 (lowest allowed application priority). Users can change NUM_PREEMPT_PRIORITIES, so a build time check will ensure the configured priority is valid. config MEMFAULT_PERIODIC_UPLOAD_INTERVAL_SECS int "Periodic upload task interval in seconds" default MEMFAULT_HTTP_PERIODIC_UPLOAD_INTERVAL_SECS if MEMFAULT_HTTP_PERIODIC_UPLOAD default 3600 help The first check will run between [60, 60+MEMFAULT_PERIODIC_UPLOAD_INTERVAL_SECS] Subsequent checks will run at MEMFAULT_PERIODIC_UPLOAD_INTERVAL_SECS intervals. config MEMFAULT_PERIODIC_UPLOAD_LOGS bool "Upload log buffer in addition to other data" default n config MEMFAULT_PERIODIC_UPLOAD_ENABLED_DEFAULT bool "Periodic upload is enabled at boot" default y help Sets the state of periodic Memfault data uploads at boot. When enabled, periodic uploads will start automatically. This can be controlled at runtime using memfault_zephyr_port_periodic_upload_enable(). config MEMFAULT_PERIODIC_FOTA_CHECK bool "Periodically check for FOTA updates" default n depends on MEMFAULT_HTTP_ENABLE || MEMFAULT_NRF_CONNECT_SDK help When enabled, the device will periodically check for FOTA updates via the Memfault HTTP API. The check will be performed at the same interval as MEMFAULT_PERIODIC_UPLOAD_INTERVAL_SECS. If a new update is available, the device will download it and the subsequent install and reboot behavior will depend on the selected Zephyr FOTA backend. For example, the MCUboot backend will install the update and reboot, and the nRF Connect SDK backend will delegate to the Nordic FOTA Download library. Note: MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE should usually be enabled when this feature is enabled (and is the default in that case), to avoid stalling the system workqueue while performing FOTA checks and downloads. endif # MEMFAULT_PERIODIC_UPLOAD config MEMFAULT_ZEPHYR_FOTA_DOWNLOAD_CALLBACK_CUSTOM bool "Provide a custom implementation for FOTA download" default n help By default, the Memfault SDK implements a stub for the FOTA download callback invoked when a download completes. An end user can provide their own implementation by enabling this option and implementing memfault_zephyr_fota_download_callback(). choice MEMFAULT_ZEPHYR_FOTA_BACKEND prompt "Backend for FOTA updates" default MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS if MEMFAULT_NRF_CONNECT_SDK default MEMFAULT_ZEPHYR_FOTA_BACKEND_MCUBOOT default MEMFAULT_ZEPHYR_FOTA_BACKEND_DUMMY help Choose the backend for FOTA updates config MEMFAULT_ZEPHYR_FOTA_BACKEND_MCUBOOT bool "Use MCUboot backend implementation for FOTA updates" depends on MEMFAULT_HTTP_ENABLE && BOOTLOADER_MCUBOOT && !MEMFAULT_NRF_CONNECT_SDK help Adds support for performing OTA updates via MCUboot. If an update is available, the binary will be downloaded and installed in the secondary slot(slot1_partition). By default, the device will be rebooted into the new firmware. Users can customize the behavior after the update is downloaded by providing a custom implementation of memfault_zephyr_fota_download_callback() and setting MEMFAULT_ZEPHYR_FOTA_DOWNLOAD_CALLBACK_CUSTOM=y. For information on release management with Memfault: https://mflt.io/release-mgmt. config MEMFAULT_ZEPHYR_FOTA_BACKEND_DUMMY bool "Use a dummy backend implementation for FOTA updates" depends on MEMFAULT_HTTP_ENABLE help The dummy implementation is a no-op OTA update and is useful for testing the download of an OTA playload. It will pull the OTA payload but not install it on the device. For information on release management with Memfault: https://mflt.io/release-mgmt. endchoice # MEMFAULT_ZEPHYR_FOTA_BACKEND config MEMFAULT_MCUMGR_GRP bool "Memfault MCUmgr group" depends on MCUMGR default y select MCUMGR_SMP_CBOR_MIN_DECODING_LEVEL_2 help Enables a custom MCUmgr group for accessing Memfault device information and project key over Bluetooth. This is useful for FOTA over Bluetooth scenarios where MDS (Memfault Diagnostic GATT Service) is not used. The group provides commands to read device serial, hardware version, software type, current version, and project key - all information needed for Memfault FOTA functionality. config MEMFAULT_EVENT_STORAGE_SIZE int "Memfault Event Storage RAM Buffer Size" default 1024 help The storage area used to batch memfault events before they are flushed to persistent storage or the Memfault Cloud. choice MEMFAULT_REBOOT_REASON_GET prompt "Memfault Reboot Reason" default MEMFAULT_REBOOT_REASON_GET_CUSTOM if MEMFAULT_NRF_CONNECT_SDK && (SOC_SERIES != "nrf54h") default MEMFAULT_REBOOT_REASON_GET_HWINFO if HWINFO help Choose the implementation used to track the reboot reason config MEMFAULT_REBOOT_REASON_GET_HWINFO bool "Use the hardware information to track the reboot reason" depends on HWINFO help The Memfault SDK will use the hardware information to track the reboot reason. This is the default implementation. config MEMFAULT_REBOOT_REASON_GET_BASIC bool "Use the default implementation to track the reboot reason" help The Memfault SDK will use a built-in implementation to track the reboot reason. This is a basic implementation that does not rely on hardware information. config MEMFAULT_REBOOT_REASON_GET_CUSTOM bool "Provide a custom implementation for recovering reboot information" help User of SDK must provide their own implementation of memfault_reboot_reason_get() when enabled endchoice # MEMFAULT_REBOOT_REASON_GET config MEMFAULT_ENABLE_REBOOT_DIAG_DUMP bool "Print reboot reason information on boot" default y help Print out the MCU-derived and Memfault-stored reboot reason information on boot. Adds a few hundred bytes of code space to pretty print MCU-derived reason (e.g. "Watchdog"). config MEMFAULT_REBOOT_TRACKING_REGION string "Memfault Reboot Tracking Region" default ".rtc_noinit" if (MEMFAULT_SOC_FAMILY_ESP32) default ".noinit.mflt_reboot_info" help The region in memory where the reboot tracking information will be stored. This region should be marked as NOINIT in the linker script. config MEMFAULT_CLEAR_RESET_REG bool "Whether or not to clear bits in MCU reset reason register" default y help When disabled, the end user is responsible for clearing the reset register. (Bits generally persist across resets) menuconfig MEMFAULT_METRICS bool "Metrics collection" default y help Enable the memfault Metrics subsystem if MEMFAULT_METRICS config MEMFAULT_METRICS_EXTRA_DEFS_FILE bool "Include additional metrics definition file" default n help Enables inclusion of an additional metric definition file, 'memfault_metrics_heartbeat_extra.def'. This can be useful for third party libraries that want to include additional default metrics. config MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS int "The interval in seconds between heartbeats" default 3600 help The interval in seconds between heartbeat metrics tallying. config MEMFAULT_METRICS_TIMER_CUSTOM bool "Provide a custom implementation of Memfault Metric Deps" default n help By default the Memfault SDK makes use of Zephyrs k_timer_* to schedule the heartbeat timer and k_work_* to run the heartbeat callback work. This option can be used to disable that implementation in ports/zephyr/common/memfault_platform_metrics.c and allow for a custom implementation of memfault_platform_metrics_timer_boot() to be provided choice MEMFAULT_PLATFORM_TIME_SINCE_BOOT prompt "Select the time since boot implementation" default MEMFAULT_PLATFORM_TIME_SINCE_BOOT_K_UPTIME_GET help Select the implementation of memfault_platform_get_time_since_boot_ms() config MEMFAULT_PLATFORM_TIME_SINCE_BOOT_K_UPTIME_GET bool "Deep-sleep compensating implementation" help Implementation using k_uptime_get(), suitable for most applications. config MEMFAULT_PLATFORM_TIME_SINCE_BOOT_CUSTOM bool "User provided implementation" help User must implement memfault_platform_get_time_since_boot_ms(). endchoice config MEMFAULT_METRICS_DEFAULT_SET_ENABLE bool "Default set of metrics by the Zephyr port" default y depends on INIT_STACKS depends on THREAD_RUNTIME_STATS depends on THREAD_STACK_INFO help By default the Memfault SDK will automatically capture heartbeat metrics to help track how the Zephyr RTOS is operating. For more details about the metrics collected, see ports/zephyr/config/memfault_metrics_heartbeat_zephyr_port_config.def When disabled, no default metrics will be collected. config MEMFAULT_FS_BYTES_FREE_METRIC bool "Metric for the amount of free space on the filesystem" default y if "$(dt_path_enabled,/fstab)" depends on FILE_SYSTEM help Collects a metric for the amount of free space on the virtual filesystem. If using fstab, the first mount point found will be used. Otherwise, the user must update MEMFAULT_FS_BYTES_FREE_VFS_PATH to the mount point of the filesystem to collect metrics for. Note: may be performance intensive depending on flash technology and filesystem type. config MEMFAULT_FS_BYTES_FREE_VFS_PATH string "Path to the mount point to collect free space metrics for" default "" depends on FILE_SYSTEM && MEMFAULT_FS_BYTES_FREE_METRIC help The path to the virtual filesystem mount point to collect free space metrics for, omitting the leading '/'. If left empty and an fstab node exists in the device tree, the first available mount point from the fstab nodes will be used. config MEMFAULT_METRICS_CPU_TEMP bool "CPU temperature metrics" default y if "$(dt_alias_enabled,memfault_cpu_temp)" default y if "$(dt_alias_enabled,die-temp)" default y if "$(dt_alias_enabled,die-temp0)" default y if "$(dt_nodelabel_enabled,temp)" default y if DT_HAS_NORDIC_NRF_TEMP_ENABLED depends on SENSOR help Memfault CPU temperature metric component. Requires a compatible temperature sensor to be enabled in the device tree. config MEMFAULT_NRF54_CPU_TEMP_SENSOR_HELPER bool default y if DT_HAS_NORDIC_NRF_TEMP_ENABLED && BOARD_NRF54L15DK imply SENSOR help Helper symbol to enable SENSOR by default when using the nRF54L15-DK, for demonstration purposes. config MEMFAULT_METRICS_SYNC_SUCCESS bool "Sync success metrics" default y help Memfault user-defined sync success metric component. More information at https://mflt.io/core-metrics . config MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS bool "Memfault sync success metrics" default y depends on MEMFAULT_HTTP_ENABLE || MEMFAULT_USE_NRF_CLOUD_COAP help Collects Memfault metrics for the number of successful and failed syncs to the Memfault cloud. More information at https://mflt.io/core-metrics . config MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME bool "Memfault connectivity time metrics" default y help Memfault connectivity time metric component. More information at https://mflt.io/core-metrics . config MEMFAULT_METRICS_BATTERY_ENABLE bool "Battery metrics" default n help Memfault battery metric component. More information at https://mflt.io/core-metrics . config MEMFAULT_BATTERY_METRICS_BOOT_ON_SYSTEM_INIT bool "Auto initialize battery metrics on system init" default y depends on MEMFAULT_METRICS_BATTERY_ENABLE help Automatically initialize the battery metric subsystem on bootup config MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE int "Scale factor for battery state of charge percentage metric" range 1 1000 default 1000 depends on MEMFAULT_METRICS_BATTERY_ENABLE help The scale factor to use for the battery state of charge percentage metric. The value recorded is multiplied by this factor before being stored in the metric. For example, if the battery state of charge is 75.00%, and the scale factor is 100, the value stored in the metric will be 7500, and is scaled back to 75.00% when Memfault's cloud processes the data. config MEMFAULT_METRICS_TCP_IP bool "TCP/IP metrics" default y depends on NET_STATISTICS && NET_STATISTICS_USER_API help Collects Memfault metrics for TCP/IP statistics. config MEMFAULT_METRICS_WIFI bool "Wi-Fi metrics" default y depends on NET_MGMT_EVENT && WIFI config MEMFAULT_METRICS_BLUETOOTH bool "Bluetooth metrics" default y depends on BT && BT_CONN # BT_REMOTE_INFO enables the connection callback when remote info is # available imply BT_REMOTE_INFO # BT_REMOTE_VERSION enables the bt_conn_get_remote_info() API imply BT_REMOTE_VERSION # BT_CTLR_CONN_RSSI enables reading the connection RSSI imply BT_CTLR_CONN_RSSI config MEMFAULT_METRICS_BLUETOOTH_DELAYED_MS int "Delay before reading additional Bluetooth connection metrics" default 1000 depends on MEMFAULT_METRICS_BLUETOOTH help The delay, in milliseconds, before reading additional Bluetooth connection metrics after a connection is established. Some values like RSSI + remote info are invalid on initial connection callback. config MEMFAULT_METRICS_MEMORY_USAGE bool "Memory usage metrics" default y depends on SYS_HEAP_RUNTIME_STATS help Collects Memfault metrics for memory usage stats. config MEMFAULT_METRICS_THREADS bool "Per-thread metrics" default y depends on THREAD_STACK_INFO depends on INIT_STACKS help Collects Memfault metrics for thread stats. See memfault/ports/zephyr/thread_metrics.h on how to register threads for metrics. config MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR int "Scale factor for memory metrics" default 100 depends on MEMFAULT_METRICS_THREADS help The scale factor to use for memory metrics. The value recorded is multiplied by this factor before being stored in the metric. For example, if stack usage is 50.00%, and the scale factor is 100, the value stored in the metric will be 5000, and is scaled back to 50.00% when Memfault's cloud processes the data. config MEMFAULT_METRICS_THREADS_DEFAULTS bool "Default thread metrics" default y depends on MEMFAULT_METRICS_THREADS help Enables default set of threads to track metrics on (idle, sysworkq). If providing a custom set of threads to track, disable this option. config MEMFAULT_METRICS_BOOT_TIME bool "Boot time metrics" default y help Collects boot time metrics for the device endif # MEMFAULT_METRICS config MEMFAULT_SOFTWARE_WATCHDOG_CUSTOM bool "Provide a custom implementation of Software Watchdog" default n help By default Memfault implements a software watchdog implementation. Users can call memfault_software_watchdog_enable() on boot and memfault_software_watchdog_feed() when they feed the hardware watchdog. When the software watchdog expires, Memfault will capture a coredump and then the system will be rebooted. This option can be used to disable the implementation in ports/zephyr/common/memfault_software_watchdog.c config MEMFAULT_SOFTWARE_WATCHDOG_TIMEOUT_SECS int "The time, in seconds, to configure the software watchdog expiration for" default 15 config MEMFAULT_RECORD_REBOOT_ON_SYSTEM_INIT bool "Record reboot reason during system initialization" default y help Record the reboot reason event during system initialization. Disable this if device info is not valid until later in startup, then call `memfault_zephyr_collect_reset_info()` from `#include "memfault/ports/zephyr/core.h"` to store the reboot reason event. config MEMFAULT_INIT_PRIORITY int "The priority of Memfault initialization on system start" default KERNEL_INIT_PRIORITY_DEFAULT help The SYS_INIT relative priority for Memfault initialization. config MEMFAULT_INIT_LEVEL_POST_KERNEL bool "Use POST_KERNEL init level for Memfault initialization" default n help Set the Memfault initialization SYS_INIT priority level to "POST_KERNEL". Default is "APPLICATION". config MEMFAULT_CATCH_ZEPHYR_ASSERT bool "Support backtrace through Zephyr __ASSERT() calls" default y help When enabled, Memfault will support proper backtraces when a Zephyr __ASSERT() trips. If disabled (i.e. user needs a custom implementation of assert_post_action()), backtraces will not be correctly captured from __ASSERT() macro call sites, unless assert_post_action() contains a MEMFAULT_ASSERT() invocation. config MEMFAULT_PLATFORM_EXTRA_CONFIG_FILE bool "Additional platform config file" default n help Enables inclusion of an additional platform config file, 'memfault_platform_config_extra.h'. This can be useful for third party libraries that want to include additional default platform configs. config MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT bool "Initialize platform-specific connectivity metrics on system init" default n help When enabled, memfault_metrics_boot() will call a platform-implemented memfault_platform_metrics_connectivity_boot() function. If not already implemented for the user's platform, the user must supply their own implementation. This function is typically used to register event handlers to that will mark the connectivity state changes when the device connects and disconnects. Marking these state changes is required to calculate the connectivity time metrics. config MEMFAULT_FAULT_HANDLER_RETURN bool "Return from fault handler" default n help When enabled, the Memfault fault handler will call `z_fatal_error` instead of rebooting the system. Useful if the system needs to hook into `k_sys_fatal_error_handler` at the end of system fault handling. config MEMFAULT_FAULT_HANDLER_LOG_PANIC bool "Trigger LOG_PANIC() on fault handling" default n help When enabled, the Memfault fault handler will call `LOG_PANIC()` to flush logs during fault handling. Note that this can cause unexpected behavior in some log backends that are not safe to call from fault context. choice MEMFAULT_SYSTEM_TIME_SOURCE prompt "Memfault system time source" default MEMFAULT_SYSTEM_TIME_SOURCE_DATETIME if DATE_TIME default MEMFAULT_SYSTEM_TIME_SOURCE_RTC if RTC default MEMFAULT_SYSTEM_TIME_SOURCE_SYS_CLOCK if SYS_CLOCK_EXISTS default MEMFAULT_SYSTEM_TIME_SOURCE_CUSTOM help Choose the source of the system time used by the Memfault SDK config MEMFAULT_SYSTEM_TIME_SOURCE_CUSTOM bool "Custom/disabled" help When enabled, the user can provide an implementation of memfault_platform_time_get_current() in their port. This also can be selected to disable the built-in implementations. config MEMFAULT_SYSTEM_TIME_SOURCE_DATETIME bool "Datetime" depends on DATE_TIME help Provides a built-in implementation of memfault_platform_time_get_current() using the Zephyr datetime subsystem. config MEMFAULT_SYSTEM_TIME_SOURCE_RTC bool "RTC" depends on RTC help Provides a built-in implementation of memfault_platform_time_get_current() using the Zephyr RTC subsystem. config MEMFAULT_SYSTEM_TIME_SOURCE_SYS_CLOCK bool "Zephyr sys_clock_gettime()" depends on SYS_CLOCK_EXISTS help Provides a built-in implementation of memfault_platform_time_get_current() using the Zephyr sys_clock_gettime() API with SYS_CLOCK_REALTIME. Requires Zephyr 4.2.0 or later. On older Zephyr versions, enable CONFIG_POSIX_TIMERS to use the POSIX clock_gettime() fallback. endchoice # MEMFAULT_SYSTEM_TIME_SOURCE config MEMFAULT_DATETIME_TIMESTAMP_EVENT_CALLBACK bool "Install a callback on boot for date_time events" default y depends on MEMFAULT_SYSTEM_TIME_SOURCE_DATETIME help When enabled, a callback will be installed on boot to update the Memfault SDK's timestamp when the Zephyr datetime subsystem is updated. Note that the date_time library only has 1 event callback, so if it's needed elsewhere, this option should be disabled and the memfault_zephyr_date_time_evt_handler() function should be called from the application's date_time event handler. config MEMFAULT_CDR_ENABLE bool "Memfault CDR component" default y help Permits usage of the Memfault CDR feature. See https://mflt.io/custom-data-recordings for more information. config MEMFAULT_CRC16_BUILTIN bool "Use built-in CRC16 implementation" default y help When enabled, the Memfault SDK will use a built-in CRC16 implementation. When disabled, the platform must provide its own compatible implementation of memfault_crc16_compute(). choice MEMFAULT_BUILD_ID_TYPE prompt "Build ID type" default MEMFAULT_USE_MEMFAULT_BUILD_ID if (MEMFAULT_SOC_FAMILY_ESP32 && !BOOTLOADER_MCUBOOT) default MEMFAULT_USE_GNU_BUILD_ID help Choose the type of build ID to include in the image config MEMFAULT_USE_MEMFAULT_BUILD_ID bool "Use a Memfault build ID in an image" # Memfault build ID should be used if the ESP32 bootloader is used. # esptool.py generates the flashable image, which ignores the # .note.gnu.build-id section despite being marked as ALLOC. help Use a Memfault build ID in an image menuconfig MEMFAULT_USE_GNU_BUILD_ID bool "Use a GNU build ID in an image" depends on !(MEMFAULT_SOC_FAMILY_ESP32 && !BOOTLOADER_MCUBOOT) if MEMFAULT_USE_GNU_BUILD_ID choice MEMFAULT_GNU_BUILD_ID_SOURCE prompt "GNU Build ID source" default MEMFAULT_GNU_BUILD_ID_SOURCE_CUSTOM if ARCH_POSIX default MEMFAULT_GNU_BUILD_ID_SOURCE_BUILTIN help Choose if a GNU build ID is generated by built-in SDK features or by other means config MEMFAULT_GNU_BUILD_ID_SOURCE_BUILTIN bool "Built-in via Memfault SDK" help Use the Memfault SDK to generate and include a GNU build ID config MEMFAULT_GNU_BUILD_ID_SOURCE_CUSTOM bool "Custom source" help Use a custom source to generate and include a GNU build ID Set the name of the GNU build ID symbol with MEMFAULT_GNU_BUILD_ID_SYMBOL in your application's memfault_platform_config.h endchoice # MEMFAULT_GNU_BUILD_ID_SOURCE endif # MEMFAULT_USE_GNU_BUILD_ID endchoice # MEMFAULT_BUILD_ID_TYPE menu "Memfault Built-In Device Info Options" config MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_VERSION string "Builtin device info .software_version" default "$(APP_VERSION_TWEAK_STRING)" if (("$(VERSION_TWEAK)") && ("$(VERSION_TWEAK)" != "0")) default "$(APPVERSION)" if "$(APPVERSION)" != "" default "0.0.0" config MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_TYPE string "Builtin device info .software_type" default "app" config MEMFAULT_BUILTIN_DEVICE_INFO_HARDWARE_VERSION string "Builtin device info .hardware_version" default BOARD endmenu # MEMFAULT_BUILTIN_DEVICE_INFO config MEMFAULT_COMPRESS_DEBUG_INFO bool "Compress debug info" default y help Compress debug info to reduce the size of the output elf file. config MEMFAULT_BUILD_META_IN_ELF bool "Include build meta in ELF" default y imply BUILD_OUTPUT_META help Include build meta in the ELF file. config MEMFAULT_DTS_IN_ELF bool "Include DTS in the ELF file" default y help When enabled, the DTS will be included as a non-loadable section in the ELF file. module = MEMFAULT module-str = Memfault source "subsys/logging/Kconfig.template.log_config" config MEMFAULT_PLATFORM_HAS_LOG_CONFIG bool default y help Indicates that the Memfault port provides a memfault_platform_log_config.h header to control which log levels are compiled in. Disable this to compile in all MEMFAULT_LOG_x levels (enabling runtime filtering), or to provide your own custom log config header. rsource "ncs/Kconfig" endif # MEMFAULT ================================================ FILE: ports/zephyr/README.md ================================================ # Zephyr Specific Port ## Overview This directory contains an implementation of the dependency functions needed to integrate the Memfault SDK into the Zephyr RTOS. 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). ## Directory Structure 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 ``` ## Integrating SDK Follow the guide here for how to integrate the Memfault SDK into a Zephyr project: ## Demo An example integration and instructions can be found for a QEMU-emulated board in `$MEMFAULT_SDK_ROOT/examples/zephyr/qemu/README.md` ================================================ FILE: ports/zephyr/common/CMakeLists.txt ================================================ zephyr_library_sources(memfault_platform_debug_log.c) zephyr_library_sources(memfault_platform_core.c) zephyr_library_sources(memfault_zephyr_ram_regions.c) zephyr_library_sources(memfault_platform_lock.c) zephyr_library_sources(memfault_platform_coredump_regions.c) zephyr_library_sources(memfault_platform_system_time.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_HTTP_ENABLE memfault_platform_http.c) # Only build the generic FOTA backend (MCUboot/Dummy) when not using the NCS backend, # since the NCS backend (memfault_fota.c / memfault_fota_legacy.c) provides its own # memfault_zephyr_fota_start() implementation. if(CONFIG_MEMFAULT_HTTP_ENABLE AND NOT CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS) zephyr_library_sources(memfault_platform_fota.c) endif() zephyr_library_sources_ifdef(CONFIG_MEMFAULT_MCUMGR_GRP memfault_mcumgr.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_RAM_BACKED_COREDUMP memfault_platform_ram_backed_coredump.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_COREDUMP_STORAGE_AMBIQ_MRAM coredump_storage/memfault_ambiq_mram_backed_coredump.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_COREDUMP_STORAGE_NRF_MRAM coredump_storage/memfault_mram_backed_coredump.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_COREDUMP_STORAGE_NRF_RRAM coredump_storage/memfault_nrf_rram_backed_coredump.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_COREDUMP_STORAGE_STM32U5_FLASH coredump_storage/memfault_stm32u5_flash_backed_coredump.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_ROOT_CERT_STORAGE_TLS_CREDENTIAL_STORAGE memfault_tls_root_cert_storage.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_SHELL memfault_demo_cli.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_SHELL_SELF_TEST memfault_self_test_platform.c) if(CONFIG_MEMFAULT_HTTP_ENABLE OR CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) zephyr_library_sources(memfault_platform_post.c) endif() if (NOT CONFIG_MEMFAULT_SOFTWARE_WATCHDOG_CUSTOM) zephyr_library_sources(memfault_software_watchdog.c) endif() if (CONFIG_MEMFAULT_CACHE_FAULT_REGS) # We need to wrap z_arm_fault() so we can get a copy # of the fault registers before Zephyr consumes and # clears the CFSR. See ports/zephyr/common/memfault_platform_core.c. zephyr_ld_options(-Wl,--wrap=z_arm_fault) endif() if (CONFIG_MEMFAULT_METRICS_BOOT_TIME) zephyr_ld_options(-Wl,--wrap=main) endif() # Allow users to Kconfig memfault logging as a backend as needed. See # memfault/sdk/embedded/ports/zephyr/Kconfig and adjust MEMFAULT_LOGGING_XXX. if (CONFIG_MEMFAULT_LOGGING_ENABLE) # Version 2 logging was first introduced in # https://github.com/zephyrproject-rtos/zephyr/commit/f6a40ae183ed0781ffcdeac4ba9263116247a750 # and launched as part of the Zephyr v2.6 release # https://github.com/zephyrproject-rtos/zephyr/pull/31535 # It fully replaced version 1 after the 3.1 release: https://github.com/zephyrproject-rtos/zephyr/issues/46500 # and the option to enable logging v1 via CONFIG_LOG1 was entirely removed if(CONFIG_LOG_MODE_MINIMAL) target_link_libraries(app INTERFACE "-Wl,--wrap=z_log_minimal_printk") zephyr_library_sources(memfault_logging_minimal.c) elseif(${KERNEL_VERSION_MAJOR}.${KERNEL_VERSION_MINOR}.${KERNEL_PATCHLEVEL} VERSION_GREATER_EQUAL "3.1.99" OR CONFIG_LOG2) zephyr_library_sources(memfault_logging.c) else() zephyr_library_sources(memfault_logging_legacy.c) endif() endif() zephyr_library_sources_ifdef(CONFIG_MEMFAULT_PERIODIC_UPLOAD memfault_periodic_upload.c) # Make sure RAM based storage regions are not initialized on boot # by placing them in special linker sections zephyr_linker_sources(NOINIT memfault-no-init.ld) if (CONFIG_MEMFAULT_GNU_BUILD_ID_SOURCE_BUILTIN) zephyr_linker_sources(SECTIONS memfault-build-id.ld) # Override the default Zephyr setting which disables the GNU Build ID # https://github.com/zephyrproject-rtos/zephyr/blob/d7ee114106eab485688223d97a49813d33b4cf21/cmake/linker/ld/target_base.cmake#L16 zephyr_ld_options("-Wl,--build-id") endif() if (CONFIG_MEMFAULT_COMPACT_LOG) # Enable support for compact logs by adding the required linker section zephyr_linker_sources(SECTIONS memfault-compact-log.ld) # Compact logs requires a minimum C standard of gnu11, due to use of GNU # extensions and C11 features. Zephyr 3.7.0+ adds a Kconfig-native setting to # enable this. If unset, fallback to deprecated global CSTD property (for # pre-3.7.0). if (NOT (CONFIG_REQUIRES_STD_C11 AND CONFIG_GNU_C_EXTENSIONS)) set_property(GLOBAL PROPERTY CSTD gnu11) endif() endif() if(CONFIG_MEMFAULT_HEAP_STATS AND CONFIG_HEAP_MEM_POOL_SIZE GREATER 0) zephyr_ld_options(-Wl,--wrap=k_malloc) zephyr_ld_options(-Wl,--wrap=k_free) endif() if(CONFIG_MEMFAULT_SOC_FAMILY_ESP32) zephyr_linker_sources(SECTIONS memfault-rtc-noinit-region.ld) endif() zephyr_include_directories(.) add_subdirectory(metrics) ================================================ FILE: ports/zephyr/common/coredump_storage/memfault_ambiq_mram_backed_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! MRAM backed coredump storage implementation. To make use of this, be sure to //! add a fixed partition named "memfault_coredump_partition" to your device //! tree : //! //! &flash0 { //! partitions { //! memfault_coredump_partition: partition@1e6000 { //! reg = <0x1e6000 0x2000>; //! }; //! }; //! }; #include #include #include // Note: this is only used for the FIXED_PARTITION_ADDRESS / SIZE macros. The // FLASH_AMBIQ driver uses a semaphore for concurrent access protection, which // prohibits using it from the fault handler context. #include //! Apollo4 MRAM has a 16-byte write block size #define MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE (16) #include // Ensure the memfault_coredump_partition entry exists #if !FIXED_PARTITION_EXISTS(memfault_coredump_partition) #error "Be sure to add a fixed partition named 'memfault_coredump_partition'!" #endif #define MEMFAULT_COREDUMP_PARTITION_ADDRESS FIXED_PARTITION_ADDRESS(memfault_coredump_partition) #define MEMFAULT_COREDUMP_PARTITION_SIZE FIXED_PARTITION_SIZE(memfault_coredump_partition) MEMFAULT_STATIC_ASSERT(MEMFAULT_COREDUMP_PARTITION_SIZE % MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE == 0, "Storage size must be a multiple of (16 bytes)"); void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = MEMFAULT_COREDUMP_PARTITION_SIZE, }; } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // special case: if the first word is 0, the coredump is cleared, and reads // should return all zeros uint32_t first_word = *(volatile uint32_t *)(MEMFAULT_COREDUMP_PARTITION_ADDRESS); if (first_word == 0) { memset(data, 0, read_len); return true; } // read the data directly from flash memory memcpy(data, (uint8_t *)(MEMFAULT_COREDUMP_PARTITION_ADDRESS + offset), read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } // use am_hal_mram_main_fill(): // extern int am_hal_mram_main_fill(uint32_t ui32ProgramKey, uint32_t ui32Value, // uint32_t *pui32Dst, uint32_t ui32NumWords); // Note: this function requires 4-byte aligned data and size in words uint32_t aligned_value = 0; unsigned int key = irq_lock(); int ret = am_hal_mram_main_fill(AM_HAL_MRAM_PROGRAM_KEY, aligned_value, (uint32_t *)(MEMFAULT_COREDUMP_PARTITION_ADDRESS + offset), erase_size / sizeof(uint32_t)); irq_unlock(key); // Invalidate the data cache for this region to ensure subsequent reads see the new data (void)sys_cache_data_invd_range((void *)(MEMFAULT_COREDUMP_PARTITION_ADDRESS + offset), erase_size); return (ret == 0); } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { const uint32_t addr = MEMFAULT_COREDUMP_PARTITION_ADDRESS + blk->write_offset; if (!prv_op_within_flash_bounds(blk->write_offset, sizeof(blk->data))) { return false; } // Prepare aligned buffer for MRAM write (requires 4-byte aligned data) uint32_t aligned[sizeof(blk->data) / sizeof(uint32_t)]; uint32_t *src = (uint32_t *)blk->data; for (int i = 0; i < sizeof(blk->data) / sizeof(uint32_t); i++) { aligned[i] = UNALIGNED_GET((uint32_t *)src); src++; } int ret = am_hal_mram_main_program(AM_HAL_MRAM_PROGRAM_KEY, aligned, (uint32_t *)addr, sizeof(blk->data) / sizeof(uint32_t)); // Invalidate the data cache for this region to ensure subsequent reads see the new data (void)sys_cache_data_invd_range((void *)addr, sizeof(blk->data)); return (ret == 0); } //! Primarily used for debug. Test function reads the entire region, so we need //! to wipe everything. void memfault_platform_coredump_storage_clear(void) { memfault_platform_coredump_storage_erase(0, MEMFAULT_COREDUMP_PARTITION_SIZE); } ================================================ FILE: ports/zephyr/common/coredump_storage/memfault_mram_backed_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! MRAM backed coredump storage implementation. To make use of this, be sure to //! add a fixed partition named "memfault_coredump_partition" to your device //! tree or use partition manager to define one. //! //! For example, if using partition manager, you might add an entry like this to //! the application's pm_static.yml file: //! //! memfault_coredump_partition: //! address: 0x155000 //! end_address: 0x165000 //! placement: //! align: //! start: 0x1000 //! before: //! - end //! region: flash_primary //! size: 0x10000 //! //! If using device tree specified partitions, you might add something like this //! to your board's dts file/overlay: //! //! &mram1x { //! partitions { //! memfault_coredump_partition: partition@1a9000 { //! reg = <0x1a9000 DT_SIZE_K(20)>; //! }; //! }; //! }; #include #include #include //! nRF54H20 MRAM has a 16-byte write block size #define MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE (16) #include // Ensure the memfault_coredump_partition entry exists #if !FIXED_PARTITION_EXISTS(memfault_coredump_partition) #error "Be sure to add a fixed partition named 'memfault_coredump_partition'!" #endif #if defined(CONFIG_SOC_NRF54H20) #define MEMFAULT_COREDUMP_FLASH_DEVICE DEVICE_DT_GET(DT_CHOSEN(zephyr_flash)) #define MEMFAULT_COREDUMP_PARTITION_OFFSET FIXED_PARTITION_OFFSET(memfault_coredump_partition) #define MEMFAULT_COREDUMP_PARTITION_SIZE FIXED_PARTITION_SIZE(memfault_coredump_partition) #else #error "Unknown SOC, please contact support: https://mflt.io/contact-support" #endif MEMFAULT_STATIC_ASSERT(MEMFAULT_COREDUMP_PARTITION_SIZE % MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE == 0, "Storage size must be a multiple of (16 bytes)"); void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = MEMFAULT_COREDUMP_PARTITION_SIZE, }; } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // special case: if the first word is 0, the coredump is cleared, and reads // should return all zeros uint32_t first_word = 0xffffffff; (int)flash_read(MEMFAULT_COREDUMP_FLASH_DEVICE, MEMFAULT_COREDUMP_PARTITION_OFFSET, &first_word, sizeof(first_word)); if (first_word == 0) { memset(data, 0, read_len); return true; } // read the data (int)flash_read(MEMFAULT_COREDUMP_FLASH_DEVICE, MEMFAULT_COREDUMP_PARTITION_OFFSET + offset, data, read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { // Erase is only ever called just prior to writing a new coredump or in debug // testing, so we only need to wipe the first 32 bits. We'll overwrite the // first unit of write size for simplicity. uint8_t erase_buf[sizeof(((sCoredumpWorkingBuffer *)0)->data)] = { 0 }; return memfault_platform_coredump_storage_write(offset, erase_buf, sizeof(erase_buf)); } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { const uint32_t addr = MEMFAULT_COREDUMP_PARTITION_OFFSET + blk->write_offset; if (!prv_op_within_flash_bounds(blk->write_offset, sizeof(blk->data))) { return false; } (int)flash_write(MEMFAULT_COREDUMP_FLASH_DEVICE, addr, blk->data, sizeof(blk->data)); return true; } //! Primarily used for debug. Call erase to wipe out the coredump magic. void memfault_platform_coredump_storage_clear(void) { memfault_platform_coredump_storage_erase(0, 0); } ================================================ FILE: ports/zephyr/common/coredump_storage/memfault_nrf_rram_backed_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! RRAM backed coredump storage implementation. To make use of this, be sure to //! add a fixed partition named "memfault_coredump_partition" to your device //! tree or use partition manager to define one. //! //! For example, if using partition manager, you might add an entry like this to //! the application's pm_static.yml file: //! //! memfault_coredump_partition: //! address: 0x1fc000 //! end_address: 0x1fd000 //! placement: //! after: //! - mcuboot_secondary //! region: flash_primary //! size: 0x1000 //! //! If using device tree specified partitions, you might add something like this //! to your board's dts file/overlay: //! //! &mram1x { //! partitions { //! memfault_coredump_partition: partition@1d5000 { //! label = "memfault_coredump_partition"; //! reg = <0x1d5000 0x5000>; //! }; //! }; //! }; #include #include #include #include //! nRF54 series RRAM uses a 128-bit wordline- writing smaller amounts still //! causes a full wordline write, so to make the best use of write cycles, we //! buffer writes to this size. #define MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE (128 / 8) #include // Ensure the memfault_coredump_partition entry exists #if !FIXED_PARTITION_EXISTS(memfault_coredump_partition) #error "Be sure to add a fixed partition named 'memfault_coredump_partition'!" #endif // Set the partition address/size based on the fixed partition definitions. // Note that absolute address is used, because RRAM is written as memory-mapped // locations (not relative to flash device). // Fixed partitions in use #if DT_HAS_FIXED_PARTITION_LABEL(memfault_coredump_partition) #define MEMFAULT_COREDUMP_PARTITION_ADDRESS FIXED_PARTITION_ADDRESS(memfault_coredump_partition) #define MEMFAULT_COREDUMP_PARTITION_SIZE FIXED_PARTITION_SIZE(memfault_coredump_partition) // Partition-manager defined partitions #elif defined(CONFIG_PARTITION_MANAGER_ENABLED) #define MEMFAULT_COREDUMP_PARTITION_ADDRESS \ FIXED_PARTITION_DATA_FIELD(memfault_coredump_partition, _ADDRESS) #define MEMFAULT_COREDUMP_PARTITION_SIZE FIXED_PARTITION_SIZE(memfault_coredump_partition) #endif MEMFAULT_STATIC_ASSERT(MEMFAULT_COREDUMP_PARTITION_SIZE % MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE == 0, "Storage size must be a multiple of 128-bit (16 bytes)"); void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = MEMFAULT_COREDUMP_PARTITION_SIZE, }; } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // special case: if the first word is 0, the coredump is cleared, and reads // should return all zeros const uint32_t first_wordline = *(const uint32_t *)(MEMFAULT_COREDUMP_PARTITION_ADDRESS); if (first_wordline == 0) { memset(data, 0, read_len); return true; } // RRAM is memory mapped, so we can just read it directly const uint32_t address = MEMFAULT_COREDUMP_PARTITION_ADDRESS + offset; memcpy(data, (void *)address, read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { // Erase is only ever called just prior to writing a new coredump or in debug // testing, so we only need to wipe the first 32 bits. We'll overwrite the // first wordline for simplicity. uint8_t erase_buf[MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE] = { 0 }; return memfault_platform_coredump_storage_write(offset, erase_buf, sizeof(erase_buf)); } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { const uint32_t start_addr = MEMFAULT_COREDUMP_PARTITION_ADDRESS; const uint32_t addr = start_addr + blk->write_offset; if (!prv_op_within_flash_bounds(blk->write_offset, MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE)) { return false; } // save existing rramc config nrf_rramc_config_t prior_config; nrf_rramc_config_get(NRF_RRAMC, &prior_config); // enable writing nrf_rramc_config_t config = { .mode_write = true, .write_buff_size = MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE, }; nrf_rramc_config_set(NRF_RRAMC, &config); // write the data memcpy((void *)addr, blk->data, MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE); // trigger the commit nrf_rramc_task_trigger(NRF_RRAMC, NRF_RRAMC_TASK_COMMIT_WRITEBUF); // force a bus synchronization to avoid any issues with cached reads barrier_dmem_fence_full(); // disable writing config.mode_write = false; nrf_rramc_config_set(NRF_RRAMC, &config); // restore previous config nrf_rramc_config_set(NRF_RRAMC, &prior_config); return true; } //! Primarily used for debug. Call erase to wipe out the coredump magic. void memfault_platform_coredump_storage_clear(void) { memfault_platform_coredump_storage_erase(0, 0); } ================================================ FILE: ports/zephyr/common/coredump_storage/memfault_stm32u5_flash_backed_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! STM32U5 Flash backed coredump storage implementation using direct SOC flash API. //! The Zephyr flash API uses semaphores which cannot be used in ISR context, //! so this implementation directly accesses the STM32 flash controller registers. //! //! To make use of this, be sure to add a fixed partition named //! "memfault_coredump_partition" to your board's device tree: //! //! &flash0 { //! partitions { //! compatible = "fixed-partitions"; //! #address-cells = <1>; //! #size-cells = <1>; //! //! memfault_coredump_partition: partition@f0000 { //! label = "memfault_coredump_partition"; //! reg = <0xf0000 0x10000>; //! }; //! }; //! }; #include #include #include #include #include #include #include "memfault/ports/zephyr/version.h" //! STM32U5 flash has 8KB pages and 16-byte (quadword) write block size #define MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE (16) #include // The CMSIS device headers (included transitively via stm32u5xx_ll_system.h) define the // flash register bit fields for STM32U5X. These are distinct from the STM32L5 naming: // // NSCR: LOCK(31) | ... | STRT(16) | ... | BKER(11) | PNB(9:3) | ... | PER(1) | PG(0) // NSSR: ... | BSY(16) | OPTWERR(13) | PGSERR(7) | SIZERR(6) | PGAERR(5) | WRPERR(4) | // PROGERR(3) | OPERR(1) | EOP(0) // OPTR: DUALBANK(21) | SWAP_BANK(20) | ... // // Catch wrong-target builds early. #if !defined(FLASH_NSCR_LOCK) || !defined(FLASH_NSSR_BSY) || !defined(FLASH_OPTR_DUALBANK) #error "Expected STM32U5 CMSIS defines not found. Wrong target or missing includes." #endif // Ensure the memfault_coredump_partition entry exists #if !FIXED_PARTITION_EXISTS(memfault_coredump_partition) #error "Be sure to add a fixed partition named 'memfault_coredump_partition'!" #endif // FIXED_PARTITION_ADDRESS returns the absolute memory-mapped address // (flash base + partition offset), so no arithmetic needed here. #if !MEMFAULT_ZEPHYR_VERSION_GTE_STRICT(4, 3) #define FIXED_PARTITION_ADDRESS(label) \ (COND_CODE_1(DT_FIXED_SUBPARTITION_EXISTS(DT_NODELABEL(label)), \ (DT_FIXED_SUBPARTITION_ADDR(DT_NODELABEL(label))), \ (DT_FIXED_PARTITION_ADDR(DT_NODELABEL(label))))) #endif #define MEMFAULT_COREDUMP_PARTITION_ADDRESS FIXED_PARTITION_ADDRESS(memfault_coredump_partition) #define MEMFAULT_COREDUMP_PARTITION_SIZE FIXED_PARTITION_SIZE(memfault_coredump_partition) MEMFAULT_STATIC_ASSERT(MEMFAULT_COREDUMP_PARTITION_SIZE % MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE == 0, "Storage size must be a multiple of 16 bytes (quadword)"); MEMFAULT_STATIC_ASSERT(MEMFAULT_COREDUMP_PARTITION_ADDRESS % FLASH_PAGE_SIZE == 0, "Coredump partition address must be page-aligned"); MEMFAULT_STATIC_ASSERT(MEMFAULT_COREDUMP_PARTITION_SIZE % FLASH_PAGE_SIZE == 0, "Coredump partition size must be a multiple of FLASH_PAGE_SIZE"); // Flash base address derived from the device tree. #define FLASH_BASE_ADDRESS DT_REG_ADDR(DT_INST(0, st_stm32_nv_flash)) // Offset from flash base where bank 2 begins in dual-bank mode. #define FLASH_BANK2_OFFSET (CONFIG_FLASH_SIZE * 1024 / 2) // NSSR error bits to check and clear. These are all the sticky error flags // in the non-secure status register for STM32U5X. #define FLASH_NSSR_ERRORS \ (FLASH_NSSR_OPERR | FLASH_NSSR_PROGERR | FLASH_NSSR_WRPERR | FLASH_NSSR_PGAERR | \ FLASH_NSSR_SIZERR | FLASH_NSSR_PGSERR | FLASH_NSSR_OPTWERR) // Standard STM32 flash unlock key sequence (same across all STM32 families). #define FLASH_KEY1_VALUE (0x45670123UL) #define FLASH_KEY2_VALUE (0xCDEF89ABUL) // Maximum time to wait for a flash operation. STM32U5 typical page erase is ~90 ms. #define FLASH_TIMEOUT_US (200000UL) // Rough microsecond busy-wait. Only used as a watchdog; the BSY flag is the // real completion signal. The STM32U5 can run up to 160 MHz — the loop body // takes roughly 2 cycles, so 160/2 = 80 iterations ≈ 1 µs at max frequency. // At lower frequencies the wait will be longer, which is conservative and fine. static inline void prv_delay_us(uint32_t us) { volatile uint32_t cycles = us * 80; while (cycles--) { __asm__ volatile("nop"); } } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = MEMFAULT_COREDUMP_PARTITION_SIZE, .sector_size = FLASH_PAGE_SIZE, }; } static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { sMfltCoredumpStorageInfo info = { 0 }; memfault_platform_coredump_storage_get_info(&info); return (offset + data_len) <= info.size; } static inline bool prv_flash_is_busy(void) { // Only check BSY (bit 16 of NSSR for STM32U5X), matching the Zephyr STM32 flash // driver. NSWBNE / WDW can stay set briefly after BSY clears; polling it would // cause spurious timeouts. return (FLASH->NSSR & FLASH_NSSR_BSY) != 0; } static bool prv_wait_flash_idle(void) { uint32_t timeout_us = FLASH_TIMEOUT_US; while (prv_flash_is_busy() && timeout_us > 0) { prv_delay_us(1); timeout_us--; } if (FLASH->NSSR & FLASH_NSSR_ERRORS) { FLASH->NSSR = FLASH_NSSR_ERRORS; // clear sticky error bits by writing 1s return false; } return timeout_us > 0; } static bool prv_flash_unlock(void) { // NSCR_LOCK is bit 31 for STM32U5X (set = locked, clear = unlocked). if ((FLASH->NSCR & FLASH_NSCR_LOCK) == 0) { return true; } FLASH->NSKEYR = FLASH_KEY1_VALUE; FLASH->NSKEYR = FLASH_KEY2_VALUE; return (FLASH->NSCR & FLASH_NSCR_LOCK) == 0; } static void prv_flash_lock(void) { FLASH->NSCR |= FLASH_NSCR_LOCK; } //! Erase the 8 KB page that contains @p flash_address. //! //! @p flash_address must be the absolute memory-mapped address (0x08xxxxxx). //! icache must be disabled by the caller. static bool prv_erase_page(uint32_t flash_address) { if (!prv_wait_flash_idle()) { return false; } if (!prv_flash_unlock()) { return false; } // Work in terms of offset-from-flash-base for bank/page arithmetic, matching // the logic in Zephyr's flash_stm32l5x driver. const uint32_t offset = flash_address - FLASH_BASE_ADDRESS; const bool dual_bank = (FLASH->OPTR & FLASH_OPTR_DUALBANK) != 0; const bool bank_swap = (FLASH->OPTR & FLASH_OPTR_SWAP_BANK) != 0; uint32_t page; if (dual_bank) { // Four cases: (physical bank 1 or 2) × (swap active or not). // With SWAP_BANK=0: bank 1 occupies [0, BANK2_OFFSET), bank 2 the rest. // With SWAP_BANK=1: the logical mapping is flipped. if ((offset < FLASH_BANK2_OFFSET) && !bank_swap) { // Physically bank 1, no swap FLASH->NSCR &= ~FLASH_NSCR_BKER_Msk; page = offset / FLASH_PAGE_SIZE; } else if ((offset >= FLASH_BANK2_OFFSET) && bank_swap) { // Physically beyond BANK2_OFFSET, but swap makes it bank 1 FLASH->NSCR &= ~FLASH_NSCR_BKER_Msk; page = (offset - FLASH_BANK2_OFFSET) / FLASH_PAGE_SIZE; } else if ((offset < FLASH_BANK2_OFFSET) && bank_swap) { // Physically below BANK2_OFFSET, but swap makes it bank 2 FLASH->NSCR |= FLASH_NSCR_BKER; page = offset / FLASH_PAGE_SIZE; } else { // offset >= BANK2_OFFSET, no swap → bank 2 FLASH->NSCR |= FLASH_NSCR_BKER; page = (offset - FLASH_BANK2_OFFSET) / FLASH_PAGE_SIZE; } } else { // Single-bank: pages numbered from 0 across the full flash FLASH->NSCR &= ~FLASH_NSCR_BKER_Msk; page = offset / FLASH_PAGE_SIZE; } // Configure and start page erase FLASH->NSCR |= FLASH_NSCR_PER; FLASH->NSCR &= ~FLASH_NSCR_PNB_Msk; FLASH->NSCR |= (page << FLASH_NSCR_PNB_Pos); FLASH->NSCR |= FLASH_NSCR_STRT; // Flush the register pipeline before polling BSY volatile uint32_t tmp = FLASH->NSCR; (void)tmp; const bool result = prv_wait_flash_idle(); // Clear erase-mode bits if (dual_bank) { FLASH->NSCR &= ~(FLASH_NSCR_PER | FLASH_NSCR_BKER); } else { FLASH->NSCR &= ~FLASH_NSCR_PER; } prv_flash_lock(); return result; } static bool prv_write_quadword(uint32_t flash_address, const uint32_t *data) { if (!prv_wait_flash_idle()) { return false; } // Per RM0456 §7.3.7: writing a non-zero quadword over a location that has // not been erased (all 0xFF) is illegal. Writing all-zeros is always allowed // (programming 1→0 is fine; only 0→1 requires an erase). volatile uint32_t *flash_ptr = (volatile uint32_t *)flash_address; bool all_zeros = true; for (int i = 0; i < 4; i++) { if (data[i] != 0) { all_zeros = false; break; } } if (!all_zeros) { for (int i = 0; i < 4; i++) { if (flash_ptr[i] != 0xFFFFFFFFUL) { return false; // destination not erased } } } if (!prv_flash_unlock()) { return false; } FLASH->NSCR |= FLASH_NSCR_PG; // Flush the register write before touching flash volatile uint32_t tmp = FLASH->NSCR; (void)tmp; for (int i = 0; i < 4; i++) { flash_ptr[i] = data[i]; } const bool result = prv_wait_flash_idle(); FLASH->NSCR &= ~FLASH_NSCR_PG; prv_flash_lock(); return result; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if (!prv_op_within_flash_bounds(offset, read_len)) { return false; } // Flash is memory-mapped so we can read directly memcpy(data, (const void *)(MEMFAULT_COREDUMP_PARTITION_ADDRESS + offset), read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if (!prv_op_within_flash_bounds(offset, erase_size)) { return false; } // Per memfault/panics/platform/coredump.h, offset must be sector-aligned and // erase_size must be a multiple of sector_size. Refuse unaligned requests to // avoid erasing more (or less) than the caller asked for. if ((offset % FLASH_PAGE_SIZE) != 0 || (erase_size % FLASH_PAGE_SIZE) != 0) { return false; } // Disable ICACHE and DCACHE1 before any flash operation. // Per RM0456 §7.3.10, ICACHE must be disabled during NVM write/erase or ERRF // is set in ICACHE_SR and the operation may not complete. DCACHE1 must be // off so data writes reach the flash controller bus (not a write-back line); // re-enabling DCACHE1 afterwards triggers an automatic full invalidation // (RM0456 §6.3) so subsequent reads see the fresh erased/written data. // sys_cache_instr/data_disable are NOT used: on Zephyr < 4.3 both are no-ops // for STM32U5 (CONFIG_ICACHE/DCACHE absent), so we call the LL functions // directly — matching what Zephyr's own STM32 flash driver does. const bool icache_was_enabled = LL_ICACHE_IsEnabled(); if (icache_was_enabled) { CLEAR_BIT(ICACHE->FCR, ICACHE_FCR_CBSYENDF); LL_ICACHE_Disable(); while (LL_ICACHE_IsEnabled()) { } } const bool dcache_was_enabled = LL_DCACHE_IsEnabled(DCACHE1); if (dcache_was_enabled) { LL_DCACHE_Disable(DCACHE1); } bool result = true; const uint32_t start_addr = MEMFAULT_COREDUMP_PARTITION_ADDRESS + offset; const uint32_t end_addr = start_addr + erase_size; for (uint32_t addr = start_addr; addr < end_addr; addr += FLASH_PAGE_SIZE) { if (!prv_erase_page(addr)) { result = false; break; } } if (icache_was_enabled) { LL_ICACHE_Enable(); } if (dcache_was_enabled) { LL_DCACHE_Enable(DCACHE1); // re-enable triggers automatic full invalidation } return result; } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *blk) { if (!prv_op_within_flash_bounds(blk->write_offset, sizeof(blk->data))) { return false; } const uint32_t addr = MEMFAULT_COREDUMP_PARTITION_ADDRESS + blk->write_offset; // Disable ICACHE and DCACHE1 around flash writes (see erase comment above). const bool icache_was_enabled = LL_ICACHE_IsEnabled(); if (icache_was_enabled) { CLEAR_BIT(ICACHE->FCR, ICACHE_FCR_CBSYENDF); LL_ICACHE_Disable(); while (LL_ICACHE_IsEnabled()) { } } const bool dcache_was_enabled = LL_DCACHE_IsEnabled(DCACHE1); if (dcache_was_enabled) { LL_DCACHE_Disable(DCACHE1); } const bool result = prv_write_quadword(addr, (const uint32_t *)blk->data); if (icache_was_enabled) { LL_ICACHE_Enable(); } if (dcache_was_enabled) { LL_DCACHE_Enable(DCACHE1); // re-enable triggers automatic full invalidation } return result; } //! Erase the first page of the coredump partition to invalidate any stored //! coredump (erased STM32 flash reads back as 0xFF, so any prior coredump //! header is no longer considered valid). void memfault_platform_coredump_storage_clear(void) { memfault_platform_coredump_storage_erase(0, FLASH_PAGE_SIZE); } ================================================ FILE: ports/zephyr/common/memfault-build-id.ld ================================================ SECTION_PROLOGUE(.note.gnu.build-id,,) { __start_gnu_build_id_start = .; KEEP(*(.note.gnu.build-id)) /* For ESP32s, GROUP_LINK_IN() results in the placement of the build id at * a bad address but GROUP_DATA_LINK_IN() gives it a reasonable one. * GROUP_DATA_LINK_IN() is newer to Zephyr so it may not catch all cases; * #ifdef on the ESP32 family for now. */ #if defined(CONFIG_MEMFAULT_SOC_FAMILY_ESP32) } GROUP_DATA_LINK_IN(RODATA_REGION, ROMABLE_REGION) #else } GROUP_LINK_IN(ROMABLE_REGION) #endif ================================================ FILE: ports/zephyr/common/memfault-compact-log.ld ================================================ /* Custom section to be included in the linker command file to support compact logs. Read more at https://mflt.io/compact-logs */ SECTION_PROLOGUE(log_fmt,0xF0000000 (INFO),) { /* explicitly define this symbol, in case the linker doesn't auto-define it */ __start_log_fmt = ABSOLUTE(.); KEEP(*(*.log_fmt_hdr)) KEEP(*(log_fmt)) } ================================================ FILE: ports/zephyr/common/memfault-mbedtls.conf ================================================ #define MBEDTLS_SSL_SERVER_NAME_INDICATION 1 #define MBEDTLS_BASE64_C 1 #define MBEDTLS_PEM_PARSE_C 1 ================================================ FILE: ports/zephyr/common/memfault-no-init.ld ================================================ /** * Memfault stores some state across reboots. These regions must be placed in a region of RAM that * does not get initialized on bootup */ KEEP(*(*.mflt_coredump)); KEEP(*(*.mflt_reboot_info)); ================================================ FILE: ports/zephyr/common/memfault-rtc-noinit-region.ld ================================================ /** * For ESP32 variants other than ESP32-S3/S2, provide a region of noinit placed * in RTC memory, for holding reboot reason information through OTA updates. * The normal Zephyr noinit region is not suitable for this, because its offset * can change through an OTA update, invalidating the reboot reason data. * * Unfortunately this doesn't place the section in the same order in RTC as in * the ESP32-S3/S2, but it puts it in a usable location. * * This was later fixed in Zephyr 4.1.0, so we only need this workaround for * Zephyr 4.0.0 and earlier. */ #include "memfault/ports/zephyr/version.h" #if !MEMFAULT_ZEPHYR_VERSION_GT_STRICT(4, 0) && \ !defined(CONFIG_SOC_SERIES_ESP32S3) && !defined(CONFIG_SOC_SERIES_ESP32S2) SECTION_PROLOGUE(.rtc_noinit,(NOLOAD),) { . = ALIGN(4); _rtc_noinit_start = ABSOLUTE(.); *(.rtc_noinit .rtc_noinit.*) . = ALIGN(4) ; _rtc_noinit_end = ABSOLUTE(.); } GROUP_LINK_IN(rtc_slow_seg) #endif /* Zephyr < 4.1.0 && not ESP32-S3/S2 */ ================================================ FILE: ports/zephyr/common/memfault_demo_cli.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Adds a basic set of commands for interacting with Memfault SDK // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(shell/shell.h) #include #include "memfault/components.h" #include "memfault/ports/zephyr/http.h" #include "memfault/ports/zephyr/fota.h" #include "memfault/ports/zephyr/periodic_upload.h" #include MEMFAULT_ZEPHYR_INCLUDE(sys/__assert.h) #include MEMFAULT_ZEPHYR_INCLUDE(sys/printk.h) #if defined(CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) #include "memfault/nrfconnect_port/coap.h" #endif // clang-format on static int prv_clear_core_cmd(const struct shell *shell, size_t argc, char **argv) { return memfault_demo_cli_cmd_clear_core(argc, argv); } static int prv_get_core_cmd(const struct shell *shell, size_t argc, char **argv) { return memfault_demo_cli_cmd_get_core(argc, argv); } static int prv_test_log(const struct shell *shell, size_t argc, char **argv) { return memfault_demo_cli_cmd_test_log(argc, argv); } static int prv_trigger_logs(const struct shell *shell, size_t argc, char **argv) { return memfault_demo_cli_cmd_trigger_logs(argc, argv); } static int prv_get_device_info(const struct shell *shell, size_t argc, char **argv) { return memfault_demo_cli_cmd_get_device_info(argc, argv); } static int prv_get_reboot_reason(const struct shell *shell, size_t argc, char **argv) { sMfltRebootReason reboot_reason; int result = memfault_reboot_tracking_get_reboot_reason(&reboot_reason); if (result != 0) { shell_print(shell, "Error retrieving reboot reason: %d", result); return result; } shell_print(shell, "Current Reboot Reason Reg: 0x%04x", reboot_reason.reboot_reg_reason); shell_print(shell, "Prior Stored Reboot Reason: 0x%04x", reboot_reason.prior_stored_reason); return 0; } //! Route the 'export' command to output using shell_print when available. static const struct shell *s_memfault_shell; void memfault_data_export_base64_encoded_chunk(const char *base64_chunk) { if (s_memfault_shell != NULL) { shell_print(s_memfault_shell, "%s", base64_chunk); } else { printk("%s\n", base64_chunk); } } static int prv_chunk_data_export(const struct shell *shell, size_t argc, char **argv) { // Set the shell context so our overridden chunk handler can use it to print s_memfault_shell = shell; memfault_data_export_dump_chunks(); s_memfault_shell = NULL; return 0; } static int prv_example_trace_event_capture(const struct shell *shell, size_t argc, char **argv) { // For more information on user-defined error reasons, see // the MEMFAULT_TRACE_REASON_DEFINE macro in trace_reason_user.h . MEMFAULT_TRACE_EVENT_WITH_LOG(MemfaultCli_Test, "Num args: %d", (int)argc); MEMFAULT_LOG_DEBUG("Trace Event Generated!"); return 0; } #if defined(CONFIG_NETWORKING) static int prv_post_data(const struct shell *shell, size_t argc, char **argv) { // For more information on user-defined error reasons, see // the MEMFAULT_TRACE_REASON_DEFINE macro in trace_reason_user.h . #if defined(CONFIG_MEMFAULT_HTTP_ENABLE) || defined(CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) MEMFAULT_LOG_INFO("Posting Memfault Data"); ssize_t rv = memfault_zephyr_port_post_data_return_size(); if (rv < 0) { MEMFAULT_LOG_ERROR("Failed to post data, err=%d", rv); } else if (rv == 0) { MEMFAULT_LOG_INFO("Done: no data to send"); } else { MEMFAULT_LOG_INFO("Data posted successfully, %d bytes sent", rv); } return (rv < 0) ? rv : 0; #else shell_print(shell, "CONFIG_MEMFAULT_HTTP_ENABLE or CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP not enabled"); return 0; #endif } #endif // CONFIG_NETWORKING // Select CoAP or HTTP for FOTA URL retrieval based on Kconfig options, // preferring CoAP if enabled. These wrappers are just used to simplify the // logic below. #if defined(CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) #define PRIV_OTA_TRANSPORT_STR "CoAP" #define PRIV_OTA_GET_URL(url) memfault_zephyr_port_coap_get_download_url(url) #define PRIV_OTA_RELEASE_URL(url) memfault_zephyr_port_coap_release_download_url(url) #elif defined(CONFIG_MEMFAULT_HTTP_ENABLE) #define PRIV_OTA_TRANSPORT_STR "HTTPS" #define PRIV_OTA_GET_URL(url) memfault_zephyr_port_get_download_url(url) #define PRIV_OTA_RELEASE_URL(url) memfault_zephyr_port_release_download_url(url) #endif static int prv_get_latest_url_cmd(const struct shell *shell, size_t argc, char **argv) { #if defined(CONFIG_MEMFAULT_HTTP_ENABLE) || defined(CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) char *url = NULL; shell_print(shell, "Checking for update over " PRIV_OTA_TRANSPORT_STR); int rv = PRIV_OTA_GET_URL(&url); if (rv < 0) { MEMFAULT_LOG_ERROR("Unable to fetch OTA url, rv=%d", rv); return rv; } else if (rv == 0) { MEMFAULT_LOG_INFO("Up to date!"); return 0; } shell_print(shell, "Download URL: '%s'", url); rv = PRIV_OTA_RELEASE_URL(&url); return rv; #else shell_print(shell, "CONFIG_MEMFAULT_HTTP_ENABLE or CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP not enabled"); return 0; #endif /* CONFIG_MEMFAULT_HTTP_ENABLE */ } #undef PRIV_OTA_TRANSPORT_STR #undef PRIV_OTA_GET_URL #undef PRIV_OTA_RELEASE_URL static int prv_check_and_fetch_ota_payload_cmd(const struct shell *shell, size_t argc, char **argv) { #if defined(CONFIG_MEMFAULT_NRF_CONNECT_SDK) // The nRF Connect SDK comes with a download client module that can be used to // perform an actual e2e OTA so use that instead and don't link this code in at all! shell_print(shell, "Use 'mflt_nrf fota' instead. See https://mflt.io/nrf-fota-setup for more details"); #elif defined(CONFIG_MEMFAULT_HTTP_ENABLE) return memfault_zephyr_fota_start(); #else shell_print(shell, "CONFIG_MEMFAULT_HTTP_ENABLE not enabled"); #endif return 0; } static int prv_coredump_size(const struct shell *shell, size_t argc, char **argv) { (void)argc, (void)argv; size_t capacity, total_size; memfault_coredump_size_and_storage_capacity(&total_size, &capacity); shell_print(shell, "coredump storage capacity: %zuB", capacity); shell_print(shell, "coredump size required: %zuB", total_size); return 0; } static int prv_trigger_heartbeat(const struct shell *shell, size_t argc, char **argv) { #if CONFIG_MEMFAULT_METRICS shell_print(shell, "Triggering Heartbeat"); memfault_metrics_heartbeat_debug_trigger(); return 0; #else shell_print(shell, "CONFIG_MEMFAULT_METRICS not enabled"); return 0; #endif } static int prv_metrics_dump(const struct shell *shell, size_t argc, char **argv) { #if defined(CONFIG_MEMFAULT_METRICS) if (argc < 2) { memfault_metrics_heartbeat_debug_print(); } else { if (!strcmp(argv[1], "sessions")) { memfault_metrics_all_sessions_debug_print(); } else if (!strcmp(argv[1], "heartbeat")) { memfault_metrics_heartbeat_debug_print(); } else { shell_print(shell, "Unknown option. Enter 'heartbeat' or 'sessions'"); return 0; } } #else shell_print(shell, "CONFIG_MEMFAULT_METRICS not enabled"); #endif return 0; } static int prv_test_reboot(const struct shell *shell, size_t argc, char **argv) { MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_UserReset); memfault_platform_reboot(); return 0; // should be unreachable } static int prv_memfault_assert_example(const struct shell *shell, size_t argc, char **argv) { memfault_demo_cli_cmd_assert(argc, argv); return -1; } static int prv_hang_example(const struct shell *shell, size_t argc, char **argv) { #if !CONFIG_WATCHDOG shell_print(shell, "No watchdog configured, this will hang forever"); #else MEMFAULT_LOG_DEBUG("Hanging system and waiting for watchdog!"); #endif while (1) { } return -1; } #if defined(CONFIG_ARM) static int prv_busfault_example(const struct shell *shell, size_t argc, char **argv) { #if defined(CONFIG_MEMFAULT_NRF_CONNECT_SDK) && defined(CONFIG_BUILD_WITH_TFM) && \ !defined(CONFIG_TFM_ALLOW_NON_SECURE_FAULT_HANDLING) // Starting in NCS v2.6.0, enabling CONFIG_TFM_ALLOW_NON_SECURE_FAULT_HANDLING will cause // Memfault's fault handlers to be invoked for BusFaults originating from non-secure code. shell_print(shell, "CONFIG_TFM_ALLOW_NON_SECURE_FAULT_HANDLING is disabled or undefined, no " "coredump will be collected"); // Allow the shell output buffer to be flushed before the crash k_sleep(K_MSEC(100)); #endif //! Note: The Zephyr fault handler dereferences the pc which triggers a fault //! if the pc itself is from a bad pointer: //! https://github.com/zephyrproject-rtos/zephyr/blob/f400c94/arch/arm/core/aarch32/cortex_m/fault.c#L664 //! //! We set the BFHFNMIGN bit to prevent a lockup from happening due to de-referencing the bad PC //! which generated the fault in the first place volatile uint32_t *ccr = (uint32_t *)0xE000ED14; *ccr |= 0x1 << 8; memfault_demo_cli_cmd_busfault(argc, argv); return -1; } static int prv_hardfault_example(const struct shell *shell, size_t argc, char **argv) { #if defined(CONFIG_MEMFAULT_NRF_CONNECT_SDK) && defined(CONFIG_BUILD_WITH_TFM) // Memfault's fault handlers are not invoked during the handling of HardFaults in NCS when using // TF-M. CONFIG_TFM_ALLOW_NON_SECURE_FAULT_HANDLING=y will only invoke Memfault's fault handlers // for BusFaults and SecureFaults originating from non-secure code, not Hardfaults. shell_print(shell, "HardFaults are handled by TF-M, no coredump will be collected"); // Allow the shell output buffer to be flushed before the crash k_sleep(K_MSEC(100)); #endif memfault_demo_cli_cmd_hardfault(argc, argv); return -1; } static int prv_usagefault_example(const struct shell *shell, size_t argc, char **argv) { memfault_demo_cli_cmd_usagefault(argc, argv); return -1; } static int prv_memmanage_example(const struct shell *shell, size_t argc, char **argv) { memfault_demo_cli_cmd_memmanage(argc, argv); return -1; } #endif // CONFIG_ARM static int prv_zephyr_assert_example(const struct shell *shell, size_t argc, char **argv) { #if defined(CONFIG_ASSERT) // Fire off a last-ditch log message to show how logs are flushed prior to // crash, in the case of deferred logging mode MEMFAULT_LOG_ERROR("About to crash in %s!", __func__); __ASSERT(0, "test __ASSERT"); #else shell_print(shell, "CONFIG_ASSERT was disabled in the build, this command has no effect"); #endif return 0; } static int prv_zephyr_stack_overflow_example(const struct shell *shell, size_t argc, char **argv) { // get the current running thread, get its max stack address, then write // decrementing addresses 1 word at a time, yielding in between, to trigger an // MPU crash or canary check fail #if !defined(CONFIG_STACK_SENTINEL) && !defined(CONFIG_MPU_STACK_GUARD) shell_print(shell, "CONFIG_STACK_SENTINEL or CONFIG_MPU_STACK_GUARD must be enabled to test stack " "overflow detection"); #else // get the current thread struct k_thread *thread = k_current_get(); // get the stack start address (this is the lowest address) const uint32_t *stack_start = (const uint32_t *)thread->stack_info.start; // get the current address- it should be ~ the address of the above variable, // offset it by 4 words to hopefully prevent clobbering our current variables. // we'll write from this address, so the stack watermark will be updated. volatile uint32_t *stack_current = ((volatile uint32_t *)&stack_start) - 4; // starting at the stack start address, write decrementing values. this should crash! for (; stack_current > (stack_start - 64); stack_current--) { *stack_current = 0xDEADBEEF; k_yield(); } MEMFAULT_LOG_ERROR("Stack overflow test failed, stack overflow was not triggered!"); #endif return -1; } static int prv_zephyr_load_32bit_address(const struct shell *shell, size_t argc, char **argv) { return memfault_demo_cli_loadaddr(argc, argv); } static int prv_cli_cmd_double_free(const struct shell *shell, size_t argc, char **argv) { (void)shell; (void)argc; (void)argv; #if !CONFIG_MEMFAULT_HEAP_STATS shell_print( shell, "CONFIG_MEMFAULT_HEAP_STATS was disabled in the build, this command will have no effect"); #else uint8_t *ptr = k_malloc(100); k_free(ptr); k_free(ptr); shell_print(shell, "Double free should have crashed the app! 🤯 Make sure CONFIG_ASSERT=y"); #endif return -1; } //! Note: TOOLCHAIN_DISABLE_WARNING() etc were added to Zephyr in v4.1.0 MEMFAULT_DISABLE_WARNING("-Warray-bounds") static int prv_bad_ptr_deref_example(const struct shell *shell, size_t argc, char **argv) { volatile uint32_t *bad_ptr = (void *)0x1; *bad_ptr = 0xbadcafe; return -1; } static void prv_crash_timer_handler(struct k_timer *dummy) { volatile uint32_t *bad_ptr = (void *)0x1; *bad_ptr = 0xbadcafe; } MEMFAULT_ENABLE_WARNING("-Warray-bounds") K_TIMER_DEFINE(s_isr_crash_timer, prv_crash_timer_handler, NULL); static int prv_timer_isr_crash_example(const struct shell *shell, size_t argc, char **argv) { k_timer_start(&s_isr_crash_timer, K_MSEC(10), K_MSEC(10)); return 0; } static void prv_hang_timer_handler(struct k_timer *dummy) { while (1) { k_busy_wait(1000); } } K_TIMER_DEFINE(s_isr_hang_timer, prv_hang_timer_handler, NULL); static int prv_timer_isr_hang_example(const struct shell *shell, size_t argc, char **argv) { #if !defined(CONFIG_WATCHDOG) shell_print(shell, "Hanging in ISR. Warning, no watchdog configured, this may hang forever!"); #else shell_print(shell, "Hanging in ISR, waiting for watchdog to trigger"); #endif k_timer_start(&s_isr_hang_timer, K_MSEC(10), K_MSEC(10)); return 0; } #if defined(CONFIG_MEMFAULT_SHELL_SELF_TEST) static int prv_self_test(MEMFAULT_UNUSED const struct shell *shell, size_t argc, char **argv) { return memfault_demo_cli_cmd_self_test(argc, argv); } #endif #if defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD) && !defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_LOGS) static int prv_upload_logs(const struct shell *shell, size_t argc, char **argv) { if (argc < 2) { shell_print(shell, "Enter 'enable' or 'disable'"); return 0; } if (!strcmp(argv[1], "enable")) { memfault_zephyr_port_http_periodic_upload_logs(true); shell_print(shell, "Periodic log upload enabled"); } else if (!strcmp(argv[1], "disable")) { memfault_zephyr_port_http_periodic_upload_logs(false); shell_print(shell, "Periodic log upload disabled"); } else { shell_print(shell, "Unknown option. Enter 'enable' or 'disable'"); } return 0; } #endif SHELL_STATIC_SUBCMD_SET_CREATE( sub_memfault_crash_cmds, //! different crash types that should result in a coredump being collected #if defined(CONFIG_ARM) SHELL_CMD(busfault, NULL, "trigger a busfault", prv_busfault_example), SHELL_CMD(hardfault, NULL, "trigger a hardfault", prv_hardfault_example), SHELL_CMD(memmanage, NULL, "trigger a memory management fault", prv_memmanage_example), SHELL_CMD(usagefault, NULL, "trigger a usage fault", prv_usagefault_example), #endif // CONFIG_ARM SHELL_CMD(hang, NULL, "trigger a hang", prv_hang_example), SHELL_CMD(zassert, NULL, "trigger a zephyr assert", prv_zephyr_assert_example), SHELL_CMD(stack_overflow, NULL, "trigger a stack overflow", prv_zephyr_stack_overflow_example), SHELL_CMD(assert, NULL, "trigger memfault assert", prv_memfault_assert_example), SHELL_CMD(loadaddr, NULL, "test a 32 bit load from an address", prv_zephyr_load_32bit_address), SHELL_CMD(double_free, NULL, "trigger a double free error", prv_cli_cmd_double_free), SHELL_CMD(badptr, NULL, "trigger fault via store to a bad address", prv_bad_ptr_deref_example), SHELL_CMD(isr_badptr, NULL, "trigger fault via store to a bad address from an ISR", prv_timer_isr_crash_example), SHELL_CMD(isr_hang, NULL, "trigger a hang in an ISR", prv_timer_isr_hang_example), //! user initiated reboot SHELL_CMD(reboot, NULL, "trigger a reboot and record it using memfault", prv_test_reboot), //! memfault data source test commands SHELL_CMD(heartbeat, NULL, "trigger an immediate capture of all heartbeat metrics", prv_trigger_heartbeat), SHELL_CMD(log_capture, NULL, "trigger capture of current log buffer contents", prv_trigger_logs), SHELL_CMD(logs, NULL, "writes test logs to log buffer", prv_test_log), #if defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD) && !defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_LOGS) SHELL_CMD(logs_upload, NULL, "enable/disable periodic upload of logs", prv_upload_logs), #endif SHELL_CMD(trace, NULL, "capture an example trace event", prv_example_trace_event_capture), #if defined(CONFIG_MEMFAULT_SHELL_SELF_TEST) SHELL_CMD(self, NULL, "test Memfault components on-device", prv_self_test), #endif SHELL_SUBCMD_SET_END /* Array terminated. */ ); SHELL_STATIC_SUBCMD_SET_CREATE( sub_memfault_cmds, SHELL_CMD(clear_core, NULL, "clear coredump collected", prv_clear_core_cmd), SHELL_CMD(export, NULL, "dump chunks collected by Memfault SDK using https://mflt.io/chunk-data-export", prv_chunk_data_export), SHELL_CMD(get_core, NULL, "check if coredump is stored and present", prv_get_core_cmd), SHELL_CMD(get_device_info, NULL, "display device information", prv_get_device_info), SHELL_CMD(get_reboot_reason, NULL, "display last reboot reason", prv_get_reboot_reason), SHELL_CMD(get_latest_url, NULL, "gets latest release URL", prv_get_latest_url_cmd), SHELL_CMD(get_latest_release, NULL, "performs an OTA update using Memfault client", prv_check_and_fetch_ota_payload_cmd), SHELL_CMD(coredump_size, NULL, "print coredump computed size and storage capacity", prv_coredump_size), SHELL_CMD_ARG(metrics_dump, NULL, "dump current heartbeat or session metrics", prv_metrics_dump, 1, 1), #if defined(CONFIG_NETWORKING) SHELL_CMD(post_chunks, NULL, "Post Memfault data to cloud", prv_post_data), #endif SHELL_CMD(test, &sub_memfault_crash_cmds, "commands to verify memfault data collection (https://mflt.io/mcu-test-commands)", NULL), SHELL_SUBCMD_SET_END /* Array terminated. */ ); SHELL_CMD_REGISTER(mflt, &sub_memfault_cmds, "Memfault Test Commands", NULL); ================================================ FILE: ports/zephyr/common/memfault_logging.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Hooks the Memfault logging API to zephyr's latest (V2) logging system. //! As of Zephyr 3.2, it's the only logging system that can be used. // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(sys/atomic.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log_backend.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log_output.h) #include #include #include #include #include "memfault/components.h" #include "memfault/ports/zephyr/log_backend.h" #include "memfault/ports/zephyr/version.h" // clang-format on // Deal with CONFIG_LOG_IMMEDIATE getting renamed to CONFIG_LOG_MODE_IMMEDIATE in v3.0.0 release: // https://github.com/zephyrproject-rtos/zephyr/commit/262cc55609b73ea61b5f999c6c6daaba20bc5240 #if defined(CONFIG_LOG_IMMEDIATE) && !defined(CONFIG_LOG_MODE_IMMEDIATE) #define CONFIG_LOG_MODE_IMMEDIATE CONFIG_LOG_IMMEDIATE #endif // Can't be zero but should be reasonably sized. See ports/zephyr/Kconfig to change this size. BUILD_ASSERT(CONFIG_MEMFAULT_LOGGING_RAM_SIZE); // Immediate mode requires reentrancy support in the logging backend. It's very // low overhead, so to keep things simple enable the protection in deferred mode // too. enum { MFLT_LOG_BUFFER_BUSY, }; static atomic_t s_log_output_busy_flag; // Define a log_output_mflt_control_block and log_output_mflt that references // log_output_mflt_control_block via pointer. First arg to LOG_OUTPUT_DEFINE() // is a token that the C preprocessor uses to create symbol names, // e.g. struct log_output n and struct log_output_control_block n_control_block. // Within the log_output_control_block there is a void *ctx we can use to store // info we need when called by Zephyr by calling log_output_ctx_set(). static int prv_log_out(uint8_t *data, size_t length, void *ctx); static uint8_t s_zephyr_render_buf[128]; LOG_OUTPUT_DEFINE(s_log_output_mflt, prv_log_out, s_zephyr_render_buf, sizeof(s_zephyr_render_buf)); // store the log backend ID, so we can disable it during the panic handler const struct log_backend *s_memfault_log_backend; // Zephyr will call our init function so we can establish some storage. static void prv_log_init(const struct log_backend *const backend) { // static RAM storage where logs will be stored. Storage can be any size // you want but you will want it to be able to hold at least a couple logs. static uint8_t s_mflt_log_buf_storage[CONFIG_MEMFAULT_LOGGING_RAM_SIZE]; memfault_log_boot(s_mflt_log_buf_storage, sizeof(s_mflt_log_buf_storage)); // stash the log backend handle so we can disable it during the panic handler s_memfault_log_backend = backend; } // Intentionally a no-op. In immediate mode, we can't safely re-enter // prv_log_out() to flush any buffered log data, and in deferred mode, the // full prv_log_process() function is called, which has reentrancy protection. // See here: // https://github.com/zephyrproject-rtos/zephyr/blob/v3.5.0/subsys/logging/log_core.c#L393-L403 static void prv_log_panic(struct log_backend const *const backend) { } void memfault_zephyr_log_backend_disable(void) { log_backend_deactivate(s_memfault_log_backend); } static eMemfaultPlatformLogLevel prv_map_zephyr_level_to_memfault(uint32_t zephyr_level) { // Map From To return zephyr_level == LOG_LEVEL_ERR ? kMemfaultPlatformLogLevel_Error : zephyr_level == LOG_LEVEL_WRN ? kMemfaultPlatformLogLevel_Warning : zephyr_level == LOG_LEVEL_INF ? kMemfaultPlatformLogLevel_Info : /* LOG_LEVEL_DBG */ kMemfaultPlatformLogLevel_Debug; } typedef struct MfltLogProcessCtx { eMemfaultPlatformLogLevel memfault_level; #if CONFIG_LOG_MODE_IMMEDIATE int write_idx; bool flushed; #endif } sMfltLogProcessCtx; // LOG2 was added in Zephyr 2.6: // https://github.com/zephyrproject-rtos/zephyr/commit/f1bb20f6b43b8b241e45f3f132f0e7bbfc65401b // LOG2 was moved to LOG in Zephyr 3.2 // https://github.com/zephyrproject-rtos/zephyr/issues/46500 #if !MEMFAULT_ZEPHYR_VERSION_GT(3, 1) #define log_msg_generic log_msg2_generic #define log_output_msg_process log_output_msg2_process #define log_msg_get_level log_msg2_get_level #endif static void prv_log_process(const struct log_backend *const backend, union log_msg_generic *msg) { // This can be called in IMMEDIATE mode from an ISR or when flushing logs via LOG_PANIC, so // immediately bail. We currently can't safely serialize to the Memfault buffer from ISR context if (memfault_arch_is_inside_isr()) { return; } // Copied flagging from Zephry's ring buffer (rb) implementation. const uint32_t flags = IS_ENABLED(CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP) ? LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP | LOG_OUTPUT_FLAG_LEVEL : LOG_OUTPUT_FLAG_LEVEL; // Note: This is going to trigger calls to our prv_log_out() handler const uint32_t zephyr_level = log_msg_get_level(&msg->log); // This only needs to stay in scope for this function since log_output_msg_process() // calls prv_log_out() directly which is the only place we access the context sMfltLogProcessCtx log_process_ctx = { .memfault_level = prv_map_zephyr_level_to_memfault(zephyr_level), }; if (atomic_test_and_set_bit(&s_log_output_busy_flag, MFLT_LOG_BUFFER_BUSY)) { return; } log_output_ctx_set(&s_log_output_mflt, &log_process_ctx); log_output_msg_process(&s_log_output_mflt, &msg->log, flags); #if CONFIG_LOG_MODE_IMMEDIATE if (!log_process_ctx.flushed) { // In Zephyr 3.7.0, prv_log_out() won't flush data. Call it one more time to // emulate the previous behavior. (void)prv_log_out(NULL, 0, &log_process_ctx); } #endif atomic_clear_bit(&s_log_output_busy_flag, MFLT_LOG_BUFFER_BUSY); } static void prv_log_dropped(const struct log_backend *const backend, uint32_t cnt) { ARG_UNUSED(backend); log_output_dropped_process(&s_log_output_mflt, cnt); } const struct log_backend_api log_backend_mflt_api = { .process = prv_log_process, .panic = prv_log_panic, .init = prv_log_init, // dropped handler is only used in deferred mode, so save some space by not // including it when immediate mode is enabled. .dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : prv_log_dropped, }; // Define a couple of structs needed by the logging backend infrastructure. // Binds our log_backend_mflt_api into the logger. LOG_BACKEND_DEFINE(log_backend_mflt, log_backend_mflt_api, true); // Tie Memfault's log function to the Zephyr buffer sender. This is *the* connection to Memfault. static int prv_log_out(uint8_t *data, size_t length, void *ctx) { // In IMMEDIATE mode, logging can occur from ISRs. The zephyr fault handlers are chatty so don't // save info while in an ISR to avoid wrapping over the info we are collecting. This function may // also be run from LOG_PANIC. We also want to skip saving data in this case because we currently // can't safely serialize to the Memfault buffer from ISR context and the context object uses // local stack memory which may not be valid when run from LOG_PANIC if (memfault_arch_is_inside_isr()) { return (int)length; } sMfltLogProcessCtx *mflt_ctx = (sMfltLogProcessCtx *)ctx; size_t save_length = length; #if CONFIG_LOG_MODE_IMMEDIATE // A check to help us catch if behavior changes in a future release of Zephyr MEMFAULT_SDK_ASSERT(length <= 1); // A few notes about immediate mode: // // 1. We are going to re-purpose "s_zephyr_render_buf" as it is never actually used by the // Zephyr logging stack in immediate mode and we don't want to waste extra ram: // https://github.com/zephyrproject-rtos/zephyr/blob/15fdee04e3daf4d63064e4195aeeef6ccc52e694/subsys/logging/log_output.c#L105-L112 // // 2. We will use length == 0 to determine that log has been entirely // flushed. On Zephyr 3.7.0, a change was made to no longer issue a zero-length // log_output_flush().log_output_msg_process() always calls log_output_flush() but in // immediate mode there is no buffer filled to be flushed so the length will be zero if (length > 0 && mflt_ctx->write_idx < sizeof(s_zephyr_render_buf)) { s_zephyr_render_buf[mflt_ctx->write_idx] = data[0]; mflt_ctx->write_idx++; } const bool flush = (length == 0); if (!flush) { return (int)length; } // Strip EOL characters at end of log since we are storing _lines_ save_length = mflt_ctx->write_idx; while ((save_length - 1) > 0) { char c = s_zephyr_render_buf[save_length - 1]; if ((c == '\r') || (c == '\n')) { save_length--; continue; } break; } mflt_ctx->flushed = true; data = &s_zephyr_render_buf[0]; #endif // Note: Context should always be populated via our call to log_output_ctx_set() above. // Assert to catch any behavior changes in future versions of Zephyr MEMFAULT_SDK_ASSERT(mflt_ctx != NULL); memfault_log_save_preformatted_nolock(mflt_ctx->memfault_level, data, save_length); return (int)length; } ================================================ FILE: ports/zephyr/common/memfault_logging_legacy.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Hooks the Memfault logging API to zephyr's "V1" logging system. //! which was completely deprecated after the Zephyr 3.1 release. // clang-format off #include "memfault/config.h" #include "memfault/components.h" #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log_backend.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log_output.h) #include #include #include #include // clang-format on // Deal with CONFIG_LOG_IMMEDIATE getting renamed to CONFIG_LOG_MODE_IMMEDIATE in v3.0.0 release: // https://github.com/zephyrproject-rtos/zephyr/commit/262cc55609b73ea61b5f999c6c6daaba20bc5240 #if defined(CONFIG_LOG_IMMEDIATE) && !defined(CONFIG_LOG_MODE_IMMEDIATE) #define CONFIG_LOG_MODE_IMMEDIATE CONFIG_LOG_IMMEDIATE #endif // Can't be zero but should be reasonably sized. See ports/zephyr/Kconfig to change this size. BUILD_ASSERT(CONFIG_MEMFAULT_LOGGING_RAM_SIZE); // Define a log_output_mflt_control_block and log_output_mflt that references // log_output_mflt_control_block via pointer. First arg to LOG_OUTPUT_DEFINE() // is a token that the C preprocessor uses to create symbol names, // e.g. struct log_output n and struct log_output_control_block n_control_block. // Within the log_output_control_block there is a void *ctx we can use to store // info we need when called by Zephyr by calling log_output_ctx_set(). static uint8_t s_zephyr_render_buf[128]; static int prv_log_out(uint8_t *data, size_t length, void *ctx); LOG_OUTPUT_DEFINE(s_log_output_mflt, prv_log_out, s_zephyr_render_buf, sizeof(s_zephyr_render_buf)); // Construct our backend API object. Might need to check how/if we want to support // put_sync_string() & dropped(). static void prv_log_put(const struct log_backend *const backend, struct log_msg *msg); static void prv_log_put_sync_string(const struct log_backend *const backend, struct log_msg_ids src_level, uint32_t timestamp, const char *fmt, va_list ap); static void prv_log_panic(struct log_backend const *const backend); 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 = { .put = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : prv_log_put, .put_sync_string = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? prv_log_put_sync_string : NULL, // Note: We don't want to clutter Memfault circular buffer with hex dumps .put_sync_hexdump = NULL, .panic = prv_log_panic, .init = prv_log_init, .dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : prv_log_dropped, }; // Define a couple of structs needed by the logging backend infrastructure. // Binds our log_backend_mflt_api into the logger. LOG_BACKEND_DEFINE(log_backend_mflt, log_backend_mflt_api, true); // Tie Memfault's log function to the Zephyr buffer sender. This is *the* connection to Memfault. static int prv_log_out(uint8_t *data, size_t length, void *ctx) { if (memfault_arch_is_inside_isr()) { // We can't safely log from an ISR, so just drop the message return (int)length; } // Note: Context should always be populated. If it is not, flag the log as an _Error const eMemfaultPlatformLogLevel log_level = ctx != NULL ? *(eMemfaultPlatformLogLevel *)ctx : kMemfaultPlatformLogLevel_Error; memfault_log_save_preformatted(log_level, data, length); return (int)length; } static eMemfaultPlatformLogLevel prv_map_zephyr_level_to_memfault(uint32_t zephyr_level) { // Map From To return zephyr_level == LOG_LEVEL_ERR ? kMemfaultPlatformLogLevel_Error : zephyr_level == LOG_LEVEL_WRN ? kMemfaultPlatformLogLevel_Warning : zephyr_level == LOG_LEVEL_INF ? kMemfaultPlatformLogLevel_Info : /* LOG_LEVEL_DBG */ kMemfaultPlatformLogLevel_Debug; } // *** Below are the implementations for the Zephyr backend API *** // Zephyr API function. I'm assuming has been validated by the time put() is called. static void prv_log_put(const struct log_backend *const backend, struct log_msg *msg) { // Copied flagging from Zephry's ring buffer (rb) implementation. const uint32_t flags = IS_ENABLED(CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP) ? LOG_OUTPUT_FLAG_FORMAT_TIMESTAMP | LOG_OUTPUT_FLAG_LEVEL : LOG_OUTPUT_FLAG_LEVEL; // Acquire, process (eventually calls prv_data_out()) and release the message. log_msg_get(msg); const uint32_t zephyr_level = log_msg_level_get(msg); if (zephyr_level != LOG_LEVEL_NONE) { // We might log so figure out if Memfault logging level for when Zephyr calls us back. eMemfaultPlatformLogLevel memfault_level = prv_map_zephyr_level_to_memfault(zephyr_level); log_output_ctx_set(&s_log_output_mflt, &memfault_level); log_output_msg_process(&s_log_output_mflt, msg, flags); } log_msg_put(msg); } static void prv_log_put_sync_string(const struct log_backend *const backend, struct log_msg_ids src_level, uint32_t timestamp, const char *fmt, va_list ap) { if (memfault_arch_is_inside_isr()) { // In synchronous mode, logging can occur from ISRs. The zephyr fault handlers are chatty so // don't save info while in an ISR to avoid wrapping over the info we are collecting. return; } // Note: Zephyr's log_output_string() API dumps 1 char at a time to put() so // we use memfault logging directly instead eMemfaultPlatformLogLevel memfault_level = prv_map_zephyr_level_to_memfault(src_level.level); memfault_vlog_save(memfault_level, fmt, ap); } static void prv_log_panic(struct log_backend const *const backend) { log_output_flush(&s_log_output_mflt); } // Zephyr will call our init function so we can establish some storage. static void prv_log_init() { // static RAM storage where logs will be stored. Storage can be any size // you want but you will want it to be able to hold at least a couple logs. static uint8_t s_mflt_log_buf_storage[CONFIG_MEMFAULT_LOGGING_RAM_SIZE]; memfault_log_boot(s_mflt_log_buf_storage, sizeof(s_mflt_log_buf_storage)); } static void prv_log_dropped(const struct log_backend *const backend, uint32_t cnt) { ARG_UNUSED(backend); log_output_dropped_process(&s_log_output_mflt, cnt); } ================================================ FILE: ports/zephyr/common/memfault_logging_minimal.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Hooks the Memfault log collection to the Zephyr LOG_MODE_MINIMAL logging //! system. // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(sys/atomic.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include #include "memfault/components.h" // clang-format on enum { MFLT_LOG_BUFFER_BUSY, }; static atomic_t s_log_output_busy_flag; static void prv_log_init(void) { // Static RAM storage where logs will be stored. static uint8_t s_mflt_log_buf_storage[CONFIG_MEMFAULT_LOGGING_RAM_SIZE]; memfault_log_boot(s_mflt_log_buf_storage, sizeof(s_mflt_log_buf_storage)); } static void prv_memfault_log_vprintf(const char *fmt, va_list args) { // Force synchronization at this level. if (atomic_test_and_set_bit(&s_log_output_busy_flag, MFLT_LOG_BUFFER_BUSY)) { return; } // This is idempotent; but sensitive to concurrent access. The atomic flag // that protects this function should be good enough, since it only has to // succeed once. The vulnerability is only if a user is accessing // memfault_log_save_preformatted_nolock() directly (should not be done). prv_log_init(); // Render the log message into a buffer. Static storage class, so it doesn't // contribute to stack usage which could be problematic in this call site. static uint8_t s_zephyr_render_buf[128]; int save_length = vsnprintk(s_zephyr_render_buf, sizeof(s_zephyr_render_buf), fmt, args); if (save_length > 0) { // Always set to info, since we can't reliably determine the log level memfault_log_save_preformatted_nolock(kMemfaultPlatformLogLevel_Info, s_zephyr_render_buf, save_length); } atomic_clear_bit(&s_log_output_busy_flag, MFLT_LOG_BUFFER_BUSY); } void __wrap_z_log_minimal_printk(const char *fmt, ...) { va_list args; va_start(args, fmt); prv_memfault_log_vprintf(fmt, args); va_start(args, fmt); // Now call zephyr printk z_log_minimal_vprintk(fmt, args); va_end(args); } // Ensure the substituted function signature matches the original function _Static_assert(__builtin_types_compatible_p(__typeof__(&z_log_minimal_printk), __typeof__(&__wrap_z_log_minimal_printk)), "Error: check z_log_minimal_printk function signature"); ================================================ FILE: ports/zephyr/common/memfault_mcumgr.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! MCUmgr group handler for Memfault device information and project key access #include "memfault/ports/zephyr/memfault_mcumgr.h" #include #include #include #include #include #include #include #include #include #include "memfault/components.h" LOG_MODULE_REGISTER(mcumgr_memfault_grp, CONFIG_MEMFAULT_LOG_LEVEL); //! User-settable group access callback and argument static struct { memfault_mgmt_access_cb_t cb; void *user_arg; } s_memfault_mgmt_access = { .cb = NULL, .user_arg = NULL, }; static bool prv_memfault_mgmt_is_access_allowed(void) { if (s_memfault_mgmt_access.cb != NULL) { return s_memfault_mgmt_access.cb(s_memfault_mgmt_access.user_arg); } return true; } void memfault_mcumgr_set_access_callback(memfault_mgmt_access_cb_t cb, void *user_arg) { s_memfault_mgmt_access.cb = cb; s_memfault_mgmt_access.user_arg = user_arg; } //! Command handler for reading device information. //! Returns device_serial, hardware_version, software_type, and current_version //! from memfault_platform_get_device_info() static int memfault_mgmt_device_info(struct smp_streamer *ctxt) { if (!prv_memfault_mgmt_is_access_allowed()) { return MGMT_ERR_EACCESSDENIED; } zcbor_state_t *zse = ctxt->writer->zs; sMemfaultDeviceInfo device_info = { 0 }; // Get device information from the Memfault platform API memfault_platform_get_device_info(&device_info); // Store ternary results in temporary variables to avoid redundant evaluation const char *device_serial = device_info.device_serial ? device_info.device_serial : ""; const char *hardware_version = device_info.hardware_version ? device_info.hardware_version : ""; const char *software_type = device_info.software_type ? device_info.software_type : ""; const char *software_version = device_info.software_version ? device_info.software_version : ""; // Encode the response as CBOR bool ok = zcbor_tstr_put_lit(zse, "device_serial") && zcbor_tstr_put_term(zse, device_serial, strlen(device_serial)) && zcbor_tstr_put_lit(zse, "hardware_version") && zcbor_tstr_put_term(zse, hardware_version, strlen(hardware_version)) && zcbor_tstr_put_lit(zse, "software_type") && zcbor_tstr_put_term(zse, software_type, strlen(software_type)) && zcbor_tstr_put_lit(zse, "current_version") && zcbor_tstr_put_term(zse, software_version, strlen(software_version)); return (ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE); } //! Command handler for reading the Memfault project key. //! Returns the project key string from Kconfig. static int memfault_mgmt_project_key(struct smp_streamer *ctxt) { if (!prv_memfault_mgmt_is_access_allowed()) { return MGMT_ERR_EACCESSDENIED; } zcbor_state_t *zse = ctxt->writer->zs; #if defined(CONFIG_MEMFAULT_NCS_PROJECT_KEY) const char *project_key = CONFIG_MEMFAULT_NCS_PROJECT_KEY; #elif defined(CONFIG_MEMFAULT_PROJECT_KEY) const char *project_key = CONFIG_MEMFAULT_PROJECT_KEY; #else // Fallback to NULL if the project key is not configured in Kconfig. The // handler will return an error response in this case. #warning "No Memfault project key configured in Kconfig. Please set " \ "CONFIG_MEMFAULT_PROJECT_KEY or CONFIG_MEMFAULT_NCS_PROJECT_KEY to your " \ "Memfault project key https://mflt.io/project-key" const char *project_key = NULL; #endif bool ok; // Check if project key is configured if (!project_key || strlen(project_key) == 0) { ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_MEMFAULT, MEMFAULT_MGMT_ERR_NO_PROJECT_KEY); return (ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE); } // Encode the response as CBOR ok = zcbor_tstr_put_lit(zse, "project_key") && zcbor_tstr_put_term(zse, project_key, strlen(project_key)); return (ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE); } #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL //! Translate SMP version 2 error codes to legacy MCUmgr error codes. //! Only included if support for the original protocol is enabled. static int memfault_mgmt_translate_error_code(uint16_t err) { int rc; switch (err) { case MEMFAULT_MGMT_ERR_NO_PROJECT_KEY: rc = MGMT_ERR_ENOENT; break; case MEMFAULT_MGMT_ERR_UNKNOWN: default: rc = MGMT_ERR_EUNKNOWN; } return rc; } #endif static const struct mgmt_handler memfault_mgmt_handlers[] = { [MEMFAULT_MGMT_ID_DEVICE_INFO] = { .mh_read = memfault_mgmt_device_info, .mh_write = NULL, }, [MEMFAULT_MGMT_ID_PROJECT_KEY] = { .mh_read = memfault_mgmt_project_key, .mh_write = NULL, }, }; static struct mgmt_group memfault_mgmt_group = { .mg_handlers = memfault_mgmt_handlers, .mg_handlers_count = ARRAY_SIZE(memfault_mgmt_handlers), .mg_group_id = MGMT_GROUP_ID_MEMFAULT, #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL .mg_translate_error = memfault_mgmt_translate_error_code, #endif }; static void memfault_mgmt_register_group(void) { mgmt_register_group(&memfault_mgmt_group); } MCUMGR_HANDLER_DEFINE(memfault_mgmt, memfault_mgmt_register_group); ================================================ FILE: ports/zephyr/common/memfault_periodic_upload.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #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) #else #include MEMFAULT_ZEPHYR_INCLUDE(random/rand32.h) #endif #include "memfault/core/data_packetizer.h" #include "memfault/core/debug_log.h" #include "memfault/ports/zephyr/periodic_upload.h" #include "memfault/ports/zephyr/http.h" #if defined(CONFIG_MEMFAULT_PERIODIC_FOTA_CHECK) #include "memfault/ports/zephyr/fota.h" #endif // clang-format on static bool s_mflt_upload_enabled = IS_ENABLED(CONFIG_MEMFAULT_PERIODIC_UPLOAD_ENABLED_DEFAULT); #if CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE static K_THREAD_STACK_DEFINE(memfault_periodic_upload_stack_area, CONFIG_MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_STACK_SIZE); static struct k_work_q memfault_periodic_upload_work_q; MEMFAULT_STATIC_ASSERT(CONFIG_MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_PRIORITY <= K_LOWEST_APPLICATION_THREAD_PRIO, "Invalid priority for dedicated upload workqueue. Set to " "K_LOWEST_APPLICATION_THREAD_PRIO or higher (numerically lower)."); #endif #if !defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_LOGS) // Runtime configurable static bool s_mflt_upload_logs; #endif #if !defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_LOGS) void memfault_zephyr_port_periodic_upload_logs(bool enable) { s_mflt_upload_logs = enable; } #endif // API to enable/disable all periodic uploads at runtime void memfault_zephyr_port_periodic_upload_enable(bool enable) { s_mflt_upload_enabled = enable; } bool memfault_zephyr_port_periodic_upload_enabled(void) { return s_mflt_upload_enabled; } static void prv_periodic_upload_work_handler(struct k_work *work) { if (!s_mflt_upload_enabled) { return; } #if defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_LOGS) MEMFAULT_LOG_DEBUG("Triggering log collection"); memfault_log_trigger_collection(); #else if (s_mflt_upload_logs) { MEMFAULT_LOG_DEBUG("Triggering log collection"); memfault_log_trigger_collection(); } #endif // Upload any staged data first, before executing FOTA check. FOTA may trigger // a reboot, so we want all data uploaded before that happens. if (memfault_packetizer_data_available()) { MEMFAULT_LOG_DEBUG("POSTing Memfault Data"); ssize_t rv = memfault_zephyr_port_post_data_return_size(); if (rv > 0) { MEMFAULT_LOG_DEBUG("Uploaded %zd bytes of Memfault data", rv); } } else { MEMFAULT_LOG_DEBUG("No Memfault data available"); } #if defined(CONFIG_MEMFAULT_PERIODIC_FOTA_CHECK) // FOTA check always runs, even when there's no data to upload int ret = memfault_zephyr_fota_start(); if (ret < 0) { MEMFAULT_LOG_ERROR("Error checking for FOTA update, rv=%d", ret); } #endif } K_WORK_DEFINE(s_upload_timer_work, prv_periodic_upload_work_handler); static void prv_timer_expiry_handler(struct k_timer *dummy) { #if CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE k_work_submit_to_queue(&memfault_periodic_upload_work_q, &s_upload_timer_work); #else k_work_submit(&s_upload_timer_work); #endif } K_TIMER_DEFINE(s_upload_timer, prv_timer_expiry_handler, NULL); static int prv_background_upload_init() { const uint32_t interval_secs = CONFIG_MEMFAULT_PERIODIC_UPLOAD_INTERVAL_SECS; // Randomize the first post to spread out the reporting of // information from a fleet of devices that all reboot at once. // For very low values of CONFIG_MEMFAULT_PERIODIC_UPLOAD_INTERVAL_SECS // cut minimum delay of first post for better testing/demoing experience. const uint32_t duration_secs_minimum = interval_secs >= 60 ? 60 : 5; const uint32_t duration_secs = duration_secs_minimum + (sys_rand32_get() % interval_secs); k_timer_start(&s_upload_timer, K_SECONDS(duration_secs), K_SECONDS(interval_secs)); MEMFAULT_LOG_INFO("Periodic background upload scheduled - initial delay=%ds period=%ds", (int)duration_secs, (int)interval_secs); #if CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE struct k_work_queue_config config = { .name = "mflt_upload", .no_yield = false, }; k_work_queue_start(&memfault_periodic_upload_work_q, memfault_periodic_upload_stack_area, K_THREAD_STACK_SIZEOF(memfault_periodic_upload_stack_area), CONFIG_MEMFAULT_PERIODIC_UPLOAD_DEDICATED_WORKQUEUE_PRIORITY, &config); #endif return 0; } SYS_INIT(prv_background_upload_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); ================================================ FILE: ports/zephyr/common/memfault_platform_core.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #include "memfault/core/platform/core.h" #include #include #if defined(CONFIG_HWINFO) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/hwinfo.h) #endif #include MEMFAULT_ZEPHYR_INCLUDE(init.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log_ctrl.h) #include #if defined(CONFIG_MEMFAULT_DATETIME_TIMESTAMP_EVENT_CALLBACK) #include #include "memfault/ports/ncs/date_time_callback.h" #endif #include "memfault/components.h" #include "memfault/ports/reboot_reason.h" #include "memfault/ports/zephyr/core.h" #include "memfault/ports/zephyr/log_backend.h" #include "memfault/ports/zephyr/log_panic.h" #include MEMFAULT_ZEPHYR_INCLUDE(sys/__assert.h) #include "memfault/ports/zephyr/version.h" // clang-format on #if CONFIG_MEMFAULT_METRICS #include "memfault/metrics/metrics.h" #endif static const sMemfaultEventStorageImpl *s_memfault_event_storage; #if CONFIG_MEMFAULT_CACHE_FAULT_REGS // Zephy's z_arm_fault() function consumes and clears // the SCB->CFSR register so we must wrap their function // so we can preserve the pristine fault register values. void __wrap_z_arm_fault(uint32_t msp, uint32_t psp, uint32_t exc_return, _callee_saved_t *callee_regs) { #if defined(CONFIG_MEMFAULT_LOGGING_ENABLE) && \ (MEMFAULT_ZEPHYR_VERSION_GT(3, 1) || defined(CONFIG_LOG2)) // Trigger a LOG_PANIC() early to flush any buffered logs, then disable the // Memfault log backend to prevent any further logs from being captured // (primarily the Zephyr fault logs, which can fill up the Memfault log // buffer). Note that this approach won't work if the user has Logs enabled // but CONFIG_MEMFAULT_CACHE_FAULT_REGS=n, and the fault messages will end up // in the log buffer. That should be an unusual configuration, since the fault // register capture disable is a very small size optimization, and logs are // likely not used on devices with space constraints. MEMFAULT_LOG_PANIC(); #if !defined(CONFIG_LOG_MODE_MINIMAL) memfault_zephyr_log_backend_disable(); #endif #endif memfault_coredump_cache_fault_regs(); // Now let the Zephyr fault handler complete as normal. void __real_z_arm_fault(uint32_t msp, uint32_t psp, uint32_t exc_return, _callee_saved_t *callee_regs); __real_z_arm_fault(msp, psp, exc_return, callee_regs); } #endif #if defined(CONFIG_MEMFAULT_PLATFORM_TIME_SINCE_BOOT_K_UPTIME_GET) uint64_t memfault_platform_get_time_since_boot_ms(void) { return k_uptime_get(); } #endif //! Provide a strong implementation of assert_post_action for Zephyr's built-in //! __ASSERT() macro. #if CONFIG_MEMFAULT_CATCH_ZEPHYR_ASSERT #ifdef CONFIG_ASSERT_NO_FILE_INFO void assert_post_action(void) #else void assert_post_action(const char *file, unsigned int line) #endif { #ifndef CONFIG_ASSERT_NO_FILE_INFO ARG_UNUSED(file); ARG_UNUSED(line); #endif MEMFAULT_ASSERT(0); } #endif #if defined(CONFIG_MEMFAULT_METRICS_BOOT_TIME) #if defined(CONFIG_BOOTARGS) extern int __real_main(int, char **); int __wrap_main(int argc, char **argv); int __wrap_main(int argc, char **argv) { #else extern int __real_main(void); int __wrap_main(void); int __wrap_main(void) { #endif // Record the boot time once on startup, when main() is called. MEMFAULT_METRIC_SET_UNSIGNED(boot_time_ms, k_uptime_get()); #if defined(CONFIG_BOOTARGS) return __real_main(argc, argv); #else return __real_main(); #endif } #endif // CONFIG_MEMFAULT_METRICS_BOOT_TIME // On boot-up, log out any information collected as to why the // reset took place MEMFAULT_PUT_IN_SECTION(CONFIG_MEMFAULT_REBOOT_TRACKING_REGION) static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; static uint8_t s_event_storage[CONFIG_MEMFAULT_EVENT_STORAGE_SIZE]; #if defined(CONFIG_MEMFAULT_REBOOT_REASON_GET_HWINFO) static eMemfaultRebootReason prv_zephyr_to_memfault_reboot_reason(uint32_t reset_reason_reg) { // Some hwinfo device implementations will use a value of 0 (unset) to // implicitly specify a power on reset, for example the nRF52840: // https://docs.nordicsemi.com/bundle/ps_nrf52840/page/power.html#register.RESETREAS // // Only use this fallback when the hwinfo implementation does not explicitly // support a power on reset cause. uint32_t supported_reset_reasons; if ((reset_reason_reg == 0) && (hwinfo_get_supported_reset_cause(&supported_reset_reasons) == 0) && !(supported_reset_reasons & RESET_POR)) { return kMfltRebootReason_PowerOnReset; } // Map the Zephyr HWINFO reset reason to a Memfault reset reason. The order is // important- the first bit match will be used. // // We know that the Zephyr bits + Memfault reboot codes all fit within 16 // bits, so to cut the table size in half, use uint16_t. If either one grows // beyond 16 bits, this will need to be updated. // // A further optimization (that saves a few more bytes) would be to omit the // bitmask, since they're consecutive in the current zephyr implementation. // That would force us to keep the reset reason priority in the zephyr bit // order, so we'll keep the bitmask. eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; const struct hwinfo_bit_to_memfault_reset_reason { uint16_t hwinfo_bit; uint16_t memfault_reason; } s_hwinfo_to_memfault[] = { { RESET_BROWNOUT, kMfltRebootReason_BrownOutReset }, { RESET_POR, kMfltRebootReason_PowerOnReset }, { RESET_WATCHDOG, kMfltRebootReason_HardwareWatchdog }, { RESET_DEBUG, kMfltRebootReason_DebuggerHalted }, { RESET_SECURITY, kMfltRebootReason_SecurityViolation }, { RESET_LOW_POWER_WAKE, kMfltRebootReason_LowPower }, { RESET_CPU_LOCKUP, kMfltRebootReason_Lockup }, { RESET_PARITY, kMfltRebootReason_ParityError }, { RESET_PLL, kMfltRebootReason_ClockFailure }, { RESET_CLOCK, kMfltRebootReason_ClockFailure }, { RESET_HARDWARE, kMfltRebootReason_Hardware }, { RESET_USER, kMfltRebootReason_UserReset }, { RESET_TEMPERATURE, kMfltRebootReason_Temperature }, // Software reset is second lowest precedence since it can be set in // addition to other bits and is not as precise { RESET_SOFTWARE, kMfltRebootReason_SoftwareReset }, // Pin reset must be last; for STM32 SOCs, NRST is often wired internally to // SYSRESETREQ, which means the Pin Reset bit is always set for // software-initiated resets { RESET_PIN, kMfltRebootReason_PinReset }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_hwinfo_to_memfault); i++) { if (reset_reason_reg & (uint32_t)s_hwinfo_to_memfault[i].hwinfo_bit) { reset_reason = (eMemfaultRebootReason)s_hwinfo_to_memfault[i].memfault_reason; break; } } return reset_reason; } #endif // CONFIG_MEMFAULT_REBOOT_REASON_GET_HWINFO #if !defined(CONFIG_MEMFAULT_REBOOT_REASON_GET_CUSTOM) MEMFAULT_WEAK void memfault_reboot_reason_get(sResetBootupInfo *info) { eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown; uint32_t reset_reason_reg = 0; #if defined(CONFIG_MEMFAULT_REBOOT_REASON_GET_HWINFO) int rv = hwinfo_get_reset_cause(&reset_reason_reg); if (rv == 0) { reset_reason = prv_zephyr_to_memfault_reboot_reason(reset_reason_reg); #if defined(CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP) // Print out the reset reason to the console. const char *reset_reason_str = "Unknown"; static const struct { eMemfaultRebootReason reason; const char *str; } s_reset_reasons[] = { { kMfltRebootReason_PinReset, "Pin Reset" }, { kMfltRebootReason_SoftwareReset, "Software Reset" }, { kMfltRebootReason_BrownOutReset, "Brownout Reset" }, { kMfltRebootReason_PowerOnReset, "Power On Reset" }, { kMfltRebootReason_HardwareWatchdog, "Hardware Watchdog" }, { kMfltRebootReason_DebuggerHalted, "Debugger Halted" }, { kMfltRebootReason_SecurityViolation, "Security Violation" }, { kMfltRebootReason_LowPower, "Low Power" }, { kMfltRebootReason_Lockup, "Lockup" }, { kMfltRebootReason_ParityError, "Parity Error" }, { kMfltRebootReason_ClockFailure, "Clock Failure" }, { kMfltRebootReason_Hardware, "Hardware" }, { kMfltRebootReason_UserReset, "User Reset" }, { kMfltRebootReason_Temperature, "Temperature" }, { kMfltRebootReason_Unknown, "Unknown" }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_reset_reasons); i++) { if (s_reset_reasons[i].reason == reset_reason) { reset_reason_str = s_reset_reasons[i].str; break; } } MEMFAULT_LOG_INFO("Reset Reason, HWINFO=0x%04x", reset_reason_reg); MEMFAULT_LOG_INFO("Reset Cause: %s", reset_reason_str); #endif // CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP } #if defined(CONFIG_MEMFAULT_CLEAR_RESET_REG) (void)hwinfo_clear_reset_cause(); #endif #endif // CONFIG_MEMFAULT_REBOOT_REASON_GET_HWINFO *info = (sResetBootupInfo){ .reset_reason = reset_reason, .reset_reason_reg = reset_reason_reg, }; } #endif // !CONFIG_MEMFAULT_REBOOT_REASON_GET_CUSTOM void memfault_zephyr_collect_reset_info(void) { memfault_reboot_tracking_collect_reset_info(s_memfault_event_storage); } // This can be overridden by the application to set a custom device ID MEMFAULT_WEAK const char *memfault_zephyr_get_device_id(void) { uint8_t dev_id[16] = { 0 }; static char dev_id_str[sizeof(dev_id) * 2 + 1]; static const char *dev_str = "UNKNOWN"; // Obtain the device id #if defined(CONFIG_HWINFO) ssize_t length = hwinfo_get_device_id(dev_id, sizeof(dev_id)); #else ssize_t length = 0; #endif // If hwinfo_get_device_id() fails or isn't enabled, use a fallback string if (length <= 0) { #if defined(CONFIG_SOC) dev_str = CONFIG_SOC "-testserial"; #else dev_str = "testserial"; #endif length = strlen(dev_str); } else { // Render the obtained serial number in hexadecimal representation const size_t dev_id_len = (size_t)length > sizeof(dev_id) ? sizeof(dev_id) : (size_t)length; for (size_t i = 0; i < dev_id_len; i++) { (void)snprintf(&dev_id_str[i * 2], sizeof(dev_id_str) - (i * 2), "%02x", dev_id[i]); } dev_str = dev_id_str; } return dev_str; } void memfault_zephyr_get_device_info(sMemfaultDeviceInfo *info) { *info = (sMemfaultDeviceInfo){ .device_serial = memfault_zephyr_get_device_id(), .software_type = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_TYPE, .software_version = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_VERSION, .hardware_version = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_HARDWARE_VERSION, }; } MEMFAULT_WEAK void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { memfault_zephyr_get_device_info(info); } // Note: the function signature has changed here across zephyr releases // "struct device *dev" -> "const struct device *dev" // // Since we don't use the arguments we match anything with () to avoid // compiler warnings and share the same bootup logic static int prv_boot_memfault() { sResetBootupInfo reset_info = { 0 }; memfault_reboot_reason_get(&reset_info); memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); #if defined(CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP) sMfltRebootReason stored_reboot_reason; int rv = memfault_reboot_tracking_get_reboot_reason(&stored_reboot_reason); if (!rv) { MEMFAULT_LOG_INFO("Reset Cause Stored: 0x%04x", stored_reboot_reason.prior_stored_reason); } #endif // CONFIG_MEMFAULT_ENABLE_REBOOT_DIAG_DUMP s_memfault_event_storage = memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); #if defined(CONFIG_MEMFAULT_RECORD_REBOOT_ON_SYSTEM_INIT) memfault_zephyr_collect_reset_info(); #endif memfault_trace_event_boot(s_memfault_event_storage); #if CONFIG_MEMFAULT_METRICS sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), }; memfault_metrics_boot(s_memfault_event_storage, &boot_info); #endif #if defined(CONFIG_MEMFAULT_BATTERY_METRICS_BOOT_ON_SYSTEM_INIT) memfault_metrics_battery_boot(); #endif #if defined(CONFIG_MEMFAULT_DATETIME_TIMESTAMP_EVENT_CALLBACK) // Register a callback to get the current time from the Zephyr Date-Time library date_time_register_handler(memfault_zephyr_date_time_evt_handler); #endif memfault_build_info_dump(); return 0; } #if CONFIG_MEMFAULT_HEAP_STATS && CONFIG_HEAP_MEM_POOL_SIZE > 0 extern void *__real_k_malloc(size_t size); extern void __real_k_free(void *ptr); void *__wrap_k_malloc(size_t size) { void *ptr = __real_k_malloc(size); // Only call into heap stats from non-ISR context // Heap stats requires holding a lock if (!k_is_in_isr()) { MEMFAULT_HEAP_STATS_MALLOC(ptr, size); } return ptr; } void __wrap_k_free(void *ptr) { // Only call into heap stats from non-ISR context // Heap stats requires holding a lock if (!k_is_in_isr()) { MEMFAULT_HEAP_STATS_FREE(ptr); } __real_k_free(ptr); } #endif SYS_INIT(prv_boot_memfault, #if CONFIG_MEMFAULT_INIT_LEVEL_POST_KERNEL POST_KERNEL, #else APPLICATION, #endif CONFIG_MEMFAULT_INIT_PRIORITY); ================================================ FILE: ports/zephyr/common/memfault_platform_coredump_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! The default regions to collect on Zephyr when a crash takes place. //! Function is defined as weak so an end user can override it. // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel_structs.h) #include "memfault/panics/arch/arm/cortex_m.h" #include "memfault/panics/platform/coredump.h" #include "memfault/ports/zephyr/version.h" #if defined(CONFIG_ARM) #if MEMFAULT_ZEPHYR_VERSION_GT(3, 4) #include #else #include MEMFAULT_ZEPHYR_INCLUDE(arch/arm/aarch32/cortex_m/cmsis.h) #endif #endif #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/ports/zephyr/coredump.h" // clang-format on size_t memfault_zephyr_coredump_get_regions(const sCoredumpCrashInfo *crash_info, sMfltCoredumpRegion *regions, size_t num_regions) { // Check that regions is valid and has enough space to store all required regions if (regions == NULL || num_regions < MEMFAULT_ZEPHYR_COREDUMP_REGIONS) { return 0; } size_t region_idx = 0; #if CONFIG_MEMFAULT_COREDUMP_COLLECT_STACK_REGIONS size_t stack_size_to_collect = memfault_platform_sanitize_address_range( crash_info->stack_address, CONFIG_MEMFAULT_COREDUMP_ACTIVE_TASK_STACK_SIZE_TO_COLLECT); regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(crash_info->stack_address, stack_size_to_collect); region_idx++; #if defined(CONFIG_ARM) const bool msp_was_active = (crash_info->exception_reg_state->exc_return & (1 << 2)) == 0; if (msp_was_active) { // System crashed in an ISR but the running task state is on PSP so grab that too void *psp = (void *)(uintptr_t)__get_PSP(); // Collect a little bit more stack for the PSP since there is an // exception frame that will have been stacked on it as well const uint32_t extra_stack_bytes = 128; stack_size_to_collect = memfault_platform_sanitize_address_range( psp, CONFIG_MEMFAULT_COREDUMP_ACTIVE_TASK_STACK_SIZE_TO_COLLECT + extra_stack_bytes); regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(psp, stack_size_to_collect); region_idx++; } #endif // CONFIG_ARM #endif #if CONFIG_MEMFAULT_COREDUMP_COLLECT_KERNEL_REGION regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&_kernel, sizeof(_kernel)); region_idx++; #endif #if CONFIG_MEMFAULT_COREDUMP_COLLECT_TASKS_REGIONS region_idx += memfault_zephyr_get_task_regions(®ions[region_idx], num_regions - region_idx); #endif // // Now that we have captured all the task state, we will // fill whatever space remains in coredump storage with the // data and bss we can collect! // #if CONFIG_MEMFAULT_COREDUMP_COLLECT_DATA_REGIONS region_idx += memfault_zephyr_get_data_regions(®ions[region_idx], num_regions - region_idx); #endif #if CONFIG_MEMFAULT_COREDUMP_COLLECT_BSS_REGIONS region_idx += memfault_zephyr_get_bss_regions(®ions[region_idx], num_regions - region_idx); #endif return region_idx; } MEMFAULT_WEAK const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( const sCoredumpCrashInfo *crash_info, size_t *num_regions) { static sMfltCoredumpRegion s_coredump_regions[MEMFAULT_ZEPHYR_COREDUMP_REGIONS]; *num_regions = memfault_zephyr_coredump_get_regions(crash_info, s_coredump_regions, MEMFAULT_ARRAY_SIZE(s_coredump_regions)); return &s_coredump_regions[0]; } ================================================ FILE: ports/zephyr/common/memfault_platform_debug_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Maps memfault platform logging API to zephyr kernel logs #include "memfault/core/platform/debug_log.h" // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include #include "memfault/config.h" #include "memfault/ports/zephyr/version.h" #include MEMFAULT_ZEPHYR_INCLUDE(sys/printk.h) // clang-format on LOG_MODULE_REGISTER(mflt, CONFIG_MEMFAULT_LOG_LEVEL); #ifndef MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES #define MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES (128) #endif //! Translate Memfault logs to Zephyr logs. void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { va_list args; va_start(args, fmt); char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; vsnprintf(log_buf, sizeof(log_buf), fmt, args); char *log_str = log_buf; #if !MEMFAULT_ZEPHYR_VERSION_GT(3, 1) #if defined(CONFIG_LOG) && !defined(CONFIG_LOG2) // Before zephyr 3.1, LOG was a different option from LOG2 and required // manually duplicating string argument values. Only required if CONFIG_LOG is // in use. log_str = log_strdup(log_buf); #endif #endif // If the user doesn't have logging enabled, optionally route Memfault logs to // printk, allowing the user to also disable that behavior if no Memfault logs // are desired. #if CONFIG_MEMFAULT_PLATFORM_LOG_FALLBACK_TO_PRINTK #define MFT_LOG_DBG(fmt_, arg) printk(" mflt: " fmt_ "\n", arg) #define MFT_LOG_INF(fmt_, arg) printk(" mflt: " fmt_ "\n", arg) #define MFT_LOG_WRN(fmt_, arg) printk(" mflt: " fmt_ "\n", arg) #define MFT_LOG_ERR(fmt_, arg) printk(" mflt: " fmt_ "\n", arg) #else // Either the user has logging enabled, or they don't want to use printk (in // which case logs will be dropped via Zephyr log macros). #define MFT_LOG_DBG(...) LOG_DBG(__VA_ARGS__) #define MFT_LOG_INF(...) LOG_INF(__VA_ARGS__) #define MFT_LOG_WRN(...) LOG_WRN(__VA_ARGS__) #define MFT_LOG_ERR(...) LOG_ERR(__VA_ARGS__) #endif switch (level) { case kMemfaultPlatformLogLevel_Debug: MFT_LOG_DBG("%s", log_str); break; case kMemfaultPlatformLogLevel_Info: MFT_LOG_INF("%s", log_str); break; case kMemfaultPlatformLogLevel_Warning: MFT_LOG_WRN("%s", log_str); break; case kMemfaultPlatformLogLevel_Error: MFT_LOG_ERR("%s", log_str); break; default: MFT_LOG_ERR("??? %s", log_str); break; } va_end(args); } void memfault_platform_log_raw(const char *fmt, ...) { va_list args; va_start(args, fmt); char log_buf[MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES]; vsnprintf(log_buf, sizeof(log_buf), fmt, args); #if MEMFAULT_ZEPHYR_VERSION_GTE_STRICT(3, 0) LOG_PRINTK("%s\n", log_buf); #else printk("%s\n", log_buf); #endif va_end(args); } ================================================ FILE: ports/zephyr/common/memfault_platform_fota.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Performs an OTA update using the Memfault Zephyr HTTP client #include #include "memfault/core/platform/core.h" #if defined(CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_MCUBOOT) #include MEMFAULT_ZEPHYR_INCLUDE(dfu/flash_img.h) #include MEMFAULT_ZEPHYR_INCLUDE(dfu/mcuboot.h) #endif #include "memfault/components.h" #include "memfault/ports/zephyr/fota.h" #include "memfault/ports/zephyr/http.h" #if defined(CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_MCUBOOT) static bool s_fota_in_progress = false; typedef struct { sMemfaultOtaInfo info; struct flash_img_context flash_ctx; } sMemfaultOtaDownloadCtx; #if !defined(CONFIG_MEMFAULT_ZEPHYR_FOTA_DOWNLOAD_CALLBACK_CUSTOM) bool memfault_zephyr_fota_download_callback(void) { int rv = boot_request_upgrade(BOOT_UPGRADE_TEST); if (rv != 0) { MEMFAULT_LOG_DEBUG("Error requesting upgrade, rv=%d", rv); return false; } // Reboot to apply the update MEMFAULT_LOG_INFO("OTA download complete! Rebooting..."); MEMFAULT_REBOOT_MARK_RESET_IMMINENT(kMfltRebootReason_FirmwareUpdate); memfault_platform_reboot(); // We should never reach this point, but if we do, return false to indicate an error return false; } #endif static bool prv_handle_update_available(const sMemfaultOtaInfo *info, void *user_ctx) { sMemfaultOtaDownloadCtx *ctx = (sMemfaultOtaDownloadCtx *)user_ctx; ctx->info = *info; MEMFAULT_LOG_INFO("Downloading OTA payload, size=%d bytes", ctx->info.size); return true; } static bool prv_handle_data(void *buf, size_t buf_len, void *user_ctx) { sMemfaultOtaDownloadCtx *ctx = (sMemfaultOtaDownloadCtx *)user_ctx; int rv = flash_img_buffered_write(&ctx->flash_ctx, buf, buf_len, false); if (rv != 0) { MEMFAULT_LOG_DEBUG("Error writing to flash, rv=%d", rv); return false; } int bytes_written = flash_img_bytes_written(&ctx->flash_ctx); int percent_complete = (bytes_written * 100) / ctx->info.size; // todo int error possible? static int prev_percent_complete = 0; if ((percent_complete != prev_percent_complete)) { MEMFAULT_LOG_DEBUG("Downloaded %d/%d bytes (%d%%)", bytes_written, ctx->info.size, (bytes_written * 100) / ctx->info.size); } prev_percent_complete = percent_complete; return true; } static bool prv_handle_download_complete(void *user_ctx) { // This function will not return if the download was successful when using the default // implementation. However, we allow a return value for custom implementations that may // want to return the result to the calling context. return memfault_zephyr_fota_download_callback(); } int memfault_zephyr_fota_start(void) { // Ensure we don't start a new FOTA download if one is already in progress if (s_fota_in_progress) { MEMFAULT_LOG_INFO("FOTA already in progress"); return -1; } s_fota_in_progress = true; uint8_t working_buf[CONFIG_IMG_BLOCK_BUF_SIZE]; sMemfaultOtaDownloadCtx ctx; sMemfaultOtaUpdateHandler handler = { .buf = working_buf, .buf_len = sizeof(working_buf), .handle_update_available = prv_handle_update_available, .handle_data = prv_handle_data, .handle_download_complete = prv_handle_download_complete, .user_ctx = &ctx, }; // Initialize a new flash image context when OTA is started. Note this OTA implementation does // not support resuming an OTA download, but if that support were added, this initialization // should only be done once. The flash context stores the number of bytes written to flash so far // and the current offset, among other state variables, which allows picking up where the last OTA // download session left off. // // Also note: slot1_partition is the default slot used by this flash image writer. For more // flexibility, we may want to parameterize the area used for OTA updates, which is supported by // this interface with flash_img_init_id(, ). int rv = flash_img_init(&ctx.flash_ctx); if (rv != 0) { MEMFAULT_LOG_ERROR("Error preparing to write OTA to flash, rv=%d", rv); return rv; } rv = memfault_zephyr_port_ota_update(&handler); if (rv < 0) { MEMFAULT_LOG_ERROR("Error upgrading firmware, rv=%d", rv); } else if (rv == 0) { MEMFAULT_LOG_INFO("Up to date!"); } else { // Added for completeness - we likely won't reach this branch since we generally reboot in // handler.handle_download_complete() after a successful download MEMFAULT_LOG_INFO("Update successful!"); } s_fota_in_progress = false; return rv; } #endif /* CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_MCUBOOT */ #if defined(CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_DUMMY) typedef struct { size_t total_size; } sMemfaultOtaDownloadCtx; static bool prv_handle_update_available(const sMemfaultOtaInfo *info, void *user_ctx) { sMemfaultOtaDownloadCtx *ctx = (sMemfaultOtaDownloadCtx *)user_ctx; ctx->total_size = info->size; MEMFAULT_LOG_INFO("Downloading OTA payload, size=%d bytes", (int)ctx->total_size); return true; } static bool prv_handle_data(void *buf, size_t buf_len, void *user_ctx) { // this is an example cli command so we just drop the data on the floor // a real implementation could save the data in this callback! return true; } static bool prv_handle_download_complete(void *user_ctx) { MEMFAULT_LOG_INFO("OTA download complete!"); return true; } int memfault_zephyr_fota_start(void) { uint8_t working_buf[256]; sMemfaultOtaDownloadCtx user_ctx; sMemfaultOtaUpdateHandler handler = { .buf = working_buf, .buf_len = sizeof(working_buf), .user_ctx = &user_ctx, .handle_update_available = prv_handle_update_available, .handle_data = prv_handle_data, .handle_download_complete = prv_handle_download_complete, }; MEMFAULT_LOG_INFO("Checking for OTA update"); int rv = memfault_zephyr_port_ota_update(&handler); if (rv == 0) { MEMFAULT_LOG_INFO("Up to date!"); } else if (rv < 0) { MEMFAULT_LOG_INFO("OTA update failed, rv=%d, errno=%d", rv, errno); } return rv; } #endif /* CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_DUMMY */ ================================================ FILE: ports/zephyr/common/memfault_platform_http.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! // clang-format off #include #include #include #include #include #include MEMFAULT_ZEPHYR_INCLUDE(init.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #if !defined(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) #include MEMFAULT_ZEPHYR_INCLUDE(net/tls_credentials.h) #endif #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer.h" #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/http/http_client.h" #if !defined(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) #include "memfault/http/root_certs.h" #endif #include "memfault/http/utils.h" #include "memfault/metrics/connectivity.h" #include "memfault/panics/assert.h" #include "memfault/ports/zephyr/http.h" #if !defined(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) #include "memfault/ports/zephyr/root_cert_storage.h" #include "memfault/ports/zephyr/deprecated_root_cert.h" #endif #include "memfault/ports/zephyr/version.h" #if !defined(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) #if MEMFAULT_ZEPHYR_VERSION_GT_STRICT(4, 3) #if defined(CONFIG_MBEDTLS_BUILTIN) && !defined(CONFIG_PSA_WANT_ALG_SHA_1) #error "CONFIG_PSA_WANT_ALG_SHA_1 must be enabled" #endif #elif MEMFAULT_ZEPHYR_VERSION_GT_STRICT(3, 6) //! Zephyr 3.7.0 removed default enabling of hash algorithms needed for CA certificate parsing. Confirm the one we need is set. #if defined(CONFIG_MBEDTLS_BUILTIN) && !defined(CONFIG_MBEDTLS_SHA1) #error "CONFIG_MBEDTLS_SHA1 must be enabled" #endif #endif #endif /* !CONFIG_MEMFAULT_HTTP_DISABLE_TLS */ #include MEMFAULT_ZEPHYR_INCLUDE(net/socket.h) // clang-format on #if defined(CONFIG_MEMFAULT_HTTP_USES_MBEDTLS) // Sanity check that SNI extension is enabled when using Mbed TLS since as of 2.4 Zephyr doesn't // enable it by default #if !defined(MBEDTLS_CONFIG_FILE) #if MEMFAULT_ZEPHYR_VERSION_GT(3, 5) #include #else #include "mbedtls/config.h" #endif #else #include MBEDTLS_CONFIG_FILE #endif #if !defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) #error \ "MBEDTLS_SSL_SERVER_NAME_INDICATION must be enabled for Memfault HTTPS. This can be done with CONFIG_MBEDTLS_USER_CONFIG_ENABLE/FILE" #endif #endif /* CONFIG_MEMFAULT_HTTP_USES_MBEDTLS */ // Timeout for the socket file descriptor to become available for I/0 #define POLL_TIMEOUT_MS CONFIG_MEMFAULT_HTTP_CLIENT_TIMEOUT_MS // The nRF-Connect SDK has an implementation of this structure #if !defined(CONFIG_MEMFAULT_NCS_PROJECT_KEY) MEMFAULT_STATIC_ASSERT( sizeof(CONFIG_MEMFAULT_PROJECT_KEY) > 1, "Memfault Project Key not configured. Please visit https://mflt.io/project-key " "and add CONFIG_MEMFAULT_PROJECT_KEY=\"YOUR_KEY\" to prj.conf"); sMfltHttpClientConfig g_mflt_http_client_config = { .api_key = CONFIG_MEMFAULT_PROJECT_KEY, #if defined(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) .disable_tls = true, .chunks_api = { .host = sizeof(CONFIG_MEMFAULT_HTTP_CHUNKS_API_HOST) > 1 ? CONFIG_MEMFAULT_HTTP_CHUNKS_API_HOST : NULL, .port = 80, }, .device_api = { .host = sizeof(CONFIG_MEMFAULT_HTTP_DEVICE_API_HOST) > 1 ? CONFIG_MEMFAULT_HTTP_DEVICE_API_HOST : NULL, .port = 80, }, #endif }; #endif #if defined(CONFIG_MEMFAULT_HTTP_SOCKET_DISPATCH) // Runtime configurable static char s_mflt_http_net_interface_name[IFNAMSIZ]; #endif // CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE was added in Zephyr v3.4.0 #if (CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE != 0) || (CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE != 0) static void *prv_calloc(size_t count, size_t size) { return calloc(count, size); } static void prv_free(void *ptr) { free(ptr); } #elif CONFIG_HEAP_MEM_POOL_SIZE > 0 static void *prv_calloc(size_t count, size_t size) { return k_calloc(count, size); } static void prv_free(void *ptr) { k_free(ptr); } #else #error \ "One of CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE, CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE, or CONFIG_HEAP_MEM_POOL_SIZE must be non-zero" #endif #if !defined(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) // clang-format off // Select either PEM or DER format to install to certificate storage. #if defined(CONFIG_MEMFAULT_TLS_CERTS_USE_DER) #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA_ptr g_memfault_cert_digicert_global_root_ca #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA_len g_memfault_cert_digicert_global_root_ca_len #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2_ptr g_memfault_cert_digicert_global_root_g2 #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2_len g_memfault_cert_digicert_global_root_g2_len #define MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1_ptr g_memfault_cert_amazon_root_ca1 #define MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1_len g_memfault_cert_amazon_root_ca1_len #elif defined(CONFIG_MEMFAULT_TLS_CERTS_USE_PEM) #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA_ptr MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA_len sizeof(MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA) #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2_ptr MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2 #define MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2_len sizeof(MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2) #define MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1_ptr MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1 #define MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1_len sizeof(MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1) #else #error "Must choose a cert format. Check CONFIG_MEMFAULT_TLS_CERTS_FORMAT for options and their dependencies." #endif // clang-format on static int prv_install_cert(eMemfaultRootCert cert_id) { const char *cert; size_t cert_len; switch (cert_id) { case kMemfaultRootCert_DigicertRootCa: cert = MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA_ptr; cert_len = MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA_len; break; case kMemfaultRootCert_DigicertRootG2: cert = MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2_ptr; cert_len = MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2_len; break; case kMemfaultRootCert_AmazonRootCa1: cert = MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1_ptr; cert_len = MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1_len; break; default: MEMFAULT_LOG_ERROR("Unknown cert id: %d", (int)cert_id); return -EINVAL; } return memfault_root_cert_storage_add(cert_id, cert, cert_len); } int memfault_zephyr_port_install_root_certs(void) { for (eMemfaultDeprecatedRootCert cert_id = kMemfaultDeprecatedRootCert_DuplicateAmazonRootCa1; cert_id < kMemfaultDeprecatedRootCert_MaxIndex; cert_id++) { memfault_root_cert_storage_remove(cert_id); } for (eMemfaultRootCert cert_id = kMemfaultRootCert_DigicertRootG2; cert_id < kMemfaultRootCert_MaxIndex; cert_id++) { const int rv = prv_install_cert(cert_id); if (rv != 0) { return rv; } } return 0; } #else /* CONFIG_MEMFAULT_HTTP_DISABLE_TLS */ int memfault_zephyr_port_install_root_certs(void) { return 0; } #endif /* !CONFIG_MEMFAULT_HTTP_DISABLE_TLS */ static bool prv_send_data(const void *data, size_t data_len, void *ctx) { int fd = *(int *)ctx; int rv = zsock_send(fd, data, data_len, 0); return (rv == data_len); } static int prv_getaddrinfo(struct zsock_addrinfo **res, const char *host, int port_num) { struct zsock_addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, }; char port[10] = { 0 }; snprintf(port, sizeof(port), "%d", port_num); int rv = zsock_getaddrinfo(host, port, &hints, res); if (rv != 0) { MEMFAULT_LOG_ERROR("DNS lookup for %s failed: %d", host, rv); } else { #if CONFIG_MEMFAULT_LOG_LEVEL >= 4 // DBG struct sockaddr_in *addr = net_sin((*res)->ai_addr); #endif MEMFAULT_LOG_DEBUG("DNS lookup for %s = %d.%d.%d.%d", host, addr->sin_addr.s4_addr[0], addr->sin_addr.s4_addr[1], addr->sin_addr.s4_addr[2], addr->sin_addr.s4_addr[3]); } return rv; } static int prv_create_socket(struct zsock_addrinfo **res, const char *host, int port_num) { const int protocol = g_mflt_http_client_config.disable_tls ? IPPROTO_TCP : IPPROTO_TLS_1_2; int rv = prv_getaddrinfo(res, host, port_num); if (rv) { return rv; } int fd = zsock_socket((*res)->ai_family, (*res)->ai_socktype, protocol); if (fd < 0) { MEMFAULT_LOG_ERROR("Failed to open socket, errno=%d", errno); } #if defined(CONFIG_MEMFAULT_HTTP_SOCKET_DISPATCH) if (s_mflt_http_net_interface_name[0] == '\0') { // No interface name has been set MEMFAULT_LOG_WARN("No network interface name set for Memfault HTTP uploads"); return fd; } static struct ifreq ifreq = { 0 }; strncpy(ifreq.ifr_name, s_mflt_http_net_interface_name, IFNAMSIZ); #if defined(CONFIG_NET_INTERFACE_NAME) // Use the name utilities if available to get the interface index for debug rv = net_if_get_by_name(ifreq.ifr_name); if (rv < 0) { MEMFAULT_LOG_ERROR("Interface \"%s\" not found, error=%d", ifreq.ifr_name, rv); return rv; } MEMFAULT_LOG_DEBUG("Binding socket to interface \"%s\", idx=%d", ifreq.ifr_name, rv); #endif rv = zsock_setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)); if (rv) { MEMFAULT_LOG_ERROR("Failed to bind socket to interface \"%s\" with errno=%d", ifreq.ifr_name, errno); return rv; } #endif return fd; } #if !defined(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) static int prv_configure_tls_socket(int sock_fd, const char *host) { const sec_tag_t sec_tag_opt[] = { kMemfaultRootCert_DigicertRootG2, kMemfaultRootCert_AmazonRootCa1, kMemfaultRootCert_DigicertRootCa }; int rv = zsock_setsockopt(sock_fd, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_opt, sizeof(sec_tag_opt)); if (rv != 0) { return rv; } /* Set up TLS peer verification */ enum { NONE = 0, OPTIONAL = 1, REQUIRED = 2, }; int verify = REQUIRED; rv = zsock_setsockopt(sock_fd, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify)); if (rv) { MEMFAULT_LOG_ERROR("Failed to setup peer verification, err %d\n", errno); return rv; } // Set TLS cert parse + copy to optional, which will allow us to use either PEM or DER formatted // certs. This feature was added in Zephyr v3.0.0. Not all socket operation implementations // support this socket option, so gate on CONFIG_MEMFAULT_TLS_CERTS_USE_DER to ensure dependencies // are met. Parsing PEM does not require this socket option. #if defined(CONFIG_MEMFAULT_TLS_CERTS_USE_DER) && defined(TLS_CERT_NOCOPY_OPTIONAL) const int nocopy = TLS_CERT_NOCOPY_OPTIONAL; rv = zsock_setsockopt(sock_fd, SOL_TLS, TLS_CERT_NOCOPY, &nocopy, sizeof(nocopy)); if (rv) { MEMFAULT_LOG_ERROR("Failed to set tls nocopy, err %d\n", errno); return rv; } #endif const size_t host_name_len = strlen(host); return zsock_setsockopt(sock_fd, SOL_TLS, TLS_HOSTNAME, host, host_name_len + 1); } #endif /* !CONFIG_MEMFAULT_HTTP_DISABLE_TLS */ static int prv_configure_socket(int fd, const char *host) { int rv = 0; #if !defined(CONFIG_MEMFAULT_HTTP_DISABLE_TLS) if (!g_mflt_http_client_config.disable_tls) { rv = prv_configure_tls_socket(fd, host); if (rv < 0) { MEMFAULT_LOG_ERROR("Failed to configure tls w/ host, errno=%d", errno); } } #endif return rv; } static int prv_connect_socket(int fd, struct zsock_addrinfo *res) { int rv = zsock_connect(fd, res->ai_addr, res->ai_addrlen); if (rv < 0) { MEMFAULT_LOG_ERROR("Failed to connect socket, errno=%d", errno); zsock_close(fd); } return rv; } static int prv_poll_socket(int sock_fd, int events) { struct zsock_pollfd poll_fd = { .fd = sock_fd, .events = events, }; const int timeout_ms = POLL_TIMEOUT_MS; int rv = zsock_poll(&poll_fd, 1, timeout_ms); if (rv == 0) { MEMFAULT_LOG_ERROR("Timeout waiting for socket event(s): event(s)=%d, errno=%d", events, errno); } return rv; } static bool prv_try_send(int sock_fd, const uint8_t *buf, size_t buf_len) { size_t idx = 0; while (idx != buf_len) { // Wait for socket to become available within a timeout in case the socket is busy processing // other tx data. This will prevent busy looping in this loop (since the send call is // non-blocking), therefore allowing other threads to run. int rv = prv_poll_socket(sock_fd, ZSOCK_POLLOUT); if (rv <= 0) { return false; } rv = zsock_send(sock_fd, &buf[idx], buf_len - idx, ZSOCK_MSG_DONTWAIT); if (rv > 0) { idx += rv; continue; } if (rv <= 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { continue; } MEMFAULT_LOG_ERROR("Data Send Error: len=%d, errno=%d", (int)buf_len, errno); return false; } } return true; } static int prv_open_socket(struct zsock_addrinfo **res, const char *host, int port_num) { const int sock_fd = prv_create_socket(res, host, port_num); if (sock_fd < 0) { return -1; } int rv = prv_configure_socket(sock_fd, host); if (rv < 0) { zsock_close(sock_fd); return -1; } rv = prv_connect_socket(sock_fd, *res); if (rv < 0) { return -1; } return sock_fd; } //! Returns: //! 0 - no more data to send //! 1 - data sent, awaiting response //! -1 - error static int prv_send_next_msg(sMemfaultHttpContext *ctx) { int sock = ctx->sock_fd; #if CONFIG_MEMFAULT_HTTP_MAX_POST_SIZE uint8_t buf[CONFIG_MEMFAULT_HTTP_MAX_POST_SIZE]; size_t buf_len = sizeof(buf); bool data_available = memfault_packetizer_get_chunk(buf, &buf_len); if (!data_available) { MEMFAULT_LOG_DEBUG("No more data to send"); return 0; // no more data to send } memfault_http_start_chunk_post(prv_send_data, &sock, buf_len); if (!prv_try_send(sock, buf, buf_len)) { // unexpected failure, abort in-flight transaction memfault_packetizer_abort(); return -1; } // count bytes sent ctx->bytes_sent += buf_len; // message sent, await response return 1; #else const sPacketizerConfig cfg = { // let a single msg span many "memfault_packetizer_get_next" calls .enable_multi_packet_chunk = true, }; // will be populated with size of entire message queued for sending sPacketizerMetadata metadata; const bool data_available = memfault_packetizer_begin(&cfg, &metadata); if (!data_available) { MEMFAULT_LOG_DEBUG("No more data to send"); return 0; } memfault_http_start_chunk_post(prv_send_data, &sock, metadata.single_chunk_message_length); // If the configured buffer size is large, use malloc instead of stack to // avoid stack overflow. #if CONFIG_MEMFAULT_HTTP_PACKETIZER_BUFFER_SIZE > 512 uint8_t *buf = prv_calloc(1, CONFIG_MEMFAULT_HTTP_PACKETIZER_BUFFER_SIZE); if (buf == NULL) { MEMFAULT_LOG_ERROR("Failed to allocate buffer for reading data"); memfault_packetizer_abort(); return -1; } #else uint8_t buf[CONFIG_MEMFAULT_HTTP_PACKETIZER_BUFFER_SIZE]; #endif while (1) { size_t buf_len = CONFIG_MEMFAULT_HTTP_PACKETIZER_BUFFER_SIZE; eMemfaultPacketizerStatus status = memfault_packetizer_get_next(buf, &buf_len); if (status == kMemfaultPacketizerStatus_NoMoreData) { break; } if (!prv_try_send(sock, buf, buf_len)) { // unexpected failure, abort in-flight transaction #if CONFIG_MEMFAULT_HTTP_PACKETIZER_BUFFER_SIZE > 512 prv_free(buf); #endif memfault_packetizer_abort(); return -1; } // count bytes sent ctx->bytes_sent += buf_len; if (status == kMemfaultPacketizerStatus_EndOfChunk) { break; } } #if CONFIG_MEMFAULT_HTTP_PACKETIZER_BUFFER_SIZE > 512 prv_free(buf); #endif // message sent, await response return 1; #endif /* CONFIG_MEMFAULT_HTTP_MAX_POST_SIZE */ } static bool prv_read_socket_data(int sock_fd, void *buf, size_t *buf_len) { int rv = prv_poll_socket(sock_fd, ZSOCK_POLLIN); if (rv <= 0) { return false; } const int len = zsock_recv(sock_fd, buf, *buf_len, ZSOCK_MSG_DONTWAIT); if (len <= 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { *buf_len = 0; return true; } MEMFAULT_LOG_ERROR("Receive error: len=%d, errno=%d", len, errno); return false; } *buf_len = len; return true; } //! Wait for the HTTP response to be fully received and parsed, returning the //! HTTP status code of the response. //! //! @return HTTP status code on success, or -1 on error static int prv_wait_for_http_response(int sock_fd) { sMemfaultHttpResponseContext ctx = { 0 }; while (1) { // We don't expect any response that needs to be parsed so // just use an arbitrarily small receive buffer char buf[32]; size_t bytes_read = sizeof(buf); if (!prv_read_socket_data(sock_fd, buf, &bytes_read)) { return -1; } bool done = memfault_http_parse_response(&ctx, buf, bytes_read); if (done) { MEMFAULT_LOG_DEBUG("Response Complete: Parse Status %d HTTP Status %d!", (int)ctx.parse_error, ctx.http_status_code); if (ctx.parse_error != kMfltHttpParseStatus_Ok) { MEMFAULT_LOG_ERROR("Failed to parse response: Parse Status %d", (int)ctx.parse_error); return -1; } if (ctx.http_body != NULL) { MEMFAULT_LOG_DEBUG("Body: %s", ctx.http_body); } return ctx.http_status_code; } } } static bool prv_wait_for_http_response_header(int sock_fd, sMemfaultHttpResponseContext *ctx, void *buf, size_t *buf_len) { const size_t orig_buf_len = *buf_len; size_t bytes_read; while (1) { bytes_read = orig_buf_len; if (!prv_read_socket_data(sock_fd, buf, &bytes_read)) { return false; } const bool done = memfault_http_parse_response_header(ctx, buf, bytes_read); if (done) { break; } } if (ctx->parse_error != kMfltHttpParseStatus_Ok) { MEMFAULT_LOG_ERROR("Failed to parse response: Parse Status %d", (int)ctx->parse_error); return false; } // Move unprocessed message-body bytes to the beginning of the working buf // and update buf_len with the number of read bytes const size_t message_body_bytes = bytes_read - ctx->data_bytes_processed; if (message_body_bytes != 0) { const void *message_body_bufp = &((uint8_t *)buf)[ctx->data_bytes_processed]; memmove(buf, message_body_bufp, message_body_bytes); } *buf_len = message_body_bytes; if (ctx->http_status_code >= 400) { MEMFAULT_LOG_ERROR("Unexpected HTTP Status: %d", ctx->http_status_code); // A future improvement here would be to dump the message-body on error // if it is text or application/json return false; } return true; } static bool prv_install_ota_payload(int sock_fd, const sMemfaultOtaUpdateHandler *handler) { sMemfaultHttpResponseContext ctx = { 0 }; size_t buf_len = handler->buf_len; if (!prv_wait_for_http_response_header(sock_fd, &ctx, handler->buf, &buf_len)) { return false; } sMemfaultOtaInfo ota_info = { .size = ctx.content_length, }; if (!handler->handle_update_available(&ota_info, handler->user_ctx)) { return false; } if (buf_len != 0 && !handler->handle_data(handler->buf, buf_len, handler->user_ctx)) { return false; } size_t curr_offset = buf_len; while (curr_offset != ctx.content_length) { size_t bytes_read = MEMFAULT_MIN(ctx.content_length - curr_offset, handler->buf_len); if (!prv_read_socket_data(sock_fd, handler->buf, &bytes_read)) { return false; } if (!handler->handle_data(handler->buf, bytes_read, handler->user_ctx)) { return false; } curr_offset += bytes_read; } return handler->handle_download_complete(handler->user_ctx); } static bool prv_fetch_ota_payload(const char *url, const sMemfaultOtaUpdateHandler *handler) { bool success = false; sMemfaultUriInfo uri_info; const size_t url_len = strlen(url); if (!memfault_http_parse_uri(url, url_len, &uri_info)) { MEMFAULT_LOG_ERROR("Unable to parse url: %s", url); return false; } // create nul terminated host string from parsed url char host[uri_info.host_len + 1]; memcpy(host, uri_info.host, uri_info.host_len); host[uri_info.host_len] = '\0'; // create the connection struct zsock_addrinfo *res = NULL; int sock_fd = prv_open_socket(&res, host, uri_info.port); if (sock_fd < 0) { goto cleanup; } if (!memfault_http_get_ota_payload(prv_send_data, &sock_fd, url, url_len)) { goto cleanup; } success = prv_install_ota_payload(sock_fd, handler); cleanup: if (sock_fd >= 0) { zsock_close(sock_fd); } if (res != NULL) { zsock_freeaddrinfo(res); } return success; } static bool prv_parse_new_ota_payload_url_response(int sock_fd, char **download_url_out) { sMemfaultHttpResponseContext ctx = { 0 }; char working_buf[32]; size_t working_buf_len = sizeof(working_buf); if (!prv_wait_for_http_response_header(sock_fd, &ctx, working_buf, &working_buf_len)) { return false; } if (ctx.http_status_code != 200) { return true; // up to date! } const size_t url_len = ctx.content_length + 1 /* for '\0' */; char *download_url = prv_calloc(1, url_len); if (download_url == NULL) { MEMFAULT_LOG_ERROR("Unable to allocate %d bytes for url", (int)url_len); return false; } // copy any parts of the message-body we already received into the storage holding // the download url if (working_buf_len != 0) { memcpy(download_url, working_buf, working_buf_len); } size_t curr_offset = working_buf_len; while (curr_offset != ctx.content_length) { size_t bytes_read = ctx.content_length - curr_offset; if (!prv_read_socket_data(sock_fd, &download_url[curr_offset], &bytes_read)) { goto error; } curr_offset += bytes_read; } *download_url_out = download_url; return true; error: prv_free(download_url); return false; } //! Checks to see if a new OTA Update is available //! //! @return true on success, false otherwise. On success, the download_url will be populated with //! the link to the new ota payload iff a new update is available. static bool prv_check_for_ota_update(char **download_url) { bool success = false; const char *host = MEMFAULT_HTTP_GET_DEVICE_API_HOST(); const int port = MEMFAULT_HTTP_GET_DEVICE_API_PORT(); struct zsock_addrinfo *res = NULL; int sock_fd = prv_open_socket(&res, host, port); if (sock_fd < 0) { goto cleanup; } if (!memfault_http_get_latest_ota_payload_url(prv_send_data, &sock_fd)) { goto cleanup; } // We successfully sent the http request for a latest ota payload parse the response success = prv_parse_new_ota_payload_url_response(sock_fd, download_url); cleanup: if (sock_fd >= 0) { zsock_close(sock_fd); } if (res) { zsock_freeaddrinfo(res); } return success; } int memfault_zephyr_port_get_download_url(char **download_url) { const bool success = prv_check_for_ota_update(download_url); if (!success) { return -1; // error } if (*download_url == NULL) { return 0; // up to date } return 1; } int memfault_zephyr_port_release_download_url(char **download_url) { prv_free(*download_url); *download_url = NULL; return 0; } int memfault_zephyr_port_ota_update(const sMemfaultOtaUpdateHandler *handler) { MEMFAULT_ASSERT((handler != NULL) && (handler->buf != NULL) && (handler->buf > 0) && (handler->handle_update_available != NULL) && (handler->handle_data != NULL) && (handler->handle_download_complete != NULL)); char *download_url = NULL; bool success = prv_check_for_ota_update(&download_url); if (!success) { return -1; // error } if (download_url == NULL) { return 0; // up to date } success = prv_fetch_ota_payload(download_url, handler); prv_free(download_url); return success ? 1 : -1; } ssize_t memfault_zephyr_port_http_post_data_return_size(void) { if (!memfault_packetizer_data_available()) { return 0; } sMemfaultHttpContext ctx = { 0 }; ctx.sock_fd = -1; int rv = memfault_zephyr_port_http_open_socket(&ctx); if (rv == 0) { rv = memfault_zephyr_port_http_upload_sdk_data(&ctx); memfault_zephyr_port_http_close_socket(&ctx); } #if defined(CONFIG_MEMFAULT_METRICS_SYNC_SUCCESS) if (rv == 0) { memfault_metrics_connectivity_record_memfault_sync_success(); } else { memfault_metrics_connectivity_record_memfault_sync_failure(); } #endif return (rv == 0) ? (ctx.bytes_sent) : rv; } int memfault_zephyr_port_http_open_socket(sMemfaultHttpContext *ctx) { const char *host = MEMFAULT_HTTP_GET_CHUNKS_API_HOST(); const int port = MEMFAULT_HTTP_GET_CHUNKS_API_PORT(); memfault_zephyr_port_http_close_socket(ctx); MEMFAULT_LOG_DEBUG("Opening socket to %s:%d", host, port); ctx->sock_fd = prv_open_socket(&(ctx->res), host, port); if (ctx->sock_fd < 0) { memfault_zephyr_port_http_close_socket(ctx); return -1; } return 0; } int memfault_zephyr_port_http_create_socket(sMemfaultHttpContext *ctx) { const char *host = MEMFAULT_HTTP_GET_CHUNKS_API_HOST(); const int port = MEMFAULT_HTTP_GET_CHUNKS_API_PORT(); memfault_zephyr_port_http_close_socket(ctx); ctx->sock_fd = prv_create_socket(&(ctx->res), host, port); if (ctx->sock_fd < 0) { memfault_zephyr_port_http_close_socket(ctx); return -1; } return 0; } int memfault_zephyr_port_http_configure_socket(sMemfaultHttpContext *ctx) { const char *host = MEMFAULT_HTTP_GET_CHUNKS_API_HOST(); int rv = prv_configure_socket(ctx->sock_fd, host); if (rv < 0) { memfault_zephyr_port_http_close_socket(ctx); } return rv; } int memfault_zephyr_port_http_connect_socket(sMemfaultHttpContext *ctx) { int rv = prv_connect_socket(ctx->sock_fd, ctx->res); if (rv < 0) { memfault_zephyr_port_http_close_socket(ctx); } return rv; } void memfault_zephyr_port_http_close_socket(sMemfaultHttpContext *ctx) { if (ctx->sock_fd >= 0) { zsock_close(ctx->sock_fd); } ctx->sock_fd = -1; if (ctx->res != NULL) { zsock_freeaddrinfo(ctx->res); ctx->res = NULL; } } bool memfault_zephyr_port_http_is_connected(sMemfaultHttpContext *ctx) { return ctx->sock_fd >= 0; } int memfault_zephyr_port_http_upload_sdk_data(sMemfaultHttpContext *ctx) { int max_messages_to_send = CONFIG_MEMFAULT_HTTP_MAX_MESSAGES_TO_SEND; #if CONFIG_MEMFAULT_HTTP_MAX_POST_SIZE && CONFIG_MEMFAULT_RAM_BACKED_COREDUMP // The largest data type we will send is a coredump. If CONFIG_MEMFAULT_HTTP_MAX_POST_SIZE // is being used, make sure we issue enough HTTP POSTS such that an entire coredump will be sent. max_messages_to_send = MEMFAULT_MAX(max_messages_to_send, CONFIG_MEMFAULT_RAM_BACKED_COREDUMP_SIZE / CONFIG_MEMFAULT_HTTP_MAX_POST_SIZE); #endif bool success = true; int rv = -1; while (max_messages_to_send-- > 0) { rv = prv_send_next_msg(ctx); if (rv == 0) { // no more messages to send break; } else if (rv < 0) { success = false; break; } const int http_status = prv_wait_for_http_response(ctx->sock_fd); success = (http_status / 100 == 2); if (!success) { break; } } if ((max_messages_to_send <= 0) && memfault_packetizer_data_available()) { MEMFAULT_LOG_WARN( "Hit max message limit: " STRINGIFY(CONFIG_MEMFAULT_HTTP_MAX_MESSAGES_TO_SEND)); } return success ? 0 : -1; } int memfault_zephyr_port_http_post_chunk(sMemfaultHttpContext *ctx, void *p_data, size_t data_len) { if (!memfault_zephyr_port_http_is_connected(ctx)) { return -1; } memfault_http_start_chunk_post(prv_send_data, &(ctx->sock_fd), data_len); if (!prv_try_send(ctx->sock_fd, p_data, data_len)) { return -2; } if (prv_wait_for_http_response(ctx->sock_fd) / 100 != 2) { return -3; } return 0; } #if defined(CONFIG_MEMFAULT_HTTP_SOCKET_DISPATCH) int memfault_zephyr_port_http_set_interface_name(const char *if_name) { const size_t if_name_len = strlen(if_name); if (if_name_len == 0 || if_name_len >= IFNAMSIZ) { return -1; } strncpy(s_mflt_http_net_interface_name, if_name, IFNAMSIZ - 1); s_mflt_http_net_interface_name[IFNAMSIZ - 1] = '\0'; return 0; } #endif ================================================ FILE: ports/zephyr/common/memfault_platform_lock.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Wire up Zephyr locks to the Memfault mutex API #include "memfault/core/platform/core.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) K_MUTEX_DEFINE(s_memfault_mutex); void memfault_lock(void) { k_mutex_lock(&s_memfault_mutex, K_FOREVER); } void memfault_unlock(void) { k_mutex_unlock(&s_memfault_mutex); } ================================================ FILE: ports/zephyr/common/memfault_platform_post.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Selects which backend to use for posting data. #include "memfault/ports/zephyr/http.h" #if defined(CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) // NCS v1.9.2 does not have a recent enough Zephyr version to include this // header. This Kconfig option is blocked at CMake stage, so here it is safe // to include. #include "memfault/nrfconnect_port/coap.h" #endif ssize_t memfault_zephyr_port_post_data_return_size(void) { #if defined(CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) return memfault_zephyr_port_coap_post_data_return_size(); #else // default to HTTP transport return memfault_zephyr_port_http_post_data_return_size(); #endif } int memfault_zephyr_port_post_data(void) { ssize_t rv = memfault_zephyr_port_post_data_return_size(); return (rv >= 0) ? 0 : rv; } ================================================ FILE: ports/zephyr/common/memfault_platform_ram_backed_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/panics/arch/arm/cortex_m.h" #include "memfault/panics/platform/coredump.h" MEMFAULT_PUT_IN_SECTION(CONFIG_MEMFAULT_RAM_BACKED_COREDUMP_REGION) MEMFAULT_ALIGNED(8) static uint8_t s_ram_backed_coredump_region[CONFIG_MEMFAULT_RAM_BACKED_COREDUMP_SIZE]; void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = sizeof(s_ram_backed_coredump_region), .sector_size = sizeof(s_ram_backed_coredump_region), }; } bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if ((offset + read_len) > sizeof(s_ram_backed_coredump_region)) { return false; } const uint8_t *read_ptr = &s_ram_backed_coredump_region[offset]; memcpy(data, read_ptr, read_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if ((offset + erase_size) > sizeof(s_ram_backed_coredump_region)) { return false; } uint8_t *erase_ptr = &s_ram_backed_coredump_region[offset]; memset(erase_ptr, 0x0, erase_size); return true; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if ((offset + data_len) > sizeof(s_ram_backed_coredump_region)) { return false; } uint8_t *write_ptr = &s_ram_backed_coredump_region[offset]; memcpy(write_ptr, data, data_len); return true; } void memfault_platform_coredump_storage_clear(void) { const uint8_t clear_byte = 0x0; memfault_platform_coredump_storage_write(0, &clear_byte, sizeof(clear_byte)); } ================================================ FILE: ports/zephyr/common/memfault_platform_system_time.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Memfault system time provider for Zephyr. //! //! Note: The implementations of memfault_platform_time_get_current() MUST NOT //! call any logging functions, as it is called from within the Memfault logging //! subsystem when log timestamps are enabled. Any logs emitted from such a //! call site may infinitely recurse. #include "memfault/core/compiler.h" #include "memfault/core/platform/system_time.h" // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) // clang-format on // Disable logging in this module. memfault_platform_time_get_current() can be // called from the Memfault logging subsystem, and if that function then logs, // it can infinitely recurse. LOG_MODULE_REGISTER(mflt_systime, LOG_LEVEL_NONE); // This implementation uses the Nordic Date-Time library: // https://docs.nordicsemi.com/bundle/ncs-2.5.0/page/nrf/libraries/others/date_time.html #if defined(CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_DATETIME) #include // event handler for when date/time is updated static bool date_time_is_ready = false; void memfault_zephyr_date_time_evt_handler(const struct date_time_evt *evt) { LOG_DBG("Date time event handler: %d", evt->type); if (evt->type == DATE_TIME_NOT_OBTAINED) { LOG_WRN("failed to obtain date-time"); if (date_time_is_ready) { LOG_ERR("date-time lost"); date_time_is_ready = false; } } else { date_time_is_ready = true; int64_t unix_time_ms = 0; int err = date_time_now(&unix_time_ms); LOG_DBG("date-time obtained: err=%d, %u.%us", err, (uint32_t)(unix_time_ms / 1000), (uint32_t)(unix_time_ms % 1000)); } } bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { if (date_time_is_ready) { int64_t unix_time_ms; int err = date_time_now(&unix_time_ms); if (!err) { *time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = unix_time_ms / 1000, }, }; return true; } } // otherwise, unable to retrieve time return false; } #endif // defined(CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_DATETIME) #if defined(CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_RTC) // Zephyr RTC subsystem based system time #include #include MEMFAULT_ZEPHYR_INCLUDE(drivers/rtc.h) // Use RTC node if it exists, otherwise use an alias #if DT_NODE_EXISTS(DT_NODELABEL(rtc)) #define MFLT_RTC_NODE DT_NODELABEL(rtc) #else #define MFLT_RTC_NODE DT_ALIAS(rtc) #endif #if !DT_NODE_HAS_STATUS(MFLT_RTC_NODE, okay) #error "Error, requires rtc device tree entry" #endif bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { struct rtc_time rtctime; const struct device *rtc = DEVICE_DT_GET(MFLT_RTC_NODE); rtc_get_time(rtc, &rtctime); // If pre-2023, something is wrong if ((rtctime.tm_year < 123) || (rtctime.tm_year > 200)) { return false; } struct tm *tm_time = rtc_time_to_tm(&rtctime); time_t time_now = mktime(tm_time); if (time_now == (time_t)-1) { return false; } // load the timestamp and return true for a valid timestamp *time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = (uint64_t)time_now, }, }; return true; } #endif // defined(CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_RTC) #if defined(CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_SYS_CLOCK) #include #include "memfault/ports/zephyr/version.h" #if MEMFAULT_ZEPHYR_VERSION_GT_STRICT(4, 1) #include MEMFAULT_ZEPHYR_INCLUDE(sys/clock.h) static int prv_get_realtime(struct timespec *ts) { return sys_clock_gettime(SYS_CLOCK_REALTIME, ts); } #elif defined(CONFIG_POSIX_TIMERS) // Fallback for Zephyr < 4.2.0: use POSIX clock_gettime() (requires CONFIG_POSIX_TIMERS) static int prv_get_realtime(struct timespec *ts) { return clock_gettime(CLOCK_REALTIME, ts); } #else static int prv_get_realtime(struct timespec *ts) { (void)ts; return -1; } #endif bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { struct timespec ts; int err = prv_get_realtime(&ts); if (err != 0) { return false; } // If pre-2023, something is wrong if (ts.tv_sec < 1672531200) { // 2023-01-01 00:00:00 UTC return false; } *time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = (uint64_t)ts.tv_sec, }, }; return true; } #endif // defined(CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_SYS_CLOCK) ================================================ FILE: ports/zephyr/common/memfault_self_test_platform.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "memfault/core/self_test.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) void memfault_self_test_platform_delay(uint32_t delay_ms) { k_msleep(delay_ms); } #if defined(CONFIG_MEMFAULT_SHELL_SELF_TEST_COREDUMP_STORAGE) static unsigned int s_self_test_lock = 0; bool memfault_self_test_platform_disable_irqs(void) { s_self_test_lock = irq_lock(); return true; } bool memfault_self_test_platform_enable_irqs(void) { irq_unlock(s_self_test_lock); return true; } #endif ================================================ FILE: ports/zephyr/common/memfault_software_watchdog.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A Software Watchdog implementation backed by the Zephyr timer subsystem //! //! The implementation uses the k_timer_ module. When the timer expires, //! the implementation will assert so a coredump of the system state is captured. // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include "memfault/panics/assert.h" #include "memfault/ports/watchdog.h" // clang-format on static void prv_software_watchdog_timeout(struct k_timer *dummy) { MEMFAULT_SOFTWARE_WATCHDOG(); } K_TIMER_DEFINE(s_watchdog_timer, prv_software_watchdog_timeout, NULL); static uint32_t s_software_watchdog_timeout_ms = MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS * 1000; static void prv_start_or_reset(uint32_t timeout_ms) { k_timer_start(&s_watchdog_timer, K_MSEC(timeout_ms), K_MSEC(timeout_ms)); } int memfault_software_watchdog_enable(void) { prv_start_or_reset(s_software_watchdog_timeout_ms); return 0; } int memfault_software_watchdog_disable(void) { k_timer_stop(&s_watchdog_timer); return 0; } int memfault_software_watchdog_feed(void) { prv_start_or_reset(s_software_watchdog_timeout_ms); return 0; } int memfault_software_watchdog_update_timeout(uint32_t timeout_ms) { s_software_watchdog_timeout_ms = timeout_ms; prv_start_or_reset(s_software_watchdog_timeout_ms); return 0; } ================================================ FILE: ports/zephyr/common/memfault_tls_root_cert_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Installs root certificates using Zephyrs default infrastructure #include "memfault/ports/zephyr/root_cert_storage.h" // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(net/tls_credentials.h) // clang-format on #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #define MEMFAULT_NUM_CERTS_REGISTERED 3 #if !defined(CONFIG_TLS_MAX_CREDENTIALS_NUMBER) || \ (CONFIG_TLS_MAX_CREDENTIALS_NUMBER < MEMFAULT_NUM_CERTS_REGISTERED) #pragma message( \ "ERROR: CONFIG_TLS_MAX_CREDENTIALS_NUMBER must be >= " MEMFAULT_EXPAND_AND_QUOTE( \ MEMFAULT_NUM_CERTS_REGISTERED)) #error "Update CONFIG_TLS_MAX_CREDENTIALS_NUMBER in prj.conf" #endif #if !defined(CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS) || \ (CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS < MEMFAULT_NUM_CERTS_REGISTERED) #pragma message( \ "ERROR: CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS must be >= " MEMFAULT_EXPAND_AND_QUOTE( \ MEMFAULT_NUM_CERTS_REGISTERED)) #error "Update CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS in prj.conf" #endif MEMFAULT_STATIC_ASSERT((kMemfaultRootCert_MaxIndex - (kMemfaultRootCert_Base + 1)) == MEMFAULT_NUM_CERTS_REGISTERED, "MEMFAULT_NUM_CERTS_REGISTERED define out of sync"); int memfault_root_cert_storage_add(eMemfaultRootCert cert_id, const char *cert, size_t cert_length) { return tls_credential_add(cert_id, TLS_CREDENTIAL_CA_CERTIFICATE, cert, cert_length); } //! Placeholder implementation. Most setups do not save certs to NVM so removing unused certs is //! unnecessary int memfault_root_cert_storage_remove(eMemfaultRootCert cert_id) { // TODO: use tls_credential_delete() return 0; } ================================================ FILE: ports/zephyr/common/memfault_zephyr_ram_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Implements convenience APIs that can be used when building the set of //! RAM regions to collect as part of a coredump. See header for more details/ // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel_structs.h) #include // clang-format on #include "memfault/components.h" #include "memfault/ports/zephyr/coredump.h" // Use this config flag to manually select the old data region names #if !defined(MEMFAULT_ZEPHYR_USE_OLD_DATA_REGION_NAMES) #define MEMFAULT_ZEPHYR_USE_OLD_DATA_REGION_NAMES 0 #endif 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 visit https://mflt.io/contact-support" #endif #if defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) #define MEMFAULT_THREAD_STACK_0_BYTES_UNUSED 0xffffffff static struct MfltTaskWatermarks { uint32_t bytes_unused; } s_memfault_task_watermarks_v2[CONFIG_MEMFAULT_COREDUMP_MAX_TRACKED_TASKS]; #endif // defined(CONFIG_MEMFAULT_COMPUTE_STACK_HIGHWATER_ON_DEVICE) #endif #define EMPTY_SLOT 0 static bool prv_find_slot(size_t *idx, struct k_thread *desired_tcb) { for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_task_tcbs); i++) { if (s_task_tcbs[i] == desired_tcb) { *idx = i; return true; } } return false; } // We intercept calls to arch_new_thread() so we can track when new tasks // are created // // It would be nice to use '__builtin_types_compatible_p' and '__typeof__' to // enforce strict abi matching in the wrapped functions, but the target // functions are declared in private zephyr ./kernel/include files, which are // not really supposed to be accessed from user code and would require some // dubious path hacks to get to. void __wrap_arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack, char *stack_ptr, k_thread_entry_t entry, void *p1, void *p2, void *p3); void __real_arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack, char *stack_ptr, k_thread_entry_t entry, void *p1, void *p2, void *p3); void __wrap_arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack, char *stack_ptr, k_thread_entry_t entry, void *p1, void *p2, void *p3) { size_t idx = 0; const bool slot_found = prv_find_slot(&idx, EMPTY_SLOT); if (slot_found) { s_task_tcbs[idx] = thread; } __real_arch_new_thread(thread, stack, stack_ptr, entry, p1, p2, p3); } void __wrap_z_thread_abort(struct k_thread *thread); void __real_z_thread_abort(struct k_thread *thread); void __wrap_z_thread_abort(struct k_thread *thread) { size_t idx = 0; const bool slot_found = prv_find_slot(&idx, thread); if (slot_found) { s_task_tcbs[idx] = EMPTY_SLOT; } __real_z_thread_abort(thread); } MEMFAULT_WEAK size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { #if (defined(CONFIG_RISCV) || defined(CONFIG_XTENSA) || defined(CONFIG_ARCH_POSIX)) // Linker script does not define _image_ram_start/end for RISC-V or Xtensa currently. Zephyr may // be defining them in >3.6.0, however, so this could change soon const uint32_t ram_start = 0; const uint32_t ram_end = 0xffffffff; #else // NB: This only works for MCUs which have a contiguous RAM address range. The // allowed addresses for capturing coredump data is anything the kernel has // access to. extern uint32_t __kernel_ram_start[]; extern uint32_t __kernel_ram_end[]; const uint32_t ram_start = (uint32_t)__kernel_ram_start; const uint32_t ram_end = (uint32_t)__kernel_ram_end; #endif if ((uintptr_t)start_addr >= ram_start && (uintptr_t)start_addr < ram_end) { return MEMFAULT_MIN(desired_size, ram_end - (uintptr_t)start_addr); } return 0; } #if defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) //! Compute the high watermark of a task's stack. This is the amount of stack //! that has been written to since the task was created. static ssize_t prv_stack_bytes_unused(uintptr_t stack_start, size_t stack_size) { // First confirm that it's safe to traverse the stack region. If the TCB has // been corrupted, we don't want to trigger a memory error. if (memfault_platform_sanitize_address_range((void *)stack_start, stack_size) != stack_size) { return 0; } // This algorithm assumes the stack is full-descending. Start at the lowest // address (highest stack index) and go up in addresses (down in the stack), // looking for the first address that contains something other than the stack // painting pattern, 0xAA. #if defined(CONFIG_STACK_SENTINEL) // Ideally, we would check if this stack was the one where the sentinel was // clobbered. Unfortunately, the sentinel is restored in the check: // https://github.com/zephyrproject-rtos/zephyr/blob/98a16b424aaf1a409db872a5f54f3d8d5fc4af21/kernel/thread.c#L340-L341 // So we're not going to correctly mark stack utilization in this case. // Stack sentinel pattern takes the top 4 bytes of the stack, so skip over that const uint8_t *stack_max = (const uint8_t *)stack_start + sizeof(STACK_SENTINEL); #else const uint8_t *stack_max = (const uint8_t *)stack_start; #endif const uint8_t *const stack_bottom = (const uint8_t *const)(stack_start + stack_size); for (; (stack_max < stack_bottom) && (*stack_max == 0xAA); stack_max++) { } // Return the "bytes unused" count for the stack const ssize_t bytes_unused = (uintptr_t)stack_max - stack_start; // In the case of a fully exhausted stack, return -1. This is converted back // to 0 in the backend. We can't use 0 here, because that's also the // initialization value of the s_memfault_task_watermarks_v2 data structure, and if // this routine isn't called before coredump saving for some reason (eg // programming error) we don't want to incorrectly show the stacks as fully // used ("0 bytes unused"). if (bytes_unused == 0) { return MEMFAULT_THREAD_STACK_0_BYTES_UNUSED; } else { return bytes_unused; } } #endif // defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) size_t memfault_zephyr_get_task_regions(sMfltCoredumpRegion *regions, size_t num_regions) { if (regions == NULL || num_regions == 0) { return 0; } size_t region_idx = 0; // Collect the s_task_tcbs array, in case any TCB or thread stack fails to // collect completely, we can still recover the state of the other threads. regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(s_task_tcbs, sizeof(s_task_tcbs)); region_idx++; // First we will try to store all the task TCBs. This way if we run out of // space while storing task stacks, we will still be able to recover the state // of all the threads. Zephyr stores the thread list as a linked list, so we // need to successfully capture all the TCBs in the coredump to be able to // decode the full thread list. for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_task_tcbs) && region_idx < num_regions; i++) { struct k_thread *thread = s_task_tcbs[i]; if (thread == EMPTY_SLOT) { continue; } const size_t tcb_size = memfault_platform_sanitize_address_range(thread, sizeof(*thread)); if (tcb_size == 0) { // An invalid address, scrub the TCB from the list so we don't try to dereference // it when grabbing stacks below and move on. s_task_tcbs[i] = EMPTY_SLOT; continue; } regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(thread, tcb_size); region_idx++; } // Now we store the region of the stack where context is saved. This way // we can unwind the stacks for threads that are not actively running for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_task_tcbs) && region_idx < num_regions; i++) { struct k_thread *thread = s_task_tcbs[i]; if (thread == EMPTY_SLOT) { continue; } // When capturing full thread stacks, also include the active thread. Note // that the active stack may already be partially collected in a previous // region, so we might be duplicating it here; it's a little wasteful, but // it's good to always prioritize the currently running stack, in case the // coredump is truncated due to lack of space. #if !defined(CONFIG_MEMFAULT_COREDUMP_FULL_THREAD_STACKS) if ((uintptr_t)_kernel.cpus[0].current == (uintptr_t)thread) { // when collecting partial stacks, thread context is only valid when task // is _not_ running so we skip collecting it. just update the watermark // for the thread #if defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) s_memfault_task_watermarks_v2[i].bytes_unused = prv_stack_bytes_unused(thread->stack_info.start, thread->stack_info.size); #endif // defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) continue; } #endif void *sp = #if defined(CONFIG_ARM) && defined(CONFIG_USE_SWITCH) // With USE_SWITCH (Zephyr 4.4.0+), callee_saved.psp is never written. // switch_handle points to the saved context frame at the top of the thread's // stack - use it as the stack capture start. (void *)thread->switch_handle #elif defined(CONFIG_ARM) (void *)thread->callee_saved.psp #elif CONFIG_RISCV (void *)thread->callee_saved.sp #elif CONFIG_XTENSA // The switch_handle is the "restore handle" for the saved Xtensa register // frame. It points to the ptr_to_bsa slot at the bottom of the saved // frame (_xtensa_irq_stack_frame_raw_t), which is the lowest address of // the captured context. Using this as the capture start ensures the entire // saved register frame (BSA + high register blocks) is collected. // Reference: arch/xtensa/include/xtensa_asm2_context.h (void *)thread->switch_handle #elif CONFIG_ARCH_POSIX // Stubbed out, not supported 0 #endif ; #if defined(CONFIG_THREAD_STACK_INFO) #if defined(CONFIG_MEMFAULT_COREDUMP_FULL_THREAD_STACKS) // Capture the entire stack for this thread size_t stack_size_to_collect = thread->stack_info.size - thread->stack_info.delta; sp = (void *)thread->stack_info.start; #else // We know where the top of the stack is. Use that information to shrink // the area we need to collect if less than CONFIG_MEMFAULT_COREDUMP_STACK_SIZE_TO_COLLECT // is in use const uint32_t stack_top = thread->stack_info.start + thread->stack_info.size - thread->stack_info.delta; size_t stack_size_to_collect = MEMFAULT_MIN(stack_top - (uintptr_t)sp, CONFIG_MEMFAULT_COREDUMP_STACK_SIZE_TO_COLLECT); #endif #else size_t stack_size_to_collect = CONFIG_MEMFAULT_COREDUMP_STACK_SIZE_TO_COLLECT; #endif stack_size_to_collect = memfault_platform_sanitize_address_range(sp, stack_size_to_collect); if (stack_size_to_collect == 0) { continue; } #if defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) // compute high watermarks for each task s_memfault_task_watermarks_v2[i].bytes_unused = prv_stack_bytes_unused(thread->stack_info.start, thread->stack_info.size); #endif // defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(sp, stack_size_to_collect); region_idx++; } #if defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) regions[region_idx] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(s_memfault_task_watermarks_v2, sizeof(s_memfault_task_watermarks_v2)); region_idx++; #endif // defined(CONFIG_MEMFAULT_COREDUMP_COMPUTE_THREAD_STACK_USAGE) return region_idx; } size_t memfault_zephyr_get_data_regions(sMfltCoredumpRegion *regions, size_t num_regions) { if (num_regions == 0) { return 0; } #if defined(CONFIG_ARCH_POSIX) return 0; #else // These linker variables are defined in linker.ld in Zephyr RTOS. Note that // the data region name changed in v2.7 of the kernel. // // 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 // 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 extern uint32_t ZEPHYR_DATA_REGION_START[]; extern uint32_t ZEPHYR_DATA_REGION_END[]; const size_t size_to_collect = (uint32_t)ZEPHYR_DATA_REGION_END - (uint32_t)ZEPHYR_DATA_REGION_START; regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(ZEPHYR_DATA_REGION_START, size_to_collect); return 1; #endif // defined(CONFIG_ARCH_POSIX) } size_t memfault_zephyr_get_bss_regions(sMfltCoredumpRegion *regions, size_t num_regions) { if (num_regions == 0) { return 0; } // Linker variables defined in linker.ld in Zephyr RTOS extern uint32_t __bss_start[]; extern uint32_t __bss_end[]; const size_t size_to_collect = (uintptr_t)__bss_end - (uintptr_t)__bss_start; regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(__bss_start, size_to_collect); return 1; } ================================================ FILE: ports/zephyr/common/metrics/CMakeLists.txt ================================================ zephyr_library_sources(memfault_platform_metrics.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_METRICS_BLUETOOTH memfault_platform_bluetooth_metrics.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_METRICS_THREADS memfault_platform_thread_metrics.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_METRICS_WIFI memfault_platform_wifi_metrics.c) ================================================ FILE: ports/zephyr/common/metrics/memfault_platform_bluetooth_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(bluetooth/bluetooth.h) #include MEMFAULT_ZEPHYR_INCLUDE(bluetooth/conn.h) #include MEMFAULT_ZEPHYR_INCLUDE(bluetooth/gatt.h) #include MEMFAULT_ZEPHYR_INCLUDE(bluetooth/hci.h) #include MEMFAULT_ZEPHYR_INCLUDE(sys/byteorder.h) #include "controller/util/memq.h" // keep below memq.h to avoid missing definition error #include "controller/ll_sw/lll.h" // clang-format on #include #include #include "memfault/core/debug_log.h" #include "memfault/core/platform/core.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/timer.h" #include "memfault/ports/zephyr/version.h" #if defined(CONFIG_MEMFAULT_NRF_CONNECT_SDK) #include "memfault/ports/ncs/version.h" #endif static struct bt_conn *s_mflt_bt_current_conn = NULL; static uint32_t s_mflt_bt_connection_interval_us = 0; struct k_work_delayable s_mflt_bt_delayed_metrics_work; static void prv_record_gatt_mtu(struct bt_conn *conn) { uint16_t mtu = bt_gatt_get_mtu(conn); MEMFAULT_METRIC_SET_UNSIGNED(bt_gatt_mtu_size, mtu); } static void prv_count_connection_events(uint32_t interval_us, bool reset_time) { // to accumulate data correctly on: // - connection interval change // - connection up/down // // keep track of the last time the measurement was taken static uint64_t s_last_measurement_time_ms = 0; // if resetting tracked time, reset and return if (reset_time) { s_last_measurement_time_ms = memfault_platform_get_time_since_boot_ms(); return; } if (interval_us) { // compute connection events accumulated const uint64_t current_time_ms = memfault_platform_get_time_since_boot_ms(); // Note: this computation has some quantization error, but should be close // enough to draw conclusions safely const int32_t connection_events = (int32_t)((current_time_ms - s_last_measurement_time_ms) * 1000ull / (uint64_t)interval_us); MEMFAULT_METRIC_ADD(bt_connection_event_count, connection_events); s_last_measurement_time_ms = current_time_ms; } } #if defined(CONFIG_BT_CTLR_CONN_RSSI) static void prv_record_connection_rssi(struct bt_conn *conn) { // Try to get RSSI using vendor-specific command // Note: This API may not be available on all platforms uint16_t hci_conn_handle; int err = bt_hci_get_conn_handle(s_mflt_bt_current_conn, &hci_conn_handle); if (err) { MEMFAULT_LOG_ERROR("Failed to get RSSI connection handle: %d", err); return; } struct bt_hci_cp_read_rssi *cp; #if MEMFAULT_ZEPHYR_VERSION_GT(4, 1) struct net_buf *buf = bt_hci_cmd_alloc(K_FOREVER); #else struct net_buf *buf = bt_hci_cmd_create(BT_HCI_OP_READ_RSSI, sizeof(*cp)); #endif if (!buf) { return; } cp = net_buf_add(buf, sizeof(*cp)); cp->handle = sys_cpu_to_le16(hci_conn_handle); struct net_buf *rsp = NULL; err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_RSSI, buf, &rsp); if (err) { MEMFAULT_LOG_ERROR("Read RSSI err: %d\n", err); } else { struct bt_hci_rp_read_rssi *rp = (void *)rsp->data; int8_t rssi = rp->rssi; MEMFAULT_METRIC_SET_SIGNED(bt_connection_rssi, rssi); } net_buf_unref(rsp); } #else static void prv_record_connection_rssi(struct bt_conn *conn) { // RSSI reading not available on this platform configuration (void)conn; } #endif // defined(CONFIG_BT_CTLR_CONN_RSSI) static void prv_delayed_metrics_work_handler(struct k_work *work) { (void)work; // if connection has gone down before this runs, just return if (!s_mflt_bt_current_conn) { return; } // Read RSSI now prv_record_connection_rssi(s_mflt_bt_current_conn); #if defined(CONFIG_BT_REMOTE_INFO) char remote_info_str[32] = { 0 }; struct bt_conn_remote_info remote_info; int err = bt_conn_get_remote_info(s_mflt_bt_current_conn, &remote_info); if (err) { MEMFAULT_LOG_ERROR("Failed to get remote info: %d", err); } else { snprintf(remote_info_str, sizeof(remote_info_str), "%02x:%04x.%04x", remote_info.version, remote_info.manufacturer, remote_info.subversion); MEMFAULT_METRIC_SET_STRING(bt_connection_remote_info, remote_info_str); } #endif // defined(CONFIG_BT_REMOTE_INFO) } //! Helper function to contain some ugly compile-time logic to handle a change //! in the BT LE info structure in Zephyr > 4.3.0 (backported to NCS >= 3.2) static void prv_set_connection_interval_us(const struct bt_conn_info *info) { #if MEMFAULT_ZEPHYR_VERSION_GT(4, 3) s_mflt_bt_connection_interval_us = info->le.interval_us; #elif defined(CONFIG_MEMFAULT_NRF_CONNECT_SDK) // nRF Connect SDK needs special handling, due to backporting of Zephyr changes #if MEMFAULT_ZEPHYR_VERSION_GT(4, 2) && MEMFAULT_NCS_VERSION_GT(3, 1) s_mflt_bt_connection_interval_us = info->le.interval_us; #else s_mflt_bt_connection_interval_us = info->le.interval * CONN_INT_UNIT_US; #endif #else s_mflt_bt_connection_interval_us = info->le.interval * CONN_INT_UNIT_US; #endif } //! Read connection params, and update connection interval for later usage static void prv_record_connection_params(struct bt_conn *conn) { struct bt_conn_info info; if (bt_conn_get_info(conn, &info) == 0) { MEMFAULT_METRIC_SET_UNSIGNED(bt_connection_latency, info.le.latency); MEMFAULT_METRIC_SET_UNSIGNED(bt_connection_timeout, info.le.timeout); prv_set_connection_interval_us(&info); MEMFAULT_METRIC_SET_UNSIGNED(bt_connection_interval_us, s_mflt_bt_connection_interval_us); } else { MEMFAULT_LOG_ERROR("Failed to get connection info"); s_mflt_bt_connection_interval_us = 0; } } static void prv_bt_connected_cb(struct bt_conn *conn, uint8_t err) { if (err) { return; } s_mflt_bt_current_conn = bt_conn_ref(conn); // Start connected time timer MEMFAULT_METRIC_TIMER_START(bt_connected_time_ms); // Record connection metrics prv_record_gatt_mtu(conn); prv_record_connection_params(conn); // Start a one-shot timer to read additional metrics after a delay k_work_schedule(&s_mflt_bt_delayed_metrics_work, K_MSEC(CONFIG_MEMFAULT_METRICS_BLUETOOTH_DELAYED_MS)); // Reset connection event count timer prv_count_connection_events(0, true); } static void prv_bt_disconnected_cb(struct bt_conn *conn, uint8_t reason) { // tally connection events prv_count_connection_events(s_mflt_bt_connection_interval_us, false); s_mflt_bt_connection_interval_us = 0; if (s_mflt_bt_current_conn == conn) { bt_conn_unref(s_mflt_bt_current_conn); s_mflt_bt_current_conn = NULL; } // Increment disconnect counter MEMFAULT_METRIC_ADD(bt_disconnect_count, 1); // Stop connected time timer MEMFAULT_METRIC_TIMER_STOP(bt_connected_time_ms); } #if defined(CONFIG_BT_REMOTE_INFO) // Note: this does not seem to fire when the remote initiates the connection, // only when this device does. static void prv_record_remote_info_cb(struct bt_conn *conn, struct bt_conn_remote_info *remote_info) { // Record remote device information char remote_info_str[32] = { 0 }; snprintf(remote_info_str, sizeof(remote_info_str), "%02x:%04x.%04x", remote_info->version, remote_info->manufacturer, remote_info->subversion); MEMFAULT_METRIC_SET_STRING(bt_connection_remote_info, remote_info_str); } #endif // defined(CONFIG_BT_REMOTE_INFO) static void prv_bt_le_param_updated_cb(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout) { uint32_t interval_us = interval * CONN_INT_UNIT_US; // Record LE connection parameters MEMFAULT_METRIC_SET_UNSIGNED(bt_connection_interval_us, interval_us); MEMFAULT_METRIC_SET_UNSIGNED(bt_connection_latency, latency); MEMFAULT_METRIC_SET_UNSIGNED(bt_connection_timeout, timeout); // Tally connection event counts received with the previous interval setting prv_count_connection_events(s_mflt_bt_connection_interval_us, false); // Update connection interval for computing connection event count s_mflt_bt_connection_interval_us = interval_us; } BT_CONN_CB_DEFINE(bt_metrics_conn_callbacks) = { .connected = prv_bt_connected_cb, .disconnected = prv_bt_disconnected_cb, .le_param_updated = prv_bt_le_param_updated_cb, #if defined(CONFIG_BT_REMOTE_INFO) .remote_info_available = prv_record_remote_info_cb, #endif }; static void prv_bt_att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx) { // note: the BT spec requires a symmetric tx/rx MTU size // see reference: // https://github.com/zephyrproject-rtos/zephyr/blob/v4.3.0/subsys/bluetooth/host/att.c#L136-L144 MEMFAULT_METRIC_SET_UNSIGNED(bt_gatt_mtu_size, MIN(tx, rx)); } static struct bt_gatt_cb prv_gatt_callbacks = { .att_mtu_updated = prv_bt_att_mtu_updated, }; void memfault_bluetooth_metrics_heartbeat_update(void) { if (s_mflt_bt_current_conn != NULL) { // Update connection event count estimate prv_count_connection_events(s_mflt_bt_connection_interval_us, false); // Record current RSSI (if available) prv_record_connection_rssi(s_mflt_bt_current_conn); // And connection parameters + MTU. Callbacks should refresh these on // change, but we want to report them if there was no change too. prv_record_connection_params(s_mflt_bt_current_conn); prv_record_gatt_mtu(s_mflt_bt_current_conn); } } static int prv_init_bluetooth_metrics(void) { k_work_init_delayable(&s_mflt_bt_delayed_metrics_work, prv_delayed_metrics_work_handler); bt_gatt_cb_register(&prv_gatt_callbacks); return 0; } SYS_INIT(prv_init_bluetooth_metrics, APPLICATION, CONFIG_MEMFAULT_INIT_PRIORITY); ================================================ FILE: ports/zephyr/common/metrics/memfault_platform_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #if defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) #include MEMFAULT_ZEPHYR_INCLUDE(device.h) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/sensor.h) #endif #include #include #include "memfault/core/debug_log.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/timer.h" #include "memfault/ports/zephyr/version.h" #include "memfault/ports/zephyr/thread_metrics.h" #if defined(CONFIG_MEMFAULT_METRICS_BLUETOOTH) #include "memfault/ports/zephyr/bluetooth_metrics.h" #endif #if defined(CONFIG_MEMFAULT_METRICS_TCP_IP) #include MEMFAULT_ZEPHYR_INCLUDE(net/net_stats.h) // Directory traversal is needed to access the header for net_stats_reset(), // which is not in the global Zephyr search path. #include MEMFAULT_ZEPHYR_INCLUDE(../../subsys/net/ip/net_stats.h) #endif #if CONFIG_MEMFAULT_FS_BYTES_FREE_METRIC #include MEMFAULT_ZEPHYR_INCLUDE(fs/fs.h) #endif // clang-format on #if defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) //! _system_heap is the heap used by k_malloc/k_free extern struct sys_heap _system_heap; #endif // defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) #if defined(CONFIG_MEMFAULT_METRICS_DEFAULT_SET_ENABLE) static uint64_t prv_cycle_delta_update(uint64_t curr_cycles, uint64_t *prev_cycles) { // computes delta correctly if prev > curr due to overflow uint64_t delta = curr_cycles - *prev_cycles; *prev_cycles = curr_cycles; return delta; } #endif #if defined(CONFIG_MEMFAULT_METRICS_TCP_IP) static void prv_collect_ip_statistics(void) { struct net_stats data; int err = net_mgmt(NET_REQUEST_STATS_GET_ALL, NULL, &data, sizeof(data)); if (err) { return; } // save the most interesting stats as metrics, and reset the stats structure #if defined(CONFIG_NET_STATISTICS_UDP) MEMFAULT_METRIC_SET_UNSIGNED(net_udp_recv, data.udp.recv); MEMFAULT_METRIC_SET_UNSIGNED(net_udp_sent, data.udp.sent); #endif #if defined(CONFIG_NET_STATISTICS_TCP) MEMFAULT_METRIC_SET_UNSIGNED(net_tcp_recv, data.tcp.recv); MEMFAULT_METRIC_SET_UNSIGNED(net_tcp_sent, data.tcp.sent); #endif MEMFAULT_METRIC_SET_UNSIGNED(net_bytes_received, data.bytes.received); MEMFAULT_METRIC_SET_UNSIGNED(net_bytes_sent, data.bytes.sent); // reset the stats net_stats_reset(NULL); } #endif // defined(MEMFAULT_METRICS_TCP_IP) #if defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) static void prv_collect_cpu_temp(void) { struct sensor_value val; // Check for a compatible CPU temperature sensor node in the device tree // 1. Check for a user-defined alias first #if DT_NODE_HAS_STATUS(DT_ALIAS(memfault_cpu_temp), okay) #define CPU_TEMP_NODE_ID DT_ALIAS(memfault_cpu_temp) // 2. 'nordic_nrf_temp' is used on Nordic nRF51/nRF52/nRF53/nRF54 series #elif DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_temp) #define CPU_TEMP_NODE_ID DT_COMPAT_GET_ANY_STATUS_OKAY(nordic_nrf_temp) // 3. 'temp' is used on some Nordic platforms #elif DT_NODE_HAS_STATUS(DT_NODELABEL(temp), okay) #define CPU_TEMP_NODE_ID DT_NODELABEL(temp) // 4. 'die_temp0' and 'die_temp' are commonly used on STM32 and NXP platforms #elif DT_NODE_HAS_STATUS(DT_ALIAS(die_temp0), okay) #define CPU_TEMP_NODE_ID DT_ALIAS(die_temp0) #elif DT_NODE_HAS_STATUS(DT_ALIAS(die_temp), okay) #define CPU_TEMP_NODE_ID DT_ALIAS(die_temp) #else #error \ "No CPU temperature sensor found, make sure to enable a compatible node in the device tree" #endif const struct device *dev = DEVICE_DT_GET(CPU_TEMP_NODE_ID); if (!device_is_ready(dev)) { return; } if (sensor_sample_fetch(dev)) { return; } if (sensor_channel_get(dev, SENSOR_CHAN_DIE_TEMP, &val)) { return; } // val1 is the integer part and val2 is fractional millionths. Scale both to match metric // precision const int32_t temperature = (val.val1 * 10) + (val.val2 / 100000); MEMFAULT_LOG_DEBUG("CPU Temp: %d.%06d", val.val1, val.val2); MEMFAULT_METRIC_SET_SIGNED(thermal_cpu_c, temperature); } #endif // defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) #if defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) static void prv_collect_memory_usage_metrics(void) { #if MEMFAULT_ZEPHYR_VERSION_GT(3, 1) // struct sys_memory_stats introduced in v3.2 struct sys_memory_stats stats = { 0 }; #else struct sys_heap_runtime_stats stats = { 0 }; #endif sys_heap_runtime_stats_get(&_system_heap, &stats); MEMFAULT_METRIC_SET_UNSIGNED(Heap_BytesFree, stats.free_bytes); #if MEMFAULT_ZEPHYR_VERSION_GT(3, 0) // stats.max_allocated_bytes was added in Zephyr 3.1 // // Range is 0-10000 for 0.00-100.00% // Multiply by 100 for 2 decimals of precision via the scale factor, then by 100 again // for percentage conversion MEMFAULT_METRIC_SET_UNSIGNED(memory_pct_max, (uint32_t)(stats.max_allocated_bytes * 10000.f) / (stats.free_bytes + stats.allocated_bytes)); #endif // MEMFAULT_ZEPHYR_VERSION_GT(3, 0) } #endif // defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) #if defined(CONFIG_MEMFAULT_FS_BYTES_FREE_METRIC) void prv_collect_fs_bytes_free_metric(void) { const char *mount_point = NULL; // User configured path takes priority #ifdef CONFIG_MEMFAULT_FS_BYTES_FREE_VFS_PATH char normalized_path[64]; if (CONFIG_MEMFAULT_FS_BYTES_FREE_VFS_PATH[0] != '\0') { MEMFAULT_LOG_DEBUG("Using user-configured mount point for FS bytes free metric"); snprintf(normalized_path, sizeof(normalized_path), "/%s", CONFIG_MEMFAULT_FS_BYTES_FREE_VFS_PATH); mount_point = normalized_path; } #endif // Auto-detect from fstab only #if DT_NODE_EXISTS(DT_PATH(fstab)) MEMFAULT_LOG_DEBUG("Auto-detecting mount point from fstab for FS bytes free metric"); // Create a ternary chain to find the first mount point available // // The return statement here will expand to something like: // return (DT_NODE_HAS_PROP(child1, mount_point) ? DT_PROP(child1, mount_point) :) // (DT_NODE_HAS_PROP(child2, mount_point) ? DT_PROP(child2, mount_point) :) // (DT_NODE_HAS_PROP(child3, mount_point) ? DT_PROP(child3, mount_point) :) // NULL; #define FIND_FIRST_MOUNT(node_id) \ DT_NODE_HAS_PROP(node_id, mount_point) ? DT_PROP(node_id, mount_point): mount_point = DT_FOREACH_CHILD_SEP(DT_PATH(fstab), FIND_FIRST_MOUNT, ) NULL; #endif if (mount_point == NULL) { MEMFAULT_LOG_WARN("No mount point configured - skipping FS bytes free metric"); return; } MEMFAULT_LOG_DEBUG("Collecting FS bytes free metric for mount point %s", mount_point); struct fs_statvfs fs_stats; int retval = fs_statvfs(mount_point, &fs_stats); if (retval == 0) { // compute free bytes uint32_t bytes_free = fs_stats.f_frsize * fs_stats.f_bfree; MEMFAULT_METRIC_SET_UNSIGNED(FileSystem_BytesFree, bytes_free); } } #endif /* CONFIG_MEMFAULT_FS_BYTES_FREE_METRIC */ // Written as a function vs. in-line b/c we might want to extern this at some point? // See ports/zephyr/config/memfault_metrics_heartbeat_zephyr_port_config.def for // where the metrics key names come from. void memfault_metrics_heartbeat_collect_sdk_data(void) { #if defined(CONFIG_MEMFAULT_METRICS_DEFAULT_SET_ENABLE) struct k_thread *me = k_current_get(); size_t free_stack_size; k_thread_stack_space_get(me, &free_stack_size); MEMFAULT_METRIC_SET_UNSIGNED(TimerTaskFreeStack, free_stack_size); static uint64_t s_prev_timer_task_cycles = 0; static uint64_t s_prev_all_tasks_cycles = 0; k_thread_runtime_stats_t timer_task_stats; k_thread_runtime_stats_get(me, &timer_task_stats); uint64_t task_cycles_delta = prv_cycle_delta_update(timer_task_stats.execution_cycles, &s_prev_timer_task_cycles); MEMFAULT_METRIC_SET_UNSIGNED(TimerTaskCpuUsage, (uint32_t)task_cycles_delta); MEMFAULT_LOG_DEBUG("Timer task cycles: %u", (uint32_t)task_cycles_delta); k_thread_runtime_stats_t all_tasks_stats; k_thread_runtime_stats_all_get(&all_tasks_stats); uint64_t all_tasks_cycles_delta = prv_cycle_delta_update(all_tasks_stats.execution_cycles, &s_prev_all_tasks_cycles); MEMFAULT_METRIC_SET_UNSIGNED(AllTasksCpuUsage, (uint32_t)all_tasks_cycles_delta); MEMFAULT_LOG_DEBUG("All tasks cycles: %u", (uint32_t)all_tasks_cycles_delta); // stats.total_cycles added in Zephyr 3.0 #if MEMFAULT_ZEPHYR_VERSION_GTE_STRICT(3, 0) static uint64_t s_prev_non_idle_tasks_cycles = 0; if (all_tasks_cycles_delta > 0) { uint64_t non_idle_tasks_cycles_delta = prv_cycle_delta_update(all_tasks_stats.total_cycles, &s_prev_non_idle_tasks_cycles); MEMFAULT_LOG_DEBUG("Non-idle tasks cycles: %u", (uint32_t)non_idle_tasks_cycles_delta); // Range is 0-10000 for 0.00-100.00% // Multiply by 100 for 2 decimals of precision via the scale factor, then by 100 again // for percentage conversion uint32_t usage_pct = (uint32_t)(non_idle_tasks_cycles_delta * 10000 / all_tasks_cycles_delta); MEMFAULT_METRIC_SET_UNSIGNED(cpu_usage_pct, usage_pct); MEMFAULT_LOG_DEBUG("CPU usage: %u.%02u%%", usage_pct / 100, usage_pct % 100); } #endif // MEMFAULT_ZEPHYR_VERSION_GT_STRICT(3, 0) #if defined(CONFIG_MEMFAULT_FS_BYTES_FREE_METRIC) prv_collect_fs_bytes_free_metric(); #endif /* CONFIG_MEMFAULT_FS_BYTES_FREE_METRIC */ #endif /* CONFIG_MEMFAULT_METRICS_DEFAULT_SET_ENABLE */ #if defined(CONFIG_MEMFAULT_METRICS_TCP_IP) prv_collect_ip_statistics(); #endif #if defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) prv_collect_cpu_temp(); #endif #if defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) prv_collect_memory_usage_metrics(); #endif #if defined(CONFIG_MEMFAULT_METRICS_THREADS) memfault_zephyr_thread_metrics_record(); #endif #if defined(CONFIG_MEMFAULT_METRICS_BLUETOOTH) memfault_bluetooth_metrics_heartbeat_update(); #endif } #if !defined(CONFIG_MEMFAULT_METRICS_TIMER_CUSTOM) static MemfaultPlatformTimerCallback *s_metrics_timer_callback; static void prv_metrics_work_handler(struct k_work *work) { if (s_metrics_timer_callback != NULL) { s_metrics_timer_callback(); } } K_WORK_DEFINE(s_metrics_timer_work, prv_metrics_work_handler); //! Timer handlers run from an ISR so we dispatch the heartbeat job to the worker task static void prv_timer_expiry_handler(struct k_timer *dummy) { k_work_submit(&s_metrics_timer_work); } K_TIMER_DEFINE(s_metrics_timer, prv_timer_expiry_handler, NULL); bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MemfaultPlatformTimerCallback callback) { s_metrics_timer_callback = callback; k_timer_start(&s_metrics_timer, K_SECONDS(period_sec), K_SECONDS(period_sec)); return true; } #endif // !defined(CONFIG_MEMFAULT_METRICS_TIMER_CUSTOM) ================================================ FILE: ports/zephyr/common/metrics/memfault_platform_thread_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #include "memfault/ports/zephyr/thread_metrics.h" #include #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include "memfault/core/debug_log.h" #include "memfault/core/math.h" #include "memfault/metrics/metrics.h" // clang-format on MEMFAULT_WEAK MEMFAULT_METRICS_DEFINE_THREAD_METRICS( #if defined(CONFIG_MEMFAULT_METRICS_THREADS_DEFAULTS) #if defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE) { .thread_name = "mflt_upload", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_mflt_upload_pct_max), }, #endif { .thread_name = "idle", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle_pct_max), }, { .thread_name = "sysworkq", .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_sysworkq_pct_max), } #endif ); static uint32_t prv_get_stack_usage_pct(const struct k_thread *thread) { size_t free_stack_size; k_thread_stack_space_get(thread, &free_stack_size); // The stack size is the total stack size, so we need to subtract the free // space to get the used space. const size_t stack_usage = thread->stack_info.size - free_stack_size; return (uint32_t)(stack_usage * 100 * CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) / thread->stack_info.size; } static void prv_record_thread_metrics(const struct k_thread *thread, void *user_data) { ARG_UNUSED(user_data); const char *name = k_thread_name_get((k_tid_t)thread); if (!name || name[0] == '\0') { return; } // Iterate over the thread list. A blank thread name indicates the end of the list. const sMfltZephyrThreadMetricsIndex *thread_metrics = g_memfault_thread_metrics_index; while (thread_metrics->thread_name) { if (strcmp(thread_metrics->thread_name, thread->name) == 0) { uint32_t stack_usage_pct = prv_get_stack_usage_pct(thread); memfault_metrics_heartbeat_set_unsigned(thread_metrics->stack_usage_metric_key, stack_usage_pct); break; } thread_metrics++; } } void memfault_zephyr_thread_metrics_record(void) { k_thread_foreach_unlocked(prv_record_thread_metrics, NULL); } ================================================ FILE: ports/zephyr/common/metrics/memfault_platform_wifi_metrics.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include #include MEMFAULT_ZEPHYR_INCLUDE(net/net_mgmt.h) #include MEMFAULT_ZEPHYR_INCLUDE(net/wifi.h) #include MEMFAULT_ZEPHYR_INCLUDE(net/wifi_mgmt.h) #include #include "memfault/core/debug_log.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/timer.h" #include "memfault/ports/zephyr/version.h" // clang-format on const char *prv_link_mode_to_str(enum wifi_link_mode link_mode) { switch (link_mode) { case WIFI_0: return "802.11"; case WIFI_1: return "802.11b"; case WIFI_2: return "802.11a"; case WIFI_3: return "802.11g"; case WIFI_4: return "802.11n"; case WIFI_5: return "802.11ac"; case WIFI_6: return "802.11ax"; case WIFI_6E: return "802.11ax/6GHz"; case WIFI_7: return "802.11be"; case WIFI_LINK_MODE_UNKNOWN: default: return "unknown"; } } static const char *prv_wifi_security_type_to_string(enum wifi_security_type type) { switch (type) { case WIFI_SECURITY_TYPE_NONE: return "NONE"; case WIFI_SECURITY_TYPE_PSK: return "WPA2-PSK"; case WIFI_SECURITY_TYPE_PSK_SHA256: return "WPA2-PSK-SHA256"; case WIFI_SECURITY_TYPE_SAE: // case WIFI_SECURITY_TYPE_SAE_HNP: return "WPA3-SAE"; case WIFI_SECURITY_TYPE_SAE_H2E: return "WPA3-SAE-H2E"; case WIFI_SECURITY_TYPE_SAE_AUTO: return "WPA3-SAE-AUTO"; case WIFI_SECURITY_TYPE_WAPI: return "WAPI"; case WIFI_SECURITY_TYPE_EAP: // case WIFI_SECURITY_TYPE_EAP_TLS: return "EAP"; case WIFI_SECURITY_TYPE_WEP: return "WEP"; case WIFI_SECURITY_TYPE_WPA_PSK: return "WPA-PSK"; case WIFI_SECURITY_TYPE_WPA_AUTO_PERSONAL: return "WPA-AUTO-PERSONAL"; case WIFI_SECURITY_TYPE_DPP: return "DPP"; case WIFI_SECURITY_TYPE_EAP_PEAP_MSCHAPV2: return "EAP-PEAP-MSCHAPV2"; case WIFI_SECURITY_TYPE_EAP_PEAP_GTC: return "EAP-PEAP-GTC"; case WIFI_SECURITY_TYPE_EAP_TTLS_MSCHAPV2: return "EAP-TTLS-MSCHAPV2"; case WIFI_SECURITY_TYPE_EAP_PEAP_TLS: return "EAP-PEAP-TLS"; // only for zephyr >= 4.1.0 #if MEMFAULT_ZEPHYR_VERSION_GTE_STRICT(4, 1) case WIFI_SECURITY_TYPE_FT_PSK: return "FT-PSK"; case WIFI_SECURITY_TYPE_FT_SAE: return "FT-SAE"; case WIFI_SECURITY_TYPE_FT_EAP: return "FT-EAP"; case WIFI_SECURITY_TYPE_FT_EAP_SHA384: return "FT-EAP-SHA384"; case WIFI_SECURITY_TYPE_SAE_EXT_KEY: return "SAE-EXT-KEY"; #endif default: return "UNKNOWN"; } } const char *prv_wifi_frequency_band_to_str(enum wifi_frequency_bands band) { switch (band) { case WIFI_FREQ_BAND_2_4_GHZ: return "2.4"; case WIFI_FREQ_BAND_5_GHZ: return "5"; case WIFI_FREQ_BAND_6_GHZ: return "6"; default: return "x"; } } static void prv_record_wifi_connection_metrics(struct net_if *iface) { struct wifi_iface_status status = { 0 }; if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &status, sizeof(struct wifi_iface_status))) { return; } MEMFAULT_METRIC_SET_STRING(wifi_standard_version, prv_link_mode_to_str(status.link_mode)); MEMFAULT_METRIC_SET_STRING(wifi_security_type, prv_wifi_security_type_to_string(status.security)); MEMFAULT_METRIC_SET_STRING(wifi_frequency_band, prv_wifi_frequency_band_to_str(status.band)); MEMFAULT_METRIC_SET_UNSIGNED(wifi_primary_channel, status.channel); if (status.iface_mode == WIFI_MODE_INFRA) { MEMFAULT_METRIC_SET_SIGNED(wifi_sta_rssi, status.rssi); } MEMFAULT_METRIC_SET_UNSIGNED(wifi_beacon_interval, status.beacon_interval); MEMFAULT_METRIC_SET_UNSIGNED(wifi_dtim_interval, status.dtim_period); MEMFAULT_METRIC_SET_UNSIGNED(wifi_twt_capable, status.twt_capable); #if MEMFAULT_ZEPHYR_VERSION_GTE_STRICT(4, 1) // some devices will not have this value set if (status.current_phy_tx_rate != 0) { MEMFAULT_METRIC_SET_UNSIGNED(wifi_tx_rate_mbps, status.current_phy_tx_rate); } #endif char oui[9]; snprintf(oui, sizeof(oui), "%02x:%02x:%02x", status.bssid[0], status.bssid[1], status.bssid[2]); MEMFAULT_METRIC_SET_STRING(wifi_ap_oui, oui); } static void prv_wifi_event_callback(struct net_mgmt_event_callback *cb, #if MEMFAULT_ZEPHYR_VERSION_GT(4, 2) uint64_t mgmt_event, #else uint32_t mgmt_event, #endif struct net_if *iface) { switch (mgmt_event) { case NET_EVENT_WIFI_CONNECT_RESULT: { const struct wifi_status *status = (const struct wifi_status *)cb->info; if (status->status) { // Future: Record connect errors } else { // start connected-time timer MEMFAULT_METRIC_TIMER_START(wifi_connected_time_ms); // record connection metrics prv_record_wifi_connection_metrics(iface); } } break; case NET_EVENT_WIFI_DISCONNECT_RESULT: { const struct wifi_status *status = (const struct wifi_status *)cb->info; if (status->status) { // TODO: Record disconnect error } else { // stop connected-time timer, increment disconnect count MEMFAULT_METRIC_TIMER_STOP(wifi_connected_time_ms); MEMFAULT_METRIC_ADD(wifi_disconnect_count, 1); } } break; default: // shouldn't happen since this callback only subscribes to the above events break; } } static int prv_init_wifi_metrics(void) { static struct net_mgmt_event_callback wifi_shell_mgmt_cb; net_mgmt_init_event_callback(&wifi_shell_mgmt_cb, prv_wifi_event_callback, // TODO: NET_EVENT_L4_CONNECTED + NET_EVENT_L4_DISCONNECTED instead? NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT); net_mgmt_add_event_callback(&wifi_shell_mgmt_cb); return 0; } SYS_INIT(prv_init_wifi_metrics, APPLICATION, CONFIG_MEMFAULT_INIT_PRIORITY); ================================================ FILE: ports/zephyr/config/memfault_metrics_heartbeat_zephyr_port_config.def ================================================ //! @file #include "memfault/ports/zephyr/version.h" #if defined(CONFIG_MEMFAULT_METRICS_DEFAULT_SET_ENABLE) #if defined(CONFIG_THREAD_STACK_INFO) MEMFAULT_METRICS_KEY_DEFINE(TimerTaskFreeStack, kMemfaultMetricType_Unsigned) #endif #if defined(CONFIG_THREAD_RUNTIME_STATS) // stats.total_cycles added in Zephyr 3.0 #if MEMFAULT_ZEPHYR_VERSION_GTE_STRICT(3, 0) // Two decimal places of precision MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(cpu_usage_pct, kMemfaultMetricType_Unsigned, 100) #endif // MEMFAULT_ZEPHYR_VERSION_GT(3, 0) MEMFAULT_METRICS_KEY_DEFINE(TimerTaskCpuUsage, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(AllTasksCpuUsage, kMemfaultMetricType_Unsigned) #endif #if defined(CONFIG_MEMFAULT_FS_BYTES_FREE_METRIC) MEMFAULT_METRICS_KEY_DEFINE(FileSystem_BytesFree, kMemfaultMetricType_Unsigned) #endif #endif // defined(CONFIG_MEMFAULT_METRICS_DEFAULT_SET_ENABLE) #if defined(CONFIG_MEMFAULT_METRICS_TCP_IP) // save the most interesting stats as metrics, and reset the stats structure #if defined(CONFIG_NET_STATISTICS_UDP) MEMFAULT_METRICS_KEY_DEFINE(net_udp_recv, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(net_udp_sent, kMemfaultMetricType_Unsigned) #endif // defined(CONFIG_NET_STATISTICS_UDP) #if defined(CONFIG_NET_STATISTICS_TCP) MEMFAULT_METRICS_KEY_DEFINE(net_tcp_recv, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(net_tcp_sent, kMemfaultMetricType_Unsigned) #endif // defined(CONFIG_NET_STATISTICS_TCP) MEMFAULT_METRICS_KEY_DEFINE(net_bytes_received, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(net_bytes_sent, kMemfaultMetricType_Unsigned) #endif // defined(CONFIG_MEMFAULT_METRICS_TCP_IP) #if defined(CONFIG_MEMFAULT_METRICS_WIFI) MEMFAULT_METRICS_KEY_DEFINE(wifi_connected_time_ms, kMemfaultMetricType_Timer) // MEMFAULT_METRICS_KEY_DEFINE(wifi_sta_min_rssi, kMemfaultMetricType_Signed) MEMFAULT_METRICS_KEY_DEFINE(wifi_disconnect_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_STRING_KEY_DEFINE(wifi_standard_version, sizeof("802.11ax/6GHz") - 1) MEMFAULT_METRICS_STRING_KEY_DEFINE(wifi_security_type, sizeof("WPA-AUTO-PERSONAL") - 1) MEMFAULT_METRICS_STRING_KEY_DEFINE(wifi_frequency_band, sizeof("2.4") - 1) MEMFAULT_METRICS_KEY_DEFINE(wifi_primary_channel, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(wifi_sta_rssi, kMemfaultMetricType_Signed) MEMFAULT_METRICS_KEY_DEFINE(wifi_beacon_interval, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(wifi_dtim_interval, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(wifi_twt_capable, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(wifi_tx_rate_mbps, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_STRING_KEY_DEFINE(wifi_ap_oui, sizeof("00:00:00") - 1) #endif // defined(CONFIG_MEMFAULT_METRICS_WIFI) #if defined(CONFIG_MEMFAULT_METRICS_BLUETOOTH) MEMFAULT_METRICS_KEY_DEFINE(bt_gatt_mtu_size, kMemfaultMetricType_Unsigned) #if defined(CONFIG_BT_REMOTE_VERSION) // String representation of remote info: // [bt version]:[manufacturer id].[subversion] // [8 bits]:[16 bits].[16 bits] MEMFAULT_METRICS_STRING_KEY_DEFINE(bt_connection_remote_info, sizeof("ff:ffff.ffff") - 1) #endif // defined(CONFIG_BT_REMOTE_VERSION) MEMFAULT_METRICS_KEY_DEFINE(bt_connection_event_count, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(bt_connection_interval_us, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(bt_connection_latency, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(bt_connection_timeout, kMemfaultMetricType_Unsigned) #if defined(CONFIG_BT_CTLR_CONN_RSSI) MEMFAULT_METRICS_KEY_DEFINE(bt_connection_rssi, kMemfaultMetricType_Signed) #endif // defined(CONFIG_BT_CTLR_CONN_RSSI) MEMFAULT_METRICS_KEY_DEFINE(bt_connected_time_ms, kMemfaultMetricType_Timer) MEMFAULT_METRICS_KEY_DEFINE(bt_disconnect_count, kMemfaultMetricType_Unsigned) #endif // defined(CONFIG_MEMFAULT_METRICS_BLUETOOTH) #if defined(CONFIG_MEMFAULT_METRICS_CPU_TEMP) // One decimal place of precision MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(thermal_cpu_c, kMemfaultMetricType_Signed, 10) #endif #if defined(CONFIG_MEMFAULT_METRICS_MEMORY_USAGE) #if MEMFAULT_ZEPHYR_VERSION_GT(3, 0) // Two decimal places of precision MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_pct_max, kMemfaultMetricType_Unsigned, 100) #endif // MEMFAULT_ZEPHYR_VERSION_GT(3, 0) MEMFAULT_METRICS_KEY_DEFINE(Heap_BytesFree, kMemfaultMetricType_Unsigned) #endif #if defined(CONFIG_MEMFAULT_METRICS_THREADS_DEFAULTS) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_idle_pct_max, kMemfaultMetricType_Unsigned, CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_sysworkq_pct_max, kMemfaultMetricType_Unsigned, CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #if defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory_mflt_upload_pct_max, kMemfaultMetricType_Unsigned, CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR) #endif #endif // defined(CONFIG_MEMFAULT_METRICS_THREADS_DEFAULTS) #if defined(CONFIG_MEMFAULT_METRICS_BOOT_TIME) MEMFAULT_METRICS_KEY_DEFINE(boot_time_ms, kMemfaultMetricType_Unsigned) #endif // defined(CONFIG_MEMFAULT_METRICS_BOOT_TIME) #if defined(CONFIG_MEMFAULT_METRICS_EXTRA_DEFS_FILE) #include "memfault_metrics_heartbeat_extra.def" #endif #if defined(CONFIG_MEMFAULT_NRF_CONNECT_SDK) #include "memfault_metrics_heartbeat_ncs_port_config.def" #endif // Pull in the user's heartbeat defs #if defined(CONFIG_MEMFAULT_USER_CONFIG_ENABLE) #if defined(CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL) #if __has_include("memfault_metrics_heartbeat_config.def") #include "memfault_metrics_heartbeat_config.def" #endif #else #include "memfault_metrics_heartbeat_config.def" #endif // defined(CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL) #endif ================================================ FILE: ports/zephyr/config/memfault_platform_log_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Zephyr port implementation of memfault_platform_log_config.h //! //! This file maps the Memfault SDK log macros to implementations that respect //! the CONFIG_MEMFAULT_LOG_LEVEL Kconfig setting. Logs below the configured //! level will be compiled out. #include "memfault/config.h" #include "memfault/core/log.h" #include "memfault/core/platform/debug_log.h" #ifdef __cplusplus extern "C" { #endif #if !MEMFAULT_SDK_LOG_SAVE_DISABLE // Note that this call will be a no-op if the system has not initialized the log module // by calling memfault_log_boot(). See ./log.h for more details. #define MEMFAULT_SDK_LOG_SAVE MEMFAULT_LOG_SAVE #else #define MEMFAULT_SDK_LOG_SAVE(...) #endif #define _MEMFAULT_LOG_IMPL(_level, ...) \ do { \ MEMFAULT_SDK_LOG_SAVE(_level, __VA_ARGS__); \ memfault_platform_log(_level, __VA_ARGS__); \ } while (0) // Map Memfault log levels to Zephyr log levels // LOG_LEVEL values: 0=OFF, 1=ERR, 2=WRN, 3=INF, 4=DBG // kMemfaultPlatformLogLevel: Debug=0, Info=1, Warning=2, Error=3 // Compile out logs below the configured level #if CONFIG_MEMFAULT_LOG_LEVEL >= 4 // DBG #define MEMFAULT_LOG_DEBUG(...) _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Debug, __VA_ARGS__) #else #define MEMFAULT_LOG_DEBUG(...) #endif #if CONFIG_MEMFAULT_LOG_LEVEL >= 3 // INF #define MEMFAULT_LOG_INFO(...) _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Info, __VA_ARGS__) #else #define MEMFAULT_LOG_INFO(...) #endif #if CONFIG_MEMFAULT_LOG_LEVEL >= 2 // WRN #define MEMFAULT_LOG_WARN(...) _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Warning, __VA_ARGS__) #else #define MEMFAULT_LOG_WARN(...) #endif #if CONFIG_MEMFAULT_LOG_LEVEL >= 1 // ERR #define MEMFAULT_LOG_ERROR(...) _MEMFAULT_LOG_IMPL(kMemfaultPlatformLogLevel_Error, __VA_ARGS__) #else #define MEMFAULT_LOG_ERROR(...) #endif //! Only needs to be implemented when using demo component #define MEMFAULT_LOG_RAW(...) memfault_platform_log_raw(__VA_ARGS__) #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/config/memfault_trace_reason_zephyr_port_config.def ================================================ #if CONFIG_MEMFAULT_USER_CONFIG_ENABLE #if CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL # if __has_include("memfault_trace_reason_user_config.def") # include "memfault_trace_reason_user_config.def" # endif #else #include "memfault_trace_reason_user_config.def" #endif /* CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL */ #endif ================================================ FILE: ports/zephyr/config/memfault_zephyr_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Zephyr port overrides for the default configuration settings in the memfault-firmware-sdk. #ifdef __cplusplus extern "C" { #endif #if defined(CONFIG_MEMFAULT_PLATFORM_HAS_LOG_CONFIG) #define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 #endif #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 // version between editing and compiling the code. This will lead to issues when // recovering backtraces or symbol information because the debug information in // the symbol file may be out of sync with the actual binary. Tracking a build id // enables the Memfault cloud to identify and surface when this happens! Below // requires the "-Wl,--build-id" flag. #define MEMFAULT_USE_GNU_BUILD_ID 1 #endif // We need to define MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS=1 for the logs to // show up in the Memfault UI on crash. #ifndef MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS #define MEMFAULT_COREDUMP_COLLECT_LOG_REGIONS 1 #endif #define MEMFAULT_WATCHDOG_SW_TIMEOUT_SECS CONFIG_MEMFAULT_SOFTWARE_WATCHDOG_TIMEOUT_SECS // Logs are saved to the Memfault logging system as part of // memfault logging integration (CONFIG_MEMFAULT_LOGGING_ENABLE=y) // so no need to save from the SDK #define MEMFAULT_SDK_LOG_SAVE_DISABLE 1 #if defined(CONFIG_MEMFAULT_CACHE_FAULT_REGS) // Map Zephyr config to Memfault define so that we can // collect the HW fault regs before Zephyr modifies them. #define MEMFAULT_CACHE_FAULT_REGS 1 #endif #if defined(CONFIG_MEMFAULT_HEAP_STATS) // Map Zephyr config to Memfault define to enable heap // tracing collection. #define MEMFAULT_COREDUMP_COLLECT_HEAP_STATS 1 #endif #if defined(CONFIG_MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE) #define MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE 1 #endif #if defined(CONFIG_MEMFAULT_FAULT_HANDLER_RETURN) #define MEMFAULT_FAULT_HANDLER_RETURN 1 #endif #if defined(CONFIG_MEMFAULT_COMPACT_LOG) #define MEMFAULT_COMPACT_LOG_ENABLE 1 #endif #if defined(CONFIG_MEMFAULT_METRICS_SYNC_SUCCESS) #define MEMFAULT_METRICS_SYNC_SUCCESS 1 #endif #if defined(CONFIG_MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS) #define MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS 1 #endif #if defined(CONFIG_MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME) #define MEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME 1 #endif #if defined(CONFIG_MEMFAULT_METRICS_BATTERY_ENABLE) #define MEMFAULT_METRICS_BATTERY_ENABLE 1 #endif #if defined(CONFIG_MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE) #define MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE \ CONFIG_MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE #endif #if defined(CONFIG_MEMFAULT_SHELL_SELF_TEST) #define MEMFAULT_DEMO_CLI_SELF_TEST 1 #define MEMFAULT_SELF_TEST_COREDUMP_STORAGE_DISABLE_MSG \ "Set CONFIG_MEMFAULT_SHELL_SELF_TEST_COREDUMP_STORAGE in your prj.conf" #endif #if defined(CONFIG_MEMFAULT_SHELL_SELF_TEST_COREDUMP_STORAGE) #define MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE 1 #endif #if defined(CONFIG_MEMFAULT_CDR_ENABLE) #define MEMFAULT_CDR_ENABLE 1 #endif // Defaults to 1, so only disable if Kconfig setting is unset #if !defined(CONFIG_MEMFAULT_METRICS_LOGS_ENABLE) #define MEMFAULT_METRICS_LOGS_ENABLE 0 #endif #if defined(CONFIG_MEMFAULT_COREDUMP_COLLECT_MPU_STATE) #define MEMFAULT_COLLECT_MPU_STATE 1 #endif #if defined(CONFIG_MEMFAULT_COREDUMP_MPU_REGIONS_TO_COLLECT) #define MEMFAULT_MPU_REGIONS_TO_COLLECT CONFIG_MEMFAULT_COREDUMP_MPU_REGIONS_TO_COLLECT #endif #if defined(CONFIG_MEMFAULT_CRC16_BUILTIN) #define MEMFAULT_CRC16_BUILTIN 1 #else #define MEMFAULT_CRC16_BUILTIN 0 #endif #if defined(CONFIG_MEMFAULT_COREDUMP_NVIC_INTERRUPTS_TO_COLLECT) #define MEMFAULT_NVIC_INTERRUPTS_TO_COLLECT CONFIG_MEMFAULT_COREDUMP_NVIC_INTERRUPTS_TO_COLLECT #endif // This must be set to correctly wrap the Zephyr fault handler, and is not // user-configurable #define MEMFAULT_EXC_HANDLER_NMI __wrap_z_arm_nmi #if defined(CONFIG_MEMFAULT_USER_CONFIG_ENABLE) // Pick up any user configuration overrides. This should be kept at the bottom // of this file #if defined(CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL) #if __has_include("memfault_platform_config.h") #include "memfault_platform_config.h" #endif #else #include "memfault_platform_config.h" #endif /* CONFIG_MEMFAULT_USER_CONFIG_SILENT_FAIL */ #else /* ! CONFIG_MEMFAULT_USER_CONFIG_ENABLE */ #define MEMFAULT_DISABLE_USER_TRACE_REASONS 1 #endif /* CONFIG_MEMFAULT_USER_CONFIG_ENABLE */ // Pick up any extra configuration settings #if defined(CONFIG_MEMFAULT_PLATFORM_EXTRA_CONFIG_FILE) #include "memfault_platform_config_extra.h" #endif // User and platform config are included above. Settings after this point can // be used as fallbacks or to enforce deprecations. // Alternate default HTTP URLs for nRF Connect SDK devices #if defined(CONFIG_MEMFAULT_NRF_CONNECT_SDK) // Skip if we are proxying requests via COAP & use the default HTTP urls #if !defined(CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) #ifndef MEMFAULT_HTTP_CHUNKS_API_HOST #define MEMFAULT_HTTP_CHUNKS_API_HOST "chunks-nrf.memfault.com" #endif #ifndef MEMFAULT_HTTP_DEVICE_API_HOST #define MEMFAULT_HTTP_DEVICE_API_HOST "device-nrf.memfault.com" #endif #endif #endif #if defined(CONFIG_MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT) #define MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT 1 #endif #if defined(MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) #error \ "MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS is deprecated. Use CONFIG_MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS instead." #endif #define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS CONFIG_MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/ncs/date_time_callback.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief interface for hooking up a date_time event callback #ifdef __cplusplus extern "C" { #endif #if defined(CONFIG_MEMFAULT_SYSTEM_TIME_SOURCE_DATETIME) #include void memfault_zephyr_date_time_evt_handler(const struct date_time_evt *evt); #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/ncs/version.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief A little convenience header to assist in checks which can be run at compile time for //! backward compatibility based on NCS version. #ifdef __cplusplus extern "C" { #endif //! NCS Version was introduced in nRF Connect SDK >= 1.4 #include "ncs_version.h" //! Returns true if current nRF Connect Version is greater than the one specified //! //! Three checks: //! - First check if major version is greater than the one specified //! - Next check if the major version matches and the minor version is greater //! - Finally check if we are on a development build that is greater than the version. After a //! release is shipped, a PATCHLEVEL of 99 is used to indicate this. For example, a version of //! "1.7.99" means development for version "1.8.0". #define MEMFAULT_NCS_VERSION_GT(major, minor) \ ((NCS_VERSION_MAJOR > (major)) || \ ((NCS_VERSION_MAJOR == (major)) && (NCS_VERSION_MINOR > (minor))) || \ ((NCS_VERSION_MAJOR == (major)) && ((NCS_VERSION_MINOR + 1) > (minor)) && \ (NCS_PATCHLEVEL == 99))) #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/bluetooth_metrics.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif //! Called during heartbeat to update Bluetooth metrics void memfault_bluetooth_metrics_heartbeat_update(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/core.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Core Zephyr Memfault port module. #ifdef __cplusplus extern "C" { #endif #include "memfault/core/platform/device_info.h" void memfault_zephyr_collect_reset_info(void); void memfault_zephyr_get_device_info(sMemfaultDeviceInfo *info); #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/coredump.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include #include "memfault/panics/platform/coredump.h" #ifdef __cplusplus extern "C" { #endif //! For each task, we will collect the TCB and the portion of the stack where context is saved #define MEMFAULT_COREDUMP_MAX_TASK_REGIONS (CONFIG_MEMFAULT_COREDUMP_MAX_TRACKED_TASKS * 2) //! Define base number of regions to collect #define MEMFAULT_ZEPHYR_BASE_COREDUMP_REGIONS \ (/* active stack(s) [2] */ \ (IS_ENABLED(CONFIG_MEMFAULT_COREDUMP_COLLECT_STACK_REGIONS) * 2) \ \ /* _kernel [1] */ \ + IS_ENABLED(CONFIG_MEMFAULT_COREDUMP_COLLECT_KERNEL_REGION) \ \ /* s_task_watermarks + s_task_tcbs [2] */ \ + (IS_ENABLED(CONFIG_MEMFAULT_COREDUMP_COLLECT_TASKS_REGIONS) * 2)) //! Define the total regions to collect #define MEMFAULT_ZEPHYR_COREDUMP_REGIONS \ (MEMFAULT_ZEPHYR_BASE_COREDUMP_REGIONS + MEMFAULT_COREDUMP_MAX_TASK_REGIONS + \ IS_ENABLED(CONFIG_MEMFAULT_COREDUMP_COLLECT_BSS_REGIONS) + \ IS_ENABLED(CONFIG_MEMFAULT_COREDUMP_COLLECT_DATA_REGIONS)) //! Helper to collect regions required to decode Zephyr state //! //! These regions include the active task, the kernel state, other tasks in the system, //! and bss/data regions if enabled. //! //! @param crash_info Contains info regarding the location and state of the crash //! @param regions Pointer to save region info into //! @param num_regions The number of entries in the 'regions' array size_t memfault_zephyr_coredump_get_regions(const sCoredumpCrashInfo *crash_info, sMfltCoredumpRegion *regions, size_t num_regions); //! Helper to collect minimal RAM needed for backtraces of non-running Zephyr tasks //! //! @param[out] regions Populated with the regions that need to be collected in order //! for task and stack state to be recovered for non-running Zephyr tasks //! @param[in] num_regions The number of entries in the 'regions' array //! //! @return The number of entries that were populated in the 'regions' argument. Will always //! be <= num_regions size_t memfault_zephyr_get_task_regions(sMfltCoredumpRegion *regions, size_t num_regions); //! Helper to collect regions of RAM used for BSS variables //! //! @return The number of entries that were populated in the 'regions' argument. Will always //! be <= num_regions size_t memfault_zephyr_get_bss_regions(sMfltCoredumpRegion *regions, size_t num_regions); //! Helper to collect regions of RAM used for DATA variables //! //! @return The number of entries that were populated in the 'regions' argument. Will always //! be <= num_regions size_t memfault_zephyr_get_data_regions(sMfltCoredumpRegion *regions, size_t num_regions); //! Run the Zephyr z_fatal_error function. This is used to execute the Zephyr //! error console prints, which are suppressed due to the Memfault fault handler //! replacing the z_fatal_error function at link time. //! //! This can be useful when locally debugging without a debug probe connected. //! It's called as part of the built-in implementation of //! memfault_platform_reboot(); if a user-implemented version of that function //! is used, this function can be called from there. void memfault_zephyr_z_fatal_error(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/deprecated_root_cert.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include "memfault/core/compiler.h" #include "memfault/ports/zephyr/root_cert_storage.h" #ifdef __cplusplus extern "C" { #endif typedef enum { // The Baltimore Cyber Trust Root was the actual deprecated root // but its previous cert id, 1003, is now the cert id for the // kMemfaultRootCert_AmazonRootCa1. Therefore, modems will have a // duplicate Amazon cert that should be removed kMemfaultDeprecatedRootCert_DuplicateAmazonRootCa1 = 1004, kMemfaultDeprecatedRootCert_MaxIndex, } eMemfaultDeprecatedRootCert; MEMFAULT_STATIC_ASSERT((kMemfaultRootCert_MaxIndex - 1) < kMemfaultDeprecatedRootCert_DuplicateAmazonRootCa1, "Overlap detected between in-use root certs and deprecated root certs."); #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/fota.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #if defined(__cplusplus) extern "C" { #endif #include //! @brief A callback invoked after a download has completed //! //! @note The default implementation will reboot the system after an OTA download has completed. //! A boolean return value is added to allow for custom implementations that may want to handle the //! download completion differently (e.g. in a testing framework context where returning from the //! FOTA will allow the result of the image download to be captured). //! //! A custom implementation can be provided by setting //! CONFIG_MEMFAULT_ZEPHYR_FOTA_DOWNLOAD_CALLBACK_CUSTOM=y and implementing this function //! //! @return true if the download was successful, false otherwise bool memfault_zephyr_fota_download_callback(void); //! @brief Initiates a FOTA update for Zephyr systems //! //! @note This function will block until the FOTA update is complete. If the update is successful, //! the device will be rebooted to apply the update. //! //! @return //! < 0 Error while trying to perform the FOTA update //! 0 Check completed successfully - No new update available int memfault_zephyr_fota_start(void); #if defined(__cplusplus) } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/http.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Zephyr specific http utility for interfacing with Memfault HTTP utilities #include #include #include #include "memfault/ports/zephyr/periodic_upload.h" #ifdef __cplusplus extern "C" { #endif //! Installs the root CAs Memfault uses (the certs in "memfault/http/root_certs.h") //! //! @note: MUST be called prior to LTE network init (calling NRF's lte_lc_init_and_connect()) //! for the settings to take effect //! //! @return 0 on success, else error code int memfault_zephyr_port_install_root_certs(void); //! Sends diagnostic data to the Memfault cloud chunks endpoint //! //! @note If the socket is unavailable after a timeout of 5 seconds, or if the underlying socket //! send operation returns with error, the function will abort the transfer. Otherwise, this //! function will block and return once posting of chunk data has completed. //! //! For more info see https://mflt.io/data-to-cloud //! //! @return 0 on success, else error code int memfault_zephyr_port_post_data(void); //! Same as memfault_zephyr_port_post_data, but returns the size of the data //! that was sent, on success. //! //! @return negative error code on error, else the size of the data that was //! sent, in bytes. 0 indicates no data was ready to send (and no data was sent) ssize_t memfault_zephyr_port_post_data_return_size(void); //! HTTP specific version of memfault_zephyr_port_post_data_return_size //! //! @return negative error code on error, else the size of the data that was //! sent, in bytes. 0 indicates no data was ready to send (and no data was sent) ssize_t memfault_zephyr_port_http_post_data_return_size(void); typedef struct MemfaultOtaInfo { // The size, in bytes, of the OTA payload. size_t size; } sMemfaultOtaInfo; typedef struct { //! Caller provided buffer to be used for the duration of the OTA lifecycle void *buf; size_t buf_len; //! Optional: Context for use by the caller void *user_ctx; //! Called if a new ota update is available //! @return true to continue, false to abort the OTA download bool (*handle_update_available)(const sMemfaultOtaInfo *info, void *user_ctx); //! Invoked as bytes are downloaded for the OTA update //! //! @return true to continue, false to abort the OTA download bool (*handle_data)(void *buf, size_t buf_len, void *user_ctx); //! Called once the entire ota payload has been downloaded bool (*handle_download_complete)(void *user_ctx); } sMemfaultOtaUpdateHandler; //! Handler which can be used to run OTA update using Memfault's Release Mgmt Infra //! For more details see: //! https://mflt.io/release-mgmt //! //! @param handler Context with info necessary to perform an OTA. See struct //! definition for more details. //! //! @note This function is blocking. 'handler' callbacks will be invoked prior to the function //! returning. //! //! @return //! < 0 Error while trying to figure out if an update was available //! 0 Check completed successfully - No new update available //! 1 New update is available and handlers were invoked int memfault_zephyr_port_ota_update(const sMemfaultOtaUpdateHandler *handler); //! Query Memfault's Release Mgmt Infra for an OTA update //! //! @param download_url populated with a string containing the download URL to use //! if an OTA update is available. //! //! @note After use, memfault_zephyr_port_release_download_url() must be called //! to free the memory where the download URL is stored. //! //! @return //! < 0 Error while trying to figure out if an update was available //! 0 Check completed successfully - No new update available //! 1 New update is available and download_url has been populated with //! the url to use for download int memfault_zephyr_port_get_download_url(char **download_url); //! Releases the memory returned from memfault_zephyr_port_get_download_url() int memfault_zephyr_port_release_download_url(char **download_url); //! //! Utility functions for manually posting memfault data. //! Context structure used to carry state information about the HTTP connection typedef struct { int sock_fd; struct zsock_addrinfo *res; size_t bytes_sent; } sMemfaultHttpContext; //! Open a socket to the Memfault chunks upload server //! //! This function is the simplest way to connect to Memfault. Internally this function combines the //! required socket operations into a single function call. See the other socket functions for //! advanced usage. //! //! @param ctx If the socket is opened successfully, this will be populated with //! the connection state for the other HTTP functions below //! //! @note After use, memfault_zephyr_port_http_close_socket() must be called //! to close the socket and free any associated memory. //! @note In the case of an error, it is not required to call memfault_zephyr_port_http_close_socket //! //! @return //! 0 : Success //! < 0 : Error int memfault_zephyr_port_http_open_socket(sMemfaultHttpContext *ctx); //! Creates a socket to the Memfault chunks upload server //! //! @param ctx If the socket is created successfully, this will be populated with //! the connection state for the other HTTP functions below //! //! @note This function only creates the socket. The user should also configure the socket with //! desired options before calling memfault_zephyr_port_http_configure_socket and //! memfault_zephyr_port_http_connect_socket. //! //! @note After use, memfault_zephyr_port_http_close_socket() must be called //! to close the socket and free any associated memory //! @note In the case of an error, it is not required to call memfault_zephyr_port_http_close_socket //! //! @return //! 0 : Success //! < 0 : Error int memfault_zephyr_port_http_create_socket(sMemfaultHttpContext *ctx); //! Configures a socket to the Memfault chunks upload server //! //! @param ctx Current HTTP context containing the socket to configure. //! //! @note This function only configures the socket TLS options for use with Memfault. The user //! should also configure the socket with desired options before calling //! memfault_zephyr_port_http_connect_socket. //! //! @note After use, memfault_zephyr_port_http_close_socket() must be called //! to close the socket and free any associated memory //! @note In the case of an error, it is not required to call memfault_zephyr_port_http_close_socket //! //! @return //! 0 : Success //! < 0 : Error int memfault_zephyr_port_http_configure_socket(sMemfaultHttpContext *ctx); //! Connect the socket to the chunks upload server //! //! @param ctx Current HTTP context containing the socket to connect with. //! //! @note After use, memfault_zephyr_port_http_close_socket() must be called //! to close the socket and free any associated memory //! @note In the case of an error, it is not required to call memfault_zephyr_port_http_close_socket //! //! @return //! 0 : Success //! < 0 : Error int memfault_zephyr_port_http_connect_socket(sMemfaultHttpContext *ctx); //! Close a socket previously opened with //! memfault_zephyr_port_http_open_socket() void memfault_zephyr_port_http_close_socket(sMemfaultHttpContext *ctx); //! Test if the socket is open bool memfault_zephyr_port_http_is_connected(sMemfaultHttpContext *ctx); //! Identical to memfault_zephyr_port_post_data() but uses the already-opened //! socket to send data //! //! @param ctx Connection context previously opened with //! memfault_zephyr_port_http_open_socket() //! //! @return 0 on success, -1 on error int memfault_zephyr_port_http_upload_sdk_data(sMemfaultHttpContext *ctx); //! Similar to memfault_zephyr_port_http_upload_sdk_data(), but instead of using //! the SDK packetizer functions under the hood, send the data passed into this //! function. //! //! Typically this function is used to send data from pre-packetized data; for //! example, data that may have been stored outside of the Memfault SDK //! internally-managed buffers, or data coming from an external source (another //! chip running the Memfault SDK). //! //! @param ctx Connection context previously opened with //! memfault_zephyr_port_http_open_socket() //! //! @return //! 0 : Success //! < 0 : Error int memfault_zephyr_port_http_post_chunk(sMemfaultHttpContext *ctx, void *p_data, size_t data_len); // Alias periodic upload functions for backwards compatibility #if !defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_LOGS) //! Enable or disable periodic log upload at runtime #define memfault_zephyr_port_http_periodic_upload_logs memfault_zephyr_port_periodic_upload_logs #endif #define memfault_zephyr_port_http_periodic_upload_enable memfault_zephyr_port_periodic_upload_enable #define memfault_zephyr_port_http_periodic_upload_enabled \ memfault_zephyr_port_periodic_upload_enabled #if defined(CONFIG_MEMFAULT_HTTP_SOCKET_DISPATCH) //! Sets the network interface name to use for Memfault HTTP uploads //! @param if_name Null-terminated string containing the interface name //! @return 0 on success, -1 on error int memfault_zephyr_port_http_set_interface_name(const char *if_name); #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/include_compatibility.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif #include "memfault/ports/zephyr/version.h" /* * Workaround for Zephyr include path changes * NB: This macro and it's usage should be removed after we remove support for < Zephyr v3.2 * NB: The clang-format blocks are required because clang-format inserts spaces which muck up the * token concatenation * * Background: Zephyr changed its include path in version v3.2 from no subpath within include/ to * adding a Zephyr namespace directory include/zephyr/ There was a brief period v3.2 - v3.3 when a * Kconfig existed to bridge support before complete deprecation. We instead opted to fixup the * paths within our CMake code. However this can lead to problems if customers have headers with * the same name as the Zephyr headers (hence the reason Zephyr changed the paths in the first * place). We still support Zephyr versions prior to v3.2 so we need a way to use the old header * paths. * * Implementation: Using this SO post as reference/inspiration * (https://stackoverflow.com/questions/32066204/construct-path-for-include-directive-with-macro) * the macros below build up a token that can be used within a #include directive. This macro * should be used with the exact strings provided to your typical #include * statements. */ // clang-format off // Used to create a token from the input #define MEMFAULT_IDENT(x) x // Encloses contents with <> using preprocessor token concatenation #define MEMFAULT_ENCLOSE_ARROW_BRACKETS(contents) \ MEMFAULT_IDENT(<)MEMFAULT_IDENT(contents)MEMFAULT_IDENT(>) #if MEMFAULT_ZEPHYR_VERSION_GT(3, 1) && !defined(CONFIG_LEGACY_INCLUDE_PATH) // Concatenates contents with zephyr/ to form a single token #define PREPEND_ZEPHYR(contents) MEMFAULT_IDENT(zephyr/)MEMFAULT_IDENT(contents) // Creates a token which prepends zephyr/ to path and encloses in <> #define MEMFAULT_ZEPHYR_INCLUDE(header_path) \ MEMFAULT_ENCLOSE_ARROW_BRACKETS(PREPEND_ZEPHYR(header_path)) #else #define MEMFAULT_ZEPHYR_INCLUDE(header_path) MEMFAULT_ENCLOSE_ARROW_BRACKETS(header_path) #endif // clang-format on #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/log_backend.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief Memfault Zephyr log backend APIs #ifdef __cplusplus extern "C" { #endif //! Disable the Memfault Zephyr log backend. Only valid after the backend has //! been initialized. void memfault_zephyr_log_backend_disable(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/log_panic.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief Memfault Zephyr log backend APIs #ifdef __cplusplus extern "C" { #endif #if defined(CONFIG_MEMFAULT_FAULT_HANDLER_LOG_PANIC) #define MEMFAULT_LOG_PANIC() LOG_PANIC() #else #define MEMFAULT_LOG_PANIC() #endif #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/memfault_mcumgr.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! MCUmgr group for Memfault device information and project key access #include #ifdef __cplusplus extern "C" { #endif //! Group ID for Memfault management group. //! Using 128 as it's sufficiently higher than the start of user-defined groups (64) #define MGMT_GROUP_ID_MEMFAULT 128 //! Command IDs for Memfault management group. #define MEMFAULT_MGMT_ID_DEVICE_INFO 0 #define MEMFAULT_MGMT_ID_PROJECT_KEY 1 //! Command result codes for Memfault management group. enum memfault_mgmt_err_code_t { // No error, this is implied if there is no ret value in the response MEMFAULT_MGMT_ERR_OK = 0, // Unknown error occurred. MEMFAULT_MGMT_ERR_UNKNOWN, // Project key not configured MEMFAULT_MGMT_ERR_NO_PROJECT_KEY, }; //! @brief Callback for enabling access to the Memfault MCUmgr group. By //! default, access is always allowed. //! //! @param[in] user_arg User argument provided when setting the callback. //! //! @retval true if access is permitted, false otherwise. if false, commands //! will return MGMT_ERR_EACCESSDENIED. typedef bool (*memfault_mgmt_access_cb_t)(void *user_arg); //! @brief Sets a callback to control access to the Memfault MCUmgr group. By //! default, access is always allowed. //! //! @param[in] cb Callback function to determine if access is allowed. //! @param[in] user_arg User argument passed to the callback when invoked. void memfault_mcumgr_set_access_callback(memfault_mgmt_access_cb_t cb, void *user_arg); #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/periodic_upload.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Core Zephyr Memfault port module. #include #ifdef __cplusplus extern "C" { #endif #if !defined(CONFIG_MEMFAULT_PERIODIC_UPLOAD_LOGS) //! Enable or disable periodic log upload at runtime void memfault_zephyr_port_periodic_upload_logs(bool enable); #endif //! Enable or disable periodic Memfault data uploads at runtime //! //! @param enable true to enable periodic uploads, false to disable void memfault_zephyr_port_periodic_upload_enable(bool enable); //! Gets the current state of periodic Memfault data uploads //! //! @return true if Memfault upload is enabled, false if disabled bool memfault_zephyr_port_periodic_upload_enabled(void); #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/root_cert_storage.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A wrapper around root certificate storage with Zephyr since different modules may use different //! implementations. For example, the nRF9160 has its own offloaded storage on the modem whereas a //! external LTE modem may use local Mbed TLS storage on the device. #include #ifdef __cplusplus extern "C" { #endif // These cert ids are used to identify certs when using them for TLS handshakes. // They persist in modem flash storage with the stored cert. If a required cert is already // stored, it should be checked to make sure it is up-to-date, and any deprecated certs should // be removed. Deprecated cert ids are kept in deprecated_root_cert.h typedef enum { // arbitrarily high base so as not to conflict with id used for other certs in use by the system kMemfaultRootCert_Base = 1000, // Keep this index first, or update the code that references it kMemfaultRootCert_DigicertRootG2, kMemfaultRootCert_AmazonRootCa1, kMemfaultRootCert_DigicertRootCa, // Must be last, used to track number of root certs in use kMemfaultRootCert_MaxIndex, } eMemfaultRootCert; //! Adds specified certificate to backing store //! //! @param cert_id Identifier to be used to reference the certificate //! @param cert root certificate to add to storage ( //! @param cert_length Length of PEM certificate //! //! @return 0 on success or if the cert was already loaded, else error code int memfault_root_cert_storage_add(eMemfaultRootCert cert_id, const char *cert, size_t cert_length); //! Remove specified certificate from backing store //! //! @param cert_id Identifier to be used to reference the certificate //! //! @return 0 on success or if the cert was not already loaded, else error code int memfault_root_cert_storage_remove(eMemfaultRootCert cert_id); #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/thread_metrics.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #ifdef __cplusplus extern "C" { #endif #include "memfault/metrics/metrics.h" //! Perform tallying of thread metrics for registered threads. Called //! automatically by the Memfault SDK. void memfault_zephyr_thread_metrics_record(void); typedef struct MfltZephyrThreadMetricsIndex { // The name of the thread to monitor. Must _exactly_ match the target thread. const char *thread_name; // The Memfault Heartbeat Metric key for stack usage. Should be defined as // follows: // MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(memory__pct_max, // kMemfaultMetricType_Unsigned, // CONFIG_MEMFAULT_METRICS_THREADS_MEMORY_SCALE_FACTOR); MemfaultMetricId stack_usage_metric_key; } sMfltZephyrThreadMetricsIndex; //! This data structure can be initialized by the user to specify which threads //! should be recorded in the thread metrics. A weak definition is provided that //! records the idle + sysworkq threads only. //! //! The array must contain a blank entry at the end to terminate the list. //! An example initialization looks like this: //! //! MEMFAULT_METRICS_DEFINE_THREAD_METRICS ( //! { //! .thread_name = "idle", //! .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle_pct_max), //! }, //! { //! .thread_name = "sysworkq", //! .stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_sysworkq_pct_max), //! }, //! ); extern const sMfltZephyrThreadMetricsIndex g_memfault_thread_metrics_index[]; #define MEMFAULT_METRICS_DEFINE_THREAD_METRICS(...) \ const sMfltZephyrThreadMetricsIndex g_memfault_thread_metrics_index[] = { \ __VA_ARGS__ __VA_OPT__(, ){ 0 } \ } #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/include/memfault/ports/zephyr/version.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief A little convenience header to assist in checks which can be run at compile time for //! backward compatibility based on Zephyr version. #ifdef __cplusplus extern "C" { #endif #include //! Returns true if current Zephyr Kernel Version is greater than the one specified //! //! Three checks: //! - First check if major version is greater than the one specified //! - Next check if the major version matches and the minor version is greater //! - Finally check if we are on a development build that is greater than the version. After a //! release is shipped, a PATCHLEVEL of 99 is used to indicate this. For example, a version of //! "1.7.99" means development for version "1.8.0". #define MEMFAULT_ZEPHYR_VERSION_GT(major, minor) \ ((KERNEL_VERSION_MAJOR > (major)) || \ ((KERNEL_VERSION_MAJOR == (major)) && (KERNEL_VERSION_MINOR > (minor))) || \ ((KERNEL_VERSION_MAJOR == (major)) && ((KERNEL_VERSION_MINOR + 1) > (minor)) && \ (KERNEL_PATCHLEVEL == 99))) //! Returns true if current Zephyr Kernel Version is strictly greater than the one specified. //! Strictly means no development patches will be considered greater than the specified version. //! This adaptation accounts for a situation where a feature is introduced in the release //! candidate phase, and is therefore not available in any development build. //! //! Two checks: //! - First check if major version is greater than the one specified //! - Next check if the major version matches and the minor version is greater #define MEMFAULT_ZEPHYR_VERSION_GT_STRICT(major, minor) \ ((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 ================================================ FILE: ports/zephyr/ncs/CMakeLists.txt ================================================ if(CONFIG_MEMFAULT_NRF_CONNECT_SDK) 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) zephyr_include_directories(include) zephyr_include_directories(config) # Note: Starting in nRF Connect SDK >= 1.3, NCS_VERSION_* fields are exposed. # Prior to that, they're unset and cause the VERSION_GREATER_EQUAL check to be # falsy if(CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS AND (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_GREATER_EQUAL 2.4.0) # nRF Connect SDK 2.6.0+ natively supports multiple root certificates, so # this workaround is no longer needed. Note that 2.5.99 is the development # version for 2.6.0, so use that as the upper bound to support pre-release # versions. AND (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_LESS 2.5.99) ) # We wrap download_client_get() in order to register multiple root certificates # See comment in src/memfault_fota.c for more details zephyr_ld_options(-Wl,--wrap=download_client_get) endif() endif() ================================================ FILE: ports/zephyr/ncs/Kconfig ================================================ config MEMFAULT_NRF_CONNECT_SDK bool "nRF Connect SDK extensions" default y if ZEPHYR_NRF_MODULE && MEMFAULT_SOC_NRF if MEMFAULT_NRF_CONNECT_SDK # Extend the Zephyr MEMFAULT_ZEPHYR_FOTA_BACKEND choice with an NCS-specific option. choice MEMFAULT_ZEPHYR_FOTA_BACKEND config MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS bool "Use nRF Connect SDK FOTA library as backend" depends on (MEMFAULT_HTTP_ENABLE || MEMFAULT_USE_NRF_CLOUD_COAP) && FOTA_DOWNLOAD help Uses the nRF Connect SDK FOTA Download library (fota_download) to perform OTA updates via Memfault Release Management. If an update is available, the binary will be downloaded and installed using the Nordic FOTA Download Client. See https://mflt.io/nrf-fota-setup for more details. endchoice # MEMFAULT_ZEPHYR_FOTA_BACKEND choice MEMFAULT_NRF_CONNECTIVITY_CONNECTED_TIME bool "Select the platform implementation for capturing connected time metric" default MEMFAULT_NRF_CONNECTIVITY_CONNECTED_TIME_NRF91X if LTE_LINK_CONTROL config MEMFAULT_NRF_CONNECTIVITY_CONNECTED_TIME_NRF91X bool "Capture connected time metric on nRF91x platforms" select MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT depends on LTE_LINK_CONTROL help Enables connection uptime device vital for the nRF91X platform. Connectivity connection uptime will track the percent of time the platform is actually connected to the cellular network when it should be connected. More information at https://mflt.io/core-metrics . endchoice if MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS config MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM bool "Add custom handler for receiving OTA events" default n help By default, the Memfault SDK implements a stub for FOTA callback events. An end user can provide their own implementation by enabling this option and implementing memfault_fota_download_callback() config MEMFAULT_FOTA_HTTP_FRAG_SIZE int "HTTP fragment size used for downloading OTA images" default 1024 help The size of the HTTP fragment to request from the server. This value should be set to the maximum size that can be received by the device. If 0, no fragmentation is used. endif # MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS config MEMFAULT_NRF_NRF5_CPU_TEMP bool default y depends on TEMP_NRF5_MPSL imply MEMFAULT_METRICS_CPU_TEMP help Enables collection of CPU temperature metrics for supported devices. Use the MEMFAULT_METRICS_CPU_TEMP config option to disable support. config MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX bool "nPM13xx Battery Support" default y if BOARD_THINGY91X_NRF9151_NS select MEMFAULT_METRICS_BATTERY_ENABLE depends on NRF_FUEL_GAUGE depends on NPM13XX_CHARGER help Enables battery metrics support for devices using the nPM13xx PMIC. The user must provide memfault_nrf_platform_battery_model.h with the model of the battery as defined by the nRF Fuel Gauge API. Note, since this port calls nrf_fuel_gauge_process(), applications that want to read state of charge should call memfault_platform_get_stateofcharge() to avoid conflicting calls to the fuel gauge library. config MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX_INIT_PRIORITY int "nPM13xx Battery Initialization Priority" # Set to the default for MEMFAULT_INIT_PRIORITY - 1 default 39 depends on MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX help Sets the initialization priority for the nPM13xx battery platform integration. This value is should be set to be lower than MEMFAULT_INIT_PRIORITY (higher priority) to ensure that the platform battery SOC can be collected during Memfault initialization. menuconfig MEMFAULT_USE_NRF_CLOUD_COAP bool "Use nRF Cloud CoAP endpoint" depends on NETWORKING depends on MODEM_JWT depends on COAP depends on COAP_CLIENT depends on (COAP_EXTENDED_OPTIONS_LEN_VALUE >= 192) depends on (NRF_CLOUD_COAP_MAX_USER_OPTIONS >= 2) help Note: Requires device to be provisioned with nRF Cloud. if MEMFAULT_USE_NRF_CLOUD_COAP config MEMFAULT_NRF_CLOUD_SEC_TAG int "Security tag to use for nRF Cloud connection" default 16842753 config MEMFAULT_NRF_CLOUD_HOST_NAME string "nRF Cloud server hostname" default "coap.nrfcloud.com" config MEMFAULT_COAP_PACKETIZER_BUFFER_SIZE int "Set the size of the CoAP packetizer buffer" default 1400 help The size of the CoAP packetizer buffer. This is the buffer used when reading incremental chunk data when posting via CoAP. It does NOT set the size of the CoAP body (determined by CONFIG_COAP_CLIENT_BLOCK_SIZE and CONFIG_COAP_CLIENT_MESSAGE_HEADER_SIZE), but is instead used when issuing reads to the underlying data source (e.g. coredump, logs, CDR, etc). Increasing the size of this buffer can improve upload performance when the data source is reading from a storage medium that benefits from larger sequential reads. Note that this buffer is dynamically allocated. config MEMFAULT_COAP_CLIENT_TIMEOUT_MS int "The CoAP client timeout in milliseconds" default 5000 help The Memfault CoAP client timeout in milliseconds. This is the maximum amount of time the CoAP client will wait for a response from the server. config MEMFAULT_COAP_MAX_MESSAGES_TO_SEND int "Set the maximum number of CoAP messages to send per upload" default 100 range -1 10000 help The maximum number of messages to send per execution when using the Memfault CoAP client to upload data (e.g. memfault_zephyr_port_post_data()). A message is a single logical data unit (e.g. coredump chunk, metrics report). When using a dedicated workqueue, uploads are unbounded and this value is unused. When running on the system workqueue, a positive value is required to avoid blocking other work. endif # MEMFAULT_USE_NRF_CLOUD_COAP endif # MEMFAULT_NRF_CONNECT_SDK ================================================ FILE: ports/zephyr/ncs/README.md ================================================ # nRF Connect SDK Port ## Overview This directory contains a port an end user can use to integrate the Memfault SDK into a project using [Nordic's nRF Connect SDK](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/introduction.html). A step-by-step integration guide can be found at: https://mflt.io/nrf-connect-sdk-integration-guide ================================================ FILE: ports/zephyr/ncs/config/memfault_metrics_heartbeat_ncs_port_config.def ================================================ #if defined(CONFIG_MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(battery_voltage, kMemfaultMetricType_Unsigned, 1000) #endif ================================================ FILE: ports/zephyr/ncs/include/memfault/nrfconnect_port/coap.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Zephyr specific coap utility for interfacing with Memfault CoAP utilities #include #include #include #include // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(net/coap.h) #include MEMFAULT_ZEPHYR_INCLUDE(net/coap_client.h) #include MEMFAULT_ZEPHYR_INCLUDE(net/socket.h) // clang-format on #ifdef __cplusplus extern "C" { #endif //! Context structure used to carry state information about the CoAP connection typedef struct { int sock_fd; struct zsock_addrinfo *res; size_t bytes_sent; uint8_t message_token[COAP_TOKEN_MAX_LEN]; int last_result_code; char *download_url; struct coap_client *coap_client; struct k_sem response_sem; bool response_received; const char *proxy_url; // Used by nrf_cloud_coap_get_user_options to add PROXY_URI option } sMemfaultCoAPContext; //! Open a socket to the Memfault chunks upload server //! //! This function is the simplest way to connect to Memfault. Internally this function combines the //! required socket operations into a single function call. See the other socket functions for //! advanced usage. //! //! @param ctx If the socket is opened successfully, this will be populated with //! the connection state for the other COAP functions below //! //! @note This function will use nRF Cloud CoAP APIs to open the socket, so that the connection id //! can be shared with other nRF Cloud CoAP operations. nrf_cloud_coap_transport_init() must be //! called before this function to initialize the nRF Cloud CoAP transport. //! //! @note After use, memfault_zephyr_port_coap_close_socket() must be called //! to close the socket and free any associated memory. //! //! @note In the case of an error, it is not required to call memfault_zephyr_port_coap_close_socket //! //! @return //! 0 : Success //! < 0 : Error int memfault_zephyr_port_coap_open_socket(sMemfaultCoAPContext *ctx); //! Close a socket previously opened with //! memfault_zephyr_port_coap_open_socket() void memfault_zephyr_port_coap_close_socket(sMemfaultCoAPContext *ctx); //! Identical to memfault_zephyr_port_post_data() but uses the already-opened //! socket to send data //! //! @param ctx Connection context previously opened with //! memfault_zephyr_port_coap_open_socket() //! //! @return 0 on success, -1 on error int memfault_zephyr_port_coap_upload_sdk_data(sMemfaultCoAPContext *ctx); //! CoAP specific version of memfault_zephyr_port_post_data_return_size //! //! @note This function will use nRF Cloud CoAP APIs to open the socket, so that the connection id //! can be shared with other nRF Cloud CoAP operations. nrf_cloud_coap_transport_init() must be //! called before this function to initialize the nRF Cloud CoAP transport. //! //! @return negative error code on error, else the size of the data that was //! sent, in bytes. 0 indicates no data was ready to send (and no data was sent) ssize_t memfault_zephyr_port_coap_post_data_return_size(void); //! Query Memfault's Release Mgmt Infra for an OTA update over nRF Cloud CoAP //! //! @param download_url populated with a string containing the download URL to use //! if an OTA update is available. //! //! @note This function will use nRF Cloud CoAP APIs to open the socket, so that the connection id //! can be shared with other nRF Cloud CoAP operations. nrf_cloud_coap_transport_init() must be //! called before this function to initialize the nRF Cloud CoAP transport. //! //! @note After use, memfault_zephyr_port_coap_release_download_url() must be called //! to free the memory where the download URL is stored. //! //! @return //! < 0 Error while trying to figure out if an update was available //! 0 Check completed successfully - No new update available //! 1 New update is available and download_url has been populated with //! the url to use for download int memfault_zephyr_port_coap_get_download_url(char **download_url); //! Releases the memory returned from memfault_zephyr_port_get_download_url() int memfault_zephyr_port_coap_release_download_url(char **download_url); #define MEMFAULT_NRF_CLOUD_COAP_PORT 5684 #define MEMFAULT_NRF_CLOUD_COAP_PROJECT_KEY_OPTION_NO 2429 #ifdef __cplusplus } #endif ================================================ FILE: ports/zephyr/ncs/src/CMakeLists.txt ================================================ # Controls where root certificates are stored zephyr_library_sources_ifdef(CONFIG_MEMFAULT_ROOT_CERT_STORAGE_NRF_MODEM memfault_nrf_modem_root_cert_storage.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_ROOT_CERT_INSTALL_ON_MODEM_LIB_INIT memfault_nrf_modem_root_cert_init.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_REBOOT_REASON_GET_CUSTOM nrfx_pmu_reboot_tracking.c) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX memfault_platform_npm13xx_battery.c) if (${NCS_VERSION_MAJOR}.${NCS_VERSION_MINOR}.${NCS_VERSION_PATCH} VERSION_GREATER_EQUAL 2.9.99) zephyr_library_sources_ifdef(CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS memfault_fota.c) else() zephyr_library_sources_ifdef(CONFIG_MEMFAULT_ZEPHYR_FOTA_BACKEND_NCS memfault_fota_legacy.c) endif() zephyr_library_sources_ifdef(CONFIG_MEMFAULT_NRF_CONNECTIVITY_CONNECTED_TIME_NRF91X memfault_platform_metrics_connectivity_lte.c) if (CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP) # if Zephyr is less than 3.5.0, print an error if(${KERNEL_VERSION_MAJOR}.${KERNEL_VERSION_MINOR}.${KERNEL_VERSION_PATCH} VERSION_LESS "3.5.0") message(FATAL_ERROR "Memfault nRF Cloud CoAP transport requires Zephyr version 3.5.0 or newer." ) endif() zephyr_library_sources(memfault_platform_coap.c) endif() ================================================ FILE: ports/zephyr/ncs/src/memfault_fota.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(shell/shell.h) #include "memfault/ports/zephyr/fota.h" #include "memfault/ports/zephyr/http.h" #if CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP #include "memfault/nrfconnect_port/coap.h" #endif #include "memfault/components.h" #include "memfault/ports/zephyr/root_cert_storage.h" #include "net/downloader.h" #include "net/fota_download.h" // clang-format on #if !defined(CONFIG_DOWNLOADER) #error "CONFIG_DOWNLOADER=y is required to use the Memfault FOTA integration" #endif //! Note: A small patch is needed to nrf in order to enable //! as of the latest SDK release (nRF Connect SDK v1.4.x) //! See https://mflt.io/nrf-fota for more details. #if CONFIG_DOWNLOADER_MAX_FILENAME_SIZE < 400 #warning "CONFIG_DOWNLOADER_MAX_FILENAME_SIZE must be >= 400" #if CONFIG_DOWNLOADER_STACK_SIZE < 1600 #warning "CONFIG_DOWNLOADER_STACK_SIZE must be >= 1600" #endif #error \ "DOWNLOADER_MAX_FILENAME_SIZE range may need to be extended in nrf/subsys/net/lib/download_client/Kconfig" #endif #if defined(CONFIG_MEMFAULT_SOC_SERIES_NRF91) #if CONFIG_MEMFAULT_FOTA_HTTP_FRAG_SIZE > 1024 #warning "nRF91 modem TLS secure socket buffer limited to 2kB" #error "Must use CONFIG_MEMFAULT_FOTA_HTTP_FRAG_SIZE=1024 for FOTA to work reliably" #endif #endif // The OTA download URL is allocated to this pointer, and must be freed when the // FOTA download ends. static char *s_download_url = NULL; // Forward declaration: default implementation is below; custom provided by user when // CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM=y. void memfault_fota_download_callback(const struct fota_download_evt *evt); static void prv_fota_url_cleanup(void) { MEMFAULT_LOG_DEBUG("Freeing download URL"); #if CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP memfault_zephyr_port_coap_release_download_url(&s_download_url); #else memfault_zephyr_port_release_download_url(&s_download_url); #endif } #if !CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM void memfault_fota_download_callback(const struct fota_download_evt *evt) { switch (evt->id) { case FOTA_DOWNLOAD_EVT_FINISHED: MEMFAULT_LOG_INFO("OTA Complete, resetting to install update!"); // not necessary to call prv_fota_url_cleanup() here since // memfault_platform_reboot() will reset the device. memfault_platform_reboot(); break; case FOTA_DOWNLOAD_EVT_ERROR: case FOTA_DOWNLOAD_EVT_CANCELLED: MEMFAULT_LOG_ERROR("FOTA download error: rv=%d", evt->id); prv_fota_url_cleanup(); // intentional fall through, no action on other event ids default: break; } } #endif // Install this callback wrapper to permit user-supplied callback (when // CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM=y), but to ensure any cleanup // is done. static void prv_fota_download_callback_wrapper(const struct fota_download_evt *evt) { // May not return if OTA was successful memfault_fota_download_callback(evt); switch (evt->id) { // For any case where the download client has exited, free the OTA URL case FOTA_DOWNLOAD_EVT_ERROR: case FOTA_DOWNLOAD_EVT_CANCELLED: case FOTA_DOWNLOAD_EVT_FINISHED: prv_fota_url_cleanup(); break; default: break; } } static const int s_memfault_fota_certs[] = { kMemfaultRootCert_DigicertRootG2, kMemfaultRootCert_AmazonRootCa1, kMemfaultRootCert_DigicertRootCa }; int memfault_zephyr_fota_start(void) { // Note: The download URL is allocated on the heap and must be freed when done #if CONFIG_MEMFAULT_USE_NRF_CLOUD_COAP int rv = memfault_zephyr_port_coap_get_download_url(&s_download_url); #else int rv = memfault_zephyr_port_get_download_url(&s_download_url); #endif if (rv <= 0) { return rv; } MEMFAULT_LOG_DEBUG("Allocated new FOTA download URL"); MEMFAULT_ASSERT(s_download_url != NULL); MEMFAULT_LOG_INFO("FOTA Update Available. Starting Download with URL: %s", s_download_url); rv = fota_download_init(&prv_fota_download_callback_wrapper); if (rv != 0) { MEMFAULT_LOG_ERROR("FOTA init failed, rv=%d", rv); goto cleanup; } // Find first '/' after the host to split URL into host and filepath components for FOTA API char *file = strchr(s_download_url, '/'); if (!file) { MEMFAULT_LOG_ERROR("Invalid URL format"); rv = -1; goto cleanup; } // Insert a null terminator to split host and file, so we avoid additional memory allocations // Note: this will not effect the free()-ing of the original URL string *file = '\0'; file++; // file now points to the path after the first '/' // Assign a new pointer, indicating we have isolated the host const char *host = s_download_url; MEMFAULT_LOG_DEBUG("Split URL - host: '%s', file: '%s'", host, file); // NCS 2.6 introduced a new API to support multiple certificates, so no need to iterate through // to find a matching one rv = fota_download_any(host, file, s_memfault_fota_certs, MEMFAULT_ARRAY_SIZE(s_memfault_fota_certs), 0 /* pdn_id */, CONFIG_MEMFAULT_FOTA_HTTP_FRAG_SIZE); if (rv != 0) { MEMFAULT_LOG_ERROR("FOTA start failed, rv=%d", rv); goto cleanup; } MEMFAULT_LOG_INFO("FOTA In Progress"); rv = 1; cleanup: // any error code other than 1 or 0 indicates an error if ((rv != 1) && (rv != 0)) { // free the allocated s_download_url prv_fota_url_cleanup(); } return rv; } ================================================ FILE: ports/zephyr/ncs/src/memfault_fota_legacy.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Note: This implementation is for NCS versions <= 2.9 // clang-format off #include "memfault/ports/zephyr/fota.h" #include "memfault/ports/zephyr/http.h" #include "memfault/components.h" #include "memfault/ports/ncs/version.h" #include "memfault/ports/zephyr/root_cert_storage.h" #include "net/download_client.h" #include "net/fota_download.h" #include MEMFAULT_ZEPHYR_INCLUDE(shell/shell.h) // clang-format on #if !defined(CONFIG_DOWNLOAD_CLIENT) #error "CONFIG_DOWNLOAD_CLIENT=y is required to use the Memfault FOTA integration" #endif //! Note: A small patch is needed to nrf in order to enable //! as of the latest SDK release (nRF Connect SDK v1.4.x) //! See https://mflt.io/nrf-fota for more details. #if CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE < 400 #warning "CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE must be >= 400" #if CONFIG_DOWNLOAD_CLIENT_STACK_SIZE < 1600 #warning "CONFIG_DOWNLOAD_CLIENT_STACK_SIZE must be >= 1600" #endif #error \ "DOWNLOAD_CLIENT_MAX_FILENAME_SIZE range may need to be extended in nrf/subsys/net/lib/download_client/Kconfig" #endif #if defined(CONFIG_MEMFAULT_SOC_SERIES_NRF91) #if CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE > 1024 #warning "nRF91 modem TLS secure socket buffer limited to 2kB" #error "Must use CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE_1024=y for FOTA to work reliably" #endif #endif // The OTA download URL is allocated to this pointer, and must be freed when the // FOTA download ends. static char *s_download_url = NULL; // Forward declaration: default implementation is below; custom provided by user when // CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM=y. void memfault_fota_download_callback(const struct fota_download_evt *evt); static void prv_fota_url_cleanup(void) { MEMFAULT_LOG_DEBUG("Freeing download URL"); memfault_zephyr_port_release_download_url(&s_download_url); } #if !CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM void memfault_fota_download_callback(const struct fota_download_evt *evt) { switch (evt->id) { case FOTA_DOWNLOAD_EVT_FINISHED: MEMFAULT_LOG_INFO("OTA Complete, resetting to install update!"); // not necessary to call prv_fota_url_cleanup() here since // memfault_platform_reboot() will reset the device. memfault_platform_reboot(); break; case FOTA_DOWNLOAD_EVT_ERROR: case FOTA_DOWNLOAD_EVT_CANCELLED: MEMFAULT_LOG_ERROR("FOTA download error: rv=%d", evt->id); prv_fota_url_cleanup(); default: break; } } #endif // Install this callback wrapper to permit user-supplied callback (when // CONFIG_MEMFAULT_FOTA_DOWNLOAD_CALLBACK_CUSTOM=y), but to ensure any cleanup // is done. static void prv_fota_download_callback_wrapper(const struct fota_download_evt *evt) { // May not return if OTA was successful memfault_fota_download_callback(evt); switch (evt->id) { // For any case where the download client has exited, free the OTA URL case FOTA_DOWNLOAD_EVT_ERROR: case FOTA_DOWNLOAD_EVT_CANCELLED: case FOTA_DOWNLOAD_EVT_FINISHED: prv_fota_url_cleanup(); break; default: break; } } static const int s_memfault_fota_certs[] = { kMemfaultRootCert_DigicertRootG2, kMemfaultRootCert_AmazonRootCa1, kMemfaultRootCert_DigicertRootCa }; #if MEMFAULT_NCS_VERSION_GT(2, 3) && !MEMFAULT_NCS_VERSION_GT(2, 5) int __real_download_client_get(struct download_client *client, const char *host, const struct download_client_cfg *config, const char *file, size_t from); int __wrap_download_client_get(struct download_client *client, const char *host, const struct download_client_cfg *config, const char *file, size_t from) { // In NCS 2.4, there were two significant changes to the download client // // 1. Connecting to the OTA (download_client_connect) server was made asynchronous and no error // information is exposed to the FOTA client. This makes it impossible to rotate through // certificates like we did for previous releases. // https://github.com/nrfconnect/sdk-nrf/commit/38978c8092#diff-6a4addb1807793400159a4f7592864c3edab770e4919c5708b583115fab54e9aL430 // // 2. Support was added to the download_client (but not the fota_download handler) to support // multiple certificates! // https://github.com/nrfconnect/sdk-nrf/commit/0d177a1282a5d9b12c9d5f7d00837d0771f5c2ee // // In this routine, we intercept the download_client_get() call so we can patch in all the // Memfault certificates before the FOTA begins. // Copy the existing config struct download_client_cfg modified_config = *config; // Modify sec_tag_list so it has all clients modified_config.sec_tag_list = &s_memfault_fota_certs[0]; modified_config.sec_tag_count = MEMFAULT_ARRAY_SIZE(s_memfault_fota_certs); return __real_download_client_get(client, host, &modified_config, file, from); } // Ensure the substituted function signature matches the original function _Static_assert( __builtin_types_compatible_p(__typeof__(&download_client_get), __typeof__(&__wrap_download_client_get)) && __builtin_types_compatible_p(__typeof__(&download_client_get), __typeof__(&__real_download_client_get)), "Error: Wrapped functions does not match original download_client_get function signature"); #endif int memfault_zephyr_fota_start(void) { // Note: The download URL is allocated on the heap and must be freed when done int rv = memfault_zephyr_port_get_download_url(&s_download_url); if (rv <= 0) { return rv; } MEMFAULT_LOG_DEBUG("Allocated new FOTA download URL"); MEMFAULT_ASSERT(s_download_url != NULL); MEMFAULT_LOG_INFO("FOTA Update Available. Starting Download!"); rv = fota_download_init(&prv_fota_download_callback_wrapper); if (rv != 0) { MEMFAULT_LOG_ERROR("FOTA init failed, rv=%d", rv); goto cleanup; } #if MEMFAULT_NCS_VERSION_GT(2, 5) // NCS 2.6 introduced a new API to support multiple certificates, so no need to iterate through // to find a matching one rv = fota_download_any(s_download_url, s_download_url, s_memfault_fota_certs, MEMFAULT_ARRAY_SIZE(s_memfault_fota_certs), 0 /* pdn_id */, 0); #else // 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++) { rv = fota_download_start(s_download_url, s_download_url, s_memfault_fota_certs[i], 0 /* pdn_id */, 0); if (rv == 0) { // success -- we are ready to start the FOTA download! break; } if (rv == -EALREADY) { MEMFAULT_LOG_INFO("fota_download_start already in progress"); break; } // Note: Between releases of Zephyr & NCS the error code returned for TLS handshake failures // has changed. // // NCS < 1.6 returned EOPNOTSUPP // NCS >= 1.6 returns ECONNREFUSED. // // The mapping of errno values has also changed across releases due to: // https://github.com/zephyrproject-rtos/zephyr/commit/165def7ea6709f7f0617591f464fb95f711d2ac0 // // Therefore we print a friendly message when an _expected_ failure takes place // but retry regardless of error condition in case value returned changes again in the future. // // Note: For NCS >= 2.4 <= 2.5, server connect() has been moved to the download_client thread // and TLS error codes are no longer bubbled up here so we do not expect to fall into this logic // anymore. See workaround above in __wrap_download_client_get() for more details. if (((errno == EOPNOTSUPP) || (errno == ECONNREFUSED)) && (i != (MEMFAULT_ARRAY_SIZE(s_memfault_fota_certs) - 1))) { MEMFAULT_LOG_INFO("fota_download_start likely unsuccessful due to root cert mismatch. " "Trying next certificate."); } else { MEMFAULT_LOG_ERROR("fota_download_start unexpected failure, errno=%d", (int)errno); } } #endif // MEMFAULT_NCS_VERSION_GT(2, 5) if (rv != 0) { MEMFAULT_LOG_ERROR("FOTA start failed, rv=%d", rv); goto cleanup; } MEMFAULT_LOG_INFO("FOTA In Progress"); rv = 1; cleanup: // any error code other than 1 or 0 indicates an error if ((rv != 1) && (rv != 0)) { // free the allocated s_download_url prv_fota_url_cleanup(); } return rv; } ================================================ FILE: ports/zephyr/ncs/src/memfault_nrf_modem_root_cert_init.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include "memfault/core/debug_log.h" #include "memfault/ports/ncs/version.h" #include "memfault/ports/zephyr/http.h" #include "modem/nrf_modem_lib.h" // NRF_MODEM_LIB_ON_INIT() was added in NCS v2.0.0 #if !MEMFAULT_NCS_VERSION_GT(1, 9) #error \ "CONFIG_MEMFAULT_BATTERY_METRICS_BOOT_ON_SYSTEM_INIT requires nRF Connect SDK > 1.9. Please upgrade, or reach out to mflt.io/contact-support for assistance." #endif static void prv_install_zephyr_port_root_certs(int ret, MEMFAULT_UNUSED void *ctx) { if (ret != 0) { MEMFAULT_LOG_ERROR("Modem library init error, ret=%d. Memfault root cert installation skipped.", ret); return; } int err = memfault_zephyr_port_install_root_certs(); if (err != 0) { MEMFAULT_LOG_ERROR("Failed to install root certs, error: %d", err); MEMFAULT_LOG_WARN("Certificates can not be provisioned while LTE is active"); } } NRF_MODEM_LIB_ON_INIT(memfault_root_cert_install, prv_install_zephyr_port_root_certs, NULL); ================================================ FILE: ports/zephyr/ncs/src/memfault_nrf_modem_root_cert_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! When using the nRF9160, certificates cannot be added via Zephyrs //! tls_credential_add() API. Instead they need to be added using the //! modem management API which is what this port does. #include #include "memfault/core/debug_log.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; int err = modem_key_mgmt_exists(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); if (err != 0) { MEMFAULT_LOG_ERROR("Failed to check if cert %d exists in storage, rv=%d", cert_id, err); return err; } if (exists) { err = modem_key_mgmt_cmp(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert, cert_length - 1); if (err == 0) { MEMFAULT_LOG_DEBUG("Cert %d in storage is up-to-date", cert_id); return 0; } // if key is mismatched, continue with writing over the entry with the correct key MEMFAULT_LOG_INFO("Cert %d in storage is not up-to-date", cert_id); } MEMFAULT_LOG_INFO("Installing Root CA %d", cert_id); err = modem_key_mgmt_write(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert, strlen(cert)); if (err != 0) { MEMFAULT_LOG_ERROR("Failed to provision certificate, err %d", err); } return err; } int memfault_root_cert_storage_remove(eMemfaultRootCert cert_id) { bool exists; int err = modem_key_mgmt_exists(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); if (err != 0) { MEMFAULT_LOG_ERROR("Failed to check if cert %d exists in storage, rv=%d", cert_id, err); return err; } if (!exists) { MEMFAULT_LOG_DEBUG("Cert %d not found in storage, skipping removal", cert_id); return 0; } MEMFAULT_LOG_INFO("Removing Root CA %d", cert_id); err = modem_key_mgmt_delete(cert_id, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN); if (err != 0) { MEMFAULT_LOG_ERROR("Failed to delete cert, err %d", err); } return err; } ================================================ FILE: ports/zephyr/ncs/src/memfault_platform_coap.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include #include #include // clang-format off #include "memfault/ports/zephyr/include_compatibility.h" #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(net/coap.h) #include MEMFAULT_ZEPHYR_INCLUDE(net/coap_client.h) #include MEMFAULT_ZEPHYR_INCLUDE(net/socket.h) #include MEMFAULT_ZEPHYR_INCLUDE(random/random.h) #include #include #include #include #include #include "memfault/components.h" #include "memfault/nrfconnect_port/coap.h" #include "memfault/ports/ncs/version.h" // clang-format on // Restrict to versions with the nrf_cloud_coap_get_user_options() API, used to optionally inject // the Memfault project key via custom CoAP option 2429 for the native /chunks endpoint. #if !MEMFAULT_NCS_VERSION_GT(3, 2) #error \ "The Memfault CoAP port currently requires support added after nRF Connect SDK version 3.2.1. Please upgrade to nRF Connect SDK > 3.2.1" #endif //! Default buffer size for proxy URLS. This may need to be increased by the user if there are //! particularly long device properties (serial, hardware version, software version, or type) #ifndef MEMFAULT_PROXY_URL_MAX_LEN #define MEMFAULT_PROXY_URL_MAX_LEN (200) #endif #if CONFIG_MEMFAULT_COAP_MAX_MESSAGES_TO_SEND == 0 #error \ "CONFIG_MEMFAULT_COAP_MAX_MESSAGES_TO_SEND must not be 0. Use -1 for unlimited or a positive value to cap uploads." #endif // Each Memfault chunk maps 1:1 to one CoAP block (no blockwise transfer per chunk). // The payload is bounded by BLOCK_SIZE with ~100 B reserved for CoAP options overhead // (Block1, project key, etc.). #define MEMFAULT_COAP_CHUNK_SIZE \ (CONFIG_COAP_CLIENT_BLOCK_SIZE + CONFIG_COAP_CLIENT_MESSAGE_HEADER_SIZE - 100) // Used by memfault_zephyr_port_coap_post_data_return_size() and // memfault_zephyr_port_coap_get_download_url(), i.e. when the user does not provide their own // context. File scope here ensures context can be referenced by async callbacks static sMemfaultCoAPContext s_async_ctx; #if (CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE > 0) static void *prv_calloc(size_t count, size_t size) { return calloc(count, size); } static void prv_free(void *ptr) { free(ptr); } #elif CONFIG_HEAP_MEM_POOL_SIZE > 0 static void *prv_calloc(size_t count, size_t size) { return k_calloc(count, size); } static void prv_free(void *ptr) { k_free(ptr); } #else #error "CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE or CONFIG_HEAP_MEM_POOL_SIZE must be > 0" #endif static void prv_coap_response_cb(const struct coap_client_response_data *data, void *user_data) { sMemfaultCoAPContext *ctx = (sMemfaultCoAPContext *)user_data; // Caller timed out and returned, abort the operation if (ctx->last_result_code == -ETIMEDOUT) { return; } if (data->last_block || data->result_code < 0) { ctx->last_result_code = data->result_code; ctx->response_received = true; k_sem_give(&ctx->response_sem); } } // Implement nrf_cloud_coap_get_user_options to add Memfault-specific options // This is called by nRF Cloud CoAP transport when CONFIG_NRF_CLOUD_COAP_MAX_USER_OPTIONS > 0 void nrf_cloud_coap_get_user_options(struct coap_client_option *options, size_t *num_options, const char *resource, const char *user_data) { // For the native /chunks endpoint, optionally inject the Memfault project key (CoAP option 2429). // If no project key is configured, the server uses its auto-configured mapping. if (strcmp(resource, "chunks") == 0) { size_t api_key_len = g_mflt_http_client_config.api_key ? strlen(g_mflt_http_client_config.api_key) : 0; if (api_key_len == 0) { // No project key configured; rely on server-side auto-forwarding *num_options = 0; return; } if (api_key_len > CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE) { MEMFAULT_LOG_ERROR("API key too long: %zu", api_key_len); *num_options = 0; return; } options[0].code = MEMFAULT_NRF_CLOUD_COAP_PROJECT_KEY_OPTION_NO; options[0].len = api_key_len; memcpy(options[0].value, g_mflt_http_client_config.api_key, api_key_len); *num_options = 1; return; } // For the proxy resource (used by FOTA URL lookup), add PROXY_URI and project key options if (strcmp(resource, NRF_CLOUD_COAP_PROXY_RSC) != 0) { *num_options = 0; return; } const sMemfaultCoAPContext *ctx = (const sMemfaultCoAPContext *)(const void *)user_data; if (!ctx || !ctx->proxy_url) { *num_options = 0; return; } size_t opt_idx = 0; // PROXY_URI option size_t proxy_url_len = strlen(ctx->proxy_url); if (proxy_url_len > CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE) { MEMFAULT_LOG_ERROR("Proxy URL too long: %zu", proxy_url_len); *num_options = 0; return; } options[opt_idx].code = COAP_OPTION_PROXY_URI; options[opt_idx].len = proxy_url_len; memcpy(options[opt_idx].value, ctx->proxy_url, proxy_url_len); opt_idx++; // Custom project key option size_t api_key_len = g_mflt_http_client_config.api_key ? strlen(g_mflt_http_client_config.api_key) : 0; if (api_key_len > CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE) { MEMFAULT_LOG_ERROR("API key too long: %zu", api_key_len); *num_options = 0; return; } options[opt_idx].code = MEMFAULT_NRF_CLOUD_COAP_PROJECT_KEY_OPTION_NO; options[opt_idx].len = api_key_len; memcpy(options[opt_idx].value, g_mflt_http_client_config.api_key, api_key_len); opt_idx++; *num_options = opt_idx; } static void prv_fota_url_response_cb(const struct coap_client_response_data *data, void *user_data) { sMemfaultCoAPContext *ctx = (sMemfaultCoAPContext *)user_data; // Caller timed out and returned, abort URL parsing if (ctx->last_result_code == -ETIMEDOUT) { return; } if (data->last_block || data->result_code < 0) { if (data->result_code == COAP_RESPONSE_CODE_CONTENT && data->payload && data->payload_len > 0) { // The response is JSON: {"data": {"url": "https://..."}} // Extract the URL from the JSON response using manual string parsing const char *payload_str = (const char *)data->payload; const char *url_start = NULL; const char *url_end = NULL; const char *url_key = strstr(payload_str, "\"url\""); if (url_key) { const char *colon = strchr(url_key, ':'); if (colon) { url_start = strchr(colon, '"'); if (url_start) { url_start++; // Skip the opening quote url_end = strchr(url_start, '"'); // Find the closing quote } } } if (url_start && url_end && url_end > url_start) { size_t url_len = url_end - url_start; ctx->download_url = prv_calloc(1, url_len + 1); if (ctx->download_url) { memcpy(ctx->download_url, url_start, url_len); ctx->download_url[url_len] = '\0'; MEMFAULT_LOG_DEBUG("Extracted URL from JSON: %s", ctx->download_url); } else { MEMFAULT_LOG_ERROR("Failed to allocate memory for download URL"); } } else { MEMFAULT_LOG_ERROR("Failed to parse URL from JSON response"); } } ctx->last_result_code = data->result_code; ctx->response_received = true; k_sem_give(&ctx->response_sem); } } static int prv_send_next_msg(sMemfaultCoAPContext *ctx) { int rv = 0; size_t chunk_size = CONFIG_MEMFAULT_COAP_PACKETIZER_BUFFER_SIZE; if (chunk_size > MEMFAULT_COAP_CHUNK_SIZE) { chunk_size = MEMFAULT_COAP_CHUNK_SIZE; } uint8_t *chunk_buf = prv_calloc(1, chunk_size); if (!chunk_buf) { return -ENOMEM; } bool data_available = memfault_packetizer_get_chunk(chunk_buf, &chunk_size); if (!data_available) { MEMFAULT_LOG_DEBUG("No more data to send"); prv_free(chunk_buf); return 0; } // Reset response state ctx->response_received = false; ctx->last_result_code = -1; k_sem_reset(&ctx->response_sem); // Send chunk data to the native /chunks endpoint. The project key (CoAP option 2429) is // injected optionally via nrf_cloud_coap_get_user_options if configured. MEMFAULT_LOG_DEBUG("Sending CoAP message, size %zu", chunk_size); rv = nrf_cloud_coap_post("chunks", NULL, chunk_buf, chunk_size, COAP_CONTENT_FORMAT_APP_OCTET_STREAM, true, prv_coap_response_cb, ctx); if (rv < 0) { MEMFAULT_LOG_ERROR("Failed to send CoAP request: %d", rv); memfault_packetizer_abort(); prv_free(chunk_buf); return -1; } // Wait for response rv = k_sem_take(&ctx->response_sem, K_SECONDS(CONFIG_MEMFAULT_COAP_CLIENT_TIMEOUT_MS / 1000)); if (rv < 0 || !ctx->response_received) { MEMFAULT_LOG_ERROR("Timeout or error waiting for CoAP response: %d", rv); ctx->last_result_code = -ETIMEDOUT; // Signal async callback to abort prv_free(chunk_buf); return -1; } if (ctx->last_result_code != COAP_RESPONSE_CODE_CREATED) { MEMFAULT_LOG_ERROR("Unexpected CoAP response code: %d", ctx->last_result_code); prv_free(chunk_buf); return -1; } // Count bytes sent ctx->bytes_sent += chunk_size; prv_free(chunk_buf); return 1; } int memfault_zephyr_port_coap_open_socket(sMemfaultCoAPContext *ctx) { int rv = 0; // If connected, use existing connection. This check is where connection id sharing occurs if (!nrf_cloud_coap_is_connected()) { MEMFAULT_LOG_DEBUG("nRF Cloud CoAP not connected, connecting..."); rv = nrf_cloud_coap_connect(NULL); if (rv < 0) { MEMFAULT_LOG_ERROR("Failed to connect to nRF Cloud CoAP: %d", rv); return -1; } } // Initialize context *ctx = (sMemfaultCoAPContext){ .sock_fd = -1, .coap_client = NULL, .res = NULL, .download_url = NULL, .proxy_url = NULL, .response_received = false, .last_result_code = -1, }; k_sem_init(&ctx->response_sem, 0, 1); return 0; } void memfault_zephyr_port_coap_close_socket(sMemfaultCoAPContext *ctx) { // Note: We don't manage the socket/client here since they're managed by nRF Cloud CoAP, so we // just reset our context *ctx = (sMemfaultCoAPContext){ .sock_fd = -1, .coap_client = NULL, .res = NULL, .proxy_url = NULL, .response_received = false, .last_result_code = -1, }; // Note: download_url is freed by the caller via memfault_zephyr_port_coap_release_download_url } int memfault_zephyr_port_coap_upload_sdk_data(sMemfaultCoAPContext *ctx) { #if CONFIG_MEMFAULT_PERIODIC_UPLOAD_USE_DEDICATED_WORKQUEUE // Dedicated workqueue: drain everything - no risk of blocking other work. int max_messages_to_send = INT_MAX; #else // System workqueue: cap message count to avoid blocking other work. // -1 means unlimited; normalize to INT_MAX so the loop condition works correctly. int max_messages_to_send = (CONFIG_MEMFAULT_COAP_MAX_MESSAGES_TO_SEND < 0) ? INT_MAX : CONFIG_MEMFAULT_COAP_MAX_MESSAGES_TO_SEND; #endif bool success = true; int rv = -1; while (max_messages_to_send-- > 0) { rv = prv_send_next_msg(ctx); if (rv == 0) { // no more messages to send break; } else if (rv < 0) { success = false; break; } success = (rv > 0); if (!success) { break; } } if ((max_messages_to_send <= 0) && memfault_packetizer_data_available()) { MEMFAULT_LOG_WARN( "Hit max message limit: " STRINGIFY(CONFIG_MEMFAULT_COAP_MAX_MESSAGES_TO_SEND)); } return success ? 0 : -1; } ssize_t memfault_zephyr_port_coap_post_data_return_size(void) { if (!memfault_packetizer_data_available()) { return 0; } sMemfaultCoAPContext *ctx = &s_async_ctx; int rv = memfault_zephyr_port_coap_open_socket(ctx); MEMFAULT_LOG_DEBUG("Opened CoAP socket, rv=%d", rv); size_t bytes_sent = 0; if (rv == 0) { rv = memfault_zephyr_port_coap_upload_sdk_data(ctx); bytes_sent = ctx->bytes_sent; if (ctx->last_result_code != -ETIMEDOUT) { memfault_zephyr_port_coap_close_socket(ctx); } } #if defined(CONFIG_MEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS) if (rv == 0) { memfault_metrics_connectivity_record_memfault_sync_success(); } else { memfault_metrics_connectivity_record_memfault_sync_failure(); } #endif return (rv == 0) ? (ssize_t)bytes_sent : (ssize_t)rv; } int memfault_zephyr_port_coap_get_download_url(char **download_url) { int rv = 0; char *latest_url = prv_calloc(1, MEMFAULT_PROXY_URL_MAX_LEN); if (!latest_url) { return -ENOMEM; } if (!memfault_http_build_latest_ota_url(latest_url, MEMFAULT_PROXY_URL_MAX_LEN)) { prv_free(latest_url); return -1; } sMemfaultCoAPContext *ctx = &s_async_ctx; rv = memfault_zephyr_port_coap_open_socket(ctx); if (rv != 0) { prv_free(latest_url); return rv; } // Store proxy URL in context for nrf_cloud_coap_get_user_options ctx->proxy_url = latest_url; // Reset response state ctx->response_received = false; ctx->last_result_code = -1; k_sem_reset(&ctx->response_sem); // Send request using nrf_cloud_coap_get (which will call nrf_cloud_coap_get_user_options) // For GET with no payload: fmt_out can be 0 (ignored since no payload) // fmt_in is JSON: {"data": {"url": "https://..."}} rv = nrf_cloud_coap_get(NRF_CLOUD_COAP_PROXY_RSC, NULL, NULL, 0, 0, COAP_CONTENT_FORMAT_APP_JSON, true, prv_fota_url_response_cb, ctx); if (rv < 0) { MEMFAULT_LOG_ERROR("Failed to send CoAP request: %d", rv); prv_free(latest_url); memfault_zephyr_port_coap_close_socket(ctx); return -1; } rv = k_sem_take(&ctx->response_sem, K_MSEC(CONFIG_MEMFAULT_COAP_CLIENT_TIMEOUT_MS)); if (rv < 0 || !ctx->response_received) { MEMFAULT_LOG_ERROR("Timeout or error waiting for CoAP response: %d", rv); ctx->last_result_code = -ETIMEDOUT; // Signal async callback not to touch context prv_free(latest_url); return -1; } if (ctx->last_result_code == COAP_RESPONSE_CODE_CONTENT && ctx->download_url) { *download_url = ctx->download_url; rv = 1; // Update available } else if (ctx->last_result_code == COAP_RESPONSE_CODE_NOT_FOUND || ctx->last_result_code == COAP_RESPONSE_CODE_VALID) { rv = 0; // No update available } else { MEMFAULT_LOG_ERROR("Unexpected CoAP response code: %d", ctx->last_result_code); rv = -1; } prv_free(latest_url); memfault_zephyr_port_coap_close_socket(ctx); return rv; } int memfault_zephyr_port_coap_release_download_url(char **download_url) { prv_free(*download_url); *download_url = NULL; return 0; } ================================================ FILE: ports/zephyr/ncs/src/memfault_platform_metrics_connectivity_lte.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include "memfault/ports/ncs/version.h" //! In NCS < 2.0, lte_lc.h is missing integer type definitions, so we need to include them before //! lte_lc.h #if !MEMFAULT_NCS_VERSION_GT(1, 9) #include #include #endif #include #include #include "memfault/core/debug_log.h" #include "memfault/metrics/connectivity.h" #include "memfault/metrics/platform/connectivity.h" //! Handler for LTE events static void prv_memfault_lte_event_handler(const struct lte_lc_evt *const evt) { switch (evt->type) { case LTE_LC_EVT_NW_REG_STATUS: switch (evt->nw_reg_status) { case LTE_LC_NW_REG_REGISTERED_HOME: // intentional fallthrough case LTE_LC_NW_REG_REGISTERED_ROAMING: MEMFAULT_LOG_DEBUG("Connected state: connected"); memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_Connected); break; case LTE_LC_NW_REG_NOT_REGISTERED: // intentional fallthrough case LTE_LC_NW_REG_SEARCHING: // intentional fallthrough case LTE_LC_NW_REG_REGISTRATION_DENIED: // intentional fallthrough case LTE_LC_NW_REG_UNKNOWN: // intentional fallthrough case LTE_LC_NW_REG_UICC_FAIL: MEMFAULT_LOG_DEBUG("Connected state: connection lost"); memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_ConnectionLost); break; default: break; } break; default: break; } } //! The LTE_LC_ON_CFUN macro was introduced in nRF Connect SDK 2.0 #if MEMFAULT_NCS_VERSION_GT(2, 0) //! Callback for LTE mode changes #if MEMFAULT_NCS_VERSION_GT(2, 7) static void prv_memfault_lte_mode_cb(int mode, MEMFAULT_UNUSED void *ctx) { #else static void prv_memfault_lte_mode_cb(enum lte_lc_func_mode mode, MEMFAULT_UNUSED void *ctx) { #endif switch (mode) { case LTE_LC_FUNC_MODE_NORMAL: // intentional fallthrough case LTE_LC_FUNC_MODE_ACTIVATE_LTE: MEMFAULT_LOG_DEBUG("Connected state: started"); memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_Started); break; case LTE_LC_FUNC_MODE_POWER_OFF: // intentional fallthrough case LTE_LC_FUNC_MODE_OFFLINE: // intentional fallthrough case LTE_LC_FUNC_MODE_DEACTIVATE_LTE: MEMFAULT_LOG_DEBUG("Connected state: stopped"); memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_Stopped); break; default: break; } } // For NCS 2.8.0+, use NRF_MODEM_LIB_ON_CFUN instead of LTE_LC_ON_CFUN #if MEMFAULT_NCS_VERSION_GT(2, 7) NRF_MODEM_LIB_ON_CFUN(memfault_lte_mode_cb, prv_memfault_lte_mode_cb, NULL); #else LTE_LC_ON_CFUN(memfault_lte_mode_cb, prv_memfault_lte_mode_cb, NULL); #endif #endif /* MEMFAULT_VERSION_GT */ void memfault_platform_metrics_connectivity_boot(void) { lte_lc_register_handler(prv_memfault_lte_event_handler); } ================================================ FILE: ports/zephyr/ncs/src/memfault_platform_npm13xx_battery.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #include MEMFAULT_ZEPHYR_INCLUDE(devicetree.h) #include MEMFAULT_ZEPHYR_INCLUDE(kernel.h) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/sensor.h) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/sensor/npm13xx_charger.h) #include MEMFAULT_ZEPHYR_INCLUDE(drivers/mfd/npm13xx.h) #include "memfault/components.h" #include "memfault_nrf_platform_battery_model.h" #include "nrf_fuel_gauge.h" // nPM13xx BCHGCHARGESTATUS register mask definitions, compatible with both nPM1300 and nPM1304 // See https://docs-be.nordicsemi.com/bundle/ps_npm1300/page/nPM1300_PS_v1.2.pdf and // https://docs-be.nordicsemi.com/bundle/ps_npm1304/page/pdf/nPM1304_Preliminary_Datasheet_v0.7.pdf #define NPM13XX_CHG_STATUS_TC_MASK BIT(2) #define NPM13XX_CHG_STATUS_CC_MASK BIT(3) #define NPM13XX_CHG_STATUS_CV_MASK BIT(4) #if defined(CONFIG_DT_HAS_NORDIC_NPM1300_CHARGER_ENABLED) static const struct device *s_npm13xx_dev = DEVICE_DT_GET(DT_NODELABEL(npm1300_charger)); #elif defined(CONFIG_DT_HAS_NORDIC_NPM1304_CHARGER_ENABLED) static const struct device *s_npm13xx_dev = DEVICE_DT_GET(DT_NODELABEL(npm1304_charger)); #else #error \ "Unsupported nPM13xx charger device or device not enabled in devicetree. Contact mflt.io/contact-support for assistance." #endif static int64_t s_ref_time; static int prv_npm13xx_read_sensors(float *voltage, float *current, float *temp, int *charging_status) { struct sensor_value reading; int err; err = sensor_sample_fetch(s_npm13xx_dev); if (err < 0) { return err; } err = sensor_channel_get(s_npm13xx_dev, SENSOR_CHAN_GAUGE_VOLTAGE, &reading); if (err) { return err; } *voltage = sensor_value_to_float(&reading); err = sensor_channel_get(s_npm13xx_dev, SENSOR_CHAN_GAUGE_TEMP, &reading); if (err) { return err; } *temp = sensor_value_to_float(&reading); err = sensor_channel_get(s_npm13xx_dev, SENSOR_CHAN_GAUGE_AVG_CURRENT, &reading); if (err) { return err; } // Zephyr sensor API returns current as negative for discharging, positive for charging // but nRF fuel gauge library expects opposite. Flip here for uniformity *current = -sensor_value_to_float(&reading); // optionally read charging status if (charging_status == NULL) { return 0; } err = sensor_channel_get(s_npm13xx_dev, (enum sensor_channel)SENSOR_CHAN_NPM13XX_CHARGER_STATUS, &reading); if (err) { return err; } *charging_status = reading.val1; return 0; } static bool prv_npm13xx_is_discharging(int charging_status) { return (charging_status & (NPM13XX_CHG_STATUS_TC_MASK | NPM13XX_CHG_STATUS_CC_MASK | NPM13XX_CHG_STATUS_CV_MASK)) == 0; } int memfault_platform_get_stateofcharge(sMfltPlatformBatterySoc *soc) { // Float type required to retain precision when passing units of volts, amps, and degrees C to the // nRF fuel gauge API float voltage, current, temp; int charging_status; int err = prv_npm13xx_read_sensors(&voltage, ¤t, &temp, &charging_status); if (err < 0) { MEMFAULT_LOG_ERROR("Failure reading charger sensors, error: %d", err); return -1; } int64_t time_delta_ms = k_uptime_delta(&s_ref_time); float time_delta_s = (float)time_delta_ms / 1000.0f; struct nrf_fuel_gauge_state_info info; nrf_fuel_gauge_process(voltage, current, temp, time_delta_s, &info); // soc_raw is already 0-100, so scale only by scale value soc->soc = (uint32_t)(info.soc_raw * (float)CONFIG_MEMFAULT_METRICS_BATTERY_SOC_PCT_SCALE_VALUE); soc->discharging = prv_npm13xx_is_discharging(charging_status); // Scale by scale value (must match definition in memfault_metrics_heartbeat_ncs_port_config.def) uint32_t voltage_scaled = (uint32_t)(voltage * 1000.0f); MEMFAULT_METRIC_SET_UNSIGNED(battery_voltage, voltage_scaled); MEMFAULT_LOG_DEBUG("Battery SOC %u.%03u%%, discharging=%d, voltage=%d mv", soc->soc / 1000, soc->soc % 1000, soc->discharging, (int)voltage_scaled); s_ref_time = k_uptime_get(); return 0; } static int prv_platform_battery_init(void) { if (!device_is_ready(s_npm13xx_dev)) { printk("Charger device not ready\n"); return -1; } struct nrf_fuel_gauge_init_parameters parameters = { .model = &battery_model }; int err = prv_npm13xx_read_sensors(¶meters.v0, ¶meters.i0, ¶meters.t0, NULL); if (err < 0) { printk("Failure reading charger sensors, error: %d\n", err); return err; } err = nrf_fuel_gauge_init(¶meters, NULL); if (err) { printk("Failure initializing fuel gauge, error: %d\n", err); return err; } s_ref_time = k_uptime_get(); return 0; } #if defined(CONFIG_MEMFAULT_INIT_LEVEL_POST_KERNEL) #error \ "CONFIG_MEMFAULT_INIT_LEVEL_POST_KERNEL=y not supported with CONFIG_MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX. Please contact mflt.io/contact-support for assistance." #elif (CONFIG_MEMFAULT_INIT_PRIORITY <= CONFIG_MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX_INIT_PRIORITY) #error \ "MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX_INIT_PRIORITY must be set to a value less than MEMFAULT_INIT_PRIORITY (lower value = higher priority)" #endif SYS_INIT(prv_platform_battery_init, #if defined(CONFIG_MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX_INIT_LEVEL_POST_KERNEL) POST_KERNEL, #else APPLICATION, #endif CONFIG_MEMFAULT_NRF_PLATFORM_BATTERY_NPM13XX_INIT_PRIORITY); ================================================ FILE: ports/zephyr/ncs/src/nrfx_pmu_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A port for recovering reset reason information by reading the //! "Power management unit" (PMU)'s "reset reason" (RESETREAS) Register. //! //! More details can be found in the "RESETREAS" section in an NRF reference manual #include #include #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/debug_log.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/sdk_assert.h" #include "memfault/ports/reboot_reason.h" //! Note: Nordic uses different headers for reset reason information depending on platform. NRF52 & //! NRF91 reasons are defined in nrf_power.h whereas nrf53 reasons are defined in nrf_reset.h #if !NRF_POWER_HAS_RESETREAS #include #endif #if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP #define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) #else #define MEMFAULT_PRINT_RESET_INFO(...) #endif #if NRF_POWER_HAS_RESETREAS static eMemfaultRebootReason prv_decode_power_resetreas(uint32_t reset_cause) { eMemfaultRebootReason reset_reason; if (reset_cause & NRF_POWER_RESETREAS_RESETPIN_MASK) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else if (reset_cause & NRF_POWER_RESETREAS_DOG_MASK) { MEMFAULT_PRINT_RESET_INFO(" Watchdog"); reset_reason = kMfltRebootReason_HardwareWatchdog; } else if (reset_cause & NRF_POWER_RESETREAS_SREQ_MASK) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & NRF_POWER_RESETREAS_LOCKUP_MASK) { MEMFAULT_PRINT_RESET_INFO(" Lockup"); reset_reason = kMfltRebootReason_Lockup; } else if (reset_cause & NRF_POWER_RESETREAS_OFF_MASK) { MEMFAULT_PRINT_RESET_INFO(" GPIO Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #if defined(POWER_RESETREAS_LPCOMP_Msk) } else if (reset_cause & NRF_POWER_RESETREAS_LPCOMP_MASK) { MEMFAULT_PRINT_RESET_INFO(" LPCOMP Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #endif } else if (reset_cause & NRF_POWER_RESETREAS_DIF_MASK) { MEMFAULT_PRINT_RESET_INFO(" Debug Interface Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #if defined(NRF_POWER_HAS_RESETREAS_NFC) #if NRF_POWER_HAS_RESETREAS_NFC } else if (reset_cause & NRF_POWER_RESETREAS_NFC_MASK) { MEMFAULT_PRINT_RESET_INFO(" NFC Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #endif #endif #if defined(NRF_POWER_RESETREAS_VBUS_MASK) } else if (reset_cause & NRF_POWER_RESETREAS_VBUS_MASK) { MEMFAULT_PRINT_RESET_INFO(" VBUS Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #endif #if defined(NRF_POWER_HAS_RESETREAS_CTRLAP) #if NRF_POWER_HAS_RESETREAS_CTRLAP } else if (reset_cause & NRF_POWER_RESETREAS_CTRLAP_MASK) { MEMFAULT_PRINT_RESET_INFO(" CTRLAP Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #endif #endif } else if (reset_cause == 0) { // absence of a value, means a power on reset took place MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); reset_reason = kMfltRebootReason_PowerOnReset; } else { MEMFAULT_PRINT_RESET_INFO(" Unknown"); reset_reason = kMfltRebootReason_Unknown; } return reset_reason; } #else // nRF Connect SDK v3.2.0 changed how these macros are defined. Check for the // presence of the new macros, falling back on the prior // 'NRF_RESET_HAS_NETWORK' workaround. #if defined(NRF_RESET_HAS_LSREQ_RESET) #define MEMFAULT_NRF_RESET_HAS_LSREQ_RESET NRF_RESET_HAS_LSREQ_RESET #else #define MEMFAULT_NRF_RESET_HAS_LSREQ_RESET NRF_RESET_HAS_NETWORK #endif #if defined(NRF_RESET_HAS_LCTRLAP_RESET) #define MEMFAULT_NRF_RESET_HAS_LCTRLAP_RESET NRF_RESET_HAS_LCTRLAP_RESET #else #define MEMFAULT_NRF_RESET_HAS_LCTRLAP_RESET NRF_RESET_HAS_NETWORK #endif static eMemfaultRebootReason prv_decode_reset_resetreas(uint32_t reset_cause) { eMemfaultRebootReason reset_reason; if (reset_cause & NRF_RESET_RESETREAS_RESETPIN_MASK) { MEMFAULT_PRINT_RESET_INFO(" Pin Reset"); reset_reason = kMfltRebootReason_PinReset; } else if (reset_cause & NRF_RESET_RESETREAS_DOG0_MASK) { MEMFAULT_PRINT_RESET_INFO(" Watchdog 0"); reset_reason = kMfltRebootReason_HardwareWatchdog; #if NRF_RESET_HAS_CTRLAP_RESET } else if (reset_cause & NRF_RESET_RESETREAS_CTRLAP_MASK) { MEMFAULT_PRINT_RESET_INFO(" Debugger"); reset_reason = kMfltRebootReason_DebuggerHalted; #endif #if NRF_RESET_HAS_CTRLAPPIN_RESET } else if (reset_cause & NRF_RESET_RESETREAS_CTRLAPPIN_MASK) { MEMFAULT_PRINT_RESET_INFO(" Debugger"); reset_reason = kMfltRebootReason_DebuggerHalted; #endif } else if (reset_cause & NRF_RESET_RESETREAS_SREQ_MASK) { MEMFAULT_PRINT_RESET_INFO(" Software"); reset_reason = kMfltRebootReason_SoftwareReset; } else if (reset_cause & NRF_RESET_RESETREAS_LOCKUP_MASK) { MEMFAULT_PRINT_RESET_INFO(" Lockup"); reset_reason = kMfltRebootReason_Lockup; } else if (reset_cause & NRF_RESET_RESETREAS_OFF_MASK) { MEMFAULT_PRINT_RESET_INFO(" GPIO Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #if NRF_RESET_HAS_LPCOMP_RESET } else if (reset_cause & NRF_RESET_RESETREAS_LPCOMP_MASK) { MEMFAULT_PRINT_RESET_INFO(" LPCOMP Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #endif } else if (reset_cause & NRF_RESET_RESETREAS_DIF_MASK) { MEMFAULT_PRINT_RESET_INFO(" Debug Interface Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #if MEMFAULT_NRF_RESET_HAS_LSREQ_RESET } else if (reset_cause & NRF_RESET_RESETREAS_LSREQ_MASK) { MEMFAULT_PRINT_RESET_INFO(" Software (Network)"); reset_reason = kMfltRebootReason_SoftwareReset; #endif #if NRF_RESET_HAS_LLOCKUP_RESET } else if (reset_cause & NRF_RESET_RESETREAS_LLOCKUP_MASK) { MEMFAULT_PRINT_RESET_INFO(" Lockup (Network)"); reset_reason = kMfltRebootReason_Lockup; #endif #if NRF_RESET_HAS_LDOG_RESET } else if (reset_cause & NRF_RESET_RESETREAS_LDOG_MASK) { MEMFAULT_PRINT_RESET_INFO(" Watchdog (Network)"); reset_reason = kMfltRebootReason_HardwareWatchdog; #endif #if NRF_RESET_HAS_MFORCEOFF_RESET } else if (reset_cause & NRF_RESET_RESETREAS_MFORCEOFF_MASK) { MEMFAULT_PRINT_RESET_INFO(" Force off (Network)"); reset_reason = kMfltRebootReason_SoftwareReset; #endif #if MEMFAULT_NRF_RESET_HAS_LCTRLAP_RESET } else if (reset_cause & NRF_RESET_RESETREAS_LCTRLAP_MASK) { MEMFAULT_PRINT_RESET_INFO(" Debugger (Network)"); reset_reason = kMfltRebootReason_SoftwareReset; #endif #if NRF_RESET_HAS_VBUS_RESET } else if (reset_cause & NRF_RESET_RESETREAS_VBUS_MASK) { MEMFAULT_PRINT_RESET_INFO(" VBUS Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #endif } else if (reset_cause & NRF_RESET_RESETREAS_DOG1_MASK) { MEMFAULT_PRINT_RESET_INFO(" Watchdog 1"); reset_reason = kMfltRebootReason_HardwareWatchdog; #if NRF_RESET_HAS_NFC_RESET } else if (reset_cause & NRF_RESET_RESETREAS_NFC_MASK) { MEMFAULT_PRINT_RESET_INFO(" NFC Wakeup"); reset_reason = kMfltRebootReason_DeepSleep; #endif } else if (reset_cause == 0) { // absence of a value, means a power on reset took place MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); reset_reason = kMfltRebootReason_PowerOnReset; } else { MEMFAULT_PRINT_RESET_INFO(" Unknown"); reset_reason = kMfltRebootReason_Unknown; } return reset_reason; } #endif MEMFAULT_WEAK void memfault_reboot_reason_get(sResetBootupInfo *info) { MEMFAULT_SDK_ASSERT(info != NULL); #if NRF_POWER_HAS_RESETREAS volatile uint32_t *resetreas_reg = &NRF_POWER->RESETREAS; #else volatile uint32_t *resetreas_reg = &NRF_RESET->RESETREAS; #endif const uint32_t reset_cause = *resetreas_reg; MEMFAULT_PRINT_RESET_INFO("Reset Reason, RESETREAS=0x%" PRIx32, reset_cause); MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); eMemfaultRebootReason reset_reason; #if NRF_POWER_HAS_RESETREAS reset_reason = prv_decode_power_resetreas(reset_cause); #else reset_reason = prv_decode_reset_resetreas(reset_cause); #endif #if CONFIG_MEMFAULT_CLEAR_RESET_REG *resetreas_reg |= reset_cause; #endif *info = (sResetBootupInfo){ .reset_reason_reg = reset_cause, .reset_reason = reset_reason, }; } ================================================ FILE: ports/zephyr/panics/CMakeLists.txt ================================================ if (CONFIG_RISCV) zephyr_library_sources(memfault_fault_handler_riscv.c) elseif(CONFIG_ARM) zephyr_library_sources(memfault_fault_handler.c) elseif(CONFIG_XTENSA) zephyr_library_sources(memfault_fault_handler_xtensa.c) # Due to variable stack frame widths, Zephyr does not give a definition for # z_arch_esf_t/struct arch_esf (stack information passed to Zephyr's fault # handler) for Xtensa, unlike the other architectures our Zephyr port # supports. Therefore, we have to add an include path to the folder where # the header defining xtensa stack frames lives. zephyr_include_directories(${ZEPHYR_BASE}/arch/xtensa/include) elseif(CONFIG_ARCH_POSIX) zephyr_library_sources(memfault_fault_handler_posix.c) else() # Unsupported configuration message(FATAL_ERROR "Unsupported chip architecture") endif() zephyr_include_directories(.) # Zephyr fatals for ARM Cortex-M's take the following path: # # frame 3 k_sys_fatal_error_handler nrf/lib/fatal_error/fatal_error.c # (overrides weak impl in zephyr/kernel/fatal.c) # frame 2 z_fatal_error zephyr/kernel/fatal.c # frame 1 z_arm_fault zephyr/arch/arm/core/aarch32/cortex_m/fault.c # frame 0 z_arm_hard_fault zephyr/arch/arm/core/aarch32/cortex_m/fault_s.S # # The nrf-connect-sdk does not expose a mechanism to override "k_sys_fatal_error_handler" # and simply reboots the system so instead we intercept the frame above and install the # Memfault fault handler which will collect a coredump. target_link_libraries(app INTERFACE "-Wl,--wrap=z_fatal_error") # We trace task creation and deletion so the task TCBs and stacks can be collected at the time of a # crash and all thread backtraces can be made available in the Memfault UI. # # To do this we wrap the arch_new_thread() function call that is made from thread.c # # A nicer way to do this would be to make use of the "sys_trace_thread_create" macro. # Unfortunately, to override the macro, one must patch the Zephyr RTOS today. # https://github.com/zephyrproject-rtos/zephyr/blob/390537b/include/tracing/tracing.h#L57-L61 target_link_libraries(app INTERFACE "-Wl,--wrap=arch_new_thread") target_link_libraries(app INTERFACE "-Wl,--wrap=z_thread_abort") # To backtrace correctly through NMI exception, we need to substitute our # function in the vector table, only when CONFIG_CPU_CORTEX_M is set. if (CONFIG_CPU_CORTEX_M) target_link_libraries(app INTERFACE "-Wl,--wrap=z_arm_nmi") endif() ================================================ FILE: ports/zephyr/panics/memfault_fault_handler.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! // clang-format off #include MEMFAULT_ZEPHYR_INCLUDE(arch/cpu.h) #include MEMFAULT_ZEPHYR_INCLUDE(cache.h) #include MEMFAULT_ZEPHYR_INCLUDE(fatal.h) #include MEMFAULT_ZEPHYR_INCLUDE(init.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log_ctrl.h) #include #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/panics/arch/arm/cortex_m.h" #include "memfault/panics/coredump.h" #include "memfault/panics/fault_handling.h" #include "memfault/ports/zephyr/version.h" #include "memfault/ports/zephyr/log_panic.h" // The z_fatal_error() signature changed in Zephyr 3.7.0, during release // candidate development. NCS uses an intermediate version in NCS v2.7.0, so use // a strict version check instead of also accepting 3.6.99 as equivalent to // 3.7.0. // This header is used on Zephyr 3.7+ to get the exception frame declaration #if MEMFAULT_ZEPHYR_VERSION_GT_STRICT(3, 6) #include #endif // clang-format on static eMemfaultRebootReason prv_zephyr_to_memfault_fault_reason(unsigned int reason) { // See the lists here for reference: // https://github.com/zephyrproject-rtos/zephyr/blob/053347375bc00a490ce08fe2b9d78a65183ce95a/include/zephyr/fatal_types.h#L24 // https://github.com/zephyrproject-rtos/zephyr/blob/053347375bc00a490ce08fe2b9d78a65183ce95a/include/zephyr/arch/arm/arch.h#L59 switch (reason) { // Generic CPU exception, not covered by other codes case K_ERR_CPU_EXCEPTION: default: return kMfltRebootReason_HardFault; // Unhandled hardware interrupt case K_ERR_SPURIOUS_IRQ: return kMfltRebootReason_Hardware; // Faulting context overflowed its stack buffer case K_ERR_STACK_CHK_FAIL: return kMfltRebootReason_StackOverflow; // Moderate severity software error case K_ERR_KERNEL_OOPS: // High severity software error case K_ERR_KERNEL_PANIC: return kMfltRebootReason_KernelPanic; #if MEMFAULT_ZEPHYR_VERSION_GT_STRICT(3, 2) // ARM-specific exceptions were added in Zephyr 3.3.0: // https://github.com/zephyrproject-rtos/zephyr/pull/53551 // nRF-Connect SDK v2.2 + v2.3 point to a fork of Zephyr on 3.2.99 // development version, but it doesn't have the patch with this change, so // we only accept strictly > 3.2. // Cortex-M MEMFAULT exceptions case K_ERR_ARM_MEM_GENERIC: case K_ERR_ARM_MEM_STACKING: case K_ERR_ARM_MEM_UNSTACKING: case K_ERR_ARM_MEM_DATA_ACCESS: case K_ERR_ARM_MEM_INSTRUCTION_ACCESS: case K_ERR_ARM_MEM_FP_LAZY_STATE_PRESERVATION: return kMfltRebootReason_MemFault; // Cortex-M BUSFAULT exceptions case K_ERR_ARM_BUS_GENERIC: case K_ERR_ARM_BUS_STACKING: case K_ERR_ARM_BUS_UNSTACKING: case K_ERR_ARM_BUS_PRECISE_DATA_BUS: case K_ERR_ARM_BUS_IMPRECISE_DATA_BUS: case K_ERR_ARM_BUS_INSTRUCTION_BUS: case K_ERR_ARM_BUS_FP_LAZY_STATE_PRESERVATION: return kMfltRebootReason_BusFault; // Cortex-M USAGEFAULT exceptions case K_ERR_ARM_USAGE_GENERIC: case K_ERR_ARM_USAGE_DIV_0: case K_ERR_ARM_USAGE_UNALIGNED_ACCESS: case K_ERR_ARM_USAGE_NO_COPROCESSOR: case K_ERR_ARM_USAGE_ILLEGAL_EXC_RETURN: case K_ERR_ARM_USAGE_ILLEGAL_EPSR: case K_ERR_ARM_USAGE_UNDEFINED_INSTRUCTION: return kMfltRebootReason_UsageFault; case K_ERR_ARM_USAGE_STACK_OVERFLOW: return kMfltRebootReason_StackOverflow; // Cortex-M SECURE exceptions case K_ERR_ARM_SECURE_GENERIC: case K_ERR_ARM_SECURE_ENTRY_POINT: case K_ERR_ARM_SECURE_INTEGRITY_SIGNATURE: case K_ERR_ARM_SECURE_EXCEPTION_RETURN: case K_ERR_ARM_SECURE_ATTRIBUTION_UNIT: case K_ERR_ARM_SECURE_TRANSITION: case K_ERR_ARM_SECURE_LAZY_STATE_PRESERVATION: case K_ERR_ARM_SECURE_LAZY_STATE_ERROR: return kMfltRebootReason_SecurityViolation; // 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 // K_ERR_ARM_BACKGROUND_FAULT // K_ERR_ARM_PERMISSION_FAULT // K_ERR_ARM_SYNC_EXTERNAL_ABORT // K_ERR_ARM_ASYNC_EXTERNAL_ABORT // K_ERR_ARM_SYNC_PARITY_ERROR // K_ERR_ARM_ASYNC_PARITY_ERROR // K_ERR_ARM_DEBUG_EVENT // K_ERR_ARM_TRANSLATION_FAULT // K_ERR_ARM_UNSUPPORTED_EXCLUSIVE_ACCESS_FAULT #endif // MEMFAULT_ZEPHYR_VERSION_GT(3, 2) } } // Note: There is no header exposed for this zephyr function extern void sys_arch_reboot(int type); //! Flush the data cache. This is used to ensure that any buffered data is //! written to RAM before the system reboots, on systems with a data cache. static void prv_flush_dcache(void) { #if MEMFAULT_ZEPHYR_VERSION_GT_STRICT(3, 2) // Zephyr 3.3.0 introduced a new API for flushing the data cache (void)sys_cache_data_flush_all(); #if defined(CONFIG_DCACHE) && !defined(CONFIG_CACHE_MANAGEMENT) && \ !defined(MEMFAULT_SUPPRESS_MISSING_CACHE_MGMT_ERROR) #error \ "CONFIG_DCACHE is enabled but CONFIG_CACHE_MANAGEMENT is not. Please enable CONFIG_CACHE_MANAGEMENT to avoid data loss." #endif #elif MEMFAULT_ZEPHYR_VERSION_GT(2, 5) // Zephyr 2.6.0-3.2.0 uses a different Kconfig symbol for indicating dcache is // really enabled for the current SOC. The data cache flush API name is also // different. Note: Memfault's minimum supported Zephyr version as of // 2025-04-11 is 2.7.0. #if defined(CONFIG_CPU_CORTEX_M_HAS_CACHE) (void)sys_cache_data_all(K_CACHE_WB); #if !defined(CONFIG_CACHE_MANAGEMENT) && !defined(MEMFAULT_SUPPRESS_MISSING_CACHE_MGMT_ERROR) #error \ "CONFIG_DCACHE is enabled but CONFIG_CACHE_MANAGEMENT is not. Please enable CONFIG_CACHE_MANAGEMENT to avoid data loss." #endif #endif #endif } // 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) #endif #if MEMFAULT_NEW_ARCH_ESF_STRUCT void __wrap_z_fatal_error(unsigned int reason, const struct arch_esf *esf); void __real_z_fatal_error(unsigned int reason, const struct arch_esf *esf); #else void __wrap_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf); void __real_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf); #endif // Ensure the substituted function signature matches the original function _Static_assert(__builtin_types_compatible_p(__typeof__(&z_fatal_error), __typeof__(&__wrap_z_fatal_error)) && __builtin_types_compatible_p(__typeof__(&z_fatal_error), __typeof__(&__real_z_fatal_error)), "Error: check z_fatal_error function signature"); #if MEMFAULT_NEW_ARCH_ESF_STRUCT void __wrap_z_fatal_error(unsigned int reason, const struct arch_esf *esf) #else void __wrap_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) #endif { // Optionally flush logs prior to capturing coredump & rebooting MEMFAULT_LOG_PANIC(); const struct __extra_esf_info *extra_info = &esf->extra_info; const _callee_saved_t *callee_regs = extra_info->callee; // Read the "SPSEL" bit where // 0 = Main Stack Pointer in use prior to exception // 1 = Process Stack Pointer in use prior to exception const uint32_t exc_return = extra_info->exc_return; const bool msp_was_active = (exc_return & (1 << 2)) == 0; sMfltRegState reg = { .exception_frame = (void *)(msp_was_active ? extra_info->msp : callee_regs->psp), .r4 = callee_regs->v1, .r5 = callee_regs->v2, .r6 = callee_regs->v3, .r7 = callee_regs->v4, .r8 = callee_regs->v5, .r9 = callee_regs->v6, .r10 = callee_regs->v7, .r11 = callee_regs->v8, .exc_return = exc_return, }; eMemfaultRebootReason fault_reason = prv_zephyr_to_memfault_fault_reason(reason); memfault_fault_handler(®, fault_reason); #if MEMFAULT_FAULT_HANDLER_RETURN prv_flush_dcache(); // instead of returning, call the Zephyr fatal error handler. This is done // here instead of in memfault_platform_reboot(), because we need to pass the // function parameters through __real_z_fatal_error(reason, esf); #endif } MEMFAULT_WEAK MEMFAULT_NORETURN void memfault_platform_reboot(void) { #if MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED memfault_platform_halt_if_debugging(); #endif prv_flush_dcache(); sys_arch_reboot(0); CODE_UNREACHABLE; } ================================================ FILE: ports/zephyr/panics/memfault_fault_handler_posix.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! // clang-format off #include #include #include #include #include #include #include #include #include "memfault/core/build_info.h" #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/panics/arch/posix/posix.h" #include "memfault/panics/coredump.h" #include "memfault/ports/zephyr/log_panic.h" #include "memfault/panics/fault_handling.h" // clang-format on MEMFAULT_PACKED_STRUCT sMfltElfNote { uint32_t namesz; //!< Size of the name uint32_t descsz; //!< Size of the data uint32_t type; //!< Type of the note char namedata[4 + MEMFAULT_BUILD_ID_LEN]; //!< Name and data of the note }; extern struct sMfltElfNote __start_gnu_build_id_start; MEMFAULT_WEAK struct sMfltElfNote __start_gnu_build_id_start = { .namesz = 4, // "GNU\0" .descsz = MEMFAULT_BUILD_ID_LEN, .type = 1, // kMemfaultBuildIdType_GnuBuildIdSha1 .namedata = { 'G', 'N', 'U', '\0' }, }; static eMemfaultRebootReason prv_zephyr_to_memfault_fault_reason(unsigned int reason) { // See the lists here for reference: // https://github.com/zephyrproject-rtos/zephyr/blob/053347375bc00a490ce08fe2b9d78a65183ce95a/include/zephyr/fatal_types.h#L24 // https://github.com/zephyrproject-rtos/zephyr/blob/053347375bc00a490ce08fe2b9d78a65183ce95a/include/zephyr/arch/arm/arch.h#L59 switch (reason) { // Generic CPU exception, not covered by other codes case K_ERR_CPU_EXCEPTION: default: return kMfltRebootReason_HardFault; // Unhandled hardware interrupt case K_ERR_SPURIOUS_IRQ: return kMfltRebootReason_Hardware; // Faulting context overflowed its stack buffer case K_ERR_STACK_CHK_FAIL: return kMfltRebootReason_StackOverflow; // Moderate severity software error case K_ERR_KERNEL_OOPS: // High severity software error case K_ERR_KERNEL_PANIC: return kMfltRebootReason_KernelPanic; } } // Note: There is no header exposed for this zephyr function extern void sys_arch_reboot(int type); // Intercept zephyr/kernel/fatal.c:z_fatal_error() void __wrap_z_fatal_error(unsigned int reason, const struct arch_esf *esf); void __real_z_fatal_error(unsigned int reason, const struct arch_esf *esf); // Ensure the substituted function signature matches the original function _Static_assert(__builtin_types_compatible_p(__typeof__(&z_fatal_error), __typeof__(&__wrap_z_fatal_error)) && __builtin_types_compatible_p(__typeof__(&z_fatal_error), __typeof__(&__real_z_fatal_error)), "Error: check z_fatal_error function signature"); void __wrap_z_fatal_error(unsigned int reason, const struct arch_esf *esf) { // flush logs prior to capturing coredump & rebooting MEMFAULT_LOG_PANIC(); sMfltRegState reg = { 0 }; eMemfaultRebootReason fault_reason = prv_zephyr_to_memfault_fault_reason(reason); memfault_fault_handler(®, fault_reason); #if MEMFAULT_FAULT_HANDLER_RETURN // instead of returning, call the Zephyr fatal error handler. This is done // here instead of in memfault_platform_reboot(), because we need to pass the // function parameters through __real_z_fatal_error(reason, esf); #endif } MEMFAULT_WEAK MEMFAULT_NORETURN void memfault_platform_reboot(void) { #if MEMFAULT_ASSERT_HALT_IF_DEBUGGING_ENABLED memfault_platform_halt_if_debugging(); #endif sys_arch_reboot(0); CODE_UNREACHABLE; } ================================================ FILE: ports/zephyr/panics/memfault_fault_handler_riscv.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! #include MEMFAULT_ZEPHYR_INCLUDE(arch/cpu.h) #include MEMFAULT_ZEPHYR_INCLUDE(fatal.h) #include MEMFAULT_ZEPHYR_INCLUDE(init.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log_ctrl.h) #include #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/panics/arch/riscv/riscv.h" #include "memfault/panics/coredump.h" #include "memfault/panics/fault_handling.h" #include "memfault/ports/zephyr/log_panic.h" #include "memfault/ports/zephyr/version.h" // Note: There is no header exposed for this zephyr function 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 MEMFAULT_ZEPHYR_VERSION_GT(3, 6) void __wrap_z_fatal_error(unsigned int reason, const struct arch_esf *esf); void __real_z_fatal_error(unsigned int reason, const struct arch_esf *esf); #else void __wrap_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf); void __real_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf); #endif // Ensure the substituted function signature matches the original function _Static_assert(__builtin_types_compatible_p(__typeof__(&z_fatal_error), __typeof__(&__wrap_z_fatal_error)) && __builtin_types_compatible_p(__typeof__(&z_fatal_error), __typeof__(&__real_z_fatal_error)), "Error: check z_fatal_error function signature"); #if MEMFAULT_ZEPHYR_VERSION_GT(3, 6) void __wrap_z_fatal_error(unsigned int reason, const struct arch_esf *esf) #else void __wrap_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) #endif { // TODO log panic may not be working correctly in fault handler context :( // // flush logs prior to capturing coredump & rebooting // MEMFAULT_LOG_PANIC(); sMfltRegState reg = { .mepc = esf->mepc, /* machine exception program counter */ .ra = esf->ra, #ifdef CONFIG_USERSPACE .sp = esf->sp, /* preserved (user or kernel) stack pointer */ #else .sp = esf->s0, /* sp is stashed here in the Zephyr fault shim */ #endif // .gp = ?, // .tp = ?, .t = { esf->t0, /* Caller-saved temporary register */ esf->t1, /* Caller-saved temporary register */ esf->t2, /* Caller-saved temporary register */ #if !defined(CONFIG_RISCV_ISA_RV32E) esf->t3, /* Caller-saved temporary register */ esf->t4, /* Caller-saved temporary register */ esf->t5, /* Caller-saved temporary register */ esf->t6, /* Caller-saved temporary register */ #endif }, .s = { esf->s0, /* callee-saved s0 */ }, .a = { esf->a0, /* function argument/return value */ esf->a1, /* function argument */ esf->a2, /* function argument */ esf->a3, /* function argument */ esf->a4, /* function argument */ esf->a5, /* function argument */ #if !defined(CONFIG_RISCV_ISA_RV32E) esf->a6, /* function argument */ esf->a7, /* function argument */ #endif }, .mstatus = esf->mstatus, // .mtvec = ?, // .mcause = ?, // .mtval = ?, // .mhartid = ?, }; memfault_fault_handler(®, kMfltRebootReason_HardFault); } MEMFAULT_WEAK MEMFAULT_NORETURN void memfault_platform_reboot(void) { // TODO this is not working correctly on the esp32c3 :( // memfault_platform_halt_if_debugging(); sys_arch_reboot(0); CODE_UNREACHABLE; } ================================================ FILE: ports/zephyr/panics/memfault_fault_handler_xtensa.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! #include MEMFAULT_ZEPHYR_INCLUDE(arch/cpu.h) #include MEMFAULT_ZEPHYR_INCLUDE(fatal.h) #include MEMFAULT_ZEPHYR_INCLUDE(init.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h) #include MEMFAULT_ZEPHYR_INCLUDE(logging/log_ctrl.h) #include #include #if defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32) #include "esp_private/system_internal.h" #endif #include "memfault/core/compiler.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/panics/arch/xtensa/xtensa.h" #include "memfault/panics/coredump.h" #include "memfault/panics/fault_handling.h" #include "memfault/ports/zephyr/log_panic.h" #include "memfault/ports/zephyr/version.h" // Note: There is no header exposed for this zephyr function 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 MEMFAULT_ZEPHYR_VERSION_GT(3, 6) void __wrap_z_fatal_error(unsigned int reason, const struct arch_esf *esf); void __real_z_fatal_error(unsigned int reason, const struct arch_esf *esf); #else void __wrap_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf); void __real_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf); #endif // Ensure the substituted function signature matches the original function _Static_assert(__builtin_types_compatible_p(__typeof__(&z_fatal_error), __typeof__(&__wrap_z_fatal_error)) && __builtin_types_compatible_p(__typeof__(&z_fatal_error), __typeof__(&__real_z_fatal_error)), "Error: check z_fatal_error function signature"); #if MEMFAULT_ZEPHYR_VERSION_GT(3, 6) void __wrap_z_fatal_error(unsigned int reason, const struct arch_esf *esf) #else void __wrap_z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) #endif { // TODO log panic may not be working correctly in fault handler context :( // // flush logs prior to capturing coredump & rebooting // MEMFAULT_LOG_PANIC(); const _xtensa_irq_stack_frame_raw_t *frame = (void *)esf; _xtensa_irq_bsa_t *bsa = frame->ptr_to_bsa; sMfltRegState reg = { .collection_type = kMemfaultEsp32RegCollectionType_ActiveWindow, .pc = bsa->pc, .ps = bsa->ps, .sar = bsa->sar, .exccause = bsa->exccause, #if XCHAL_HAVE_LOOPS .lbeg = bsa->lbeg, .lend = bsa->lend, .lcount = bsa->lcount, #endif // excvaddr is not available in the bsa on zephyr }; reg.a[0] = bsa->a0; // a1 is the stack pointer. Reference: Table 4-114 in Xtensa ISA Reference Manual // this decode is pulled from zephyr/arch/xtensa/core/vector_handlers.c, xtensa_dump_stack(), // which gets the stack pointer by finding the address right after the bsa reg.a[1] = (uintptr_t)((char *)bsa + sizeof(*bsa)); reg.a[2] = bsa->a2; reg.a[3] = bsa->a3; size_t i = 4; // start at index 4 since we've already filled in a[0] - a[3] size_t reg_blks_left = MEMFAULT_ARRAY_SIZE(frame->blks); const size_t num_regs_in_block = sizeof(frame->blks[0]) / sizeof(frame->blks[0].r0); // copy in registers from each register block. Ensure that we don't index out of bounds // in the reg.a[] array by checking the last expected index used in the loop: // (i + (# regs in block - 1)) < regs.a[] array size while (reg_blks_left > 0 && ((i + (num_regs_in_block - 1)) < MEMFAULT_ARRAY_SIZE(reg.a))) { reg.a[i++] = frame->blks[reg_blks_left - 1].r0; reg.a[i++] = frame->blks[reg_blks_left - 1].r1; reg.a[i++] = frame->blks[reg_blks_left - 1].r2; reg.a[i++] = frame->blks[reg_blks_left - 1].r3; reg_blks_left--; } memfault_fault_handler(®, kMfltRebootReason_HardFault); // TODO: calling __real_z_fatal_error() currently results in a infinite fault loop, so the // device isn't getting reset properly, or a fault is happening within the fault. // Unconditionally reboot the device for now. memfault_platform_reboot(); } MEMFAULT_WEAK MEMFAULT_NORETURN void memfault_platform_reboot(void) { // TODO this is not working correctly on the esp32s3 // memfault_platform_halt_if_debugging(); #if defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32) // Use esp_restart_noos() instead of sys_arch_reboot()/esp_restart() to avoid // calling k_sched_lock() and shutdown handlers from ISR/exception context, // which would trigger a mutex assertion (mutex.c:111). esp_restart_noos(); #else sys_arch_reboot(0); #endif CODE_UNREACHABLE; } ================================================ FILE: repository.yml ================================================ # In order for an SDK to be pulled into a mynewt project, there must be # a "repository.yml" in the root of the project. # # https://mynewt.apache.org/latest/tutorials/repo/add_repos.html#repo-descriptors repo.name: memfault-firmware-sdk repo.submodules: "" repo.versions: "0.0.0": "master" "0-dev": "0.0.0" # master "0.27.0": "0.27.0" # native support for mynewt added "0.27.1": "0.27.1" # Added os_coredump_cb implementation for use with Memfault repo.newt_compatibility: # Allow all versions for 0.0.0. This is a workaround to prevent a warning # from being displayed when newt doesn't know which version of the repo is # present. 0.0.0: 0.0.0: good 0.0.1: 0.0.1: good ================================================ FILE: requirements.txt ================================================ git+https://github.com/memfault/pyserial.git@master-memfault invoke==2.2.1 mbed-cli==1.10.1 west==1.2.0 pyyaml==6.0.3 colorama==0.4.6 fastcov==1.16 pytest==9.0.3 pytest-xdist==3.0.2 pytest-mock==3.10.0 snapshottest==0.6.0 ================================================ FILE: scripts/cmsis_pack_bundle.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # """ A script which can be used to convert the memfault-firmware-sdk into a CMSIS-Pack file which can then be imported and added to a Keil MDK or ARM Development Studio Based Project """ import argparse import datetime import fnmatch import glob import logging import os import pathlib import re import shutil import tempfile import xml.etree.ElementTree as ET # noqa: N817 import lxml.etree import requests # For testing, it can be useful to point the tool to another github repo GITHUB_REPO = os.getenv("GITHUB_REPO", "memfault/memfault-firmware-sdk") PDSC_TEMPLATE = """ Memfault FirmwareSDK Memfault SDK https://github.com/{GITHUB_REPO}/releases/download/{SDK_VERSION}/ hello@memfault.com https://github.com/{GITHUB_REPO}.git LICENSE Memfault Memfault Firmware SDK Memfault Firmware SDK https://docs.memfault.com/ Memfault core components RAM backed coredump implementation Template files for Memfault Platform implementation """ PIDX_TEMPLATE = """ Memfault https://github.com/{GITHUB_REPO}/releases/latest/download/ {TIMESTAMP} """ def get_file_element(file_name, common_prefix, sdk_version): relative_path = os.path.relpath( file_name, common_prefix, ) # convert to posix style paths as required by CMSIS-Pack spec relative_path = pathlib.PureWindowsPath(relative_path).as_posix() logging.debug("Adding %s", relative_path) ele = ET.fromstring( # noqa: S314 """""".format( PATH=relative_path, ) ) ele.tail = "\n " return ele def recursive_glob_backport(dir_glob): # Find first directory wildcard and walk the tree from there glob_root = dir_glob.split("/*")[0] for base, _dirs, files in os.walk(glob_root): for file_name in files: file_path = os.path.join(base, file_name) # We use fnmatch to make sure the full glob matches the files # found from the recursive scan # # fnmatch expects unix style paths. file_path_unix = file_path.replace("\\", "/") if fnmatch.fnmatch(file_path_unix, dir_glob): yield file_path def files_to_link(dir_glob, common_prefix, sdk_version): try: files = glob.glob(dir_glob, recursive=True) except TypeError: # Python < 3.5 do not support "recursive=True" arg for glob.glob files = recursive_glob_backport(dir_glob) files = recursive_glob_backport(dir_glob) for file_name in files: yield get_file_element(file_name, common_prefix, sdk_version) def get_latest_pdsc(): """Retrieve the contents of the latest pdsc file""" # Optionally override the release to fetch release = os.getenv("CMSIS_SDK_BASE_RELEASE") if release: url = f"https://github.com/{GITHUB_REPO}/releases/download/{release}/Memfault.FirmwareSDK.pdsc" else: url = f"https://github.com/{GITHUB_REPO}/releases/latest/download/Memfault.FirmwareSDK.pdsc" logging.debug("Fetching %s", url) resp = requests.get(url) # There should always be a pidx/pdsc file available. Only skip if the env # var is set and it's a 404, which is only useful before the first release. if resp.status_code == 404 or os.getenv("SKIP_MISSING_PRIOR_RELEASE"): logging.debug("Skipping missing file") return None resp.raise_for_status() return resp.text def lxml_reindent_tree(root): """Using lxml, re-indent the tree to produce consistent output""" parser = lxml.etree.XMLParser(remove_blank_text=True) tree = lxml.etree.fromstring(ET.tostring(root, encoding="utf-8"), parser=parser) lxml.etree.indent(tree, space=" ") return lxml.etree.ElementTree(tree) def write_pidx_file(sdk_version): utcnow = datetime.datetime.now().isoformat() # noqa: DTZ005 pidx_tree = ET.ElementTree( ET.fromstring(PIDX_TEMPLATE.format(GITHUB_REPO=GITHUB_REPO, TIMESTAMP=utcnow)) # noqa: S314 ) root = pidx_tree.getroot() pindex_node = root.find("./pindex") assert pindex_node is not None # if there's an existing name="FirmwareSDK" node, remove it before inserting # the new one for node in pindex_node.findall("./pdsc[@name='FirmwareSDK']"): pindex_node.remove(node) # unlike the pdsc file, we always overwrite the node in the pidx with # the latest version release_node = ET.fromstring( # noqa: S314 f'' ) pindex_node.append(release_node) # use lxml to re-indent the tree pidx_tree = lxml_reindent_tree(root) # and write it to Memfault.pidx pidx_tree.write("Memfault.pidx", encoding="utf-8", xml_declaration=True) def build_cmsis_pack( memfault_sdk_dir, components, target_port, pack_only, ): common_prefix = memfault_sdk_dir if not os.path.isdir(memfault_sdk_dir) or not os.path.isfile( "{}/VERSION".format(memfault_sdk_dir) ): raise ValueError( "Invalid path to memfault-firmware-sdk (missing VERSION file) at {}".format( memfault_sdk_dir ) ) logging.debug("===Determined Path Information===") logging.debug("Memfault Firmware SDK Path: %s", memfault_sdk_dir) sdk_version = os.getenv("SDK_VERSION") if sdk_version: logging.debug("Using SDK_VERSION env var: %s", sdk_version) else: # read the version from the memfault-firmware-sdk with open(f"{memfault_sdk_dir}/components/include/memfault/version.h", "r") as f: contents = f.read() match = re.search(r".major = (\d+), .minor = (\d+), .patch = (\d+)", contents, re.MULTILINE) assert match sdk_version = f"{match.group(1)}.{match.group(2)}.{match.group(3)}" # get the current data as YYYY-MM-DD. normally we'd use UTC, but the packchk # tool has a built-in check that the date is not in the future, and the # reference date it uses is locale time, so we need to use that. sdk_date = datetime.datetime.now().strftime("%Y-%m-%d") # noqa: DTZ005 tree = ET.ElementTree( ET.fromstring( # noqa: S314 PDSC_TEMPLATE.format( GITHUB_REPO=GITHUB_REPO, SDK_VERSION=sdk_version, SDK_DATE=sdk_date, ) ) ) root = tree.getroot() # the pdsc file name must exactly match these fields psdc_vendor = root.find(".//vendor").text # pyright: ignore[reportOptionalMemberAccess] psdc_name = root.find(".//name").text # pyright: ignore[reportOptionalMemberAccess] psdc_file_name = f"{psdc_vendor}.{psdc_name}.pdsc" component_nodes = root.findall(".//component") memfault_component = None for component_node in component_nodes: cgroup = component_node.get("Cgroup", "") if cgroup == "Memfault Core": memfault_component = component_node assert memfault_component file_resources = memfault_component.findall(".//files")[0] for component in components: for ele in files_to_link( dir_glob="{}/components/{}/**/*.c".format(memfault_sdk_dir, component), common_prefix=common_prefix, sdk_version=sdk_version, ): file_resources.append(ele) if target_port is not None: port_folder_name = "_".join(os.path.split(target_port)) port_folder_name = "memfault_{}".format(port_folder_name) for ele in files_to_link( dir_glob="{}/ports/{}/*.c".format(memfault_sdk_dir, target_port), common_prefix=common_prefix, sdk_version=sdk_version, ): file_resources.append(ele) # The DA1469x port also uses FreeRTOS so pick that up automatically when selected if target_port == "dialog/da1469x": for ele in files_to_link( dir_glob="{}/ports/freertos/**/*.c".format(memfault_sdk_dir), common_prefix=common_prefix, sdk_version=sdk_version, ): file_resources.append(ele) # Fetch the latest PDSC file from the Memfault Firmware SDK github release, # and use it to populate the release node in the pdsc file. # First insert the new release node to the start of the list url = f"https://github.com/{GITHUB_REPO}/releases/download/{sdk_version}/Memfault.FirmwareSDK.{sdk_version}.pack" release_node = ET.fromstring( # noqa: S314 f'Memfault CMSIS Pack for Memfault Firmware SDK {sdk_version}. See' " release notes at" f" https://github.com/memfault/memfault-firmware-sdk/releases/tag/{sdk_version}" ) root_releases_node = root.find("./releases") assert root_releases_node is not None root_releases_node.append(release_node) pdsc_text = get_latest_pdsc() if pdsc_text: logging.debug("Loading releases from previous pdsc file") pdsc = ET.fromstring(pdsc_text) # noqa: S314 # get the package/releases node releases_node = pdsc.find("./releases") assert releases_node is not None # and copy the fetched previous releases children to the new release node for child in releases_node: root_releases_node.append(child) # use lxml to re-indent the tree pdsc_tree = lxml_reindent_tree(root) # Set the output file name based on the psdc values. See # https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/createPackPublish.html#cp_WebDownload # for details. pack_output_file = f"{psdc_vendor}.{psdc_name}.{sdk_version}.pack" with tempfile.TemporaryDirectory() as tmpdir_str: pdsc_tree.write(f"{tmpdir_str}/{psdc_file_name}", encoding="utf-8", xml_declaration=True) # add all the components + ports files to the pack for directory in ["components", "ports"]: shutil.copytree(f"{memfault_sdk_dir}/{directory}", f"{tmpdir_str}/{directory}") aux_files = [ "CHANGELOG.md", "LICENSE", "README.md", ] for aux_file in aux_files: shutil.copy(f"{memfault_sdk_dir}/{aux_file}", f"{tmpdir_str}/{aux_file}") shutil.make_archive("memfault", "zip", tmpdir_str) shutil.move("memfault.zip", pack_output_file) if pack_only: return # write the .pdsc file pdsc_tree.write(f"{psdc_vendor}.{psdc_name}.pdsc", encoding="utf-8", xml_declaration=True) # write the .pidx file write_pidx_file(sdk_version) if __name__ == "__main__": logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=""" Generate a CMSIS-Pack distribution from the memfault-firmware-sdk, optionally including a specific target port. Example Usage: $ python cmsis_pack_bundle.py --memfault-sdk-dir /path/to/memfault-firmware-sdk """, ) parser.add_argument( "-m", "--memfault-sdk-dir", help="The directory memfault-firmware-sdk was copied to", ) parser.add_argument( "--target-port", help="An optional port to include in the pack, i.e dialog/da145xx" ) parser.add_argument( "-c", "--components", help="The components to include in the pack.", default="core,demo,util,metrics,panics", ) parser.add_argument( "--pack-only", action="store_true", help="Only generate the .pack file, don't output the .pdsc and .pidx files", ) parser.add_argument( "--verbose", default=False, action="store_true", help="enable verbose logging for debug", ) args = parser.parse_args() memfault_sdk_dir = args.memfault_sdk_dir if memfault_sdk_dir is None: memfault_sdk_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) memfault_sdk_dir = os.path.realpath(memfault_sdk_dir) components = args.components.split(",") if args.verbose: logging.getLogger().setLevel(logging.DEBUG) build_cmsis_pack( memfault_sdk_dir=memfault_sdk_dir, components=components, target_port=args.target_port, pack_only=args.pack_only, ) ================================================ FILE: scripts/create_arduino_library.py ================================================ #!/usr/bin/env python # # Copyright (c) Memfault, Inc. # See LICENSE for details # """ A script which transforms the memfault-firmware-sdk into a project suitable for inclusion as an arduino library For more details about the arduino library see: https://arduino.github.io/arduino-cli/0.20/library-specification/ The main transformations this script makes: - Removes .c & .cpp files that should not be included in an arduino build. (The arduino build system will by default recursively search and compile all .c/.cpp files in the library folder) - Patches all include paths to be relative to the root of the library. (There is no way to add additional include paths in an arduino library) Example Usage: $ python create_arduino_library.py --tag 0.28.2 --output build """ import argparse import glob import logging import os import re import shutil import tarfile import urllib.request from urllib.error import HTTPError ROOT_DIRECTORY = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) BUILD_DIRECTORY = os.path.join(ROOT_DIRECTORY, "build") MEMFAULT_RELEASE_PATH = "https://github.com/memfault/memfault-firmware-sdk/archive/refs/tags" def download_memfault_library(working_dir: str, tag: str): release_artifact_filename = f"{tag}.tar.gz" release_artifact_filepath = os.path.join(working_dir, release_artifact_filename) logging.debug("Downloading %s ...", release_artifact_filename) try: urllib.request.urlretrieve( # noqa: S310 f"{MEMFAULT_RELEASE_PATH}/{release_artifact_filename}", release_artifact_filepath ) except HTTPError as err: if err.code != 404: raise logging.error("No memfault-firmware-sdk with tag %s exists", tag) return None return release_artifact_filepath def extract_memfault_library(working_dir: str, release_artifact_filepath: str): logging.debug("Extracting %s", release_artifact_filepath) download = tarfile.open(release_artifact_filepath) # noqa: SIM115 download.extractall(working_dir) # noqa: S202 root_folder_name = download.getnames()[0] download.close() extract_dir = os.path.realpath(os.path.join(working_dir, root_folder_name)) logging.debug("Data extracted to %s", extract_dir) return extract_dir def arduinoify_memfault_sdk(sdk_root_dir: str, result_dir: str, port: str): if not os.path.exists(sdk_root_dir): raise FileNotFoundError(f"Memfault SDK directory does not exist: {sdk_root_dir}") # We will be re-package'ing the SDK into a library with the following structure: # # |-- LICENSE # |-- README.md (sourced from port_dir if provided) # |-- examples (sourced from port_dir if provided) # | # |-- library.properties (sourced from port_dir if provided) # |-- src # |-- memfault-firmware-sdk # |-- library_dir = os.path.join(result_dir) if port: port_dir = os.path.join(sdk_root_dir, "ports", port) if not os.path.exists(port_dir): logging.error("No port found for '%s' at %s", port, port_dir) else: logging.debug("Copying port from %s into %s", port_dir, library_dir) shutil.copytree(port_dir, library_dir) # After directory copy because it gets upset if file exists os.makedirs(library_dir, exist_ok=True) # Keep list for "ports" directory keep_list = ("panics",) port_root = os.path.join(sdk_root_dir, "ports") for f in os.listdir(port_root): if f in keep_list: continue path_to_rm = os.path.join(port_root, f) if not os.path.isdir(path_to_rm): continue logging.debug("Removing %s from memfault-firmware-sdk", path_to_rm) shutil.rmtree(path_to_rm) # Remove directories that aren't needed / contain .c / .cpp files we # do not want to compile remove_list = (".circleci", "examples", "tests") for directory in remove_list: path_to_rm = os.path.join(sdk_root_dir, directory) logging.debug("Removing %s from memfault-firmware-sdk", path_to_rm) shutil.rmtree(path_to_rm) files_to_patch = glob.glob(f"{sdk_root_dir}/**/*.c", recursive=True) files_to_patch.extend(glob.glob(f"{sdk_root_dir}/**/*.h", recursive=True)) # also the memfault/metrics/heartbeat_config.def file files_to_patch.extend( glob.glob(f"{sdk_root_dir}/**/include/memfault/metrics/heartbeat_config.def") ) logging.debug("Patching headers ...") # We need to swap out include paths to be relative to root of repo # since there's no way to add additional include paths in an arduino lib header_regex = re.compile(r'#include\s"memfault/') for f in files_to_patch: with open(f) as r: contents = r.read() contents = header_regex.sub( '#include "memfault-firmware-sdk/components/include/memfault/', contents ) with open(f, "w") as w: w.write(contents) # Up level a few files to the root of the repo shutil.move(os.path.join(sdk_root_dir, "LICENSE"), os.path.join(library_dir, "LICENSE.txt")) shutil.move(os.path.join(sdk_root_dir, "VERSION"), os.path.join(library_dir, "VERSION")) shutil.move( os.path.join(sdk_root_dir, "CHANGELOG.md"), os.path.join(library_dir, "CHANGELOG.md") ) shutil.move(sdk_root_dir, os.path.join(library_dir, "src", "memfault-firmware-sdk")) logging.debug("Patched SDK Generation Success! %s", library_dir) return library_dir if __name__ == "__main__": logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=""" Transforms the memfault-firmware-sdk into a project suitable for inclusion in an arduino library. Example Usage: # cd into directory with .project file $ python create_arduino_library.py --tag 0.28.2 --output build """, ) parser.add_argument( "-t", "--tag", required=False, help='The release from https://github.com/memfault/memfault-firmware-sdk/releases to convert to an arduino library (i.e "0.28.2")', ) parser.add_argument( "-m", "--memfault-sdk", required=False, help="Path to .tar.gz of the memfault-firmware-sdk to convert to an arduino library", ) parser.add_argument( "-o", "--output", help="The directory to output result to. By default a build/ directory relative to script location", default=os.path.join(BUILD_DIRECTORY, "arduino-library"), ) parser.add_argument( "-p", "--port", help="Additional porting layer to include in the arduino library", default=None, ) parser.add_argument( "-v", "--verbose", default=False, action="store_true", help="enable verbose logging for debug", ) args = parser.parse_args() if args.tag and args.memfault_sdk: raise ValueError("--tag and --memfault-sdk can not be used together") if not args.tag and not args.memfault_sdk: raise ValueError("Either --tag or --memfault-sdk must be specified") # Create output directory if it does not exist os.makedirs(BUILD_DIRECTORY, exist_ok=True) if args.verbose: logging.getLogger().setLevel(logging.DEBUG) if args.tag: release_artifact_filepath = download_memfault_library(BUILD_DIRECTORY, args.tag) extraction_working_dir = BUILD_DIRECTORY else: release_artifact_filepath = args.memfault_sdk extraction_working_dir = os.path.join(BUILD_DIRECTORY, "memfault-firmware-sdk") assert release_artifact_filepath release_artifact_dir = extract_memfault_library( extraction_working_dir, release_artifact_filepath ) result_dir = arduinoify_memfault_sdk(release_artifact_dir, args.output, args.port) logging.info("Hurray, library created at %s", result_dir) ================================================ FILE: scripts/eclipse_patch.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # """ A script which can be used to add the memfault-firmware-sdk to a project using an Eclipse-based IDE """ from __future__ import annotations import argparse import fnmatch import glob import logging import os import re import xml.etree.ElementTree as ET # noqa: N817 def get_depth_from_parent(project_dir: str, memfault_dir: str): common_prefix = os.path.commonpath([memfault_dir, project_dir]) depth = 1 dirname = project_dir # some projects are in the root of the project dir- if the memfault dir is # in the same directory, return a PROJECT_LOC value of 0 for the link # position if dirname == common_prefix: return dirname, 0 # for the normal case, walk the directory parents until we find the common # parent for the project and memfault dirs while True: parent_dir = os.path.dirname(dirname) if os.path.samefile(parent_dir, common_prefix): return common_prefix, depth elif parent_dir == dirname: raise RuntimeError( "Couldn't compute depth, aborting at directory {}".format(parent_dir) ) depth += 1 dirname = parent_dir def generate_link_element(name, path, path_type="1"): ele = ET.fromstring( # noqa: S314 """ \t \t\t{NAME} \t\t{PATH_TYPE} \t\t{PATH} \t """.format(NAME=name, PATH=path, PATH_TYPE=path_type) ) ele.tail = "\n\t" return ele def generate_linked_resources(): ele = ET.fromstring( # noqa: S314 """ \t """ ) ele.tail = "\n\t" return ele def get_file_element( file_name: str, virtual_dir: str, common_prefix: str, parent_dir: str, path_type: str = "1" ): name = "{}/{}".format(virtual_dir, os.path.basename(file_name)) relative_path = os.path.relpath( file_name, common_prefix, ) # Note: We replace '\' with '/' because eclipse on windows expects '/' for paths path = os.path.join(parent_dir, relative_path).replace("\\", "/") logging.debug("Adding %s", name) return generate_link_element(name, path, path_type=path_type) def generate_st_linker_option(): ele = ET.fromstring( # noqa: S314 """ """ ) ele.tail = "\n\t\t\t\t\t\t\t\t" return ele def generate_st_build_id_flag(): ele = ET.fromstring( # noqa: S314 """""" ) ele.tail = "\n\t\t\t\t\t\t\t\tq" return ele def recursive_glob_backport(dir_glob: str): # Find first directory wildcard and walk the tree from there glob_root = dir_glob.split("/*")[0] for base, _dirs, files in os.walk(glob_root): for file_name in files: file_path = os.path.join(base, file_name) # We use fnmatch to make sure the full glob matches the files # found from the recursive scan # # fnmatch expects unix style paths. file_path_unix = file_path.replace("\\", "/") if fnmatch.fnmatch(file_path_unix, dir_glob): yield file_path def files_to_link(dir_glob: str, virtual_dir: str, common_prefix: str, parent_dir: str): try: files = glob.glob(dir_glob, recursive=True) except TypeError: # Python < 3.5 do not support "recursive=True" arg for glob.glob files = recursive_glob_backport(dir_glob) files = recursive_glob_backport(dir_glob) # Sort the files so that the order is deterministic for file_name in sorted(files): # Note: # - xtensa targets (i.e ESP) use CMake/Make so no need to add to eclipse based projects # - skip adding "memfault_demo_http" from demo component if "xtensa" in file_name or ("http" in os.path.relpath(file_name, start=common_prefix)): continue logging.debug("Adding %s", file_name) yield get_file_element(file_name, virtual_dir, common_prefix, parent_dir) def patch_project( project_dir: str, memfault_sdk_dir: str, components: list[str], location_prefix: str | None = None, target_port: str | None = None, output_dir: str | None = None, ): project_file = "{}/.project".format(project_dir) if not os.path.isfile(project_file): raise RuntimeError("Could not location project file at {}".format(project_file)) if not os.path.isdir(memfault_sdk_dir) or not os.path.isfile( "{}/CHANGELOG.md".format(memfault_sdk_dir) ): raise RuntimeError("Could not locate memfault-firmware-sdk at {}".format(memfault_sdk_dir)) if location_prefix is None: # No prefix was given so paths will be generated relative to project root common_prefix, depth = get_depth_from_parent(project_dir, memfault_sdk_dir) parent_dir = "PARENT-{}-PROJECT_LOC".format(depth) else: common_prefix = memfault_sdk_dir relative_path = os.path.relpath( memfault_sdk_dir, os.path.commonprefix([memfault_sdk_dir, location_prefix[1]]), ) parent_dir = os.path.join(location_prefix[0], relative_path) logging.debug("===Determined Path Information===") logging.debug("Project Path: %s", project_dir) logging.debug("Memfault Firmware SDK Path: %s", memfault_sdk_dir) logging.debug("Eclipse Memfault Root: %s", parent_dir) tree = ET.parse(project_file) # noqa: S314 root = tree.getroot() linked_resources_roots = root.findall(".//linkedResources") if len(linked_resources_roots) == 0: linked_resources = generate_linked_resources() root.append(linked_resources) elif len(linked_resources_roots) == 1: linked_resources = linked_resources_roots[0] else: raise RuntimeError( "Located {} linked resources in Eclipse project file but expected 1".format( len(linked_resources_roots) ) ) # We want this script to be idempotent so remove any "memfault_" sources already # added. We will just be adding them back below. for link in linked_resources.findall("link"): name = link.find(".//name") if name is not None and "memfault_" in name.text: # pyright: ignore[reportOperatorIssue] linked_resources.remove(link) comp_folder_name = "memfault_components" linked_resources.append( generate_link_element(comp_folder_name, "virtual:/virtual", path_type="2") ) for component in components: logging.debug("Adding %s component", component) for ele in files_to_link( dir_glob="{}/components/{}/**/*.c".format(memfault_sdk_dir, component), virtual_dir=comp_folder_name, common_prefix=common_prefix, parent_dir=parent_dir, ): linked_resources.append(ele) include_folder_name = "memfault_includes" linked_resources.append( generate_link_element(include_folder_name, "virtual:/virtual", path_type="2") ) for inc_name in ["components", "ports"]: inc_path = os.path.join(memfault_sdk_dir, inc_name, "include") ele = get_file_element( file_name=inc_path, virtual_dir=os.path.join(include_folder_name, inc_name), common_prefix=common_prefix, parent_dir=parent_dir, path_type="2", ) linked_resources.append(ele) if target_port is not None: head, tail = os.path.split(target_port) port_folder_name = "_".join((head, tail)) if head != "" else tail port_folder_name = "memfault_{}".format(port_folder_name) linked_resources.append( generate_link_element(port_folder_name, "virtual:/virtual", path_type="2") ) for ele in files_to_link( dir_glob="{}/ports/{}/*.c".format(memfault_sdk_dir, target_port), virtual_dir=port_folder_name, common_prefix=common_prefix, parent_dir=parent_dir, ): linked_resources.append(ele) # The DA1469x port also uses FreeRTOS so pick that up automatically when selected if target_port == "dialog/da1469x": for ele in files_to_link( dir_glob="{}/ports/freertos/**/*.c".format(memfault_sdk_dir), virtual_dir=port_folder_name, common_prefix=common_prefix, parent_dir=parent_dir, ): linked_resources.append(ele) output_location = project_file if output_dir is None else os.path.join(output_dir, ".project") logging.info("Writing result to %s", output_location) tree.write(output_location) def patch_cproject( project_dir: str, output_dir: str | None = None, ): cproject_file = "{}/.cproject".format(project_dir) if not os.path.isfile(cproject_file): raise RuntimeError("Could not location project file at {}".format(cproject_file)) tree = ET.parse(cproject_file) # noqa: S314 root = tree.getroot() with open(cproject_file) as cproject_xml: data = cproject_xml.read() # ElementTree parser doesn't preserve XML processing instructions. # # Since .cproject files relies on a few, we will grab them now so we can re-insert # them after patching the file match = re.search(r"(.*)<\s*{}".format(root.tag), data, re.DOTALL) processing_instruction_text = match.group(1) if match else "" options = root.findall(".//option") # # Add required Memfault include paths into build for all build configurations: # ${MEMFAULT_FIRMWARE_SDK}/components/include # ${MEMFAULT_FIRMWARE_SDK}/ports/include # def _find_include_nodes(option: ET.Element): return option.get("id", "").startswith(( # this is the element id used by Dialog's Smart Snippets Studio # IDE (and possibly others) "ilg.gnuarmeclipse.managedbuild.cross.option.c.compiler.include.paths", # this is the element id used by NXP's MCUXpresso IDE "gnu.c.compiler.option.include.paths", # Element used by ST's STM32Cube IDE for include path enumeration "com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.includepaths", )) memfault_sdk_include_paths = [ "${workspace_loc:/${ProjName}/memfault_includes/components/include}", "${workspace_loc:/${ProjName}/memfault_includes/ports/include}", ] include_options = filter(_find_include_nodes, options) for include_option in include_options: list_option_values = include_option.findall(".//listOptionValue") tail = list_option_values[0].tail for include_path in list_option_values: path = include_path.get("value", "") if "memfault_includes" in path: include_option.remove(include_path) for path in memfault_sdk_include_paths: ele = ET.Element("listOptionValue", builtin="false", value='"{}"'.format(path)) ele.tail = tail include_option.append(ele) # # Add GNU build id to STM32Cube IDE based projects # def _find_st_linker_tools(tool: ET.Element): return tool.get("id", "").startswith( # Element used by ST's STM32Cube IDE for linker arguments "com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker", ) def _find_st_linker_options(option: ET.Element): return option.get("id", "").startswith( "com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.option.otherflags" ) def _find_st_build_id_linker_flag(option: ET.Element): return "--build-id" in option.get("value", "") tools = root.findall(".//tool") linker_tools = filter(_find_st_linker_tools, tools) for linker_tool in linker_tools: all_linker_options = linker_tool.findall(".//option") linker_options = filter(_find_st_linker_options, all_linker_options) if len(list(linker_options)) != 0: continue ele = generate_st_linker_option() linker_tool.insert(0, ele) # reload all linker options and now add the flag itself linker_options = filter(_find_st_linker_options, linker_tool.findall(".//option")) for linker_option in linker_options: linker_flags = filter( _find_st_build_id_linker_flag, linker_option.findall(".//listOptionValue") ) if len(list(linker_flags)) != 0: continue ele = generate_st_build_id_flag() linker_option.insert(0, ele) # # Add GNU build id generation for all build configurations: # def _find_linker_flags(option: ET.Element): return option.get("id", "").startswith( # Element used by Dialog's Smart Snippets Studio IDE "ilg.gnuarmeclipse.managedbuild.cross.option.c.linker.other" ) and option.get("name", "").startswith("Other linker flags") ld_flag_options = filter(_find_linker_flags, options) for ld_flag_option in ld_flag_options: value = ld_flag_option.get("value", "") if "-Wl,--build-id" not in value: ld_flag_option.set("value", value + " -Wl,--build-id") # # Overwrite original .cproject file with updates and pull back in processing instruction text # which was extracted earlier # output_location = cproject_file if output_dir is None else os.path.join(output_dir, ".cproject") logging.info("Writing result to %s", output_location) tree.write(output_location) with open(output_location, "r") as out_f: new_contents = out_f.read() with open(output_location, "w") as out_f: out_f.write(processing_instruction_text) out_f.write(new_contents) if __name__ == "__main__": logging.basicConfig( format="%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s", datefmt="%Y-%m-%d:%H:%M:%S", level=logging.INFO, ) parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=""" Patches an Eclipse .project file to include sources for memfault-firmware-sdk. Example Usage: # cd into directory with .project file $ python eclipse_patch.py --project-dir . --memfault-sdk-dir /path/to/memfault-firmware-sdk """, ) parser.add_argument( "-p", "--project-dir", required=True, help="The directory with the Eclipse .project to update", ) # get the current directory of this script, and go up one level to get the # default memfault-sdk-dir default_memfault_sdk_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) parser.add_argument( "-m", "--memfault-sdk-dir", default=default_memfault_sdk_dir, help="The directory memfault-firmware-sdk was copied to. Default is the parent directory of this script", ) parser.add_argument( "--target-port", help="The port to pick up for a project, i.e dialog/da145xx" ) parser.add_argument( "-l", "--location-prefix", help=( "The default behavior will add memfault-firmware-sdk files to the eclipse project using" " paths relative to the project root. This can be used to control the root used instead" ), ) parser.add_argument( "-c", "--components", help="The components to include in an eclipse project.", default="core,util,metrics,panics,demo", ) parser.add_argument( "--output", help=( "The directory to output result to. By default, the .project/.cproject files for the" " project will be overwritten" ), ) parser.add_argument( "--verbose", default=False, action="store_true", help="enable verbose logging for debug", ) args = parser.parse_args() project_dir = os.path.realpath(args.project_dir) memfault_sdk_dir = os.path.realpath(args.memfault_sdk_dir) components = args.components.split(",") if args.output and not os.path.isdir(args.output): raise RuntimeError("Output directory does not exist: {}".format(args.output)) if args.location_prefix: location_prefix = args.location_prefix.split("=") if len(location_prefix) != 2: raise RuntimeError("Location Prefix must be of form 'VAR=/path/'") else: location_prefix = None if args.verbose: logging.getLogger().setLevel(logging.DEBUG) patch_project( project_dir=project_dir, memfault_sdk_dir=memfault_sdk_dir, components=components, location_prefix=location_prefix, target_port=args.target_port, output_dir=args.output, ) patch_cproject( project_dir=project_dir, output_dir=args.output, ) logging.info( "Hurray, .project & .cproject have been successfully patched! Be sure to 'Refresh' project" " to synchronize changes!" ) ================================================ FILE: scripts/fw_build_id.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # """ Shim around mflt_build_id to keep the original fw_build_id.py file (this file) working as before. See mflt-build-id/src/mflt_build_id/__init__.py for actual source code. """ import os import sys scripts_dir = os.path.dirname(os.path.realpath(__file__)) bundled_mflt_build_id_src_dir = os.path.join(scripts_dir, "mflt-build-id", "src") internal_mflt_build_id_src_dir = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(scripts_dir))), "py-packages", "mflt-build-id", "src", ) if os.path.exists(bundled_mflt_build_id_src_dir): # Released SDK: sys.path.insert(0, bundled_mflt_build_id_src_dir) if os.path.isdir(internal_mflt_build_id_src_dir): sys.path.append(internal_mflt_build_id_src_dir) from mflt_build_id import * # noqa: E402, F403 # pyright: ignore[reportWildcardImportFromLibrary] if __name__ == "__main__": from mflt_build_id import main main() ================================================ FILE: scripts/memfault_gdb.py ================================================ # -*- coding: utf-8 -*- # # Copyright (c) 2019, Memfault # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code or in binary form must reproduce # the above copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided with the # distribution. # # 2. Neither the name of Memfault nor the names of its contributors may be # used to endorse or promote products derived from this software without # specific prior written permission. # # 3. This software, with or without modification, must only be used with # the Memfault services and integrated with the Memfault server. # # 4. Any software provided in binary form under this license must not be # reverse engineered, decompiled, modified and/or disassembled. # # THIS SOFTWARE IS PROVIDED BY MEMFAULT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT # SHALL MEMFAULT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. import argparse import os import platform import re import sys import traceback import uuid from binascii import b2a_base64 from hashlib import md5 from json import dump, dumps, load, loads from os.path import expanduser from struct import pack, unpack from tempfile import TemporaryFile from threading import Thread from time import sleep, time try: import gdb except ImportError: error_str = """ This script can only be run within gdb! """ raise ImportError(error_str) # (no raise-from in Python 2.7) # Note: not using `requests` but using the built-in http.client instead, so # there will be no additional dependencies other than Python itself. try: from httplib import HTTPConnection, HTTPSConnection from Queue import Queue from urlparse import urlparse, urlunparse except ImportError: from http.client import HTTPConnection, HTTPSConnection from queue import Queue from urllib.parse import urlparse, urlunparse MEMFAULT_DEFAULT_INGRESS_BASE_URI = "https://ingress.memfault.com" MEMFAULT_DEFAULT_CHUNKS_BASE_URI = "https://chunks.memfault.com" MEMFAULT_DEFAULT_API_BASE_URI = "https://api.memfault.com" try: # noqa: SIM105 # In Python 3.x, raw_input was renamed to input # NOTE: Python 2.x also had an input() function which eval'd the input...! input = raw_input except NameError: pass class MemfaultConfig(object): ingress_uri = MEMFAULT_DEFAULT_INGRESS_BASE_URI api_uri = MEMFAULT_DEFAULT_API_BASE_URI email = None password = None organization = None project = None user_id = None # indirection so tests can mock this prompt = input # Added `json_path` and `input` as attributes on the config to aid unit testing: json_path = expanduser("~/.memfault/gdb.json") def can_make_project_api_request(self): return self.email and self.password and self.project and self.organization MEMFAULT_CONFIG = MemfaultConfig() def register_value_to_bytes(gdb_scalar_value, little_endian=True): """ This helper is meant to be used with values that are not addressable, i.e. registers. If you've got an addressable value, it's probably faster/better to use the Inferior.read_memory() API. """ # try to get the int representation of the value from gdb via py-value.c valpy_long/ valpy_int # # If that fails, fallback to parsing the string. Older versions of the GDB python API # can't do int conversions on certain types such as pointers which some registers get # defined as (i.e pc & sp) try: value_as_int = int(gdb_scalar_value) except gdb.error: # register string representation can look something like: "0x17d8 <__start>" value_as_str = str(gdb_scalar_value).split()[0] value_as_int = int(value_as_str, 0) # if the value we get back from gdb python is negative, use the signed representation instead # of the unsigned representation # # We represent all registers in the kMfltCoredumpBlockType_CurrentRegisters section of the coredump as 32 bit values (MfltCortexMRegs) so try # to convert to a 4 byte representation regardless of the width reported by the gdb-server fmt = "i" if value_as_int < 0 else "I" return pack(fmt, value_as_int) def _get_register_value(reglist, name): return unpack(" 10: # Not in an exception so keep the register list we already have return # If we have faulted it's typically useful to get the backtrace for the code where the fault # originated. The integrated memfault-firmware-sdk does this by installing into the fault # handlers and capturing the necessary registers. For the try script, we'll try to apply the # same logic fault_start_prompt = """ We see you are trying out Memfault from a Fault handler. That's great! For the best results and to mirror the behavior of our firmware SDK, please run "memfault coredump" at exception entry before other code has run in the exception handler """ gdb_how_to_fault_prompt = """ It's easy to halt at exception entry by installing a breakpoint from gdb. For example, (gdb) breakpoint HardFault_Handler """ exc_return = _get_register_value(register_list, "lr") if exc_return >> 28 != 0xF: print("{} {}".format(fault_start_prompt, gdb_how_to_fault_prompt)) raise RuntimeError("LR no longer set to EXC_RETURN value") # DCRS - armv8m only - only relevant when chaining secure and non-secure exceptions # so pretty unlikely to be hit in a try test scenario if exc_return & (1 << 5) == 0: raise RuntimeError("DCRS exception unwinding unimplemented") if not _pc_in_vector_table(register_list, exception_number, analytics_props): analytics_props["displayed_fault_prompt"] = True # The pc is not at the start of the exception handler. Some firmware implementations # will redirect the vector table to a software vector table. If that's the case, it's # hard to detect programmatically, let's check in with the user y = MEMFAULT_CONFIG.prompt( "{}\nAre you currently at the start of an exception handler [y/n]?".format( fault_start_prompt ) ) if "Y" not in y.upper(): print(gdb_how_to_fault_prompt) raise RuntimeError("User did not confirm being at beginning of exception") else: analytics_props["displayed_fault_prompt"] = False sp_name = "psp" if (exc_return & 0x4 != 0) else "msp" sp = _get_register_value(register_list, sp_name) # restore the register state prior to exception entry so we get an unwind # from where the exception actually occurred exception_frame = ("r0", "r1", "r2", "r3", "r12", "lr", "pc", "xpsr") for idx, r in enumerate(exception_frame): _, data = _read_register(sp + idx * 4) register_list[r] = data orig_sp_offset = 0x68 if (exc_return & (1 << 4) == 0) else 0x20 if _get_register_value(register_list, "xpsr") & (1 << 9) != 0: orig_sp_offset += 0x4 register_list["sp"] = pack("> 4) & 0xFFF cortex_m_cpuids = { 0xC20: "M0", 0xC21: "M1", 0xC23: "M3", 0xC24: "M4", 0xC27: "M7", 0xC60: "M0+", } if partno not in cortex_m_cpuids: return None print("Cortex-{} detected".format(cortex_m_cpuids[partno])) mpu_type = 0xE000ED90 mpu_ctrl = 0xE000ED94 mpu_rnr = 0xE000ED98 mpu_rbar = 0xE000ED9C mpu_rasr = 0xE000EDA0 result = b"" mpu_type, mpu_type_data = _read_register(mpu_type) result += mpu_type_data mpu_ctrl, mpu_ctrl_data = _read_register(mpu_ctrl) result += mpu_ctrl_data num_regions = (mpu_type >> 8) & 0xFF for i in range(num_regions): _write_register(mpu_rnr, i) _, data = _read_register(mpu_rbar) result += data _, data = _read_register(mpu_rasr) result += data return result def add_platform_specific_sections(self, cd_writer, inferior, analytics_props): mem_mapped_regs = [ ("ictr", "Interrupt Controller Type Register", 0xE000E004, 0xE000E008), ("systick", "ARMv7-M System Timer", 0xE000E010, 0xE000E020), ("scb", "ARMv7-M System Control Block", 0xE000ED00, 0xE000ED8F), ("scs_debug", "ARMv7-M SCS Debug Registers", 0xE000EDFC, 0xE000EE00), ("nvic", "ARMv7-M External Interrupt Controller", 0xE000E100, 0xE000E600), ] for mem_mapped_reg in mem_mapped_regs: try: short_name, desc, base, top = mem_mapped_reg section = Section(base, top - base, desc) section.data = inferior.read_memory(section.addr, section.size) cd_writer.add_section(section) analytics_props["{}_ok".format(short_name)] = True except Exception: analytics_props["{}_collection_error".format(short_name)] = { "traceback": traceback.format_exc() } try: cd_writer.armv67_mpu = self._try_collect_mpu_settings() print("Collected MPU config") except Exception: analytics_props["mpu_collection_error"] = {"traceback": traceback.format_exc()} @staticmethod def _merge_memory_regions(regions): """Take a set of memory regions and merge any overlapping regions""" regions.sort(key=lambda x: x.addr) # Sort the regions based on starting address merged_regions = [] for region in regions: if not merged_regions: merged_regions.append(region) else: prev_region = merged_regions[-1] if region.addr <= prev_region.addr + prev_region.size: prev_region.size = max( prev_region.size, region.addr + region.size - prev_region.addr ) else: merged_regions.append(region) return merged_regions def guess_ram_regions(self, elf_sections): capturable_elf_sections = list(filter(should_capture_section, elf_sections)) def _is_ram(base_addr): # See Table B3-1 ARMv7-M address map in "ARMv7-M Architecture Reference Manual" return (base_addr & 0xF0000000) in ( 0x20000000, 0x30000000, 0x60000000, 0x80000000, ) capture_size = 1024 * 1024 # Capture up to 1MB per section for section in capturable_elf_sections: section.size = capture_size # merge any overlapping sections to make the core a little more efficient capturable_elf_sections = self._merge_memory_regions(capturable_elf_sections) filtered_addrs = set(filter(_is_ram, (s.addr for s in capturable_elf_sections))) # Capture up to 1MB for each region return [(addr, capture_size) for addr in filtered_addrs] def get_current_registers(self, gdb_thread, analytics_props): gdb_thread.switch() # GDB Doesn't have a convenient way to know all of the registers in Python, so this is the # best way. Call this, rip out the first element in each row...that's the register name # # NOTE: Using the 'all-registers' command below, because on some # versions of gdb "msp, psp, etc" are not considered part of them core # set. This will also dump all the fpu registers which we don't collect # but thats fine. 'info all-registers' is the preferred command, where # 'info reg [all]' is arch-specific, see: # https://sourceware.org/gdb/onlinedocs/gdb/Registers.html try: info_reg_all_list = gdb.execute("info all-registers", to_string=True) except gdb.error: # Some versions of gdb don't support 'all' and return an error, fall # back to 'info reg' info_reg_all_list = gdb.execute("info reg", to_string=True) return (lookup_registers_from_list(self, info_reg_all_list, analytics_props),) # TODO: De-duplicate with code from rtos_register_stacking.py def concat_registers_dict_to_bytes(arch, regs): result = b"" for reg_name in arch.register_collection_list: assert reg_name.lower() == reg_name if reg_name not in regs: result += b"\x00\x00\x00\x00" continue result += regs[reg_name] return result def _is_expected_reg(arch, reg_name): return reg_name in arch.register_collection_list def _add_reg_collection_error_analytic(arch, analytics_props, reg_name, error): if not _is_expected_reg(arch, reg_name): return reg_collection_error = "reg_collection_error" if reg_collection_error not in analytics_props: analytics_props[reg_collection_error] = {} analytics_props[reg_collection_error][reg_name] = error def _try_read_register(arch, frame, lookup_name, register_list, analytics_props, result_name=None): # `info reg` will print all registers, even though they are not part of the core. # If that's the case, doing frame.read_register() will raise a gdb.error. try: if hasattr(frame, "read_register"): value = frame.read_register(lookup_name) else: # GCC <= 4.9 doesn't have the read_register API value = gdb.parse_and_eval("${}".format(lookup_name)) value_str = str(value) if value_str != "": name_to_use = lookup_name if result_name is None else result_name register_list[name_to_use] = register_value_to_bytes(value) else: _add_reg_collection_error_analytic( arch, analytics_props, lookup_name, " value" ) except Exception: _add_reg_collection_error_analytic( arch, analytics_props, lookup_name, traceback.format_exc() ) def lookup_registers_from_list(arch, info_reg_all_list, analytics_props): frame = gdb.newest_frame() frame.select() register_names = [] for reg_row in info_reg_all_list.strip().split("\n"): name = reg_row.split()[0] register_names.append(name) def _search_list_for_alt_name(reg, found_registers): # first see if it's just case getting in the way i.e 'CONTROL' instead of 'control'. We # need to preserve case when we actually issue the read so the gdb API works correctly for found_reg in found_registers: if found_reg.lower() == reg: return found_reg alt_reg_names = arch.alternative_register_name_dict.get(reg, []) for alt_reg_name in alt_reg_names: if alt_reg_name in found_registers: return alt_reg_name return None alt_reg_names = [] for expected_reg in arch.register_collection_list: if expected_reg in register_names: continue alt_reg_name = _search_list_for_alt_name(expected_reg, register_names) if alt_reg_name: alt_reg_names.append((alt_reg_name, expected_reg)) continue _add_reg_collection_error_analytic( arch, analytics_props, expected_reg, "Not found in register set" ) # Iterate over all register names and pull the value out of the frame register_list = {} # Remove register_names we don't care about before actually looking up values register_names = filter(lambda n: _is_expected_reg(arch, n), register_names) for reg_name in register_names: _try_read_register(arch, frame, reg_name, register_list, analytics_props) for lookup_reg_name, result_reg_name in alt_reg_names: _try_read_register( arch, frame, lookup_reg_name, register_list, analytics_props, result_reg_name, ) # if we can't patch the registers, we'll just fallback to the active state try: check_and_patch_reglist_for_fault(register_list, analytics_props) except Exception: analytics_props["fault_register_recover_error"] = {"traceback": traceback.format_exc()} return register_list # TODO: De-duplicate with code from core_convert.py MEMFAULT_COREDUMP_MAGIC = 0x45524F43 MEMFAULT_COREDUMP_VERSION = 1 MEMFAULT_COREDUMP_FILE_HEADER_FMT = "0x6b7a8 at 0x0004b784: .gnu_build_id ALLOC LOAD READONLY DATA HAS_CONTENTS section_matches = re.findall( r"\s+\[\d+\]\s+(0x[\da-fA-F]+)[^0]+(0x[\da-fA-F]+)[^:]+: ([^ ]+) (.*)$", output, re.MULTILINE, ) def _tuple_to_section(tpl): addr = int(tpl[0], base=16) size = int(tpl[1], base=16) - addr name = tpl[2] read_only = "READONLY" in tpl[3] return Section(addr, size, name, read_only) sections = map(_tuple_to_section, section_matches) return fn, list(sections) def read_memory_until_error(inferior, start, size, read_size=4 * 1024): data = b"" end = start + size try: for addr in range(start, end, read_size): data += bytes(inferior.read_memory(addr, min(read_size, end - addr))) except Exception as e: # Catch gdbserver read exceptions -- not sure what exception classes can get raised here print(e) return data def _create_http_connection(base_uri): url = urlparse(base_uri) if url.hostname is None: raise RuntimeError("Invalid base URI, must be http(s)://hostname") if url.scheme == "http": conn_class = HTTPConnection default_port = 80 else: conn_class = HTTPSConnection default_port = 443 port = url.port or default_port return conn_class(url.hostname, port=port) def _http(method, base_uri, path, headers=None, body=None): if headers is None: headers = {} conn = _create_http_connection(base_uri) # Convert to a string/bytes object so 'Content-Length' is set appropriately # Python 2.7 uses this by default but 3.6 & up were using 'chunked' if sys.version_info.major >= 3 and hasattr(body, "read"): body = body.read() conn.request(method, path, body=body, headers=headers) response = conn.getresponse() status = response.status reason = response.reason body = response.read() try: json_body = loads(body) except Exception: json_body = None conn.close() return status, reason, json_body def add_basic_auth(user, password, headers=None): headers = dict(headers) if headers else {} headers["Authorization"] = "Basic {}".format( b2a_base64("{}:{}".format(user, password).encode("utf8")).decode("ascii").strip() ) return headers class HttpApiError(Exception): def __init__(self, status, reason): super(Exception, self).__init__("{} (HTTP {})".format(reason, status)) def _check_http_response(status, reason): if status < 200 or status >= 300: raise HttpApiError(status, reason) def _http_api(config, method, path, headers=None, body=None, should_raise=False): headers = add_basic_auth(config.email, config.password, headers=headers) status, reason, body = _http(method, config.api_uri, path, headers, body) if should_raise: _check_http_response(status, reason) return status, reason, body def http_post_coredump(coredump_file, project_key, ingress_uri): headers = { "Content-Type": "application/octet-stream", "Memfault-Project-Key": project_key, } status, reason, _ = _http( "POST", ingress_uri, "/api/v0/upload/coredump", headers=headers, body=coredump_file, ) return status, reason def http_post_chunk(chunk_data_file, project_key, chunks_uri, device_serial): headers = { "Content-Type": "application/octet-stream", "Memfault-Project-Key": project_key, } status, reason, _ = _http( "POST", chunks_uri, "/api/v0/chunks/{}".format(device_serial), headers=headers, body=chunk_data_file, ) return status, reason def http_get_auth_me(api_uri, email, password): headers = add_basic_auth(email, password) return _http("GET", api_uri, "/auth/me", headers=headers) def http_get_prepared_url(config): _, _, body = _http_api( config, "POST", "/api/v0/organizations/{organization}/projects/{project}/upload".format( organization=config.organization, project=config.project, ), headers={"Accept": "application/json"}, should_raise=True, ) data = body["data"] return data["token"], data["upload_url"] def http_upload_file(config, file_readable): token, upload_url = http_get_prepared_url(config) url_parts = urlparse(upload_url) base_uri = urlunparse((url_parts[0], url_parts[1], "", "", "", "")) path = urlunparse(("", "", url_parts[2], url_parts[3], url_parts[4], url_parts[5])) status, reason, _ = _http( "PUT", base_uri, path, # NB: Prepared upload API does not expect Content-Type to be set so # we need to exclude the Content-Type or set it to an empty string headers={"Content-Type": ""}, body=file_readable, ) _check_http_response(status, reason) return token def http_upload_symbol_file(config, artifact_readable, software_type, software_version): token = http_upload_file(config, artifact_readable) _http_api( config, "POST", "/api/v0/organizations/{organization}/projects/{project}/symbols".format( organization=config.organization, project=config.project, ), headers={"Content-Type": "application/json", "Accept": "application/json"}, body=dumps({ "file": {"token": token, "name": "symbols.elf"}, "software_version": { "version": software_version, "software_type": software_type, }, }), should_raise=True, ) def http_get_software_version(config, software_type, software_version): software_version_url = "/api/v0/organizations/{organization}/projects/{project}/software_types/{software_type}/software_versions/{software_version}".format( organization=config.organization, project=config.project, software_type=software_type, software_version=software_version, ) status, _, body = _http_api(config, "GET", software_version_url) if status < 200 or status >= 300: return None return body["data"] def http_get_project_key(config): status, reason, body = _http_api( config, "GET", "/api/v0/organizations/{organization}/projects/{project}/api_key".format( organization=config.organization, project=config.project ), headers={"Accept": "application/json"}, ) if status < 200 or status >= 300: return None, (status, reason) return body["data"]["api_key"], None def get_file_hash(fn): with open(fn, "rb") as f: return md5(f.read()).hexdigest() def has_uploaded_symbols(config, software_type, software_version): software_version_obj = http_get_software_version(config, software_type, software_version) if not software_version_obj: return False symbol_file = software_version_obj.get("symbol_file") if not symbol_file: return False return bool(symbol_file.get("downloadable")) def upload_symbols_if_needed(config, elf_fn, software_type, software_version): has_symbols = has_uploaded_symbols(config, software_type, software_version) if has_symbols: print("Symbols have already been uploaded, skipping!") return if not has_symbols: print("Uploading symbols...") with open(elf_fn, "rb") as elf_f: try: http_upload_symbol_file(config, elf_f, software_type, software_version) # NOTE: upload is processed asynchronously. Give the symbol file # a little time to be processed. In the future, we could poll here # for completion sleep(0.3) print("Done!") except HttpApiError as e: print("Failed to upload symbols: {}".format(e)) # TODO: Duped from mflt.tools/gdb_memfault.py class MemfaultGdbArgumentParseError(Exception): pass class MemfaultGdbArgumentParser(argparse.ArgumentParser): def exit(self, status=0, message=None): if message: self._print_message(message) # Don't call sys.exit() raise MemfaultGdbArgumentParseError def populate_config_args_and_parse_args(parser, unicode_args, config): parser.add_argument( "--email", help="The username (email address) of the user to use", default=MEMFAULT_CONFIG.email, ) parser.add_argument( "--password", help="The user API key or password of the user to use", default=MEMFAULT_CONFIG.password, ) parser.add_argument( "--organization", "-o", help="Default organization (slug) to use", default=MEMFAULT_CONFIG.organization, ) parser.add_argument( "--project", "-p", help="Default project (slug) to use", default=MEMFAULT_CONFIG.project, ) parser.add_argument( "--ingress-uri", default=MEMFAULT_CONFIG.ingress_uri, help="Default ingress base URI to use (default: {})".format(MEMFAULT_CONFIG.ingress_uri), ) parser.add_argument( "--api-uri", help="Default API base URI to use (default: {})".format(MEMFAULT_CONFIG.api_uri), default=MEMFAULT_CONFIG.api_uri, ) args = list(filter(None, unicode_args.split(" "))) parsed_args = parser.parse_args(args) config.ingress_uri = parsed_args.ingress_uri config.api_uri = parsed_args.api_uri config.email = parsed_args.email config.password = parsed_args.password config.organization = parsed_args.organization config.project = parsed_args.project # If the user overrides the email, we need to re-auth to get the new user ID if parsed_args.email == MEMFAULT_CONFIG.email: config.user_id = MEMFAULT_CONFIG.user_id else: _, _, json_body = http_get_auth_me(config.api_uri, config.email, config.password) config.user_id = json_body["id"] return parsed_args class MemfaultGdbCommand(gdb.Command): GDB_CMD = "memfault override_me" def __init__(self, *args, **kwargs): super(MemfaultGdbCommand, self).__init__(self.GDB_CMD, gdb.COMMAND_USER, *args, **kwargs) def invoke(self, arg, from_tty): # Work-around for GDB-py not printing out the backtrace upon exceptions analytics_props = {} start_time = time() try: self._invoke(arg, from_tty, analytics_props, MEMFAULT_CONFIG) except Exception: print(traceback.format_exc()) ANALYTICS.track("Exception", {"traceback": traceback.format_exc(), "args": arg}) raise # Important, otherwise unit test failures may go undetected! analytics_props["duration_ms"] = int((time() - start_time) * 1000) ANALYTICS.track("Command {}".format(self.GDB_CMD), analytics_props, MEMFAULT_CONFIG.user_id) def _invoke(self, unicode_args, from_tty, analytics_props, config): pass class Memfault(MemfaultGdbCommand): """Memfault GDB commands""" GDB_CMD = "memfault" def __init__(self): super(Memfault, self).__init__(gdb.COMPLETE_NONE, prefix=True) def _invoke(self, unicode_args, from_tty, analytics_props, config): gdb.execute("help memfault") def settings_load(): try: with open(MEMFAULT_CONFIG.json_path, "rb") as f: return load(f) except Exception: return {} def settings_save(settings): try: # noqa: SIM105 # exist_ok does not exist yet in Python 2.7! os.makedirs(os.path.dirname(MEMFAULT_CONFIG.json_path)) except OSError: pass with open(MEMFAULT_CONFIG.json_path, "w") as f: dump(settings, f, sort_keys=True) def _infer_issues_html_url(ingress_uri, config): if "dev." in ingress_uri or "localhost" in ingress_uri: html_base_uri = ingress_uri elif "ingress.try" in ingress_uri: html_base_uri = ingress_uri.replace("ingress.try.", "try.") elif "ingress." in ingress_uri: html_base_uri = ingress_uri.replace("ingress.", "app.") else: return None return "{}/organizations/{}/projects/{}/issues?live".format( html_base_uri, config.organization, config.project ) def _read_register(address): reg_data = gdb.selected_inferior().read_memory(address, 4) reg_val = unpack(": syntax to ensure the # breakpoint is set for the correct function. chunk_handler_func_filename = None for line in lines: file_match = re.search(self.FILENAME_FROM_INFO_FUNC_PATTERN, line) if not file_match: continue filename = file_match.groups()[0] if chunk_handler_func_filename is None or "components/demo" not in filename: chunk_handler_func_filename = filename spec_prefix = ( "" if chunk_handler_func_filename is None else "'{}':".format(chunk_handler_func_filename) ) # We always delete any pre-existing breakpoints on the function. This way # a re-execution of the command can always be used to re-init the setup # # NB: Wrapped in a try catch because older versions of gdb.breakpoints() eventually return None # instead of raising StopIteration try: for breakpoint in gdb.breakpoints(): # noqa: A001 if chunk_handler_func in breakpoint.location: print("Deleting breakpoint for '{}'".format(chunk_handler_func)) breakpoint.delete() except TypeError: pass print("Installing Memfault GDB Chunk Handler for function '{}'".format(chunk_handler_func)) GdbMemfaultPostChunkBreakpoint( spec=spec_prefix + chunk_handler_func, project_key=parsed_args.project_key, chunks_uri=parsed_args.chunks_uri, verbose=parsed_args.verbose, device_serial=parsed_args.device_serial, internal=True, ) def parse_args(self, unicode_args): parser = MemfaultGdbArgumentParser(description=MemfaultPostChunk.__doc__) parser.add_argument( "--project-key", "-pk", help="Memfault Project Key", required=True, default=None, ) parser.add_argument( "--chunk-handler-func", "-ch", help=( "Name of function that handles sending Memfault Chunks." "(default: search for one of {})".format(self.USER_TRANSPORT_SEND_CHUNK_HANDLER) ), default=self.USER_TRANSPORT_SEND_CHUNK_HANDLER, ) parser.add_argument( "--chunks-uri", help="Default chunks base URI to use (default: {})".format( MEMFAULT_DEFAULT_CHUNKS_BASE_URI ), default=MEMFAULT_DEFAULT_CHUNKS_BASE_URI, ) parser.add_argument( "--device-serial", "-ds", help="Device Serial to post chunks as (default: {})".format( self.DEFAULT_CHUNK_DEVICE_SERIAL ), default=self.DEFAULT_CHUNK_DEVICE_SERIAL, ) parser.add_argument( "--verbose", help="Prints a summary every time a chunk is posted instead of just on failures", action="store_true", ) args = list(filter(None, unicode_args.split(" "))) return parser.parse_args(args) class MemfaultCoredump(MemfaultGdbCommand): """Captures a coredump from the target and uploads it to Memfault for analysis""" ALPHANUM_SLUG_DOTS_COLON_REGEX = r"^[-a-zA-Z0-9_\.\+:]+$" ALPHANUM_SLUG_DOTS_COLON_SPACES_PARENS_SLASH_COMMA_REGEX = r"^[-a-zA-Z0-9_\.\+: \(\)\[\]/,]+$" DEFAULT_CORE_DUMP_HARDWARE_REVISION = "DEVBOARD" DEFAULT_CORE_DUMP_SERIAL_NUMBER = "DEMOSERIALNUMBER" DEFAULT_CORE_DUMP_SOFTWARE_TYPE = "main" DEFAULT_CORE_DUMP_SOFTWARE_VERSION = "1.0.0" GDB_CMD = "memfault coredump" def _check_permission(self, analytics_props): settings = settings_load() key = "coredump.allow" allowed = settings.get(key, False) if allowed: analytics_props["permission"] = "accepted-stored" return True y = MEMFAULT_CONFIG.prompt(""" You are about to capture a coredump from the attached target. This means that memory contents of the target will be captured and sent to Memfault's web server for analysis. The currently loaded binary file (.elf) will be sent as well, because it contains debugging information (symbols) that are needed for Memfault's analysis to work correctly. Memfault will never share your data, coredumps, binary files (.elf) or other proprietary information with other companies or anyone else. Proceed? [y/n] """) # This last newline is important! If it's not here, the last line is not shown on Windows! if "Y" not in y.upper(): print("Aborting...") analytics_props["user_input"] = y analytics_props["permission"] = "rejected" return False analytics_props["permission"] = "accepted" settings[key] = True settings_save(settings) return True def _invoke(self, unicode_args, from_tty, analytics_props, config): if not self._check_permission(analytics_props): return try: parsed_args = self.parse_args(unicode_args, config) except MemfaultGdbArgumentParseError: return can_make_project_api_request = config.can_make_project_api_request() analytics_props["has_project_key"] = bool(parsed_args.project_key) analytics_props["can_make_project_api_request"] = bool(can_make_project_api_request) analytics_props["regions"] = len(parsed_args.region) if parsed_args.region else 0 analytics_props["no_symbols"] = bool(parsed_args.no_symbols) analytics_props["api_uri"] = config.api_uri analytics_props["ingress_uri"] = config.ingress_uri if not can_make_project_api_request and not parsed_args.project_key: print( "Cannot post coredump. Please specify either --project-key or" " use `memfault login` and specify the project and organization.\n" "See `memfault login --help` for more information" ) ANALYTICS.error("Missing login or key") return cd_writer, elf_fn = self.build_coredump_writer(parsed_args, analytics_props) if not cd_writer: return elf_hash = get_file_hash(elf_fn) # TODO: try calling memfault_platform_get_device_info() and use returned info if present # Populate the version based on hash of the .elf for now: software_version = cd_writer.software_version + "-md5+{}".format(elf_hash[:8]) cd_writer.software_version = software_version if not parsed_args.no_symbols: if can_make_project_api_request: upload_symbols_if_needed(config, elf_fn, cd_writer.software_type, software_version) else: print("Skipping symbols upload because not logged in. Hint: use `memfault login`") if parsed_args.project_key: project_key = parsed_args.project_key else: assert can_make_project_api_request project_key, status_and_reason = http_get_project_key(config) if not project_key: error = "Failed to get Project Key: {}".format(status_and_reason) analytics_props["error"] = error ANALYTICS.error(error) print(error) return with TemporaryFile() as f_out: cd_writer.write(f_out) f_out.seek(0) status, reason = http_post_coredump(f_out, project_key, parsed_args.ingress_uri) analytics_props["http_status"] = status if status == 409: print("Coredump already exists!") elif status >= 200 and status < 300: print("Coredump uploaded successfully!") print("Once it has been processed, it will appear here:") # TODO: Print direct link to trace # MFLT-461 print(_infer_issues_html_url(parsed_args.ingress_uri, config)) else: print("Error occurred... HTTP Status {} {}".format(status, reason)) def parse_args(self, unicode_args, config): parser = MemfaultGdbArgumentParser(description=MemfaultCoredump.__doc__) parser.add_argument( "source", help=( "Source of coredump: 'live' (default) or 'storage' (target's live RAM or stored" " coredump)" ), default="live", choices=["live", "storage"], nargs="?", ) parser.add_argument("--project-key", "-pk", help="Project Key", default=None) parser.add_argument( "--no-symbols", "-ns", help="Do not upload symbol (.elf) automatically if missing", default=False, action="store_true", ) def _auto_int(x): try: return int(x, 0) except ValueError: return int(gdb.parse_and_eval(x)) parser.add_argument( "--region", "-r", help=( "Specify memory region and size in bytes to capture. This option can be passed" " multiple times. Example: `-r 0x20000000 1024 0x80000000 512` will capture 1024" " bytes starting at 0x20000000. When no regions are passed, the command will" " attempt to infer what to capture based on the sections in the loaded .elf." ), type=_auto_int, nargs=2, action="append", ) def _character_check(regex, name): def _check_inner(input): pattern = re.compile(regex) if not pattern.match(input): raise argparse.ArgumentTypeError( "Invalid characters in {}: {}.".format(name, input) ) return input return _check_inner parser.add_argument( "--device-serial", type=_character_check(self.ALPHANUM_SLUG_DOTS_COLON_REGEX, "device serial"), help=( "Overrides the device serial that will be reported in the core dump. (default: {})".format( self.DEFAULT_CORE_DUMP_SERIAL_NUMBER ) ), default=self.DEFAULT_CORE_DUMP_SERIAL_NUMBER, ) parser.add_argument( "--software-type", type=_character_check(self.ALPHANUM_SLUG_DOTS_COLON_REGEX, "software type"), help=( "Overrides the software type that will be reported in the core dump. (default: {})".format( self.DEFAULT_CORE_DUMP_SOFTWARE_TYPE ) ), default=self.DEFAULT_CORE_DUMP_SOFTWARE_TYPE, ) parser.add_argument( "--software-version", type=_character_check( self.ALPHANUM_SLUG_DOTS_COLON_SPACES_PARENS_SLASH_COMMA_REGEX, "software version", ), help=( "Overrides the software version that will be reported in the core dump." " (default: {})".format(self.DEFAULT_CORE_DUMP_SOFTWARE_VERSION) ), default=self.DEFAULT_CORE_DUMP_SOFTWARE_VERSION, ) parser.add_argument( "--hardware-revision", type=_character_check(self.ALPHANUM_SLUG_DOTS_COLON_REGEX, "hardware revision"), help=( "Overrides the hardware revision that will be reported in the core dump." " (default: {})".format(self.DEFAULT_CORE_DUMP_HARDWARE_REVISION) ), default=self.DEFAULT_CORE_DUMP_HARDWARE_REVISION, ) return populate_config_args_and_parse_args(parser, unicode_args, config) @staticmethod def _get_arch(current_arch, analytics_props): if "arm" in current_arch: return ArmCortexMCoredumpArch() if "xtensa" in current_arch: target = gdb.execute("monitor target current", to_string=True) if "esp32" in target: return XtensaCoredumpArch() analytics_props["xtensa_target"] = target return None def build_coredump_writer(self, parsed_args, analytics_props): # inferior.architecture() is a relatively new API, so let's use "show arch" instead: show_arch_output = gdb.execute("show arch", to_string=True).lower() current_arch_matches = re.search(r"currently ([^)]+)", show_arch_output) if current_arch_matches: # Using groups() here instead of fn_match[1] for python2.x compatibility current_arch = current_arch_matches.groups()[0] # Should tell us about different arm flavors: analytics_props["arch"] = current_arch arch = self._get_arch(current_arch, analytics_props) if arch is None: print("This command is currently only supported for ARM and XTENSA targets!") analytics_props["error"] = "Unsupported architecture" return None, None else: error = "show arch has unexpected output" analytics_props["error"] = error ANALYTICS.error(error, info=show_arch_output) return None, None inferior = gdb.selected_inferior() # Make sure thread info has been requested before doing anything else: gdb.execute("info threads", to_string=True) threads = inferior.threads() if not inferior: print("No target!? Make sure to attach to a target first.") analytics_props["error"] = "No target" return None, None if len(threads) == 0: print("No target!? Make sure to attach to a target first.") analytics_props["error"] = "No threads" return None, None print("One moment please, capturing memory...") # [MT]: How to figure out which thread/context the CPU is actually in? # JLink nor OpenOCD do not seem to be setting thread.is_running() correctly. # print("Note: for correct results, do not switch threads!") try: thread = gdb.selected_thread() except SystemError as e: # Exception can be raised if selected thread has disappeared in the mean time # SystemError: could not find gdb thread object print(e) thread = None if not thread: print("Did not find any threads!?") analytics_props["error"] = "Failed to activate thread" return None, None info_sections_output = gdb.execute("maintenance info sections", to_string=True) elf_fn, sections = parse_maintenance_info_sections(info_sections_output) if elf_fn is None or sections is None: print("""Could not find file and sections. This command requires that you use the 'load' command to load a binary/symbol (.elf) file first""") error = "Failed to parse sections" analytics_props["error"] = error ANALYTICS.error(error, info=info_sections_output) return None, None has_debug_info = any(map(is_debug_info_section, sections)) analytics_props["has_debug_info"] = has_debug_info if not has_debug_info: print("WARNING: no debug info sections found!") print( "Hints: did you compile with -g or similar flags? did you inadvertently strip the" " binary?" ) cd_writer = MemfaultCoredumpWriter( arch, parsed_args.device_serial, parsed_args.software_type, parsed_args.software_version, parsed_args.hardware_revision, ) cd_writer.regs = arch.get_current_registers(thread, analytics_props) arch.add_platform_specific_sections(cd_writer, inferior, analytics_props) regions = parsed_args.region if regions is None: print( "No capturing regions were specified; will default to capturing the first 1MB for" " each used RAM address range." ) print( "Tip: optionally, you can use `--region ` to manually specify" " capturing regions and increase capturing speed." ) regions = arch.guess_ram_regions(sections) for addr, size in regions: print("Capturing RAM @ 0x{:x} ({} bytes)...".format(addr, size)) data = read_memory_until_error(inferior, addr, size) section = Section(addr, len(data), "RAM") section.data = data cd_writer.add_section(section) print("Captured RAM @ 0x{:x} ({} bytes)".format(section.addr, section.size)) return cd_writer, elf_fn class MemfaultLogin(MemfaultGdbCommand): """Authenticate the current GDB session with Memfault""" GDB_CMD = "memfault login" def _invoke(self, unicode_args, from_tty, analytics_props, config): try: parsed_args = self.parse_args(unicode_args) except MemfaultGdbArgumentParseError: return api_uri = parsed_args.api_uri ingress_uri = parsed_args.ingress_uri status, reason, json_body = http_get_auth_me( api_uri, parsed_args.email, parsed_args.password ) analytics_props["http_status"] = status analytics_props["api_uri"] = api_uri analytics_props["ingress_uri"] = ingress_uri if status >= 200 and status < 300: config.api_uri = MEMFAULT_CONFIG.api_uri = api_uri config.email = MEMFAULT_CONFIG.email = parsed_args.email config.ingress_uri = MEMFAULT_CONFIG.ingress_uri = ingress_uri config.organization = MEMFAULT_CONFIG.organization = parsed_args.organization config.password = MEMFAULT_CONFIG.password = parsed_args.password config.project = MEMFAULT_CONFIG.project = parsed_args.project config.user_id = MEMFAULT_CONFIG.user_id = json_body["id"] print("Authenticated successfully!") elif status == 404: print("Login failed. Make sure you have entered the email and password/key correctly.") else: print("Error occurred... HTTP Status {} {}".format(status, reason)) def parse_args(self, unicode_args): parser = MemfaultGdbArgumentParser(description=MemfaultLogin.__doc__) parser.add_argument( "email", help="The username (email address) of the user to authenticate" ) parser.add_argument( "password", help="The user API key or password of the user to authenticate" ) parser.add_argument( "--organization", "-o", help="Default organization (slug) to use", default=None, ) parser.add_argument("--project", "-p", help="Default project (slug) to use", default=None) parser.add_argument( "--api-uri", help="Default API base URI to use (default: {})".format(MEMFAULT_DEFAULT_API_BASE_URI), default=MEMFAULT_DEFAULT_API_BASE_URI, ) parser.add_argument( "--ingress-uri", help="Default ingress base URI to use (default: {})".format( MEMFAULT_DEFAULT_INGRESS_BASE_URI ), default=MEMFAULT_DEFAULT_INGRESS_BASE_URI, ) args = list(filter(None, unicode_args.split(" "))) return parser.parse_args(args) Memfault() MemfaultCoredump() MemfaultLogin() MemfaultPostChunk() class AnalyticsTracker(Thread): def __init__(self): super(AnalyticsTracker, self).__init__() self._queue = Queue() def track(self, event_name, event_properties=None, user_id=None): # Put in queue to offload to background thread, to avoid slowing down the GDB commands self._queue.put((event_name, event_properties, user_id)) def log(self, level, type, **kwargs): # noqa: A002 props = dict(**kwargs) props["type"] = type props["level"] = level self.track("Log", props) def error(self, type, info=None): # noqa: A002 self.log("error", type, info=info) def _is_analytics_disabled(self): settings = settings_load() key = "analytics.disabled" return settings.get(key, False) def _is_unittest(self): # Pretty gross, but because importing this script has side effects (hitting the analytics endpoint), # mocking gets pretty tricky. Use this check to disable analytics for unit and integration tests return ( hasattr(gdb, "MEMFAULT_MOCK_IMPLEMENTATION") or os.environ.get("MEMFAULT_DISABLE_ANALYTICS", None) is not None ) def run(self): # uuid.getnode() is based on the mac address, so should be stable across multiple sessions on the same machine: anonymous_id = md5(uuid.UUID(int=uuid.getnode()).bytes).hexdigest() while True: try: event_name, event_properties, user_id = self._queue.get() if self._is_analytics_disabled() or self._is_unittest(): continue if event_properties is None: event_properties = {} conn = HTTPSConnection("api.segment.io") body = { "anonymousId": anonymous_id, "event": event_name, "properties": event_properties, } if user_id is not None: body["userId"] = "user{}".format(user_id) headers = { "Content-Type": "application/json", "Authorization": "Basic RFl3Q0E1TENRTW5Uek95ajk5MTJKRjFORTkzMU5yb0o6", } conn.request("POST", "/v1/track", body=dumps(body), headers=headers) response = conn.getresponse() response.read() # Throttle a bit sleep(0.2) except Exception: # noqa: S110 pass # Never fail due to analytics requests erroring out ANALYTICS = AnalyticsTracker() ANALYTICS.daemon = True ANALYTICS.start() def _track_script_sourced(): system, _, release, version, machine, processor = platform.uname() try: from platform import mac_ver mac_version = mac_ver()[0] except Exception: mac_version = "" try: gdb_version = gdb.execute("show version", to_string=True).strip().split("\n")[0] except Exception: gdb_version = "" ANALYTICS.track( "Script sourced", # TODO: MFLT-497 -- properly version this { "version": "1", "python": platform.python_version(), "uname_system": system, "uname_release": release, "uname_version": version, "uname_machine": machine, "uname_processor": processor, "mac_version": mac_version, "gdb_version": gdb_version, }, ) _track_script_sourced() print("Memfault Commands Initialized successfully.") ================================================ FILE: scripts/memfault_group.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # """ smpmgr Memfault group (128) commands. Example usage (note: plugin-path must be a directory containing this file): $ uvx smpmgr --ble xx:xx:xx:xx:xx:xx --plugin-path=. memfault device-info ⠇ Connecting to xx:xx:xx:xx:xx:xx... OK DeviceInfoReadResponse( header=Header(op=, version=, flags=, length=108, group_id=128, sequence=1, command_id=0), version=, sequence=1, smp_data=b'\t\x00\x00l\x00\x80\x01\x00\xbfmdevice_serialp65071B2E77FB7451phardware_versionjnrf54l15dkmsoftware_typecappocurrent_versionl0.0.1+d07bd2\xff', device_serial='abcdef', hardware_version='nrf54l15dk', software_type='app', current_version='0.0.1+d07bd2' ) $ uvx smpmgr --ble xx:xx:xx:xx:xx:xx --plugin-path=. memfault project-key ⠋ Connecting to xx:xx:xx:xx:xx:xx... OK ProjectKeyReadResponse( header=Header(op=, version=, flags=, length=24, group_id=128, sequence=1, command_id=1), version=, sequence=1, smp_data=b'\t\x00\x00\x18\x00\x80\x01\x01\xbfkproject_keyidummy-key\xff', project_key='dummy-key' ) """ import asyncio import logging from enum import IntEnum, unique from typing import cast import rich import smp.error as smperr import smp.message as smpmsg import typer from smpmgr.common import Options, connect_with_spinner, get_smpclient app = typer.Typer(name="memfault", help="Memfault MCUmgr group 128") logger = logging.getLogger(__name__) @unique class MEMFAULT_RET_RC(IntEnum): # noqa: N801 OK = 0 UNKNOWN = 1 NO_PROJECT_KEY = 2 class MemfaultErrorV1(smperr.ErrorV1): _GROUP_ID = 128 class MemfaultErrorV2(smperr.ErrorV2[MEMFAULT_RET_RC]): _GROUP_ID = 128 class DeviceInfoReadRequest(smpmsg.ReadRequest): _GROUP_ID = 128 _COMMAND_ID = 0 class DeviceInfoReadResponse(smpmsg.ReadResponse): _GROUP_ID = 128 _COMMAND_ID = 0 device_serial: str hardware_version: str software_type: str current_version: str class DeviceInfoRead(DeviceInfoReadRequest): _Response = DeviceInfoReadResponse _ErrorV1 = MemfaultErrorV1 _ErrorV2 = MemfaultErrorV2 class ProjectKeyReadRequest(smpmsg.ReadRequest): _GROUP_ID = 128 _COMMAND_ID = 1 class ProjectKeyReadResponse(smpmsg.ReadResponse): _GROUP_ID = 128 _COMMAND_ID = 1 project_key: str class ProjectKeyRead(ProjectKeyReadRequest): _Response = ProjectKeyReadResponse _ErrorV1 = MemfaultErrorV1 _ErrorV2 = MemfaultErrorV2 @app.command() def device_info(ctx: typer.Context) -> None: """Read Memfault device information (serial, hardware version, software type, current version).""" options = cast("Options", ctx.obj) smpclient = get_smpclient(options) async def f() -> None: await connect_with_spinner(smpclient, options.timeout) r = await smpclient.request(DeviceInfoRead()) rich.print(r) asyncio.run(f()) @app.command() def project_key(ctx: typer.Context) -> None: """Read Memfault project key.""" options = cast("Options", ctx.obj) smpclient = get_smpclient(options) async def f() -> None: await connect_with_spinner(smpclient, options.timeout) r = await smpclient.request(ProjectKeyRead()) rich.print(r) asyncio.run(f()) ================================================ FILE: scripts/mflt-build-id/.gitignore ================================================ /build/ ================================================ FILE: scripts/mflt-build-id/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: scripts/mflt-build-id/README.md ================================================ # Memfault Build ID Tool This package contains the `mflt_build_id` CLI tool. The purpose of the tool is simplify reading or writing [Build IDs](https://interrupt.memfault.com/blog/gnu-build-id-for-firmware) in a firmware image irrespective of the compiler or build system being used in a project. ## Example Usage ``` $ mflt_build_id --help usage: mflt_build_id [-h] [--dump [DUMP]] [--crc CRC] [--sha1 SHA1] elf Inspects provided ELF for a Build ID and when missing adds one if possible. If a pre-existing Build ID is found (either a GNU Build ID or a Memfault Build ID), no action is taken. If no Build ID is found, this script will generate a unique ID by computing a SHA1 over the contents that will be in the final binary. Once computed, the build ID will be "patched" into a read-only struct defined in memfault-firmware-sdk/components/core/src/memfault_build_id.c to hold the info. If the --crc argument is used, instead of populating the Memfault Build ID structure, the symbol specified will be updated with a CRC32 computed over the contents that will be in the final binary. If the --sha1 argument is used, instead of populating the Memfault Build ID structure, the symbol specified will be updated directly with Memfault SHA1 using the same strategy discussed above. The only expectation in this mode is that a global symbol has been defined as follow: const uint8_t g_your_symbol_build_id[20] = { 0x1, }; For further reading about Build Ids in general see: https://mflt.io//symbol-file-build-ids positional arguments: elf options: -h, --help show this help message and exit --dump [DUMP] --crc CRC --sha1 SHA1 ``` ## Changes ### [1.1.1] - 2025-04-18 - Fix `mflt_build_id` script to be installed correctly again (regression in 1.1.0) - Add an alternate name `mflt-build-id` for the `mflt_build_id` script ### [1.1.0] - 2024-11-07 - Remove support for Python 3.6 + 3.7 ================================================ FILE: scripts/mflt-build-id/pyproject.toml ================================================ [project] name = "mflt-build-id" version = "1.1.1" description = "Memfault CLI tool" readme = "README.md" license-files = ["LICENSE"] authors = [ { name = "Memfault", email = "hello@memfault.com" } ] dependencies = [ "pyelftools >=0.31", ] requires-python = ">=3.8,<4.0" [project.urls] homepage = "https://docs.memfault.com" [project.scripts] mflt_build_id = 'mflt_build_id:main' mflt-build-id = 'mflt_build_id:main' [dependency-groups] dev = [ "pytest >=6", "pytest-cov >=7.0.0", "pytest-timeout >=2.0,<3.0", ] [tool.uv] package = true ================================================ FILE: scripts/mflt-build-id/setup.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # #!/usr/bin/env python try: from setuptools import setup except ImportError: from distutils.core import setup # This is a stub file generated so that `pip` can install this package in "editable mode". setup(name="mflt-build-id", packages=["mflt_build_id"], version="0.0.5", package_dir={"": "src"}) ================================================ FILE: scripts/mflt-build-id/src/mflt_build_id/__init__.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # # 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. If a pre-existing Build ID is found (either a GNU Build ID or a Memfault Build ID), no action is taken. If no Build ID is found, this script will generate a unique ID by computing a SHA1 over the contents that will be in the final binary. Once computed, the build ID will be "patched" into a read-only struct defined in memfault-firmware-sdk/components/core/src/memfault_build_id.c to hold the info. If the --crc argument is used, instead of populating the Memfault Build ID structure, the symbol specified will be updated with a CRC32 computed over the contents that will be in the final binary. If the --sha1 argument is used, instead of populating the Memfault Build ID structure, the symbol specified will be updated directly with Memfault SHA1 using the same strategy discussed above. The only expectation in this mode is that a global symbol has been defined as follow: const uint8_t g_your_symbol_build_id[20] = { 0x1, }; For further reading about Build Ids in general see: https://mflt.io//symbol-file-build-ids """ from __future__ import annotations import argparse import hashlib import importlib.metadata import struct import zlib from collections import defaultdict from enum import Enum from typing import IO try: from elftools.elf.constants import SH_FLAGS from elftools.elf.elffile import ELFFile from elftools.elf.sections import NoteSection, Section, Symbol, SymbolTableSection except ImportError: raise ImportError( """ Script depends on pyelftools. Add it to your requirements.txt or run: $ pip install pyelftools """ ) def hexlify(data: bytes) -> str: return data.hex() SHA1_BUILD_ID_SIZE_BYTES = 20 class SectionType(Enum): TEXT = 1 DATA = 2 BSS = 3 UNALLOCATED = 5 class ELFFileHelper: def __init__(self, elf: ELFFile) -> None: """ :param elf: An elftools.elf.elffile.ELFFile instance """ self.elf = elf self._symtab: SymbolTableSection | None = None @staticmethod 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: SymbolTableSection, little_endian: bool ) -> dict[str | None, list[int]]: # An optimized imlementation for building a cache for quick symbol info lookups # # Replacing implementation here: # https://github.com/eliben/pyelftools/blob/49ffaf4/elftools/elf/sections.py#L198-L210 # # Two main performance optimizations # 1) The "struct_parse" utility pyelftools relies on for decoding structures # is extremely slow. We will use Python's struct.unpack instead here # 2) pyelftools passes around a file stream object while doing deserialization # which means there are a ton of disk seeks that get kicked off # # Empirically, seeing about 10x performance improvement symtab_data = symtab.data() symtab_entry_size = symtab["sh_entsize"] symbol_name_map = defaultdict(list) stringtable_data = symtab.stringtable.data() def _get_string(start_offset: int) -> str | None: end_offset = stringtable_data.find(b"\x00", start_offset) if end_offset == -1: return None s = stringtable_data[start_offset:end_offset] return s.decode("utf-8", errors="replace") for idx in range(symtab.num_symbols()): entry_offset = idx * symtab_entry_size # The first word of a "Symbol Table Entry" is "st_name" # For more details, see the "Executable and Linking Format" specification symtab_entry_data = symtab_data[entry_offset : entry_offset + 4] endianess_prefix = "<" if little_endian else ">" 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) -> SymbolTableSection | None: # Cache the SymbolTableSection, to avoid re-parsing if self._symtab: return self._symtab self._symtab = self.elf.get_section_by_name(".symtab") # Pyelftools maintains a symbol_name to index cache (_symbol_name_map) which is extremely # slow to build when there are many symbols present in an ELF so we build the cache here # using an optimized implementation if self._symtab: self._symtab._symbol_name_map = self.build_symbol_by_name_cache( self._symtab, little_endian=self.elf.little_endian ) return self._symtab def find_symbol_and_section(self, symbol_name: str) -> tuple[Symbol | None, Section | None]: symtab = self.symtab if symtab is None: return None, None symbol = symtab.get_symbol_by_name(symbol_name) if not symbol: return None, None symbol = symbol[0] symbol_start = symbol["st_value"] symbol_size = symbol["st_size"] section = self.find_section_for_address_range((symbol_start, symbol_start + symbol_size)) if section is None: raise BuildIdError(f"Could not locate a section with symbol {symbol_name}") return symbol, section def find_section_for_address_range(self, addr_range: tuple[int, int]) -> Section | None: for section in self.elf.iter_sections(): if not ELFFileHelper.section_in_binary(section): continue sec_start = section["sh_addr"] sh_size = section["sh_size"] sec_end = sec_start + sh_size addr_start, addr_end = addr_range range_in_section = (sec_start <= addr_start < sec_end) and ( sec_start <= addr_end <= sec_end ) if not range_in_section: continue return section return None @staticmethod def get_symbol_offset_in_sector(symbol: Symbol, section: Section) -> int: return symbol["st_value"] - section["sh_addr"] 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: Section) -> SectionType: if not self.section_in_binary(section): return SectionType.UNALLOCATED sh_flags = section["sh_flags"] is_text = sh_flags & SH_FLAGS.SHF_EXECINSTR != 0 or sh_flags & SH_FLAGS.SHF_WRITE == 0 if is_text: return SectionType.TEXT if section["sh_type"] != "SHT_NOBITS": return SectionType.DATA return SectionType.BSS class MemfaultBuildIdTypes(Enum): # "eMemfaultBuildIdType" from "core/src/memfault_build_id_private.h" NONE = 1 GNU_BUILD_ID_SHA1 = 2 MEMFAULT_BUILD_ID_SHA1 = 3 class BuildIdError(Exception): pass class BuildIdInspectorAndPatcher: 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 :param elf_helper: optional, already instantiated ELFFileHelper """ self.elf_file = elf_file 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: 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): continue sec_start = section["sh_addr"] build_id.update(struct.pack(" str | None: def _get_note_sections(elf): for section in elf.iter_sections(): if not isinstance(section, NoteSection): continue yield from section.iter_notes() for note in _get_note_sections(self.elf): if note.n_type == "NT_GNU_BUILD_ID": return note.n_desc return None 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(f"Could not locate '{sha1_sym_name}' symbol in provided ELF") if symbol["st_size"] != 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)) symbol_offset = self._helper.get_symbol_offset_in_sector(symbol, section) sha1_hash = self._generate_build_id( sha1_symbol_section=section, sha1_symbol_section_offset=symbol_offset ) sha1_hash_bytes = sha1_hash.digest() if current_sha1_bytes == sha1_hash_bytes: print(f"Memfault Generated SHA1 Build ID at '{sha1_sym_name}': {sha1_hash.hexdigest()}") return sha1_hash if dump_only: 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: derived_id_patch_offset = section[ "sh_offset" ] + self._helper.get_symbol_offset_in_sector(symbol, section) fh.seek(derived_id_patch_offset) fh.write(sha1_hash_bytes) 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: 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(f"Could not locate '{sdk_build_id_sym_name}' symbol in provided ELF") gnu_build_id = self._get_build_id() # Maps to sMemfaultBuildIdStorage from "core/src/memfault_build_id_private.h" data = bytearray(self._helper.get_symbol_data(symbol, section)) # FW SDK's <= 0.20.1 did not encode the configured short length in the # "sMemfaultBuildIdStorage". In this situation the byte was a zero-initialized padding # byte. In this scenario we report "None" to signify we do not know the short len short_len = data[2] or None build_id_type = data[0] if build_id_type == MemfaultBuildIdTypes.GNU_BUILD_ID_SHA1.value: if gnu_build_id is None: raise BuildIdError( "Couldn't locate GNU Build ID but 'MEMFAULT_USE_GNU_BUILD_ID' is in use" ) return MemfaultBuildIdTypes.GNU_BUILD_ID_SHA1, gnu_build_id, short_len 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(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) return MemfaultBuildIdTypes.MEMFAULT_BUILD_ID_SHA1, hexlify(build_id_bytes), short_len if gnu_build_id is not None: 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(f"Unrecognized Build Id Type '{build_id_type}'") if dump_only: return MemfaultBuildIdTypes.NONE, None, None build_id_hash = self._generate_build_id() with open(self.elf_file.name, "r+b") as fh: build_id_type_patch_offset = section[ "sh_offset" ] + self._helper.get_symbol_offset_in_sector(symbol, section) fh.seek(build_id_type_patch_offset) fh.write(struct.pack("B", MemfaultBuildIdTypes.MEMFAULT_BUILD_ID_SHA1.value)) derived_id_patch_offset = sdk_build_id_section[ "sh_offset" ] + self._helper.get_symbol_offset_in_sector(sdk_build_id, sdk_build_id_section) fh.seek(derived_id_patch_offset) fh.write(build_id_hash.digest()) build_id = build_id_hash.hexdigest() 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) -> None: build_type, build_id, _ = self._write_and_return_build_info(dump_only=False) if build_type == MemfaultBuildIdTypes.GNU_BUILD_ID_SHA1: print(f"Found GNU Build ID: {build_id}") elif build_type == MemfaultBuildIdTypes.MEMFAULT_BUILD_ID_SHA1: print(f"Found Memfault Build Id: {build_id}") 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") print(build_id[:num_chars]) def _generate_crc32_build_id( 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(): if not self._helper.section_in_binary(section): continue sec_start = section["sh_addr"] crc32 = zlib.crc32(struct.pack(" None: symbol, section = self._helper.find_symbol_and_section(crc_symbol_name) if symbol is None or section is None: 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(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) symbol_length = 4 crc_build_id = self._generate_crc32_build_id(section, symbol_offset, symbol_length) endianess_prefix = "<" if self.elf.little_endian else ">" current_crc_build_id = struct.unpack( f"{endianess_prefix}I", section.data()[symbol_offset : symbol_offset + symbol_length], )[0] if current_crc_build_id == crc_build_id: print( f"CRC32 Generated Build ID at '{crc_symbol_name}' to ELF already written: {hex(crc_build_id)}" ) return if dump_only: print(f"CRC32 Build ID at '{crc_symbol_name}' is not written") return 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 fh.seek(derived_id_patch_offset) fh.write(crc_build_id_bytes) print(f"Added CRC32 Generated Build ID at '{crc_symbol_name}' to ELF: {hex(crc_build_id)}") 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() -> None: parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument("elf", action="store") parser.add_argument("--dump", nargs="?", const=7, type=int) parser.add_argument("--crc", action="store") parser.add_argument("--sha1", action="store") # get version from importlib.metadata try: version = importlib.metadata.version("mflt-build-id") except importlib.metadata.PackageNotFoundError: # script is being used without installation, provide a fall back version version = "0.0.0" parser.add_argument("--version", action="version", version=f"%(prog)s {version}") args = parser.parse_args() if args.sha1 and args.crc: raise RuntimeError("Only one of 'crc' or 'sha1' can be specified") with open(args.elf, "rb") as elf_file: b = BuildIdInspectorAndPatcher(elf_file=elf_file) if args.sha1: b.check_or_update_sha1_build_id(args.sha1, dump_only=args.dump) elif args.crc: b.check_or_update_crc_build_id(args.sha1, dump_only=args.dump) elif args.dump is None: if args.crc is None: b.check_or_update_build_id() else: b.dump_build_info(args.dump) if __name__ == "__main__": main() ================================================ FILE: scripts/mflt-build-id/src/mflt_build_id/py.typed ================================================ ================================================ FILE: scripts/mflt-build-id/tasks/__init__.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # from mflt.testing.task_templates import make_test_task test = make_test_task() ================================================ FILE: scripts/mflt-build-id/tests_mflt_build_id/__init__.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # ================================================ FILE: scripts/mflt-build-id/tests_mflt_build_id/conftest.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # ================================================ FILE: scripts/mflt-build-id/tests_mflt_build_id/elf_fixtures/__init__.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # import os ELF_FIXTURES_DIR = os.path.dirname(os.path.realpath(__file__)) ================================================ FILE: scripts/mflt-build-id/tests_mflt_build_id/test_elf_file_helper.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # import os import sys import elftools.elf.sections from elftools.elf.elffile import ELFFile from .elf_fixtures import ELF_FIXTURES_DIR test_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(test_dir) sys.path.append(script_dir) from mflt_build_id import ELFFileHelper # noqa: E402 # isort:skip class TestELFFileHelper: def test_basic(self): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "gnu_id_present_and_used.elf") with open(elf_fixture_filename, "rb") as f: helper = ELFFileHelper(ELFFile(f)) symbol, section = helper.find_symbol_and_section("g_memfault_build_id") assert isinstance(symbol, elftools.elf.sections.Symbol) assert isinstance(section, elftools.elf.sections.Section) assert symbol.name == "g_memfault_build_id" data = helper.get_symbol_data(symbol, section) assert isinstance(data, bytes) ================================================ FILE: scripts/mflt-build-id/tests_mflt_build_id/test_fw_build_id.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # import filecmp import os import shutil import sys import pytest from .elf_fixtures import ELF_FIXTURES_DIR test_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(test_dir) sys.path.append(script_dir) from mflt_build_id import ( # noqa: E402 # isort:skip BuildIdInspectorAndPatcher, MemfaultBuildIdTypes, ) @pytest.fixture() def copy_file(tmp_path): """Copies a file into the tests tmp path""" idx = 0 def _copy_file(src): nonlocal idx tmp_name = str(tmp_path / f"file_{idx}.bin") idx += 1 shutil.copy2(src, tmp_name) return tmp_name return _copy_file def test_gnu_build_id_in_use(capsys, copy_file): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "gnu_id_present_and_used.elf") with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) b.check_or_update_build_id() assert filecmp.cmp(elf_copy_file.name, elf_fixture_filename) out, _ = capsys.readouterr() lines = out.splitlines() assert lines == ["Found GNU Build ID: 3d6f95306bbc5fda4183728b3829f88b30f7aa1c"] def test_gnu_build_id_present_but_not_used(capsys, copy_file): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "gnu_id_present_and_not_used.elf") result_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "memfault_id_used_gnu_id_present.elf") with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) b.check_or_update_build_id() assert filecmp.cmp(elf_copy_file.name, result_fixture_filename) out, _ = capsys.readouterr() lines = out.splitlines() assert lines == [ "WARNING: Located a GNU build id but it's not being used by the Memfault SDK", "Added Memfault Generated Build ID to ELF: 028aa9800f0524c9b064711925df5ec403df6b16", "Found Memfault Build Id: 028aa9800f0524c9b064711925df5ec403df6b16", ] def test_memfault_id_unpopulated(capsys, copy_file): elf_fixture_filename = os.path.join( ELF_FIXTURES_DIR, "memfault_build_id_present_and_unpopulated.elf" ) result_fixture_filename = os.path.join( ELF_FIXTURES_DIR, "memfault_build_id_present_and_populated.elf" ) with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) b.check_or_update_build_id() assert filecmp.cmp(elf_copy_file.name, result_fixture_filename) out, _ = capsys.readouterr() lines = out.splitlines() assert lines == [ "Added Memfault Generated Build ID to ELF: 16e0fe39af176cfa4cf961321ccf5193c2590451", "Found Memfault Build Id: 16e0fe39af176cfa4cf961321ccf5193c2590451", ] def test_memfault_sha1_unpopulated(capsys, copy_file): elf_fixture_filename = os.path.join( ELF_FIXTURES_DIR, "memfault_build_id_present_and_unpopulated.elf" ) with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: # attempt to dump build id before it has been written b = BuildIdInspectorAndPatcher(elf_copy_file) sha1 = b.check_or_update_sha1_build_id("g_memfault_sdk_derived_build_id", dump_only=True) assert sha1.hexdigest() == "16e0fe39af176cfa4cf961321ccf5193c2590451" # actually write the build id b = BuildIdInspectorAndPatcher(elf_copy_file) sha1 = b.check_or_update_sha1_build_id("g_memfault_sdk_derived_build_id", dump_only=False) assert sha1.hexdigest() == "16e0fe39af176cfa4cf961321ccf5193c2590451" # We've updated the file -- force a reload of anything that is cached elf_copy_file.flush() # confirm that patching the SHA1 build id is idempotent b = BuildIdInspectorAndPatcher(elf_copy_file) sha1 = b.check_or_update_sha1_build_id("g_memfault_sdk_derived_build_id", dump_only=False) assert sha1.hexdigest() == "16e0fe39af176cfa4cf961321ccf5193c2590451" # confirm once build id is written we dump the info correctly b = BuildIdInspectorAndPatcher(elf_copy_file) sha1 = b.check_or_update_sha1_build_id("g_memfault_sdk_derived_build_id", dump_only=True) assert sha1.hexdigest() == "16e0fe39af176cfa4cf961321ccf5193c2590451" out, _ = capsys.readouterr() lines = out.splitlines() assert lines == [ "Memfault Build ID at 'g_memfault_sdk_derived_build_id' is not written", "Added Memfault Generated SHA1 Build ID to ELF: 16e0fe39af176cfa4cf961321ccf5193c2590451", "Memfault Generated SHA1 Build ID at 'g_memfault_sdk_derived_build_id': 16e0fe39af176cfa4cf961321ccf5193c2590451", "Memfault Generated SHA1 Build ID at 'g_memfault_sdk_derived_build_id': 16e0fe39af176cfa4cf961321ccf5193c2590451", ] def test_memfault_sha1_wrong_symbol(capsys, copy_file): elf_fixture_filename = os.path.join( ELF_FIXTURES_DIR, "memfault_build_id_present_and_unpopulated.elf" ) with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) # test symbol does not exist case with pytest.raises( Exception, match="Could not locate 'g_wrong_symbol_name' symbol in provided ELF" ): b.check_or_update_sha1_build_id("g_wrong_symbol_name", dump_only=False) # test symbol exists but it's the wrong size with pytest.raises(Exception, match="A build ID should be 20 bytes in size"): b.check_or_update_sha1_build_id("g_memfault_build_id", dump_only=False) def test_memfault_id_populated(capsys, copy_file): elf_fixture_filename = os.path.join( ELF_FIXTURES_DIR, "memfault_build_id_present_and_populated.elf" ) with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) b.check_or_update_build_id() assert filecmp.cmp(elf_copy_file.name, elf_fixture_filename) out, _ = capsys.readouterr() lines = out.splitlines() assert lines == ["Found Memfault Build Id: 16e0fe39af176cfa4cf961321ccf5193c2590451"] def test_no_memfault_sdk_present(): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "no_memfault_symbols.elf") with open(elf_fixture_filename, "rb") as elf_fixture_file: b = BuildIdInspectorAndPatcher(elf_fixture_file) with pytest.raises( Exception, match="Could not locate 'g_memfault_build_id' symbol in provided ELF" ): b.check_or_update_build_id() def test_no_build_id_on_dump(): elf_fixture_filename = os.path.join( ELF_FIXTURES_DIR, "memfault_build_id_present_and_unpopulated.elf" ) with open(elf_fixture_filename, "rb") as elf_fixture_file: b = BuildIdInspectorAndPatcher(elf_fixture_file) with pytest.raises(Exception, match="No Build ID Found"): b.dump_build_info(num_chars=1) def test_build_id_dump(capsys, copy_file): elf_fixture_filename = os.path.join( ELF_FIXTURES_DIR, "memfault_build_id_present_and_populated.elf" ) with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) b.dump_build_info(num_chars=1) b.dump_build_info(num_chars=2) b.dump_build_info(num_chars=30) assert filecmp.cmp(elf_copy_file.name, elf_fixture_filename) out, _ = capsys.readouterr() lines = out.splitlines() assert lines == ["1", "16", "16e0fe39af176cfa4cf961321ccf51"] @pytest.mark.parametrize( ("fixture", "expected_result"), [ ( "memfault_build_id_present_and_populated.elf", ( MemfaultBuildIdTypes.MEMFAULT_BUILD_ID_SHA1, "16e0fe39af176cfa4cf961321ccf5193c2590451", None, ), ), ( "no_memfault_symbols.elf", # BuildIdException is caught and None, None is returned: (None, None, None), ), ( "memfault_build_id_with_short_len.elf", ( MemfaultBuildIdTypes.MEMFAULT_BUILD_ID_SHA1, "ef960811c7e83525457dc76722bbbb38f362769c", 7, ), ), ( "gnu_id_with_short_len.elf", ( MemfaultBuildIdTypes.GNU_BUILD_ID_SHA1, "152121637a37ec8f8b7c3a29b8708b705e3350cb", 7, ), ), ( "gnu_id_present_and_not_used.elf", ( MemfaultBuildIdTypes.NONE, None, None, ), ), ( "no_symtab_no_text_no_data.elf", ( None, None, None, ), ), ], ) def test_get_build_info(fixture, expected_result, copy_file): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, fixture) with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) assert b.get_build_info() == expected_result assert filecmp.cmp(elf_copy_file.name, elf_fixture_filename) def test_crc_build_id_unpopulated(capsys, copy_file): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "crc32_build_id_unpopulated.elf") result_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "crc32_build_id_populated.elf") with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) b.check_or_update_crc_build_id("g_example_crc32_build_id") assert filecmp.cmp(elf_copy_file.name, result_fixture_filename) out, _ = capsys.readouterr() lines = out.splitlines() assert lines == [ "Added CRC32 Generated Build ID at 'g_example_crc32_build_id' to ELF: 0x8ac1e1ca" ] def test_crc_build_id_unpopulated_dump_only(capsys, copy_file): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "crc32_build_id_unpopulated.elf") with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) b.check_or_update_crc_build_id("g_example_crc32_build_id", dump_only=True) # in dump only mode, nothing should change even if there isn't a build id written assert filecmp.cmp(elf_copy_file.name, elf_fixture_filename) out, _ = capsys.readouterr() lines = out.splitlines() assert lines == ["CRC32 Build ID at 'g_example_crc32_build_id' is not written"] def test_crc_build_id_populated(capsys, copy_file): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "crc32_build_id_populated.elf") with open(copy_file(elf_fixture_filename), mode="rb") as elf_copy_file: b = BuildIdInspectorAndPatcher(elf_copy_file) b.check_or_update_crc_build_id("g_example_crc32_build_id") assert filecmp.cmp(elf_copy_file.name, elf_fixture_filename) out, _ = capsys.readouterr() lines = out.splitlines() assert lines == [ "CRC32 Generated Build ID at 'g_example_crc32_build_id' to ELF already written: 0x8ac1e1ca" ] def test_crc_build_id_in_bss(): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "crc32_build_id_unpopulated.elf") with open(elf_fixture_filename, "rb") as elf_fixture_file: b = BuildIdInspectorAndPatcher(elf_fixture_file) with pytest.raises( Exception, match=r"CRC symbol 'g_example_crc32_bss_build_id' in invalid Section 'SectionType.BSS'", ): b.check_or_update_crc_build_id("g_example_crc32_bss_build_id") def test_crc_build_id_symbol_dne(): elf_fixture_filename = os.path.join(ELF_FIXTURES_DIR, "crc32_build_id_unpopulated.elf") with open(elf_fixture_filename, "rb") as elf_fixture_file: b = BuildIdInspectorAndPatcher(elf_fixture_file) with pytest.raises( Exception, match="Could not locate 'g_nonexistent_bss_build_id' CRC symbol in provided ELF", ): b.check_or_update_crc_build_id("g_nonexistent_bss_build_id") ================================================ FILE: scripts/tests_embedded_scripts/__init__.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # ================================================ FILE: scripts/tests_embedded_scripts/gdb_fake.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # import dataclasses import sys from typing import Any, Optional def install_fake() -> None: sys.modules.setdefault("gdb", sys.modules[__name__]) MEMFAULT_MOCK_IMPLEMENTATION = True COMPLETE_NONE = 0 COMMAND_USER = 0 class Command: def __init__(self, *args, **kwargs): pass class Breakpoint: pass @dataclasses.dataclass class Type: sizeof: int @dataclasses.dataclass class Value: _value: Any type: Type def __int__(self) -> int: return int(self._value) def __str__(self) -> str: if self._value is None: return "" return str(self._value) class Frame: def read_register(self, name) -> Value: if name.lower() == "control": return Value(0, type=Type(sizeof=1)) return Value(None, type=Type(sizeof=4)) def select(self): pass def lookup_type(name): sizeof_by_name = { "unsigned char": 1, "unsigned short": 2, "unsigned int": 4, "unsigned long": 4, "unsigned long long": 8, } return Type(sizeof=sizeof_by_name[name]) def newest_frame() -> Optional[Frame]: return None class _Inferior: def threads(self): return [] def read_memory(self, addr, size): raise NotImplementedError def inferiors(): return [] def selected_inferior(): return None def selected_thread(): return None def execute(cmd: str, to_string=False) -> Any: raise NotImplementedError class GdbError(Exception): pass error = GdbError ================================================ FILE: scripts/tests_embedded_scripts/snapshots/__init__.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # ================================================ FILE: scripts/tests_embedded_scripts/snapshots/snap_test_eclipse_patch.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot snapshots = Snapshot() snapshots['test_eclipse_project_patcher 1'] = { '.cproject': ''' \t \t\t \t\t\t \t\t\t\t \t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t \t\t\t \t\t\t \t\t\t\t \t\t\t\t\t \t\t\t\t\t\t \t\t\t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t\t \t\t\t\t\t \t\t\t\t \t\t\t \t\t\t \t\t \t \t \t\t \t \t \t\t \t\t \t\t\t \t\t \t\t \t \t \t \t\t \t\t\t \t\t \t ''', '.project': ''' \tSTM32L152RE_NUCLEO \t \t \t \t \t\t \t\t\torg.eclipse.cdt.managedbuilder.core.genmakebuilder \t\t\tclean,full,incremental, \t\t\t \t\t\t \t\t \t\t \t\t\torg.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder \t\t\tfull,incremental, \t\t\t \t\t\t \t\t \t \t \t\torg.eclipse.cdt.core.cnature \t\torg.eclipse.cdt.managedbuilder.core.managedBuildNature \t\torg.eclipse.cdt.managedbuilder.core.ScannerConfigNature \t\tfr.ac6.mcu.ide.core.MCUProjectNature \t \t \t\t\tMiddlewares/FreeRTOS/tasks.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/tasks.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_rcc_ex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_rcc_ex.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_gpio.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_gpio.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_cortex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_cortex.c \t\t \t\t\tApplication/User/stm32l1xx_it.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/stm32l1xx_it.c \t\t \t\t\tMiddlewares/FreeRTOS/portable/heap_4.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c \t\t \t\t\tMiddlewares/FreeRTOS/CMSIS_RTOS/cmsis_os.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c \t\t \t\t\tDrivers/BSP/STM32L1xx_Nucleo/stm32l1xx_nucleo.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/BSP/STM32L1xx_Nucleo/stm32l1xx_nucleo.c \t\t \t\t\tApplication/User/main.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/main.c \t\t \t\t\tMiddlewares/FreeRTOS/queue.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/queue.c \t\t \t\t\tMiddlewares/FreeRTOS/list.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/list.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_pwr.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_pwr.c \t\t \t\t\tApplication/SW4STM32/startup_stm32l152xe.s \t\t\t1 \t\t\tPARENT-1-PROJECT_LOC/startup_stm32l152xe.s \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_rcc.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_rcc.c \t\t \t\t\tMiddlewares/FreeRTOS/portable/port.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_pwr_ex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_pwr_ex.c \t\t \t\t\tDoc/readme.txt \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/readme.txt \t\t \t\t\tMiddlewares/FreeRTOS/timers.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/timers.c \t\t \t\t\tDrivers/CMSIS/system_stm32l1xx.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/system_stm32l1xx.c \t\t \t\t \t \t\tmemfault_components \t\t2 \t\tvirtual:/virtual \t \t \t\tmemfault_components/arch_arm_cortex_m.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/arch_arm_cortex_m.c \t \t \t\tmemfault_components/memfault_batched_events.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_batched_events.c \t \t \t\tmemfault_components/memfault_build_id.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_build_id.c \t \t \t\tmemfault_components/memfault_compact_log_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_compact_log_serializer.c \t \t \t\tmemfault_components/memfault_core_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_core_utils.c \t \t \t\tmemfault_components/memfault_custom_data_recording.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_custom_data_recording.c \t \t \t\tmemfault_components/memfault_data_export.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_export.c \t \t \t\tmemfault_components/memfault_data_packetizer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_packetizer.c \t \t \t\tmemfault_components/memfault_data_source_rle.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_source_rle.c \t \t \t\tmemfault_components/memfault_event_storage.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_event_storage.c \t \t \t\tmemfault_components/memfault_heap_stats.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_heap_stats.c \t \t \t\tmemfault_components/memfault_log.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_log.c \t \t \t\tmemfault_components/memfault_log_data_source.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_log_data_source.c \t \t \t\tmemfault_components/memfault_ram_reboot_info_tracking.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_ram_reboot_info_tracking.c \t \t \t\tmemfault_components/memfault_reboot_tracking_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_reboot_tracking_serializer.c \t \t \t\tmemfault_components/memfault_sdk_assert.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_sdk_assert.c \t \t \t\tmemfault_components/memfault_self_test.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_self_test.c \t \t \t\tmemfault_components/memfault_self_test_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_self_test_utils.c \t \t \t\tmemfault_components/memfault_serializer_helper.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_serializer_helper.c \t \t \t\tmemfault_components/memfault_task_watchdog.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_task_watchdog.c \t \t \t\tmemfault_components/memfault_trace_event.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_trace_event.c \t \t \t\tmemfault_components/memfault_base64.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_base64.c \t \t \t\tmemfault_components/memfault_chunk_transport.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_chunk_transport.c \t \t \t\tmemfault_components/memfault_circular_buffer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_circular_buffer.c \t \t \t\tmemfault_components/memfault_crc16_ccitt.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_crc16_ccitt.c \t \t \t\tmemfault_components/memfault_minimal_cbor.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_minimal_cbor.c \t \t \t\tmemfault_components/memfault_rle.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_rle.c \t \t \t\tmemfault_components/memfault_varint.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_varint.c \t \t \t\tmemfault_components/memfault_metrics.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics.c \t \t \t\tmemfault_components/memfault_metrics_battery.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_battery.c \t \t \t\tmemfault_components/memfault_metrics_connectivity.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_connectivity.c \t \t \t\tmemfault_components/memfault_metrics_reliability.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_reliability.c \t \t \t\tmemfault_components/memfault_metrics_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_serializer.c \t \t \t\tmemfault_components/memfault_coredump.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump.c \t \t \t\tmemfault_components/memfault_coredump_regions_armv7.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_regions_armv7.c \t \t \t\tmemfault_components/memfault_coredump_sdk_regions.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_sdk_regions.c \t \t \t\tmemfault_components/memfault_coredump_storage_debug.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_storage_debug.c \t \t \t\tmemfault_components/memfault_coredump_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_utils.c \t \t \t\tmemfault_components/memfault_fault_handling_aarch64.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_aarch64.c \t \t \t\tmemfault_components/memfault_fault_handling_arm.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_arm.c \t \t \t\tmemfault_components/memfault_fault_handling_armv7_a_r.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_armv7_a_r.c \t \t \t\tmemfault_components/memfault_fault_handling_posix.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_posix.c \t \t \t\tmemfault_components/memfault_fault_handling_riscv.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_riscv.c \t \t \t\tmemfault_components/memfault_stdlib_assert.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_stdlib_assert.c \t \t \t\tmemfault_components/memfault_demo_cli_drain_chunks.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_drain_chunks.c \t \t \t\tmemfault_components/memfault_demo_cli_log.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_log.c \t \t \t\tmemfault_components/memfault_demo_cli_trace_event.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_trace_event.c \t \t \t\tmemfault_components/memfault_demo_core.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_core.c \t \t \t\tmemfault_components/memfault_demo_shell.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_shell.c \t \t \t\tmemfault_components/memfault_demo_shell_commands.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_shell_commands.c \t \t \t\tmemfault_components/memfault_demo_watchdog.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_watchdog.c \t \t \t\tmemfault_components/memfault_demo_cli_aux.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/panics/memfault_demo_cli_aux.c \t \t \t\tmemfault_components/memfault_demo_panics.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/panics/memfault_demo_panics.c \t \t \t\tmemfault_includes \t\t2 \t\tvirtual:/virtual \t \t \t\tmemfault_includes/components/include \t\t2 \t\tPARENT-3-PROJECT_LOC/components/include \t \t \t\tmemfault_includes/ports/include \t\t2 \t\tPARENT-3-PROJECT_LOC/ports/include \t \t ''' } snapshots['test_eclipse_project_patcher_nested_port 1'] = { '.cproject': ''' \t \t\t \t\t\t \t\t\t\t \t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t \t\t\t \t\t\t \t\t\t\t \t\t\t\t\t \t\t\t\t\t\t \t\t\t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t\t \t\t\t\t\t \t\t\t\t \t\t\t \t\t\t \t\t \t \t \t\t \t \t \t\t \t\t \t\t\t \t\t \t\t \t \t \t \t\t \t\t\t \t\t \t ''', '.project': ''' \tSTM32L152RE_NUCLEO \t \t \t \t \t\t \t\t\torg.eclipse.cdt.managedbuilder.core.genmakebuilder \t\t\tclean,full,incremental, \t\t\t \t\t\t \t\t \t\t \t\t\torg.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder \t\t\tfull,incremental, \t\t\t \t\t\t \t\t \t \t \t\torg.eclipse.cdt.core.cnature \t\torg.eclipse.cdt.managedbuilder.core.managedBuildNature \t\torg.eclipse.cdt.managedbuilder.core.ScannerConfigNature \t\tfr.ac6.mcu.ide.core.MCUProjectNature \t \t \t\t\tMiddlewares/FreeRTOS/tasks.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/tasks.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_rcc_ex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_rcc_ex.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_gpio.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_gpio.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_cortex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_cortex.c \t\t \t\t\tApplication/User/stm32l1xx_it.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/stm32l1xx_it.c \t\t \t\t\tMiddlewares/FreeRTOS/portable/heap_4.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c \t\t \t\t\tMiddlewares/FreeRTOS/CMSIS_RTOS/cmsis_os.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c \t\t \t\t\tDrivers/BSP/STM32L1xx_Nucleo/stm32l1xx_nucleo.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/BSP/STM32L1xx_Nucleo/stm32l1xx_nucleo.c \t\t \t\t\tApplication/User/main.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/main.c \t\t \t\t\tMiddlewares/FreeRTOS/queue.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/queue.c \t\t \t\t\tMiddlewares/FreeRTOS/list.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/list.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_pwr.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_pwr.c \t\t \t\t\tApplication/SW4STM32/startup_stm32l152xe.s \t\t\t1 \t\t\tPARENT-1-PROJECT_LOC/startup_stm32l152xe.s \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_rcc.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_rcc.c \t\t \t\t\tMiddlewares/FreeRTOS/portable/port.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_pwr_ex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_pwr_ex.c \t\t \t\t\tDoc/readme.txt \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/readme.txt \t\t \t\t\tMiddlewares/FreeRTOS/timers.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/timers.c \t\t \t\t\tDrivers/CMSIS/system_stm32l1xx.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/system_stm32l1xx.c \t\t \t\t \t \t\tmemfault_components \t\t2 \t\tvirtual:/virtual \t \t \t\tmemfault_components/arch_arm_cortex_m.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/arch_arm_cortex_m.c \t \t \t\tmemfault_components/memfault_batched_events.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_batched_events.c \t \t \t\tmemfault_components/memfault_build_id.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_build_id.c \t \t \t\tmemfault_components/memfault_compact_log_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_compact_log_serializer.c \t \t \t\tmemfault_components/memfault_core_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_core_utils.c \t \t \t\tmemfault_components/memfault_custom_data_recording.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_custom_data_recording.c \t \t \t\tmemfault_components/memfault_data_export.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_export.c \t \t \t\tmemfault_components/memfault_data_packetizer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_packetizer.c \t \t \t\tmemfault_components/memfault_data_source_rle.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_source_rle.c \t \t \t\tmemfault_components/memfault_event_storage.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_event_storage.c \t \t \t\tmemfault_components/memfault_heap_stats.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_heap_stats.c \t \t \t\tmemfault_components/memfault_log.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_log.c \t \t \t\tmemfault_components/memfault_log_data_source.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_log_data_source.c \t \t \t\tmemfault_components/memfault_ram_reboot_info_tracking.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_ram_reboot_info_tracking.c \t \t \t\tmemfault_components/memfault_reboot_tracking_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_reboot_tracking_serializer.c \t \t \t\tmemfault_components/memfault_sdk_assert.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_sdk_assert.c \t \t \t\tmemfault_components/memfault_self_test.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_self_test.c \t \t \t\tmemfault_components/memfault_self_test_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_self_test_utils.c \t \t \t\tmemfault_components/memfault_serializer_helper.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_serializer_helper.c \t \t \t\tmemfault_components/memfault_task_watchdog.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_task_watchdog.c \t \t \t\tmemfault_components/memfault_trace_event.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_trace_event.c \t \t \t\tmemfault_components/memfault_base64.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_base64.c \t \t \t\tmemfault_components/memfault_chunk_transport.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_chunk_transport.c \t \t \t\tmemfault_components/memfault_circular_buffer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_circular_buffer.c \t \t \t\tmemfault_components/memfault_crc16_ccitt.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_crc16_ccitt.c \t \t \t\tmemfault_components/memfault_minimal_cbor.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_minimal_cbor.c \t \t \t\tmemfault_components/memfault_rle.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_rle.c \t \t \t\tmemfault_components/memfault_varint.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_varint.c \t \t \t\tmemfault_components/memfault_metrics.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics.c \t \t \t\tmemfault_components/memfault_metrics_battery.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_battery.c \t \t \t\tmemfault_components/memfault_metrics_connectivity.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_connectivity.c \t \t \t\tmemfault_components/memfault_metrics_reliability.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_reliability.c \t \t \t\tmemfault_components/memfault_metrics_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_serializer.c \t \t \t\tmemfault_components/memfault_coredump.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump.c \t \t \t\tmemfault_components/memfault_coredump_regions_armv7.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_regions_armv7.c \t \t \t\tmemfault_components/memfault_coredump_sdk_regions.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_sdk_regions.c \t \t \t\tmemfault_components/memfault_coredump_storage_debug.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_storage_debug.c \t \t \t\tmemfault_components/memfault_coredump_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_utils.c \t \t \t\tmemfault_components/memfault_fault_handling_aarch64.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_aarch64.c \t \t \t\tmemfault_components/memfault_fault_handling_arm.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_arm.c \t \t \t\tmemfault_components/memfault_fault_handling_armv7_a_r.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_armv7_a_r.c \t \t \t\tmemfault_components/memfault_fault_handling_posix.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_posix.c \t \t \t\tmemfault_components/memfault_fault_handling_riscv.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_riscv.c \t \t \t\tmemfault_components/memfault_stdlib_assert.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_stdlib_assert.c \t \t \t\tmemfault_components/memfault_demo_cli_drain_chunks.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_drain_chunks.c \t \t \t\tmemfault_components/memfault_demo_cli_log.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_log.c \t \t \t\tmemfault_components/memfault_demo_cli_trace_event.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_trace_event.c \t \t \t\tmemfault_components/memfault_demo_core.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_core.c \t \t \t\tmemfault_components/memfault_demo_shell.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_shell.c \t \t \t\tmemfault_components/memfault_demo_shell_commands.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_shell_commands.c \t \t \t\tmemfault_components/memfault_demo_watchdog.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_watchdog.c \t \t \t\tmemfault_components/memfault_demo_cli_aux.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/panics/memfault_demo_cli_aux.c \t \t \t\tmemfault_components/memfault_demo_panics.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/panics/memfault_demo_panics.c \t \t \t\tmemfault_includes \t\t2 \t\tvirtual:/virtual \t \t \t\tmemfault_includes/components/include \t\t2 \t\tPARENT-3-PROJECT_LOC/components/include \t \t \t\tmemfault_includes/ports/include \t\t2 \t\tPARENT-3-PROJECT_LOC/ports/include \t \t \t\tmemfault_dialog_da145xx \t\t2 \t\tvirtual:/virtual \t \t \t\tmemfault_dialog_da145xx/memfault_platform_core.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_core.c \t \t \t\tmemfault_dialog_da145xx/memfault_platform_coredump_regions.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_coredump_regions.c \t \t \t\tmemfault_dialog_da145xx/memfault_platform_coredump_storage.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_coredump_storage.c \t \t \t\tmemfault_dialog_da145xx/memfault_platform_debug_log.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_debug_log.c \t \t \t\tmemfault_dialog_da145xx/memfault_platform_metrics.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/dialog/da145xx/memfault_platform_metrics.c \t \t \t\tmemfault_dialog_da145xx/reset_reboot_tracking.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/dialog/da145xx/reset_reboot_tracking.c \t \t ''' } snapshots['test_eclipse_project_patcher_single_dir_port 1'] = { '.cproject': ''' \t \t\t \t\t\t \t\t\t\t \t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t \t\t\t \t\t\t \t\t\t\t \t\t\t\t\t \t\t\t\t\t\t \t\t\t\t\t\t\t \t\t\t\t\t \t\t\t\t\t \t\t\t\t\t\t \t\t\t\t\t \t\t\t\t \t\t\t \t\t\t \t\t \t \t \t\t \t \t \t\t \t\t \t\t\t \t\t \t\t \t \t \t \t\t \t\t\t \t\t \t ''', '.project': ''' \tSTM32L152RE_NUCLEO \t \t \t \t \t\t \t\t\torg.eclipse.cdt.managedbuilder.core.genmakebuilder \t\t\tclean,full,incremental, \t\t\t \t\t\t \t\t \t\t \t\t\torg.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder \t\t\tfull,incremental, \t\t\t \t\t\t \t\t \t \t \t\torg.eclipse.cdt.core.cnature \t\torg.eclipse.cdt.managedbuilder.core.managedBuildNature \t\torg.eclipse.cdt.managedbuilder.core.ScannerConfigNature \t\tfr.ac6.mcu.ide.core.MCUProjectNature \t \t \t\t\tMiddlewares/FreeRTOS/tasks.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/tasks.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_rcc_ex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_rcc_ex.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_gpio.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_gpio.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_cortex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_cortex.c \t\t \t\t\tApplication/User/stm32l1xx_it.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/stm32l1xx_it.c \t\t \t\t\tMiddlewares/FreeRTOS/portable/heap_4.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c \t\t \t\t\tMiddlewares/FreeRTOS/CMSIS_RTOS/cmsis_os.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c \t\t \t\t\tDrivers/BSP/STM32L1xx_Nucleo/stm32l1xx_nucleo.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/BSP/STM32L1xx_Nucleo/stm32l1xx_nucleo.c \t\t \t\t\tApplication/User/main.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/main.c \t\t \t\t\tMiddlewares/FreeRTOS/queue.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/queue.c \t\t \t\t\tMiddlewares/FreeRTOS/list.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/list.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_pwr.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_pwr.c \t\t \t\t\tApplication/SW4STM32/startup_stm32l152xe.s \t\t\t1 \t\t\tPARENT-1-PROJECT_LOC/startup_stm32l152xe.s \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_rcc.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_rcc.c \t\t \t\t\tMiddlewares/FreeRTOS/portable/port.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c \t\t \t\t\tDrivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_pwr_ex.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_pwr_ex.c \t\t \t\t\tDoc/readme.txt \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/readme.txt \t\t \t\t\tMiddlewares/FreeRTOS/timers.c \t\t\t1 \t\t\tPARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/timers.c \t\t \t\t\tDrivers/CMSIS/system_stm32l1xx.c \t\t\t1 \t\t\tPARENT-2-PROJECT_LOC/Src/system_stm32l1xx.c \t\t \t\t \t \t\tmemfault_components \t\t2 \t\tvirtual:/virtual \t \t \t\tmemfault_components/arch_arm_cortex_m.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/arch_arm_cortex_m.c \t \t \t\tmemfault_components/memfault_batched_events.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_batched_events.c \t \t \t\tmemfault_components/memfault_build_id.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_build_id.c \t \t \t\tmemfault_components/memfault_compact_log_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_compact_log_serializer.c \t \t \t\tmemfault_components/memfault_core_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_core_utils.c \t \t \t\tmemfault_components/memfault_custom_data_recording.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_custom_data_recording.c \t \t \t\tmemfault_components/memfault_data_export.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_export.c \t \t \t\tmemfault_components/memfault_data_packetizer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_packetizer.c \t \t \t\tmemfault_components/memfault_data_source_rle.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_data_source_rle.c \t \t \t\tmemfault_components/memfault_event_storage.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_event_storage.c \t \t \t\tmemfault_components/memfault_heap_stats.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_heap_stats.c \t \t \t\tmemfault_components/memfault_log.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_log.c \t \t \t\tmemfault_components/memfault_log_data_source.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_log_data_source.c \t \t \t\tmemfault_components/memfault_ram_reboot_info_tracking.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_ram_reboot_info_tracking.c \t \t \t\tmemfault_components/memfault_reboot_tracking_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_reboot_tracking_serializer.c \t \t \t\tmemfault_components/memfault_sdk_assert.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_sdk_assert.c \t \t \t\tmemfault_components/memfault_self_test.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_self_test.c \t \t \t\tmemfault_components/memfault_self_test_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_self_test_utils.c \t \t \t\tmemfault_components/memfault_serializer_helper.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_serializer_helper.c \t \t \t\tmemfault_components/memfault_task_watchdog.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_task_watchdog.c \t \t \t\tmemfault_components/memfault_trace_event.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/core/src/memfault_trace_event.c \t \t \t\tmemfault_components/memfault_base64.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_base64.c \t \t \t\tmemfault_components/memfault_chunk_transport.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_chunk_transport.c \t \t \t\tmemfault_components/memfault_circular_buffer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_circular_buffer.c \t \t \t\tmemfault_components/memfault_crc16_ccitt.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_crc16_ccitt.c \t \t \t\tmemfault_components/memfault_minimal_cbor.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_minimal_cbor.c \t \t \t\tmemfault_components/memfault_rle.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_rle.c \t \t \t\tmemfault_components/memfault_varint.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/util/src/memfault_varint.c \t \t \t\tmemfault_components/memfault_metrics.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics.c \t \t \t\tmemfault_components/memfault_metrics_battery.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_battery.c \t \t \t\tmemfault_components/memfault_metrics_connectivity.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_connectivity.c \t \t \t\tmemfault_components/memfault_metrics_reliability.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_reliability.c \t \t \t\tmemfault_components/memfault_metrics_serializer.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/metrics/src/memfault_metrics_serializer.c \t \t \t\tmemfault_components/memfault_coredump.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump.c \t \t \t\tmemfault_components/memfault_coredump_regions_armv7.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_regions_armv7.c \t \t \t\tmemfault_components/memfault_coredump_sdk_regions.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_sdk_regions.c \t \t \t\tmemfault_components/memfault_coredump_storage_debug.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_storage_debug.c \t \t \t\tmemfault_components/memfault_coredump_utils.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_coredump_utils.c \t \t \t\tmemfault_components/memfault_fault_handling_aarch64.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_aarch64.c \t \t \t\tmemfault_components/memfault_fault_handling_arm.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_arm.c \t \t \t\tmemfault_components/memfault_fault_handling_armv7_a_r.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_armv7_a_r.c \t \t \t\tmemfault_components/memfault_fault_handling_posix.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_posix.c \t \t \t\tmemfault_components/memfault_fault_handling_riscv.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_fault_handling_riscv.c \t \t \t\tmemfault_components/memfault_stdlib_assert.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/panics/src/memfault_stdlib_assert.c \t \t \t\tmemfault_components/memfault_demo_cli_drain_chunks.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_drain_chunks.c \t \t \t\tmemfault_components/memfault_demo_cli_log.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_log.c \t \t \t\tmemfault_components/memfault_demo_cli_trace_event.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_cli_trace_event.c \t \t \t\tmemfault_components/memfault_demo_core.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_core.c \t \t \t\tmemfault_components/memfault_demo_shell.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_shell.c \t \t \t\tmemfault_components/memfault_demo_shell_commands.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_shell_commands.c \t \t \t\tmemfault_components/memfault_demo_watchdog.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/memfault_demo_watchdog.c \t \t \t\tmemfault_components/memfault_demo_cli_aux.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/panics/memfault_demo_cli_aux.c \t \t \t\tmemfault_components/memfault_demo_panics.c \t\t1 \t\tPARENT-3-PROJECT_LOC/components/demo/src/panics/memfault_demo_panics.c \t \t \t\tmemfault_includes \t\t2 \t\tvirtual:/virtual \t \t \t\tmemfault_includes/components/include \t\t2 \t\tPARENT-3-PROJECT_LOC/components/include \t \t \t\tmemfault_includes/ports/include \t\t2 \t\tPARENT-3-PROJECT_LOC/ports/include \t \t \t\tmemfault_freertos \t\t2 \t\tvirtual:/virtual \t \t \t\tmemfault_freertos/memfault_core_freertos.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/freertos/src/memfault_core_freertos.c \t \t \t\tmemfault_freertos/memfault_freertos_ram_regions.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/freertos/src/memfault_freertos_ram_regions.c \t \t \t\tmemfault_freertos/memfault_metrics_freertos.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/freertos/src/memfault_metrics_freertos.c \t \t \t\tmemfault_freertos/memfault_panics_freertos.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/freertos/src/memfault_panics_freertos.c \t \t \t\tmemfault_freertos/memfault_sdk_metrics_freertos.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/freertos/src/memfault_sdk_metrics_freertos.c \t \t \t\tmemfault_freertos/memfault_sdk_metrics_thread.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/freertos/src/memfault_sdk_metrics_thread.c \t \t \t\tmemfault_freertos/memfault_self_test_platform.c \t\t1 \t\tPARENT-3-PROJECT_LOC/ports/freertos/src/memfault_self_test_platform.c \t \t ''' } ================================================ FILE: scripts/tests_embedded_scripts/snapshots/snap_test_memfault_gdb.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # # -*- coding: utf-8 -*- # snapshottest: v1 - https://goo.gl/zC4yUc from __future__ import unicode_literals from snapshottest import Snapshot snapshots = Snapshot() snapshots[ "test_coredump_command_with_login_no_existing_release_or_symbols[None--fixture2.bin] 1" ] = "434f524501000000e906100000000000000000005400000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000001000000044454d4f53455249414c4e554d4245520a0000000000000012000000312e302e302d6d64352b62363162326436630b00000000000000040000006d61696e040000000000000008000000444556424f415244070000000000000004000000280000000500000000000000040000000500000001000000fcffffff04000000a288485d0100000004e000e004000000414141410100000010e000e010000000414141414141414141414141414141410100000000ed00e08f000000414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414101000000fced00e004000000414141410100000000e100e0000500004141414141414141414141414141414141414141414141" snapshots[ "test_coredump_command_with_login_no_existing_release_or_symbols[None--r 0x600000 8 -r 0x800000 4-fixture3.bin] 1" ] = "434f5245010000000107000000000000000000005400000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000001000000044454d4f53455249414c4e554d4245520a0000000000000012000000312e302e302d6d64352b62363162326436630b00000000000000040000006d61696e040000000000000008000000444556424f415244070000000000000004000000280000000500000000000000040000000500000001000000fcffffff04000000a288485d0100000004e000e004000000414141410100000010e000e010000000414141414141414141414141414141410100000000ed00e08f000000414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414101000000fced00e004000000414141410100000000e100e0000500004141414141414141414141414141414141414141414141" snapshots["test_coredump_writer 1"] = ( "434f52450100000002010000000000000000000054000000000000000101010102020202030303030404040405050505060606060707070708080808090909090a0a0a0a0b0b0b0b0c0c0c0c0d0d0d0d0e0e0e0e0f0f0f0f101010101111111112121212131313131414141402000000000000000d0000006465766963655f73657269616c0a0000000000000005000000312e322e330b00000000000000040000006d61696e0400000000000000090000006764622d70726f746f070000000000000004000000280000000500000000000000040000000500000001000000fcffffff04000000a288485d01000000040000000b00000068656c6c6f20776f726c64" ) ================================================ FILE: scripts/tests_embedded_scripts/test_eclipse_patch.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # import os import subprocess import sys import tempfile from pathlib import Path MEMFAULT_SDK_ROOT_DIR = Path(__file__).resolve().parents[2] def test_eclipse_project_patcher(snapshot): # create a temp directory for test output with tempfile.TemporaryDirectory() as temp_dir: # patch the eclipse project subprocess.run( [ sys.executable, MEMFAULT_SDK_ROOT_DIR / "scripts" / "eclipse_patch.py", "--project-dir", Path(__file__).resolve().parent / "testinput", "--output", temp_dir, ], check=True, cwd=MEMFAULT_SDK_ROOT_DIR, ) # read the patched project patched_project = {} for root, _, files in os.walk(temp_dir): for file in files: with open(os.path.join(root, file), "r") as f: patched_project[file] = f.read() snapshot.assert_match(patched_project) def test_eclipse_project_patcher_single_dir_port(snapshot): # create a temp directory for test output with tempfile.TemporaryDirectory() as temp_dir: # patch the eclipse project subprocess.run( [ sys.executable, MEMFAULT_SDK_ROOT_DIR / "scripts" / "eclipse_patch.py", "--project-dir", Path(__file__).resolve().parent / "testinput", "--output", temp_dir, "--target-port", "freertos", ], check=True, cwd=MEMFAULT_SDK_ROOT_DIR, ) # read the patched project patched_project = {} for root, _, files in os.walk(temp_dir): for file in files: with open(os.path.join(root, file), "r") as f: patched_project[file] = f.read() snapshot.assert_match(patched_project) def test_eclipse_project_patcher_nested_port(snapshot): # create a temp directory for test output with tempfile.TemporaryDirectory() as temp_dir: # patch the eclipse project subprocess.run( [ sys.executable, MEMFAULT_SDK_ROOT_DIR / "scripts" / "eclipse_patch.py", "--project-dir", Path(__file__).resolve().parent / "testinput", "--output", temp_dir, "--target-port", "dialog/da145xx", ], check=True, cwd=MEMFAULT_SDK_ROOT_DIR, ) # read the patched project patched_project = {} for root, _, files in os.walk(temp_dir): for file in files: with open(os.path.join(root, file), "r") as f: patched_project[file] = f.read() snapshot.assert_match(patched_project) ================================================ FILE: scripts/tests_embedded_scripts/test_memfault_gdb.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # import json import os import sys from io import BufferedIOBase, BytesIO from json import dumps from typing import Any, List, Tuple from unittest import mock from unittest.mock import ANY, MagicMock import pytest from . import gdb_fake gdb_fake.install_fake() tests_dir = os.path.dirname(os.path.realpath(__file__)) scripts_dir = os.path.dirname(tests_dir) sys.path.insert(0, scripts_dir) from memfault_gdb import ( # noqa: E402 ArmCortexMCoredumpArch, MemfaultConfig, MemfaultCoredump, MemfaultCoredumpWriter, MemfaultLogin, Section, _post_chunk_data, add_basic_auth, has_uploaded_symbols, lookup_registers_from_list, parse_maintenance_info_sections, read_memory_until_error, settings_save, should_capture_section, ) # We save the time in the coredump generated so each run of "memfault coredump" looks unique # Let's monkeypatch time so we get a reproducible time in out unit tests @pytest.fixture(autouse=True) def _patch_datetime_now(mocker): mocker.patch("memfault_gdb.time", return_value=float(1565034658.6)) @pytest.fixture() def http_expect_request(): connections: List[MagicMock] = [] expected_requests: List[Tuple[Any, ...]] = [] actual_request_count = 0 def _http_expect_request(url, method, assert_body_fn, req_headers, resp_status, resp_body): expected_requests.append((url, method, assert_body_fn, req_headers, resp_status, resp_body)) def _create_connection_constructor(scheme): def _connection_constructor(hostname, port=0): connection = MagicMock() try: ( exp_url, exp_method, assert_body_fn, exp_req_headers, resp_status, resp_body, ) = expected_requests[len(connections)] except IndexError as e: raise AssertionError("Unexpected mock HTTPConnection created!") from e def _request(method, path, body=None, headers=None): nonlocal actual_request_count actual_request_count += 1 standard_port = (scheme == "http" and port != 80) or ( scheme == "https" and port != 443 ) port_str = ":{}".format(port) if standard_port else "" assert "{}://{}{}{}".format(scheme, hostname, port_str, path) == exp_url assert method == exp_method if assert_body_fn and assert_body_fn is not ANY: body_bytes = body.read() if isinstance(body, BufferedIOBase) else body assert_body_fn(body_bytes) assert headers == exp_req_headers connection.request = _request response = MagicMock() response.status = resp_status response.reason = "" response.read.return_value = ( None if resp_body is None else dumps(resp_body).encode("utf8") ) connection.getresponse.return_value = response connections.append(connection) return connection return _connection_constructor with mock.patch( "memfault_gdb.HTTPSConnection", _create_connection_constructor("https") ), mock.patch("memfault_gdb.HTTPConnection", _create_connection_constructor("http")): yield _http_expect_request assert actual_request_count == len(expected_requests) TEST_EMAIL = "martijn@memfault.com" TEST_PASSWORD = "open_sesame" TEST_AUTH_HEADERS = {"Authorization": "Basic bWFydGlqbkBtZW1mYXVsdC5jb206b3Blbl9zZXNhbWU="} TEST_ORG = "acme-inc" TEST_PROJECT = "smart-sink" TEST_HW_VERSION = "DEVBOARD" TEST_PROJECT_KEY = "7f083342d30444b2b3bed65357f24a19" # gitleaks:allow @pytest.fixture(autouse=True) def test_config(mocker, tmp_path): """Updates MEMFAULT_CONFIG to contain TEST_... values""" rv = MemfaultConfig() mocker.patch("memfault_gdb.MEMFAULT_CONFIG", rv) rv.prompt = lambda prompt: "Y" rv.json_path = str(tmp_path / "settings.yml") return rv @pytest.fixture() def test_config_with_login(test_config): test_config.email = TEST_EMAIL test_config.password = TEST_PASSWORD test_config.organization = TEST_ORG test_config.project = TEST_PROJECT return test_config TEST_MAINTENANCE_INFO_SECTIONS_FIXTURE_FMT = """Exec file: `{}', file type elf32-littlearm. [0] 0x26000->0x6b784 at 0x00006000: .text ALLOC LOAD READONLY CODE HAS_CONTENTS [1] 0x2003d800->0x2003e878 at 0x0005d800: .rtt ALLOC [2] 0x6b784->0x6b7a8 at 0x0004b784: .gnu_build_id ALLOC LOAD READONLY DATA HAS_CONTENTS [3] 0x2003fe00->0x2003fe14 at 0x0005fe00: .fw_install_info ALLOC [4] 0x2003ff00->0x20040000 at 0x0005ff00: .noinit ALLOC [5] 0x6b7a8->0x6b7c8 at 0x0004b7a8: .sdh_soc_observers ALLOC LOAD READONLY DATA HAS_CONTENTS [6] 0x6b7c8->0x6b850 at 0x0004b7c8: .sdh_ble_observers ALLOC LOAD READONLY DATA HAS_CONTENTS [7] 0x6b850->0x6b860 at 0x0004b850: .sdh_stack_observers ALLOC LOAD READONLY DATA HAS_CONTENTS [8] 0x6b860->0x6b868 at 0x0004b860: .sdh_req_observers ALLOC LOAD READONLY DATA HAS_CONTENTS [9] 0x6b868->0x6b880 at 0x0004b868: .sdh_state_observers ALLOC LOAD READONLY DATA HAS_CONTENTS [10] 0x6b880->0x6b8a8 at 0x0004b880: .nrf_queue ALLOC LOAD READONLY DATA HAS_CONTENTS [11] 0x6b8a8->0x6b8d0 at 0x0004b8a8: .nrf_balloc ALLOC LOAD READONLY DATA HAS_CONTENTS [12] 0x6b8d0->0x6b8f8 at 0x0004b8d0: .cli_command ALLOC LOAD READONLY DATA HAS_CONTENTS [13] 0x6b8f8->0x6b908 at 0x0004b8f8: .crypto_data ALLOC LOAD READONLY DATA HAS_CONTENTS [14] 0x6b908->0x6b9e0 at 0x0004b908: .log_const_data ALLOC LOAD READONLY DATA HAS_CONTENTS [15] 0x6b9e0->0x6ba00 at 0x0004b9e0: .log_backends ALLOC LOAD READONLY DATA HAS_CONTENTS [16] 0x6ba00->0x6ba08 at 0x0004ba00: .ARM.exidx ALLOC LOAD READONLY DATA HAS_CONTENTS [17] 0x200057b8->0x20005bf0 at 0x000557b8: .data ALLOC LOAD DATA HAS_CONTENTS [18] 0x20005bf0->0x20005c04 at 0x00055bf0: .cli_sorted_cmd_ptrs ALLOC LOAD DATA HAS_CONTENTS [19] 0x20005c04->0x20005c2c at 0x00055c04: .fs_data ALLOC LOAD DATA HAS_CONTENTS [20] 0x20005c30->0x2000b850 at 0x00055c2c: .bss ALLOC [21] 0x2000b850->0x2000d850 at 0x00055c30: .heap READONLY HAS_CONTENTS [22] 0x2000b850->0x2000d850 at 0x00057c30: .stack_dummy READONLY HAS_CONTENTS [23] 0x0000->0x0030 at 0x00059c30: .ARM.attributes READONLY HAS_CONTENTS [24] 0x0000->0x00f4 at 0x00059c60: .comment READONLY HAS_CONTENTS [25] 0x0000->0xf3d30 at 0x00059d54: .debug_info READONLY HAS_CONTENTS [26] 0x0000->0x1eeec at 0x0014da84: .debug_abbrev READONLY HAS_CONTENTS [27] 0x0000->0x59416 at 0x0016c970: .debug_loc READONLY HAS_CONTENTS [28] 0x0000->0x2c10 at 0x001c5d88: .debug_aranges READONLY HAS_CONTENTS [29] 0x0000->0x121a8 at 0x001c8998: .debug_ranges READONLY HAS_CONTENTS [30] 0x0000->0x37f3a at 0x001dab40: .debug_macro READONLY HAS_CONTENTS [31] 0x0000->0x708cb at 0x00212a7a: .debug_line READONLY HAS_CONTENTS [32] 0x0000->0xcee28 at 0x00283345: .debug_str READONLY HAS_CONTENTS [33] 0x0000->0x91f0 at 0x00352170: .debug_frame READONLY HAS_CONTENTS [34] 0x0000->0x00df at 0x0035b360: .stabstr READONLY HAS_CONTENTS """ @pytest.fixture() def fake_elf(tmp_path): file = tmp_path / "fake.elf" file.write_bytes(b"ELF") return file @pytest.fixture() def maintenance_info_sections_fixture(fake_elf): return TEST_MAINTENANCE_INFO_SECTIONS_FIXTURE_FMT.format(fake_elf) # Data from a GDB Server with register names that differ from the final names we use @pytest.fixture() def info_reg_all_fixture(fake_elf): return """ r0 0x0 0 r1 0x1 1 r2 0xe000ed00 -536810240 r3 0x20003fe8 536887272 r4 0x20000294 536871572 r5 0x0 0 r6 0x0 0 r7 0x0 0 r8 0x0 0 r9 0x0 0 r10 0x0 0 r11 0x0 0 r12 0x0 0 sp 0x200046c8 0x200046c8 lr 0x8002649 134227529 pc 0x8007122 0x8007122 cpsr 0x61000000 1627389952 PRIMASK 0x0 0 BASExPRI 0x0 0 FAULTMASK 0x0 0 CONTROL 0x0 0 MSP 0x2001ffc0 537001920 PSP 0x200046c8 536889032 """ @pytest.fixture() def _settings_coredump_allowed(test_config): settings_save({"coredump.allow": True}) def test_parse_maintenance_info_sections_no_file(): fn, sections = parse_maintenance_info_sections("""Remote serial target in gdb-specific protocol: Debugging a target over a serial line. """) assert fn is None assert sections is None def test_parse_maintenance_info_sections_with_file(maintenance_info_sections_fixture, fake_elf): fn, sections = parse_maintenance_info_sections(maintenance_info_sections_fixture) assert fn == str(fake_elf) assert sections assert len(sections) == 35 assert sections[0] == Section(0x26000, 0x6B784 - 0x26000, ".text", read_only=True) assert sections[20] == Section(0x20005C30, 0x2000B850 - 0x20005C30, ".bss", read_only=False) def test_read_current_registers(mocker, info_reg_all_fixture): frame = MagicMock() frame.read_register.return_value = gdb_fake.Value(1, type=gdb_fake.Type(sizeof=4)) mocker.patch.object(gdb_fake, "newest_frame", return_value=frame) dummy_dict: dict = {} arch = ArmCortexMCoredumpArch() register_list = lookup_registers_from_list(arch, info_reg_all_fixture, dummy_dict) for reg in arch.register_collection_list: # A mock read_register should have taken place so the value should be non-zero assert len(register_list[reg]) == 4 assert register_list[reg][0] != 0, reg def test_should_capture_section(): # Never capture .text, even when NOT marked READONLY: assert not should_capture_section(Section(0, 10, ".text", read_only=False)) # Never capture .debug_... assert not should_capture_section(Section(0, 10, ".debug_info", read_only=True)) # Never capture 0 length sections assert not should_capture_section(Section(0, 0, ".dram0.data", read_only=False)) # Always capture sections with .bss, .heap, .data or .stack in the name, even when marked READONLY: assert should_capture_section(Section(0, 10, ".foo.bss", read_only=True)) assert should_capture_section(Section(0, 10, ".stack_dummy", read_only=True)) assert should_capture_section(Section(0, 10, ".heap_psram", read_only=True)) assert should_capture_section(Section(0, 10, ".dram0.data", read_only=True)) def test_armv7_get_used_ram_base_addresses(): sections = ( # Sections must meet the guess criteria: # 1. within an allowed region # 2. not read-only OR contain `.data`/`.bss` etc in the name Section(0x21000000, 10, "", read_only=False), Section(0x30000000, 10, "", read_only=True), Section(0x3F000000, 10, ".data.etc", read_only=True), Section(0x40000000, 10, "", read_only=True), Section(0x70000000, 10, "", read_only=True), Section(0x90000000, 10, "", read_only=True), Section(0x00000000, 10, "", read_only=True), Section(0xE0000000, 10, "", read_only=True), ) collection_size_arm = 1 * 1024 * 1024 # 1MB assert [ (0x21000000, collection_size_arm), (0x3F000000, collection_size_arm), ] == ArmCortexMCoredumpArch().guess_ram_regions(sections) def test_read_memory_until_error_no_error(): read_size = 1024 size = 1024 * 1024 inferior = MagicMock() inferior.read_memory.return_value = b"A" * read_size data = read_memory_until_error(inferior, 0x20000000, size, read_size=read_size) assert data == b"A" * size def test_read_memory_until_error_after_10k(): read_size = 1024 size = 1024 * 1024 inferior = MagicMock() total_size = 0 def _read_memory_raise_after_10k(_addr, _size): nonlocal total_size total_size += _size if total_size > _size * 10: raise ValueError return b"A" * _size inferior.read_memory.side_effect = _read_memory_raise_after_10k data = read_memory_until_error(inferior, 0x20000000, size, read_size=read_size) assert data == b"A" * read_size * 10 def test_coredump_writer(snapshot): arch = ArmCortexMCoredumpArch() device_serial = "device_serial" software_type = "main" software_version = "1.2.3" hardware_revision = "gdb-proto" cd_writer = MemfaultCoredumpWriter( arch, device_serial, software_type, software_version, hardware_revision ) cd_writer.trace_reason = 5 cd_writer.regs = [ # pyright: ignore[reportAttributeAccessIssue] { "r0": 4 * b"\x00", "r1": 4 * b"\x01", "r2": 4 * b"\x02", "r3": 4 * b"\x03", "r4": 4 * b"\x04", "r5": 4 * b"\x05", "r6": 4 * b"\x06", "r7": 4 * b"\x07", "r8": 4 * b"\x08", "r9": 4 * b"\x09", "r10": 4 * b"\x0a", "r11": 4 * b"\x0b", "r12": 4 * b"\x0c", "sp": 4 * b"\x0d", "lr": 4 * b"\x0e", "pc": 4 * b"\x0f", "xpsr": 4 * b"\x10", "msp": 4 * b"\x11", "psp": 4 * b"\x12", "primask": 4 * b"\x13", "control": 4 * b"\x14", }, ] section = Section(4, 32, ".test", "") # pyright: ignore[reportArgumentType] section.data = b"hello world" cd_writer.add_section(section) f_out = BytesIO() cd_writer.write(f_out) f_out.seek(0) snapshot.assert_match(f_out.read().hex()) def test_http_basic_auth(): headers = add_basic_auth("martijn@memfault.com", "open_sesame", {"Foo": "Bar"}) assert headers == { "Foo": "Bar", "Authorization": "Basic bWFydGlqbkBtZW1mYXVsdC5jb206b3Blbl9zZXNhbWU=", } @pytest.fixture() def _gdb_for_coredump(mocker, maintenance_info_sections_fixture, _settings_coredump_allowed): frame = MagicMock() mocker.patch.object(gdb_fake, "newest_frame", return_value=frame) def _gdb_execute(cmd, to_string): if cmd == "maintenance info sections": return maintenance_info_sections_fixture if cmd == "info all-registers": return "r0\t0\n" if cmd == "info threads": return "" if cmd.startswith("show arch"): return "The target architecture is set automatically (currently armv7m)" raise NotImplementedError(f"Command: {cmd}") mocker.patch.object(gdb_fake, "execute", side_effect=_gdb_execute) selected_thread = MagicMock() mocker.patch.object(gdb_fake, "selected_thread", return_value=selected_thread) inferior = MagicMock() inferior.threads.return_value = [selected_thread] mocker.patch.object(gdb_fake, "selected_inferior", return_value=inferior) def _read_memory(addr, size): return b"A" * size # Write 41 'A' for reads to memory regions inferior.read_memory = _read_memory @pytest.mark.usefixtures("_gdb_for_coredump") def test_coredump_command_no_target(capsys): """Test coredump command shows error when no target is connected""" inferior = gdb_fake.selected_inferior() assert inferior inferior.threads.return_value = [] cmd = MemfaultCoredump() cmd.invoke("--project-key {}".format(TEST_PROJECT_KEY), True) stdout = capsys.readouterr().out assert "No target" in stdout def test_coredump_command_non_arm(mocker, capsys): """Test coredump command shows error when no ARM or XTENSA target""" def _gdb_execute(cmd, to_string): if cmd.startswith("show arch"): return "The target architecture is set automatically (currently riscv)" return "" mocker.patch.object(gdb_fake, "execute", side_effect=_gdb_execute) cmd = MemfaultCoredump() cmd.invoke("--project-key {}".format(TEST_PROJECT_KEY), True) stdout = capsys.readouterr().out assert "This command is currently only supported for ARM and XTENSA targets!" in stdout @pytest.mark.usefixtures("_gdb_for_coredump") def test_coredump_command_with_project_key(http_expect_request, test_config): """Test coredump command project key""" # Expect to use ingress_uri from config if not provided explicitly to the command invocation test_config.ingress_uri = "https://custom-ingress.memfault.com" http_expect_request( "https://custom-ingress.memfault.com/api/v0/upload/coredump", "POST", ANY, # Not checking the request body at the moment {"Content-Type": "application/octet-stream", "Memfault-Project-Key": TEST_PROJECT_KEY}, 200, {}, ) cmd = MemfaultCoredump() cmd.invoke("--project-key {}".format(TEST_PROJECT_KEY), True) def test_coredump_command_not_allowing( http_expect_request, capsys, test_config, ): """Test coredump command and not accepting prompt""" test_config.prompt = lambda prompt: "N" cmd = MemfaultCoredump() cmd.invoke("--project-key {}".format(TEST_PROJECT_KEY), True) stdout = capsys.readouterr().out assert "Aborting" in stdout @pytest.mark.usefixtures("_gdb_for_coredump") def test_coredump_all_overrides(http_expect_request, test_config, mocker): """Test coredump command with all overrides set""" # Verify coredump is uploaded test_config.ingress_uri = "https://custom-ingress.memfault.com" http_expect_request( "https://custom-ingress.memfault.com/api/v0/upload/coredump", "POST", ANY, {"Content-Type": "application/octet-stream", "Memfault-Project-Key": TEST_PROJECT_KEY}, 200, {}, ) writer = MagicMock() mocker.patch("memfault_gdb.MemfaultCoredumpWriter", writer) hardware_revision = "TESTREVISION" software_version = "TESTVERSION" software_type = "TESTTYPE" device_serial = "TESTSERIAL" cmd = MemfaultCoredump() cmd.invoke( "--project-key {} --hardware-revision {} --software-version {} --software-type {}" " --device-serial {}".format( TEST_PROJECT_KEY, hardware_revision, software_version, software_type, device_serial ), True, ) # Verify that all args were successfully extracted and passed to writer writer.assert_called_once_with( ANY, device_serial, software_type, software_version, hardware_revision ) @pytest.mark.parametrize( ("cmd_option", "cmd_type"), [ ("--hardware-revision", "hardware revision"), ("--software-version", "software version"), ("--software-type", "software type"), ("--device-serial", "device serial"), ], ) def test_coredump_override_invalid_input(capsys, cmd_option, cmd_type): """Test coredump command with invalid input for each override""" invalid_input = 'inv"lid' cmd = MemfaultCoredump() cmd.invoke("--project-key {} {} {}".format(TEST_PROJECT_KEY, cmd_option, invalid_input), True) # Verify an error is thrown for the invalid input stderr = capsys.readouterr().err assert "Invalid characters in {}: {}".format(cmd_type, invalid_input) in stderr @pytest.mark.parametrize( ("expected_result", "extra_cmd_args", "expected_coredump_fn"), [ (None, "", "fixture2.bin"), (None, "-r 0x600000 8 -r 0x800000 4", "fixture3.bin"), ], ) @pytest.mark.usefixtures("_gdb_for_coredump") def test_coredump_command_with_login_no_existing_release_or_symbols( test_config_with_login, http_expect_request, test_config, expected_result, extra_cmd_args, expected_coredump_fn, capsys, snapshot, ): """Test coredump command without project key, but with logged in user and default org & project""" # Check whether Release and symbols exists -- No (404) http_expect_request( "https://api.memfault.com/api/v0/organizations/acme-inc/projects/smart-sink/software_types/main/software_versions/1.0.0-md5+b61b2d6c", "GET", ANY, ANY, 404, None, ) # Upload Symbols token = "token-foo" upload_url = "https://memfault-test-east1.s3.amazonaws.com/symbols/foo" http_expect_request( "https://api.memfault.com/api/v0/organizations/acme-inc/projects/smart-sink/upload", "POST", ANY, ANY, 200, { "data": { "token": token, "upload_url": upload_url, } }, ) http_expect_request( upload_url, "PUT", ANY, ANY, 200, None, ) http_expect_request( "https://api.memfault.com/api/v0/organizations/acme-inc/projects/smart-sink/symbols", "POST", lambda body_bytes: json.loads(body_bytes) == { "file": {"token": token, "name": "symbols.elf"}, "software_version": { "version": "1.0.0-md5+b61b2d6c", "software_type": "main", }, }, ANY, 200, {"data": {}}, ) # Get Project-Key: http_expect_request( "https://api.memfault.com/api/v0/organizations/acme-inc/projects/smart-sink/api_key", "GET", ANY, ANY, 200, {"data": {"api_key": TEST_PROJECT_KEY}}, ) # Upload coredump: def _check_request_body(body_bytes): # truncated in the interest of avoiding bloat in snapshot: snapshot.assert_match(body_bytes[:500].hex()) http_expect_request( "https://ingress.memfault.com/api/v0/upload/coredump", "POST", _check_request_body, {"Content-Type": "application/octet-stream", "Memfault-Project-Key": TEST_PROJECT_KEY}, 200, {}, ) cmd = MemfaultCoredump() cmd.invoke(extra_cmd_args, True) stdout = capsys.readouterr().out assert ( """Coredump uploaded successfully! Once it has been processed, it will appear here: https://app.memfault.com/organizations/acme-inc/projects/smart-sink/issues?live""" in stdout ) def test_login_command_simple(http_expect_request, test_config): http_expect_request( "https://api.memfault.com/auth/me", "GET", None, TEST_AUTH_HEADERS, 200, {"id": 123} ) login = MemfaultLogin() login.invoke("{} {}".format(TEST_EMAIL, TEST_PASSWORD), True) assert test_config.email == TEST_EMAIL assert test_config.password == TEST_PASSWORD assert test_config.organization is None assert test_config.project is None assert test_config.user_id == 123 def test_login_command_with_all_options(http_expect_request, test_config): test_api_uri = "http://dev-api.memfault.com:8000" test_ingress_uri = "http://dev-ingress.memfault.com" http_expect_request( "http://dev-api.memfault.com:8000/auth/me", "GET", None, TEST_AUTH_HEADERS, 200, {"id": 123} ) login = MemfaultLogin() login.invoke( "{} {} -o {} -p {} --api-uri {} --ingress-uri {}".format( TEST_EMAIL, TEST_PASSWORD, TEST_ORG, TEST_PROJECT, test_api_uri, test_ingress_uri, ), True, ) assert test_config.email == TEST_EMAIL assert test_config.password == TEST_PASSWORD assert test_config.organization == TEST_ORG assert test_config.project == TEST_PROJECT assert test_config.api_uri == test_api_uri assert test_config.ingress_uri == test_ingress_uri @pytest.mark.parametrize( ("expected_result", "resp_status", "resp_body"), [ ( True, 200, {"data": {"symbol_file": {"downloadable": True}}}, ), ( False, 200, {"data": {}}, ), (False, 404, None), ], ) def test_has_uploaded_symbols( http_expect_request, expected_result, resp_status, resp_body, test_config_with_login ): test_software_type = "main" test_software_version = "1.0.0" http_expect_request( "https://api.memfault.com/api/v0/organizations/{}/projects/{}/software_types/{}/software_versions/{}".format( test_config_with_login.organization, test_config_with_login.project, test_software_type, test_software_version, ), "GET", None, TEST_AUTH_HEADERS, resp_status, resp_body, ) assert ( has_uploaded_symbols(test_config_with_login, test_software_type, test_software_version) == expected_result ) def test_post_chunk_data(http_expect_request): base_uri = "https://example.chunks.memfault.com" chunk_data = bytearray([1, 2, 3, 4]) device_serial = "GDB_TESTSERIAL" def _check_request_body(body_bytes): assert body_bytes.hex() == chunk_data.hex(), body_bytes http_expect_request( "{}/api/v0/chunks/GDB_TESTSERIAL".format(base_uri), "POST", _check_request_body, {"Content-Type": "application/octet-stream", "Memfault-Project-Key": TEST_PROJECT_KEY}, 202, None, ) _post_chunk_data( chunk_data, project_key=TEST_PROJECT_KEY, chunks_uri=base_uri, device_serial=device_serial ) ================================================ FILE: scripts/tests_embedded_scripts/testinput/.cproject ================================================ ================================================ FILE: scripts/tests_embedded_scripts/testinput/.project ================================================ STM32L152RE_NUCLEO org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder full,incremental, org.eclipse.cdt.core.cnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature fr.ac6.mcu.ide.core.MCUProjectNature Middlewares/FreeRTOS/tasks.c 1 PARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/tasks.c Drivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_rcc_ex.c 1 PARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_rcc_ex.c Drivers/STM32L1xx_HAL_Driver/stm32l1xx_hal.c 1 PARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal.c Drivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_gpio.c 1 PARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_gpio.c Drivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_cortex.c 1 PARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_cortex.c Application/User/stm32l1xx_it.c 1 PARENT-2-PROJECT_LOC/Src/stm32l1xx_it.c Middlewares/FreeRTOS/portable/heap_4.c 1 PARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c Middlewares/FreeRTOS/CMSIS_RTOS/cmsis_os.c 1 PARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c Drivers/BSP/STM32L1xx_Nucleo/stm32l1xx_nucleo.c 1 PARENT-7-PROJECT_LOC/Drivers/BSP/STM32L1xx_Nucleo/stm32l1xx_nucleo.c Application/User/main.c 1 PARENT-2-PROJECT_LOC/Src/main.c Middlewares/FreeRTOS/queue.c 1 PARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/queue.c Middlewares/FreeRTOS/list.c 1 PARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/list.c Drivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_pwr.c 1 PARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_pwr.c Application/SW4STM32/startup_stm32l152xe.s 1 PARENT-1-PROJECT_LOC/startup_stm32l152xe.s Drivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_rcc.c 1 PARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_rcc.c Middlewares/FreeRTOS/portable/port.c 1 PARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c Drivers/STM32L1xx_HAL_Driver/stm32l1xx_hal_pwr_ex.c 1 PARENT-7-PROJECT_LOC/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_pwr_ex.c Doc/readme.txt 1 PARENT-2-PROJECT_LOC/readme.txt Middlewares/FreeRTOS/timers.c 1 PARENT-7-PROJECT_LOC/Middlewares/Third_Party/FreeRTOS/Source/timers.c Drivers/CMSIS/system_stm32l1xx.c 1 PARENT-2-PROJECT_LOC/Src/system_stm32l1xx.c ================================================ FILE: tasks/__init__.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # import os import pathlib from shutil import which from typing import Any, cast from invoke import Collection, task from . import esp32, nrf, wiced, zephyr from .macos_ftdi import is_macos SDK_FW_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SDK_FW_TASKS_DIR = pathlib.Path(os.path.join(SDK_FW_ROOT, "tasks")) SDK_FW_TESTS_ROOT = os.path.join(SDK_FW_ROOT, "tests/unit") @task( help={ "coverage": "Generate coverage report", "rule": "Override default make rule to run, eg run 'clean' instead. Sets no_pytest=True", "test_filter": "Test filter expression, eg '*metrics*'", "test_dir": "Test directory (typically tests/)", "extra_make_options": "Extra make options", "verbose": "Verbose output", "no_pytest": "Don't run pytest, instead run the Make-based test runner", "jobs": "Number of jobs to run in parallel; use 'auto' to detect the number of CPUs", } ) def fw_sdk_unit_test( ctx, coverage=False, rule="", test_filter=None, test_dir=SDK_FW_TESTS_ROOT, extra_make_options="", verbose=False, no_pytest=False, # set the default job limit to 4. there appear to be issues in circleci's # cgroupv2 runtime, which makes 'auto' cpu detection in pytest use an # invalid core count, which causes the test runner to fail. the default is # set to 4 to provide some parallelism by default- it's not important the # actual value, except: # 1. to provide some advantage on multi-core cpus # 2. to provide a fixed limit by default to prevent issues in circleci jobs: str = "4", ): """Runs unit tests""" if rule: # if a rule is specified, use Make-based test runner no_pytest = True # Check if it's necessary to set the CPPUTEST_HOME variable; Macos (brew) # and conda environment requires this env_dict = {} if is_macos(): # best effort- if brew exists, try to use it to source the cpputest # install location brew = which("brew") if brew: result = ctx.run("brew --prefix cpputest") cpputest_home = result.stdout.strip() env_dict["CPPUTEST_HOME"] = cpputest_home # if this is already set in the host environment, use it- it's probably a # conda environment if "CPPUTEST_HOME" in os.environ: env_dict["CPPUTEST_HOME"] = os.environ["CPPUTEST_HOME"] if test_filter: env_dict["TEST_MAKEFILE_FILTER"] = test_filter make_options = [] # set output-sync option only if make supports it. macos uses a 12+ year old # copy of make by default that doesn't have this option. result = ctx.run("make --help", hide=True) if "--output-sync" in result.stdout: make_options.append("--output-sync=recurse") if extra_make_options: make_options.append(extra_make_options) cpus = 1 if os.getenv("CIRCLECI"): # getting the number of cpus available to the circleci executor from # within the docker container is a hassle, so bail and use 2 cpus cpus = 2 else: try: # Only available on Linux, but it's better cpus = len(cast("Any", os).sched_getaffinity(0)) except AttributeError: # Available on Mac cpus = int((os.cpu_count() or 4) / 2) make_options.extend(["-j", str(cpus)]) # force colorized cpputest output env_dict["CPPUTEST_EXE_FLAGS"] = "-c " + os.environ.get("CPPUTEST_EXE_FLAGS", "") # force compiler colored output; it detects as running in a non-tty but the # color output is useful env_dict["COMPILER_SPECIFIC_WARNINGS"] = "-fdiagnostics-color=always " + os.environ.get( "COMPILER_SPECIFIC_WARNINGS", "" ) with ctx.cd(test_dir): # The main unit tests (in the 'tests' directory) use a test.py file to # drive the test run. The internal tests, in the 'internal/tests' directory, # use a Make-based test runner, so support both. if os.path.exists(os.path.join(test_dir, "test.py")) and not no_pytest: # run the tests with pytest test_cmd = "pytest" test_cmd += f" --numprocesses={jobs}" if verbose: test_cmd += " -v" test_cmd += " test.py" else: # run normal make-based tests test_cmd = "make {} {}".format(" ".join(make_options), rule) ctx.run(test_cmd, env=env_dict, pty=True) if coverage: # run lcov to generate coverage report ctx.run( "make --no-print-directory SILENCE=@ {} lcov".format(" ".join(make_options)), env=env_dict, pty=True, ) ns = Collection() ns.add_task(fw_sdk_unit_test, name="test") ns.add_collection(nrf) ns.add_collection(wiced) ns.add_collection(esp32) if os.environ.get("STM32_ENABLED"): from . import stm32 ns.add_collection(stm32) # These tasks are only included if they exist in the SDK if (SDK_FW_TASKS_DIR / "internal.py").exists(): from . import internal ns.add_collection(internal) if (SDK_FW_TASKS_DIR / "modus.py").exists(): from . import modus ns.add_collection(modus) @task( pre=[ esp32.esp32_app_clean, esp32.esp32_app_build, esp32.esp32_app_clean, esp32.esp32s3_app_build, ] ) def build_all_demos(ctx): """Builds all demo apps (for CI purposes)""" ci = Collection("~ci") ci.add_task(build_all_demos, name="build-all-demos") ci.add_task(zephyr.zephyr_project_ci_setup) ns.add_collection(ci) ================================================ FILE: tasks/esp32.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # from __future__ import annotations import os import re import shutil import sys from typing import TYPE_CHECKING from invoke import Collection, task from .gdb import gdb_build_cmd if TYPE_CHECKING: from tasks.lib.invoke_utils import Context TASKS_DIR = os.path.dirname(__file__) MEMFAULT_SDK_ROOT = os.path.join(TASKS_DIR, "..") ESP32_PLATFORM_ROOT = os.path.join(MEMFAULT_SDK_ROOT, "examples", "esp32") ESP32_IDF_ROOT = os.path.join(ESP32_PLATFORM_ROOT, "esp-idf") ESP32_IDF_SCRIPT = os.path.join(ESP32_IDF_ROOT, "tools", "idf.py") ESP32_COREDUMP_SCRIPT = os.path.join(ESP32_IDF_ROOT, "components", "espcoredump", "espcoredump.py") ESP32_TEST_APP_ROOT = os.path.join(ESP32_PLATFORM_ROOT, "apps", "memfault_demo_app") ESP32_TEST_APP_ELF = os.path.join(ESP32_TEST_APP_ROOT, "build", "memfault-esp32-demo-app.elf") ESP32_FTDI_VID_PID = [(0x0403, 0x6010)] OPENOCD_GDB_PORT_DEFAULT = 3333 def _run_idf_script(ctx: "Context", *args: str, **kwargs: object) -> None: # allow selecting a specific python interpreter instead of the active one. # this is necessary in CI, because the mbed build task modifies the python # environment 😖, and idf.py runs a pkg_resources.require() which fails if # the mbed task modified any deps in an incompatible way. it's better to # keep the esp_idf environment isolated and just use it to run idf.py python = os.getenv("ESP_IDF_PYTHON", sys.executable) with ctx.cd(ESP32_TEST_APP_ROOT): ctx.run( "{python} {idf} {args}".format( python=python, idf=ESP32_IDF_SCRIPT, args=" ".join(args) ), env={ "IDF_PATH": ESP32_IDF_ROOT, # newer versions of ESP-IDF require this too (it's much easier # when we can just do '. export.sh' instead!). provide a # reasonable default guess if unset. "ESP_ROM_ELF_DIR": os.getenv( "ESP_ROM_ELF_DIR", "~/.espressif/tools/esp-rom-elfs/20241011/" ), }, **kwargs, ) @task def run_xtensa_toolchain_check(ctx: "Context") -> None: if sys.version_info.major < 3: # shutil which is only available for python3 return xtensa_toolchain = shutil.which("xtensa-esp32-elf-gcc") if xtensa_toolchain is None: msg = ( "Couldn't find esp32 toolchain. Currently using the toolchain which can be" " found here {}".format( "https://docs.espressif.com/projects/esp-idf/en/v3.1/get-started/index.html#setup-toolchain" ) ) raise FileNotFoundError(msg) @task(pre=[run_xtensa_toolchain_check]) def esp32_app_build(ctx: "Context") -> None: """Build the ESP32 test app""" _run_idf_script(ctx, "build") @task def esp32_app_clean(ctx: "Context") -> None: """Clean the ESP32 test app""" _run_idf_script(ctx, "fullclean") @task(pre=[run_xtensa_toolchain_check]) def esp32s2_app_build(ctx: "Context") -> None: """Build the ESP32-S2 test app""" # !NOTE! 'set-target' was added in ESP-IDF v4.1. If you are using an older # version of ESP-IDF, building for the ESP32-S2 + ESP32-S3 won't work. _run_idf_script(ctx, "set-target esp32s2") _run_idf_script(ctx, "build") @task(pre=[run_xtensa_toolchain_check]) def esp32s3_app_build(ctx: "Context") -> None: """Build the ESP32-S3 test app""" _run_idf_script(ctx, "set-target esp32s3") _run_idf_script(ctx, "build") @task def esp32_app_flash(ctx: "Context") -> None: """Flash the ESP32 test app""" _run_idf_script(ctx, "flash") @task def esp32_console(ctx: "Context") -> None: """Flash the ESP32 test app""" _run_idf_script(ctx, "monitor") @task def esp32_app_menuconfig(ctx: "Context") -> None: """Run menuconfig for the ESP32 test app""" _run_idf_script(ctx, "menuconfig", pty=True) @task def esp32_openocd(ctx: "Context") -> None: """Launch openocd""" if "ESP32_OPENOCD" not in os.environ: print("Set ESP32_OPENOCD environment variable to point to openocd-esp32 root directory!") print( "Download the openocd-esp32 binaries here:" " https://github.com/espressif/openocd-esp32/releases" ) sys.exit(-1) with ctx.cd(os.environ["ESP32_OPENOCD"]): ctx.run( "bin/openocd -s share/openocd/scripts " "-f interface/ftdi/esp32_devkitj_v1.cfg -f board/esp-wroom-32.cfg", pty=True, ) @task def esp32_app_gdb(ctx: "Context", gdb: int | None = None, reset: bool = False) -> None: """Launches xtensa-gdb with app elf and connects to openocd gdb server""" if gdb is None: gdb = OPENOCD_GDB_PORT_DEFAULT with ctx.cd(ESP32_TEST_APP_ROOT): gdb_cmd = gdb_build_cmd( "", ESP32_TEST_APP_ELF, gdb, gdb_prefix="xtensa-esp32-elf-", reset=reset ) ctx.run(gdb_cmd, pty=True) @task def esp32_decode_backtrace( ctx: "Context", backtrace_str: str, symbol_file: str, verbose: bool = False ) -> None: """Decode a backtrace emitted by ESP-IDF panic handling The backtrace_str should be passed as a string of separated address pairs where each pair has the format "pc:sp". For example: "0x40081cda:0x3ffd00a0 0x40082ce3:0x3ffd00c0 0x4008927d:0x3ffd00e0" This backtrace is printed by the ESP-IDF debug helpers here: https://github.com/espressif/esp-idf/blob/v5.4.2/components/esp_system/port/arch/xtensa/debug_helpers.c Note: `idf.py monitor` will automatically decode the backtrace for you https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/tools/idf-monitor.html#automatic-address-decoding so this task is primarily used if only the raw backtrace is supplied (e.g. via support ticket or logs). """ addr_tuples = [ (int(pc, 16), int(sp, 16)) for pc, sp in re.findall(r"0x([0-9a-fA-F]+):0x([0-9a-fA-F]+)", backtrace_str) ] for i in range(len(addr_tuples)): result = ctx.run( "addr2line -e {} {address:x}".format(symbol_file, address=addr_tuples[i][0]), hide=True, warn=True, ) if result.ok: print("Frame {frame:>2}: {line}".format(frame=i, line=result.stdout.strip())) if verbose: print(" PC = 0x{address:x}".format(address=addr_tuples[i][0])) print(" SP = 0x{sp:x}".format(sp=addr_tuples[i][1])) else: print( "Error processing 0x{address:x}: {error}".format( address=addr_tuples[i][0], error=result.stderr.strip() ) ) ns = Collection("esp32") ns.add_task(esp32_console, name="console") ns.add_task(esp32_openocd, name="gdbserver") ns.add_task(esp32_app_build, name="build") ns.add_task(esp32s2_app_build, name="build-s2") ns.add_task(esp32s3_app_build, name="build-s3") ns.add_task(esp32_app_clean, name="clean") ns.add_task(esp32_app_flash, name="flash") ns.add_task(esp32_app_gdb, name="app-gdb") ns.add_task(esp32_app_menuconfig, name="app-menuconfig") ns.add_task(esp32_decode_backtrace, name="decode-backtrace") ================================================ FILE: tasks/gdb.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # from __future__ import annotations try: from shutil import which # Python 3 except ImportError: # Python 2 from distutils.spawn import find_executable as which def gdb_find(prefix: str | None = None) -> str: if prefix is None: prefix = "arm-none-eabi-" base_name = "{}gdb".format(prefix) ordered_names = ("{}-py".format(base_name), base_name) for name in ordered_names: gdb = which(name) if gdb: return gdb raise FileNotFoundError("Cannot find {}".format(" or ".join(ordered_names))) def gdb_build_cmd( extra_args: str | None, elf: str, gdb_port: int, reset: bool = True, gdb_prefix: str | None = None, ) -> str: base_cmd = '{gdb} --eval-command="target remote localhost:{port}"'.format( gdb=gdb_find(gdb_prefix), port=gdb_port ) if extra_args is None: extra_args = "" reset_cmd = '--ex="mon reset"' if reset else "" base_cmd += " {reset_cmd} {} {reset_cmd} --se={}".format(extra_args, elf, reset_cmd=reset_cmd) return base_cmd ================================================ FILE: tasks/macos_ftdi.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # from __future__ import annotations from collections.abc import Generator from contextlib import contextmanager from os import uname from typing import TYPE_CHECKING if TYPE_CHECKING: from tasks.lib.invoke_utils import Context APPLE_FTDI_DRIVER_BUNDLE_ID = "com.apple.driver.AppleUSBFTDI" def is_macos() -> bool: return uname()[0] == "Darwin" def _unload_apple_ftdi_driver_if_needed(ctx: Context) -> bool | None: if not is_macos(): return None result = ctx.run("kextstat -b {}".format(APPLE_FTDI_DRIVER_BUNDLE_ID), hide=True) loaded = APPLE_FTDI_DRIVER_BUNDLE_ID in result.stdout if loaded: print("Unloading Apple FTDI driver...") ctx.run("sudo kextunload -b {}".format(APPLE_FTDI_DRIVER_BUNDLE_ID)) return loaded def _load_apple_ftdi_driver_if_needed(ctx: Context) -> None: if not is_macos(): return print("Re-loading Apple FTDI driver...") ctx.run("sudo kextload -b {}".format(APPLE_FTDI_DRIVER_BUNDLE_ID)) @contextmanager def apple_ftdi_driver_disable(ctx: Context) -> Generator[None, None, None]: was_ftdi_driver_loaded = _unload_apple_ftdi_driver_if_needed(ctx) try: yield finally: if was_ftdi_driver_loaded: _load_apple_ftdi_driver_if_needed(ctx) ================================================ FILE: tasks/nrf.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # from __future__ import annotations import os import shutil import sys from typing import TYPE_CHECKING from invoke import Collection, task from .gdb import gdb_build_cmd from .print_chunk_watcher import PrintChunkWatcher if TYPE_CHECKING: from tasks.lib.invoke_utils import Context TASKS_DIR = os.path.dirname(__file__) MEMFAULT_SDK_ROOT = os.path.dirname(TASKS_DIR) NRF_ROOT = os.path.join(MEMFAULT_SDK_ROOT, "examples", "nrf5") NRF_SDK_ROOT = os.path.join(NRF_ROOT, "nrf5_sdk") NRF_DEMO_APP_ROOT = os.path.join(NRF_ROOT, "apps", "memfault_demo_app") NRF_DEMO_APP_ELF = os.path.join(NRF_DEMO_APP_ROOT, "build", "memfault_demo_app_nrf52840_s140.out") JLINK_GDB_SERVER_DEFAULT_PORT = 2331 JLINK_TELNET_SERVER_DEFAULT_PORT = 19021 @task def run_arm_toolchain_check(ctx: Context) -> None: if sys.version_info.major < 3: # shutil which is only available for python3 return arm_toolchain = shutil.which("arm-none-eabi-gcc") if arm_toolchain is None: msg = "Couldn't find arm toolchain. Currently using {} which can be found here {}".format( "8-2018-q4-major", "https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads", ) raise FileNotFoundError(msg) @task def nrf_console(ctx: Context, telnet: int = JLINK_TELNET_SERVER_DEFAULT_PORT) -> None: """Start a RTT console session""" ctx.run( "JLinkRTTClient -LocalEcho Off -RTTTelnetPort {telnet_port}".format(telnet_port=telnet), watchers=[PrintChunkWatcher(ctx)], ) def _run_demo_app_make_command(ctx: Context, rules: str | None = None) -> None: cmd_base = "make -j5" cmd = cmd_base if rules is None else "{} {}".format(cmd_base, rules) with ctx.cd(NRF_DEMO_APP_ROOT): ctx.run(cmd, env={"GNU_INSTALL_ROOT": ""}) @task(pre=[run_arm_toolchain_check]) def nrf_build(ctx: Context, skip_app_sign: bool = False) -> None: """Build a demo application that runs on the nrf52""" _run_demo_app_make_command(ctx) @task(pre=[run_arm_toolchain_check]) def nrf_clean(ctx: Context) -> None: """Clean demo application that runs on the nrf52""" _run_demo_app_make_command(ctx, "clean") @task( pre=[run_arm_toolchain_check], help={ "skip-softdevice-flash": "Skip flashing the softdevice. It's not changing so it only needs to be done once", "elf": "The elf to flash", }, ) def nrf_flash( ctx: Context, elf: str = NRF_DEMO_APP_ELF, gdb: int = JLINK_GDB_SERVER_DEFAULT_PORT, skip_softdevice_flash: bool = False, ) -> None: """Flash the application & softdevice (Master boot record, MBR + BLE stack)""" if not skip_softdevice_flash: _run_demo_app_make_command(ctx, "flash_softdevice") app_args = '--ex="load"' cmd = gdb_build_cmd(app_args, elf, gdb) ctx.run(cmd, pty=True) @task def nrf_gdbserver( ctx: Context, sn: str | None = None, device: str = "nRF52840_xxAA", gdb: int = JLINK_GDB_SERVER_DEFAULT_PORT, telnet: int = JLINK_TELNET_SERVER_DEFAULT_PORT, ) -> None: """Start a GDBServer that can attach to an nrf52 dev kit""" select_arg = "-select USB={}".format(sn) if sn else "" ctx.run( "JLinkGDBServer {select_arg} -if swd -device {device} -speed auto -port {gdb_port} \ -RTTTelnetPort {telnet_port}".format( device=device, select_arg=select_arg, gdb_port=gdb, telnet_port=telnet ) ) @task def nrf_eraseflash(ctx: Context) -> None: """Erase all flash contents of the nrf device putting the board back in a clean state. It's a good idea to run this before flashing a new application on the dev board""" cmd = "nrfutil device erase --all" ctx.run(cmd) @task def nrf_debug( ctx: Context, elf: str = NRF_DEMO_APP_ELF, gdb: int = JLINK_GDB_SERVER_DEFAULT_PORT ) -> None: """Run GDB, load the demo app ELF, and attach to the target via a running GDBServer""" cmd = gdb_build_cmd(None, elf, gdb, reset=False) ctx.run(cmd, pty=True) ns = Collection("nrf") ns.add_task(nrf_build, name="build") # pyright: ignore [reportUnknownMemberType] ns.add_task(nrf_clean, name="clean") # pyright: ignore [reportUnknownMemberType] ns.add_task(nrf_console, name="console") # pyright: ignore [reportUnknownMemberType] ns.add_task(nrf_debug, name="debug") # pyright: ignore [reportUnknownMemberType] ns.add_task(nrf_eraseflash, name="eraseflash") # pyright: ignore [reportUnknownMemberType] ns.add_task(nrf_flash, name="flash") # pyright: ignore [reportUnknownMemberType] ns.add_task(nrf_gdbserver, name="gdbserver") # pyright: ignore [reportUnknownMemberType] ================================================ FILE: tasks/print_chunk_watcher.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # from __future__ import annotations import sys from tempfile import NamedTemporaryFile from typing import TYPE_CHECKING from invoke.watchers import StreamWatcher if TYPE_CHECKING: from tasks.lib.invoke_utils import Context class PrintChunkWatcher(StreamWatcher): """Automagically detects and executes CLI command dumped to console via 'print_chunk' cmd The 'print_chunk' command can be used to dump the contents of the next Memfault data chunk to the console. This watcher can be installed to look for the output of this command. If the output is found the user will be prompted about whether or not they would like to upload the file. This way a user doesn't have to copy & paste the large block manually """ def __init__(self, ctx: Context) -> None: super(PrintChunkWatcher, self).__init__() self.search_start_idx = 0 self.ctx = ctx def submit(self, stream: str | None) -> list[str]: if stream is None: return [] search_stream = stream[self.search_start_idx :] start_idx = search_stream.find("echo \\") end_idx = search_stream.find("print_chunk done") if start_idx == -1 or end_idx == -1: # We haven't found a full print_chunk return [] # Forward search index so we don't keep detecting the same 'print_chunk' call self.search_start_idx = len(stream) cmd = search_stream[start_idx:end_idx] if "" in cmd: info = ( "\n\nInvoke CLI wrapper detected 'print_chunk' call but a valid\n" "'Memfault-Project-Key' was not specified. Please consult README for target\n" "platform for more info on how to set the value." ) print(info) return [] # The command can be very long since it's an encoded dump of all the memory in the coredump # and some platforms aren't consistent with how they format newlines. Let's clean up the newlines # format used and save the command run in a temp file with NamedTemporaryFile() as cmd_f: for line in cmd.splitlines(): if len(line) == 0: continue cmd_f.write("{}\n".format(line).encode()) cmd_f.flush() cmd_f.seek(0) print("\n\nInvoke CLI wrapper detected 'print_chunk' call") print("Would you like to run the command displayed above? [y/n]", end=None) val = sys.stdin.read(1) if val.lower() == "y": print("Running curl command dumped to CLI:\n\n") result = self.ctx.run("sh {}".format(cmd_f.name), hide="both") print("Result {} \n{}".format(result.exited, result.stdout)) else: print("Coredump upload skipped") return [] ================================================ FILE: tasks/wiced.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # from __future__ import annotations import contextlib import os import sys from glob import glob from typing import TYPE_CHECKING from invoke import Collection, task from .gdb import gdb_build_cmd from .print_chunk_watcher import PrintChunkWatcher if TYPE_CHECKING: from tasks.lib.invoke_utils import Context TASKS_DIR = os.path.dirname(__file__) MEMFAULT_SDK_ROOT = os.path.join(TASKS_DIR, "..") WICED_ROOT = os.path.join(MEMFAULT_SDK_ROOT, "examples", "wiced") WICED_DEMO_APP_ROOT = os.path.join(WICED_ROOT, "memfault_demo_app") WICED_DEMO_APP_MAKEFILE = os.path.join(WICED_DEMO_APP_ROOT, "Makefile") WICED_SDK_ROOT = os.path.join(WICED_ROOT, "wiced_sdk") WICED_SDK_43X_ROOT = os.path.join(WICED_SDK_ROOT, "43xxx_Wi-Fi") WICED_MAKE = os.path.join(WICED_SDK_43X_ROOT, "make") WICED_MAKEFILE = os.path.join(WICED_SDK_43X_ROOT, "Makefile") WICED_GDBINIT = os.path.join(WICED_SDK_43X_ROOT, ".gdbinit") DEMO_APP_TARGET = "memfault_demo_app-BCM943364WCD1-SDIO-debug" DEMO_APP_ELF = os.path.join( WICED_SDK_43X_ROOT, "build", DEMO_APP_TARGET, "binary", DEMO_APP_TARGET + ".elf" ) def _wiced_guess_console_port() -> str: def _wiced_find_console_port() -> str: usb_paths = glob("/dev/cu.usbserial-*1") if usb_paths: return usb_paths[0] print( "Cannot find WICED console /dev/... nor ftdi:// path, please specify it manually using --port" ) sys.exit(1) port = _wiced_find_console_port() print("No --port specified, using console port {port}".format(port=port)) return port def _wiced_make(ctx: Context, *args: str, pty: bool = False) -> None: with ctx.cd(WICED_SDK_43X_ROOT): ctx.run( "{make} {args}".format(make=WICED_MAKE, args=" ".join(args)), pty=pty, ) def _run_openocd_cmd(ctx: Context, openocd_cmd: str | None = None) -> None: cmd = ( "./tools/OpenOCD/OSX/openocd-all-brcm-libftdi" " -s ./tools/OpenOCD/scripts" " -f ./tools/OpenOCD/CYW9WCD1EVAL1.cfg" " -f ./tools/OpenOCD/stm32f4x.cfg" " -f ./tools/OpenOCD/stm32f4x_gdb_jtag.cfg" ) if openocd_cmd: cmd = '{} -c "{}"'.format(cmd, openocd_cmd) with ctx.cd(WICED_SDK_43X_ROOT): ctx.run(cmd) @task def wiced_build(ctx: Context) -> None: """Build WICED demo app""" _wiced_make(ctx, DEMO_APP_TARGET) @task def wiced_clean(ctx: Context) -> None: """Clean WICED demo app""" _wiced_make(ctx, "clean") @task def wiced_flash(ctx: Context) -> None: """Flashes WICED demo app""" # See doc/make_target_examples.txt: # "* For the BCM943364WCD1, BCM943438WCD1, and BCM9433634WCD1 examples you must also add "download_apps" to the end of # the target string to download the WLAN firmware to the external flash": _wiced_make(ctx, DEMO_APP_TARGET, "download", "download_apps") # The WICED SDK flash commands leaves the CoreDebug->DHCSR DEBUGEN bit set when the flash # commands are run. This bit only gets reset on a full POR. When set, if a breakpoint is hit, # the system will HALT. If no debugger is attached, it will look like the chip is just hung. Let's # gracefully tear down openocd so the bit gets cleared! _run_openocd_cmd(ctx, "stm32f4xx.cpu cortex_m disconnect; shutdown") @task def wiced_debug(ctx: Context) -> None: """Runs GDB using WICED's debug Makefile target -- this runs OpenOCD inside a GDB shell""" _wiced_make(ctx, DEMO_APP_TARGET, "debug", pty=True) @task def wiced_gdb(ctx: Context, elf: str = DEMO_APP_ELF, gdb: int = 3333) -> None: """Runs GDB, loads the demo app elf and attaches it to openocd""" with ctx.cd(WICED_SDK_43X_ROOT): # Remove the generated .gdbinit -- running openocd using "shell start" within gdb doesn't seem to work very # reliably across board resets for some reason... with contextlib.suppress(FileNotFoundError): os.unlink(WICED_GDBINIT) cmd = gdb_build_cmd(None, elf, gdb, reset=False) ctx.run(cmd, pty=True) @task def wiced_openocd(ctx: Context) -> None: """Runs openocd""" _run_openocd_cmd(ctx) @task def wiced_console(ctx: Context, port: str | None = None) -> None: """Attach debug console""" if port is None: port = _wiced_guess_console_port() ctx.run( "miniterm.py --raw {port} 115200".format(port=port), pty=True, watchers=[PrintChunkWatcher(ctx)], ) ns = Collection("wiced") ns.add_task(wiced_console, name="console") # pyright: ignore [reportUnknownMemberType] ns.add_task(wiced_build, name="build") # pyright: ignore [reportUnknownMemberType] ns.add_task(wiced_clean, name="clean") # pyright: ignore [reportUnknownMemberType] ns.add_task(wiced_flash, name="flash") # pyright: ignore [reportUnknownMemberType] ns.add_task(wiced_debug, name="debug") # pyright: ignore [reportUnknownMemberType] ns.add_task(wiced_gdb, name="gdb") # pyright: ignore [reportUnknownMemberType] ns.add_task(wiced_openocd, name="gdbserver") # pyright: ignore [reportUnknownMemberType] ================================================ FILE: tasks/zephyr.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # from __future__ import annotations import json import os from typing import TYPE_CHECKING from invoke import task if TYPE_CHECKING: from tasks.lib.invoke_utils import Context SDK_FW_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ZEPHYR_ROOT = os.path.join(SDK_FW_ROOT, "examples", "zephyr", "stm32l4_disco") ZEPHYR_UPSTREAM_REPO = "https://github.com/zephyrproject-rtos/zephyr.git" def _shallow_clone_and_checkout( ctx: Context, repo_uri: str, branch: str, dest_dir: str, commit: str ) -> None: ctx.run(f"git clone {repo_uri} --single-branch --branch {branch} {dest_dir}") with ctx.cd(dest_dir): ctx.run(f"git checkout {commit}") @task() def zephyr_project_ci_setup(ctx: Context) -> None: """Prepare Zephyr Projects for CI Tests Clones the repos specified in .ci-project-setup.json if and only if they don't already exist. This makes it easy to use pre-cached artifacts when running the job in CI """ zephyr_ci_cfg_json = os.path.join(ZEPHYR_ROOT, ".ci-project-setup.json") with open(zephyr_ci_cfg_json, "rb") as json_file: data = json.load(json_file) for project, info in data.items(): dest_dir = os.path.join(ZEPHYR_ROOT, "build", project) clone_dir = os.path.join(dest_dir, "zephyr") if not os.path.exists(clone_dir): _shallow_clone_and_checkout( ctx, ZEPHYR_UPSTREAM_REPO, info["branch"], clone_dir, info["commit"] ) with ctx.cd(dest_dir): ctx.run("west init -l zephyr") ctx.run("west update") ================================================ FILE: tests/README.md ================================================ # Memfault Embedded SDK Tests Tests are located in this directory. The tests are organized into two categories: ```bash unit/ # Unit tests for the SDK ports/ # Tests for SDK ports (eg Zephyr) ``` See the individual README files in each directory for more details on how to run the tests. ================================================ FILE: tests/unit/Makefile ================================================ # Collects all the Makefile_*.mk in this directory and then invokes them using # recursive make (sorry). This let's us have unit tests for our different C SDK files! TEST_MAKEFILE_ROOT := makefiles TEST_MAKEFILE_FILTER ?= * TEST_MAKEFILES := $(wildcard $(TEST_MAKEFILE_ROOT)/Makefile_$(TEST_MAKEFILE_FILTER)) MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) CURRENT_DIR := $(dir $(MKFILE_PATH)) # Convenience defines that can be used for individual tests in makefiles/ export BUILD_DIR=build export PROJECT_DIR := $(abspath $(CURRENT_DIR)/../..) export MFLT_TEST_ROOT := $(CURRENT_DIR:%/=%) export PROJECT_ROOT_DIR := $(PROJECT_DIR) export MFLT_PORTS_DIR := $(PROJECT_DIR)/ports export MFLT_COMPONENTS_DIR := $(PROJECT_DIR)/components export MFLT_UTILS_SRC_DIR := $(MFLT_COMPONENTS_DIR)/util/src export MFLT_TEST_SRC_DIR ?= $(MFLT_TEST_ROOT)/src export MFLT_TEST_COMMON_SRC_DIR := $(MFLT_TEST_ROOT)/src export MFLT_TEST_FAKE_DIR := $(MFLT_TEST_ROOT)/fakes export MFLT_TEST_MOCK_DIR := $(MFLT_TEST_ROOT)/mocks export MFLT_TEST_STUB_DIR := $(MFLT_TEST_ROOT)/stubs export CPPUTEST_MAKFILE_INFRA := $(MFLT_TEST_ROOT)/MakefileWorkerOverrides.mk # Gather -Icomponents/*/src and -Icomponents/*/include: MFLT_COMPONENTS_ALL_INCLUDE_DIRS := \ $(wildcard $(MFLT_COMPONENTS_DIR)/*/include) \ $(PROJECT_DIR)/components/include \ $(PROJECT_DIR)/ports/include MFLT_COMPONENTS_ALL_SRC_DIRS := $(wildcard $(MFLT_COMPONENTS_DIR)/*/src) MFLT_COMPONENTS_ALL_INCLUDE_FLAGS := \ $(foreach COMPONENT_INCLUDE, $(MFLT_COMPONENTS_ALL_INCLUDE_DIRS) $(MFLT_COMPONENTS_ALL_SRC_DIRS), -I$(COMPONENT_INCLUDE)) export MEMFAULT_EXTRA_INC_PATHS += \ $(MFLT_COMPONENTS_ALL_INCLUDE_FLAGS) all: $(TEST_MAKEFILES) compile: CPPUTEST_BUILD_RULE=start compile: $(TEST_MAKEFILES) .PHONY: print-makefiles print-makefiles: @echo $(TEST_MAKEFILES) # if you are building with alternate compilers, be sure gcov is compatible. you # may need to do something like this, for example: # > CC=gcc-8 CXX=g++-8 GCOV_TOOL=gcov-8 make lcov GCOV_TOOL ?= gcov # if the compiler is clang or gcc < 9, use lcov. gcc 9+ will use fastcov CC_VERSION_OUTPUT ="$(shell $(CXX) -v 2>&1)" CLANG_STR = clang # check if the compiler is clang ifeq ($(findstring $(CLANG_STR),$(CC_VERSION_OUTPUT)),$(CLANG_STR)) USE_LCOV = 1 # use llvm-cov LLVM_COV ?= llvm-cov # terminate early if LLVM_COV is not on the path ifeq (,$(shell command -v $(LLVM_COV))) $(error $(LLVM_COV) not found. Check your llvm install and path for $(LLVM_COV)) endif GCOV_TOOL = $(abspath $(MFLT_TEST_ROOT)/llvm-cov-wrapper.sh) else # if the compiler is gcc < 9, use lcov. gcc 9+ will use fastcov GCC_VERSION := $(shell $(CC) -dumpversion) GCC_MAJOR_VERSION := $(firstword $(subst ., ,$(GCC_VERSION))) GCC_VERSION_LT9 = $(shell if [ $(GCC_MAJOR_VERSION) -lt "9" ]; then echo 1; fi) ifeq ($(GCC_VERSION_LT9),1) USE_LCOV = 1 endif endif # Exclude these directory stems from the coverage results LCOV_EXCLUDE_DIRS := \ '*/tests/*' \ '*/CppUTestExt/*' \ '/usr/include/*' \ # set additional args for genhtml in environment var, like --dark-mode GENHTML_EXTRA_ARGS ?= LCOV_INFO_FILE = $(BUILD_DIR)/lcov.info lcov: $(TEST_MAKEFILES) ifeq ($(USE_LCOV),1) lcov --gcov-tool $(GCOV_TOOL) --rc lcov_branch_coverage=1 \ --base-directory . --directory . -c -o $(LCOV_INFO_FILE) \ $(addprefix --exclude ,$(LCOV_EXCLUDE_DIRS)) else fastcov --gcov $(GCOV_TOOL) --lcov -o $(LCOV_INFO_FILE) --branch-coverage \ --process-gcno --exclude-glob $(LCOV_EXCLUDE_DIRS) endif genhtml --prefix $(PROJECT_DIR) --branch-coverage --legend \ -o test_coverage -t "coverage" --num-spaces 4 \ $(LCOV_INFO_FILE) -o $(BUILD_DIR)/test_coverage/ $(GENHTML_EXTRA_ARGS) @echo See coverage output in file://$(abspath $(BUILD_DIR)/test_coverage/index.html) $(TEST_MAKEFILES): $(SILENCE)$(MAKE) -f $@ $(CPPUTEST_BUILD_RULE) COMPONENT_NAME=$(patsubst makefiles/Makefile_%.mk,%,$@) clean: rm -rf $(BUILD_DIR) .PHONY: all clean $(TEST_MAKEFILES) ================================================ FILE: tests/unit/MakefileWorker.mk ================================================ #--------- # # MakefileWorker.mk # # Include this helper file in your makefile # It makes # A static library # A test executable # # See this example for parameter settings # examples/Makefile # #---------- # Inputs - these variables describe what to build # # INCLUDE_DIRS - Directories used to search for include files. # This generates a -I for each directory # SRC_DIRS - Directories containing source files to build into the library # SRC_FILES - Specific source files to build into library. Helpful when not all code # in a directory can be built for test (hopefully a temporary situation) # TEST_SRC_DIRS - Directories containing unit test code build into the unit test runner # These do not go in a library. They are explicitly included in the test runner # TEST_SRC_FILES - Specific source files to build into the unit test runner # These do not go in a library. They are explicitly included in the test runner # MOCKS_SRC_DIRS - Directories containing mock source files to build into the test runner # These do not go in a library. They are explicitly included in the test runner #---------- # You can adjust these variables to influence how to build the test target # and where to put and name outputs # See below to determine defaults # COMPONENT_NAME - the name of the thing being built # TEST_TARGET - name of the test executable. By default it is # $(COMPONENT_NAME)_tests # Helpful if you want 1 > make files in the same directory with different # executables as output. # CPPUTEST_HOME - where CppUTest home dir found # TARGET_PLATFORM - Influences how the outputs are generated by modifying the # CPPUTEST_OBJS_DIR and CPPUTEST_LIB_DIR to use a sub-directory under the # normal objs and lib directories. Also modifies where to search for the # CPPUTEST_LIB to link against. # CPPUTEST_OBJS_DIR - a directory where o and d files go # CPPUTEST_LIB_DIR - a directory where libs go # CPPUTEST_ENABLE_DEBUG - build for debug # CPPUTEST_USE_MEM_LEAK_DETECTION - Links with overridden new and delete # CPPUTEST_USE_STD_CPP_LIB - Set to N to keep the standard C++ library out # of the test harness # CPPUTEST_USE_GCOV - Turn on coverage analysis # Clean then build with this flag set to Y, then 'make gcov' # CPPUTEST_MAPFILE - generate a map file # CPPUTEST_WARNINGFLAGS - overly picky by default # OTHER_MAKEFILE_TO_INCLUDE - a hook to use this makefile to make # other targets. Like CSlim, which is part of fitnesse # CPPUTEST_USE_VPATH - Use Make's VPATH functionality to support user # specification of source files and directories that aren't below # the user's Makefile in the directory tree, like: # SRC_DIRS += ../../lib/foo # It defaults to N, and shouldn't be necessary except in the above case. #---------- # # Other flags users can initialize to sneak in their settings # CPPUTEST_CXXFLAGS - flags for the C++ compiler # CPPUTEST_CPPFLAGS - flags for the C++ AND C preprocessor # CPPUTEST_CFLAGS - flags for the C compiler # CPPUTEST_LDFLAGS - Linker flags #---------- # Some behavior is weird on some platforms. Need to discover the platform. # Platforms UNAME_OUTPUT = "$(shell uname -a)" UNAME_VERSION = "$(shell uname -r)" UNAME_MAJOR_VERSION = $(shell echo $(UNAME_VERSION) | cut -d. -f1) VERSION_GREATER_OR_EQUAL = $(shell [[ $1 -ge $2 ]] && echo true) MACOSX_STR = Darwin MINGW_STR = MINGW CYGWIN_STR = CYGWIN LINUX_STR = Linux SUNOS_STR = SunOS UNKNOWN_OS_STR = Unknown # Compilers CC_VERSION_OUTPUT ="$(shell $(CXX) -v 2>&1)" CLANG_STR = clang SUNSTUDIO_CXX_STR = SunStudio UNAME_OS = $(UNKNOWN_OS_STR) ifeq ($(findstring $(MINGW_STR),$(UNAME_OUTPUT)),$(MINGW_STR)) UNAME_OS = $(MINGW_STR) endif ifeq ($(findstring $(CYGWIN_STR),$(UNAME_OUTPUT)),$(CYGWIN_STR)) UNAME_OS = $(CYGWIN_STR) endif ifeq ($(findstring $(LINUX_STR),$(UNAME_OUTPUT)),$(LINUX_STR)) UNAME_OS = $(LINUX_STR) endif ifeq ($(findstring $(MACOSX_STR),$(UNAME_OUTPUT)),$(MACOSX_STR)) UNAME_OS = $(MACOSX_STR) #lion has a problem with the 'v' part of -a UNAME_OUTPUT = "$(shell uname -pmnrs)" endif ifeq ($(findstring $(SUNOS_STR),$(UNAME_OUTPUT)),$(SUNOS_STR)) UNAME_OS = $(SUNOS_STR) SUNSTUDIO_CXX_ERR_STR = CC -flags ifeq ($(findstring $(SUNSTUDIO_CXX_ERR_STR),$(CC_VERSION_OUTPUT)),$(SUNSTUDIO_CXX_ERR_STR)) CC_VERSION_OUTPUT ="$(shell $(CXX) -V 2>&1)" COMPILER_NAME = $(SUNSTUDIO_CXX_STR) endif endif ifeq ($(findstring $(CLANG_STR),$(CC_VERSION_OUTPUT)),$(CLANG_STR)) COMPILER_NAME = $(CLANG_STR) endif #Kludge for mingw, it does not have cc.exe, but gcc.exe will do ifeq ($(UNAME_OS),$(MINGW_STR)) CC := gcc endif #And another kludge. Exception handling in gcc 4.6.2 is broken when linking the # Standard C++ library as a shared library. Unbelievable. ifeq ($(UNAME_OS),$(MINGW_STR)) CPPUTEST_LDFLAGS += -static endif ifeq ($(UNAME_OS),$(CYGWIN_STR)) CPPUTEST_LDFLAGS += -static endif #Kludge for MacOsX gcc compiler on Darwin9 who can't handle pedantic ifeq ($(UNAME_OS),$(MACOSX_STR)) ifeq ($(findstring Version 9,$(UNAME_OUTPUT)),Version 9) CPPUTEST_PEDANTIC_ERRORS = N endif endif # Starting in macOS Sonoma (version 23 and above), we do not pick up libs (eg libc++) automagically from the conda env. To work # around this, we explicitly set rpath and -L to point to the conda env lib directory ifeq ($(UNAME_OS), $(MACOSX_STR)) ifeq ($(call VERSION_GREATER_OR_EQUAL, $(UNAME_MAJOR_VERSION), 23), true) ifneq ($(CONDA_PREFIX),) CPPUTEST_LDFLAGS += \ -rpath $(CONDA_PREFIX)/lib \ -L$(CONDA_PREFIX)/lib endif # conda env check endif # macos major version check endif # macos check ifndef COMPONENT_NAME COMPONENT_NAME = name_this_in_the_makefile endif # Debug on by default ifndef CPPUTEST_ENABLE_DEBUG CPPUTEST_ENABLE_DEBUG = Y endif # new and delete for memory leak detection on by default ifndef CPPUTEST_USE_MEM_LEAK_DETECTION CPPUTEST_USE_MEM_LEAK_DETECTION = Y endif # Use the standard C library ifndef CPPUTEST_USE_STD_C_LIB CPPUTEST_USE_STD_C_LIB = Y endif # Use the standard C++ library ifndef CPPUTEST_USE_STD_CPP_LIB CPPUTEST_USE_STD_CPP_LIB = Y endif # Use long long, off by default ifndef CPPUTEST_USE_LONG_LONG CPPUTEST_USE_LONG_LONG = N endif # Use gcov, off by default ifndef CPPUTEST_USE_GCOV CPPUTEST_USE_GCOV = N endif ifndef CPPUTEST_PEDANTIC_ERRORS CPPUTEST_PEDANTIC_ERRORS = Y endif # Default warnings ifndef CPPUTEST_WARNINGFLAGS CPPUTEST_WARNINGFLAGS = -Wall -Wextra -Werror -Wshadow -Wswitch-default -Wswitch-enum -Wconversion -Wno-long-long ifeq ($(CPPUTEST_PEDANTIC_ERRORS), Y) CPPUTEST_WARNINGFLAGS += -pedantic-errors endif ifeq ($(UNAME_OS),$(LINUX_STR)) CPPUTEST_WARNINGFLAGS += -Wsign-conversion endif CPPUTEST_CXX_WARNINGFLAGS = -Woverloaded-virtual ifndef CPPUTEST_C_WARNINGFLAGS CPPUTEST_C_WARNINGFLAGS = -Wstrict-prototypes endif endif #Wonderful extra compiler warnings with clang ifeq ($(COMPILER_NAME),$(CLANG_STR)) # -Wno-disabled-macro-expansion -> Have to disable the macro expansion warning as the operator new overload warns on that. # -Wno-padded -> I sort-of like this warning but if there is a bool at the end of the class, it seems impossible to remove it! (except by making padding explicit) # -Wno-global-constructors Wno-exit-time-destructors -> Great warnings, but in CppUTest it is impossible to avoid as the automatic test registration depends on the global ctor and dtor # -Wno-weak-vtables -> The TEST_GROUP macro declares a class and will automatically inline its methods. Thats ok as they are only in one translation unit. Unfortunately, the warning can't detect that, so it must be disabled. # -Wno-old-style-casts -> We only use old style casts by decision # -Wno-c++11-long-long -> When it detects long long, then we can use it and no need for a warning about that CPPUTEST_CXX_WARNINGFLAGS += -Weverything -Wno-disabled-macro-expansion -Wno-padded -Wno-global-constructors -Wno-exit-time-destructors -Wno-weak-vtables -Wno-old-style-cast -Wno-c++11-long-long CPPUTEST_C_WARNINGFLAGS += -Weverything -Wno-padded # Clang "7" (Xcode 7 command-line tools) introduced new warnings by default that don't exist on previous versions of clang and cause errors when present. ifeq ($(findstring clang-7,$(CC_VERSION_OUTPUT)),clang-7) # -Wno-reserved-id-macro -> Many CppUTest macros start with __, which is a reserved namespace # -Wno-keyword-macro -> CppUTest redefines the 'new' keyword for memory leak tracking CPPUTEST_CXX_WARNINGFLAGS += -Wno-reserved-id-macro -Wno-keyword-macro CPPUTEST_C_WARNINGFLAGS += -Wno-reserved-id-macro -Wno-keyword-macro endif endif # Uhm. Maybe put some warning flags for SunStudio here? ifeq ($(COMPILER_NAME),$(SUNSTUDIO_CXX_STR)) CPPUTEST_CXX_WARNINGFLAGS = CPPUTEST_C_WARNINGFLAGS = endif # Default dir for temporary files (d, o) ifndef CPPUTEST_OBJS_DIR ifndef TARGET_PLATFORM CPPUTEST_OBJS_DIR = objs else CPPUTEST_OBJS_DIR = objs/$(TARGET_PLATFORM) endif endif # Default dir for the output library ifndef CPPUTEST_LIB_DIR ifndef TARGET_PLATFORM CPPUTEST_LIB_DIR = lib else CPPUTEST_LIB_DIR = lib/$(TARGET_PLATFORM) endif endif # No map by default ifndef CPPUTEST_MAP_FILE CPPUTEST_MAP_FILE = N endif # No extensions is default ifndef CPPUTEST_USE_EXTENSIONS CPPUTEST_USE_EXTENSIONS = N endif # No VPATH is default ifndef CPPUTEST_USE_VPATH CPPUTEST_USE_VPATH := N endif # Make empty, instead of 'N', for usage in $(if ) conditionals ifneq ($(CPPUTEST_USE_VPATH), Y) CPPUTEST_USE_VPATH := endif ifndef TARGET_PLATFORM CPPUTEST_LIB_LINK_DIR = $(CPPUTEST_HOME) else CPPUTEST_LIB_LINK_DIR = $(CPPUTEST_HOME)/$(TARGET_PLATFORM) endif # -------------------------------------- # derived flags in the following area # -------------------------------------- # Without the C library, we'll need to disable the C++ library and ... ifeq ($(CPPUTEST_USE_STD_C_LIB), N) CPPUTEST_USE_STD_CPP_LIB = N CPPUTEST_USE_MEM_LEAK_DETECTION = N CPPUTEST_CPPFLAGS += -DCPPUTEST_STD_C_LIB_DISABLED CPPUTEST_CPPFLAGS += -nostdinc endif ifeq ($(CPPUTEST_USE_MEM_LEAK_DETECTION), N) CPPUTEST_CPPFLAGS += -DCPPUTEST_MEM_LEAK_DETECTION_DISABLED else ifndef CPPUTEST_MEMLEAK_DETECTOR_NEW_MACRO_FILE CPPUTEST_MEMLEAK_DETECTOR_NEW_MACRO_FILE = -include $(CPPUTEST_HOME)/include/CppUTest/MemoryLeakDetectorNewMacros.h endif ifndef CPPUTEST_MEMLEAK_DETECTOR_MALLOC_MACRO_FILE CPPUTEST_MEMLEAK_DETECTOR_MALLOC_MACRO_FILE = -include $(CPPUTEST_HOME)/include/CppUTest/MemoryLeakDetectorMallocMacros.h endif endif ifeq ($(CPPUTEST_USE_LONG_LONG), Y) CPPUTEST_CPPFLAGS += -DCPPUTEST_USE_LONG_LONG endif ifeq ($(CPPUTEST_ENABLE_DEBUG), Y) CPPUTEST_CXXFLAGS += -g CPPUTEST_CFLAGS += -g CPPUTEST_LDFLAGS += -g endif ifeq ($(CPPUTEST_USE_STD_CPP_LIB), N) CPPUTEST_CPPFLAGS += -DCPPUTEST_STD_CPP_LIB_DISABLED ifeq ($(CPPUTEST_USE_STD_C_LIB), Y) CPPUTEST_CXXFLAGS += -nostdinc++ endif endif ifdef $(GMOCK_HOME) GTEST_HOME = $(GMOCK_HOME)/gtest CPPUTEST_CPPFLAGS += -I$(GMOCK_HOME)/include GMOCK_LIBRARY = $(GMOCK_HOME)/lib/.libs/libgmock.a LD_LIBRARIES += $(GMOCK_LIBRARY) CPPUTEST_CPPFLAGS += -DCPPUTEST_INCLUDE_GTEST_TESTS CPPUTEST_WARNINGFLAGS = CPPUTEST_CPPFLAGS += -I$(GTEST_HOME)/include -I$(GTEST_HOME) GTEST_LIBRARY = $(GTEST_HOME)/lib/.libs/libgtest.a LD_LIBRARIES += $(GTEST_LIBRARY) endif ifeq ($(CPPUTEST_USE_GCOV), Y) CPPUTEST_CXXFLAGS += -fprofile-arcs -ftest-coverage CPPUTEST_CFLAGS += -fprofile-arcs -ftest-coverage endif CPPUTEST_CXXFLAGS += $(CPPUTEST_CXX_WARNINGFLAGS) CPPUTEST_CPPFLAGS += $(CPPUTEST_WARNINGFLAGS) CPPUTEST_CFLAGS += $(CPPUTEST_C_WARNINGFLAGS) CPPUTEST_CXXFLAGS += $(CPPUTEST_MEMLEAK_DETECTOR_NEW_MACRO_FILE) # CppUTest's malloc memory leak macros hit errors when used with code that uses namespaced malloc calls (i.e. std::malloc) # The root cause of this is the combination the -include flag, the macro #defines, and the namespaced calls. # The -include flag always includes the macro header (MemoryLeakDetectorMallocMacros.h) into the source files first. # The macro header defines the malloc family replacements. The preprocessor replaces the token with the CppUTest function # but this yields std::cpputest_malloc_location or similar causing a compilation error. This most commonly occurs on # macOS due to stdlib defaulting to libc++. To avoid this but keep the most coverage for leaks only enable the malloc # macros with CPPFLAGS on Linux vs CFLAGS on macOS. If the header is needed in tests to detect malloc leaks in the tests, # include the header AFTER all stdlib includes. The malloc calls cannot use the namespaced version. # On macOS only, with this workaround we will miss any leaks caused from malloc calls within external cpp libs (stdlib, etc.) ifeq ($(UNAME_OS),$(LINUX_STR)) CPPUTEST_CPPFLAGS += $(CPPUTEST_MEMLEAK_DETECTOR_MALLOC_MACRO_FILE) else CPPUTEST_CFLAGS += $(CPPUTEST_MEMLEAK_DETECTOR_MALLOC_MACRO_FILE) endif TARGET_MAP = $(COMPONENT_NAME).map.txt ifeq ($(CPPUTEST_MAP_FILE), Y) CPPUTEST_LDFLAGS += -Wl,-map,$(TARGET_MAP) endif ifdef CPPUTEST_STATIC_REALTIME LD_LIBRARIES += -lrt endif TARGET_LIB = \ $(CPPUTEST_LIB_DIR)/lib$(COMPONENT_NAME).a ifndef TEST_TARGET ifndef TARGET_PLATFORM TEST_TARGET = $(COMPONENT_NAME)_tests else TEST_TARGET = $(COMPONENT_NAME)_$(TARGET_PLATFORM)_tests endif endif #Helper Functions get_src_from_dir = $(wildcard $1/*.cpp) $(wildcard $1/*.cc) $(wildcard $1/*.c) get_dirs_from_dirspec = $(wildcard $1) get_src_from_dir_list = $(foreach dir, $1, $(call get_src_from_dir,$(dir))) __src_to = $(subst .c,$1, $(subst .cc,$1, $(subst .cpp,$1,$(if $(CPPUTEST_USE_VPATH),$(notdir $2),$2)))) src_to = $(addprefix $(CPPUTEST_OBJS_DIR)/,$(call __src_to,$1,$2)) src_to_o = $(call src_to,.o,$1) src_to_d = $(call src_to,.d,$1) src_to_gcda = $(call src_to,.gcda,$1) src_to_gcno = $(call src_to,.gcno,$1) time = $(shell date +%s) delta_t = $(eval minus, $1, $2) debug_print_list = $(foreach word,$1,echo " $(word)";) echo; #Derived STUFF_TO_CLEAN += $(TEST_TARGET) $(TEST_TARGET).exe $(TARGET_LIB) $(TARGET_MAP) SRC += $(call get_src_from_dir_list, $(SRC_DIRS)) $(SRC_FILES) OBJ = $(call src_to_o,$(SRC)) STUFF_TO_CLEAN += $(OBJ) TEST_SRC += $(call get_src_from_dir_list, $(TEST_SRC_DIRS)) $(TEST_SRC_FILES) TEST_OBJS = $(call src_to_o,$(TEST_SRC)) STUFF_TO_CLEAN += $(TEST_OBJS) MOCKS_SRC += $(call get_src_from_dir_list, $(MOCKS_SRC_DIRS)) MOCKS_OBJS = $(call src_to_o,$(MOCKS_SRC)) STUFF_TO_CLEAN += $(MOCKS_OBJS) ALL_SRC = $(SRC) $(TEST_SRC) $(MOCKS_SRC) # If we're using VPATH ifeq ($(CPPUTEST_USE_VPATH), Y) # gather all the source directories and add them VPATH += $(sort $(dir $(ALL_SRC))) # Add the component name to the objs dir path, to differentiate between same-name objects CPPUTEST_OBJS_DIR := $(addsuffix /$(COMPONENT_NAME),$(CPPUTEST_OBJS_DIR)) endif #Test coverage with gcov GCOV_OUTPUT = gcov_output.txt GCOV_REPORT = gcov_report.txt GCOV_ERROR = gcov_error.txt GCOV_GCDA_FILES = $(call src_to_gcda, $(ALL_SRC)) GCOV_GCNO_FILES = $(call src_to_gcno, $(ALL_SRC)) TEST_OUTPUT = $(TEST_TARGET).txt STUFF_TO_CLEAN += \ $(GCOV_OUTPUT)\ $(GCOV_REPORT)\ $(GCOV_REPORT).html\ $(GCOV_ERROR)\ $(GCOV_GCDA_FILES)\ $(GCOV_GCNO_FILES)\ $(TEST_OUTPUT) #The gcda files for gcov need to be deleted before each run #To avoid annoying messages. GCOV_CLEAN = $(SILENCE)rm -f $(GCOV_GCDA_FILES) $(GCOV_OUTPUT) $(GCOV_REPORT) $(GCOV_ERROR) RUN_TEST_TARGET = $(SILENCE) $(GCOV_CLEAN) ; echo "Running $(TEST_TARGET)"; ./$(TEST_TARGET) $(CPPUTEST_EXE_FLAGS) ifeq ($(CPPUTEST_USE_GCOV), Y) ifeq ($(COMPILER_NAME),$(CLANG_STR)) LD_LIBRARIES += --coverage else LD_LIBRARIES += -lgcov endif endif INCLUDES_DIRS_EXPANDED = $(call get_dirs_from_dirspec, $(INCLUDE_DIRS)) INCLUDES += $(foreach dir, $(INCLUDES_DIRS_EXPANDED), -I$(dir)) MOCK_DIRS_EXPANDED = $(call get_dirs_from_dirspec, $(MOCKS_SRC_DIRS)) INCLUDES += $(foreach dir, $(MOCK_DIRS_EXPANDED), -I$(dir)) CPPUTEST_CPPFLAGS += $(INCLUDES) DEP_FILES = $(call src_to_d, $(ALL_SRC)) STUFF_TO_CLEAN += $(DEP_FILES) $(PRODUCTION_CODE_START) $(PRODUCTION_CODE_END) STUFF_TO_CLEAN += $(STDLIB_CODE_START) $(MAP_FILE) cpputest_*.xml junit_run_output # We'll use the CPPUTEST_CFLAGS etc so that you can override AND add to the CppUTest flags CFLAGS = $(CPPUTEST_CFLAGS) $(CPPUTEST_ADDITIONAL_CFLAGS) CPPFLAGS = $(CPPUTEST_CPPFLAGS) $(CPPUTEST_ADDITIONAL_CPPFLAGS) CXXFLAGS = $(CPPUTEST_CXXFLAGS) $(CPPUTEST_ADDITIONAL_CXXFLAGS) LDFLAGS = $(CPPUTEST_LDFLAGS) $(CPPUTEST_ADDITIONAL_LDFLAGS) # Don't consider creating the archive a warning condition that does STDERR output ARFLAGS := $(ARFLAGS)c DEP_FLAGS=-MMD -MP # Some macros for programs to be overridden. For some reason, these are not in Make defaults RANLIB = ranlib # Targets .PHONY: all all: start $(TEST_TARGET).run # Using the sin of 'touch' in makefiles- this is to prevent re-running the tests # if they've already run successfully in a previous invocation. Use the 'run' # phony target to re-run. $(TEST_TARGET).run: $(TEST_TARGET) $(RUN_TEST_TARGET) touch $@ .PHONY: run run: $(TEST_TARGET) $(RUN_TEST_TARGET) .PHONY: start start: $(TEST_TARGET) $(SILENCE)START_TIME=$(call time) .PHONY: all_no_tests all_no_tests: $(TEST_TARGET) .PHONY: flags flags: @echo @echo "OS ${UNAME_OS}" @echo "Compile C and C++ source with CPPFLAGS:" @$(call debug_print_list,$(CPPFLAGS)) @echo "Compile C++ source with CXXFLAGS:" @$(call debug_print_list,$(CXXFLAGS)) @echo "Compile C source with CFLAGS:" @$(call debug_print_list,$(CFLAGS)) @echo "Link with LDFLAGS:" @$(call debug_print_list,$(LDFLAGS)) @echo "Link with LD_LIBRARIES:" @$(call debug_print_list,$(LD_LIBRARIES)) @echo "Create libraries with ARFLAGS:" @$(call debug_print_list,$(ARFLAGS)) TEST_DEPS = $(TEST_OBJS) $(MOCKS_OBJS) $(PRODUCTION_CODE_START) $(TARGET_LIB) $(USER_LIBS) $(PRODUCTION_CODE_END) $(CPPUTEST_LIB) $(STDLIB_CODE_START) test-deps: $(TEST_DEPS) $(TEST_TARGET): $(TEST_DEPS) @echo Linking $@ $(SILENCE)$(CXX) -o $@ $^ $(LD_LIBRARIES) $(LDFLAGS) $(TARGET_LIB): $(OBJ) @echo Building archive $@ $(SILENCE)mkdir -p $(dir $@) $(SILENCE)$(AR) $(ARFLAGS) $@ $^ $(SILENCE)$(RANLIB) $@ test: $(TEST_TARGET) $(RUN_TEST_TARGET) | tee $(TEST_OUTPUT) vtest: $(TEST_TARGET) $(RUN_TEST_TARGET) -v | tee $(TEST_OUTPUT) $(CPPUTEST_OBJS_DIR)/%.o: %.cc @echo compiling $(notdir $<) $(SILENCE)mkdir -p $(dir $@) $(SILENCE)$(COMPILE.cpp) $(DEP_FLAGS) $(OUTPUT_OPTION) $< $(CPPUTEST_OBJS_DIR)/%.o: %.cpp @echo compiling $(notdir $<) $(SILENCE)mkdir -p $(dir $@) $(SILENCE)$(COMPILE.cpp) $(DEP_FLAGS) $(OUTPUT_OPTION) $< $(CPPUTEST_OBJS_DIR)/%.o: %.c @echo compiling $(notdir $<) $(SILENCE)mkdir -p $(dir $@) $(SILENCE)$(COMPILE.c) $(DEP_FLAGS) $(OUTPUT_OPTION) $< ifneq "$(MAKECMDGOALS)" "clean" -include $(DEP_FILES) endif .PHONY: clean clean: @echo Making clean $(SILENCE)$(RM) $(STUFF_TO_CLEAN) $(SILENCE)rm -rf gcov $(CPPUTEST_OBJS_DIR) $(SILENCE)find . -name "*.gcno" | xargs rm -f $(SILENCE)find . -name "*.gcda" | xargs rm -f #realclean gets rid of all gcov, o and d files in the directory tree #not just the ones made by this makefile .PHONY: realclean realclean: clean $(SILENCE)rm -rf gcov $(SILENCE)find . -name "*.gdcno" | xargs rm -f $(SILENCE)find . -name "*.[do]" | xargs rm -f gcov: test ifeq ($(CPPUTEST_USE_VPATH), Y) $(SILENCE)gcov $(GCOV_ARGS) --object-directory $(CPPUTEST_OBJS_DIR) $(SRC) >> $(GCOV_OUTPUT) 2>> $(GCOV_ERROR) else $(SILENCE)for d in $(SRC_DIRS) ; do \ FILES=`ls $$d/*.c $$d/*.cc $$d/*.cpp 2> /dev/null` ; \ gcov $(GCOV_ARGS) --object-directory $(CPPUTEST_OBJS_DIR)/$$d $$FILES >> $(GCOV_OUTPUT) 2>>$(GCOV_ERROR) ; \ done $(SILENCE)for f in $(SRC_FILES) ; do \ gcov $(GCOV_ARGS) --object-directory $(CPPUTEST_OBJS_DIR)/$$f $$f >> $(GCOV_OUTPUT) 2>>$(GCOV_ERROR) ; \ done endif ./scripts/filterGcov.sh $(GCOV_OUTPUT) $(GCOV_ERROR) $(GCOV_REPORT) $(TEST_OUTPUT) $(SILENCE)cat $(GCOV_REPORT) $(SILENCE)mkdir -p gcov $(SILENCE)mv *.gcov gcov $(SILENCE)mv gcov_* gcov @echo "See gcov directory for details" .PHONY: format format: $(CPPUTEST_HOME)/scripts/reformat.sh $(PROJECT_HOME_DIR) .PHONY: debug debug: @echo @echo "Target Source files:" @$(call debug_print_list,$(SRC)) @echo "Target Object files:" @$(call debug_print_list,$(OBJ)) @echo "Test Source files:" @$(call debug_print_list,$(TEST_SRC)) @echo "Test Object files:" @$(call debug_print_list,$(TEST_OBJS)) @echo "Mock Source files:" @$(call debug_print_list,$(MOCKS_SRC)) @echo "Mock Object files:" @$(call debug_print_list,$(MOCKS_OBJS)) @echo "All Input Dependency files:" @$(call debug_print_list,$(DEP_FILES)) @echo Stuff to clean: @$(call debug_print_list,$(STUFF_TO_CLEAN)) @echo Includes: @$(call debug_print_list,$(INCLUDES)) -include $(OTHER_MAKEFILE_TO_INCLUDE) ================================================ FILE: tests/unit/MakefileWorkerOverrides.mk ================================================ # where the cpputest includes and *.a are located # The defaults are what CI uses # # For osx, the brew path will look something like /usr/local/Cellar/cpputest/3.8 CPPUTEST_HOME ?= /usr MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) # Strip trailing slash off the current directory specifier CURRENT_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH))) # Explicitly insert the cpputest lib directory to search paths; this supports # the conda use case, where the libs are not set to the system linker LD_LIBRARIES = -L$(CPPUTEST_HOME)/lib -lCppUTest -lCppUTestExt TEST_SRC_FILES += \ $(MFLT_TEST_COMMON_SRC_DIR)/AllTests.cpp MEMFAULT_EXTRA_INC_PATHS += \ -I$(CPPUTEST_HOME)/include \ -I$(MFLT_TEST_ROOT)/ \ -I$(MFLT_TEST_ROOT)/stub_includes \ -I$(PROJECT_DIR) CPPUTEST_CPPFLAGS += $(MEMFAULT_EXTRA_INC_PATHS) \ -DMEMFAULT_UNITTEST # Clang defaults to c++98 but all modern cpp compilers implement most of the features (such as # variadic macros) introduced in c99 that were later added as part of the c++11 specification # so we pin the unit tests cpp standard to c++11. # # memfault/core/compact_log_helpers.h makes use of the gnu expansion rules for ,## __VA_ARGS__. We # explicitly opt in unit tests that need this feature with different gnu standard args for these # tests ifeq (,$(findstring MEMFAULT_COMPACT_LOG_ENABLE=1,$(CPPUTEST_CPPFLAGS))) CPPUTEST_CXXFLAGS += -std=c++11 else CPPUTEST_CFLAGS += -std=gnu11 CPPUTEST_CXXFLAGS += -std=gnu++11 endif export SILENCE ?= @ export CPPUTEST_USE_EXTENSIONS=Y export CPPUTEST_USE_MEM_LEAK_DETECTION?=Y export CPPUTEST_USE_GCOV=Y # Enable branch coverage reporting export GCOV_ARGS=-b -c CPPUTEST_WARNINGFLAGS = \ -Wall \ -Wextra \ -Werror \ -Wshadow \ -Wswitch-default # Default from MakefileWorker.mk if none defined CPPUTEST_C_WARNINGFLAGS = \ -Wstrict-prototypes CPPUTEST_CFLAGS += \ -Wbad-function-cast # These warnings aren't particularly helpful for our use case so we disable them CPPUTEST_WARNINGFLAGS += \ -Wno-missing-braces \ -Wno-missing-field-initializers \ -Wno-packed \ -Wno-vla \ CC_VERSION_OUTPUT ="$(shell $(CXX) -v 2>&1)" CLANG_STR = clang ifeq ($(findstring $(CLANG_STR),$(CC_VERSION_OUTPUT)),$(CLANG_STR)) # Clang-only C/C++ warning flags COMPILER_SPECIFIC_WARNINGS += \ -Wno-bad-function-cast \ -Wno-c++11-extensions \ -Wno-c++98-compat \ -Wno-c++98-compat-pedantic \ -Wno-c11-extensions \ -Wno-c99-extensions \ -Wno-covered-switch-default \ -Wno-documentation \ -Wno-documentation-unknown-command \ -Wno-flexible-array-extensions \ -Wno-gnu-variable-sized-type-not-at-end \ -Wno-gnu-zero-variadic-macro-arguments \ -Wno-inconsistent-missing-destructor-override \ -Wno-keyword-macro \ -Wno-missing-noreturn \ -Wno-reserved-id-macro \ -Wno-shorten-64-to-32 \ -Wno-vla-extension \ -Wno-zero-as-null-pointer-constant \ -Wno-unknown-warning-option \ -Wno-poison-system-directories \ -Wno-suggest-override \ -Wno-declaration-after-statement \ -Wno-reserved-identifier \ -Wno-zero-length-array \ -Wno-unsafe-buffer-usage \ -Wno-cast-function-type-strict \ -Wno-implicit-int-enum-cast \ -Wno-ms-bitfield-padding \ -Wno-missing-designated-field-initializers # Clang-only C-only warning flags COMPILER_SPECIFIC_C_WARNINGS += -Wc23-extensions else # GCC-only C/C++ warnings COMPILER_SPECIFIC_WARNINGS += \ -Wformat-signedness \ -fno-extended-identifiers \ -Wbidi-chars \ # GCC-only C-only warnings COMPILER_SPECIFIC_C_WARNINGS += -Wc11-c2x-compat # Only enable -fanalyzer if GCC version is >= 12 GCC_VERSION_GTEQ_12 := $(shell expr $$($(CC) -dumpversion | cut -f1 -d.) \>= 12) ifeq "$(GCC_VERSION_GTEQ_12)" "1" CFLAGS += \ -fanalyzer endif endif # Permit disabling the sanitizers via environment variable ifneq ($(MEMFAULT_DISABLE_ASAN),1) # The leak sanitizers work best on Linux, so only enable them for that platform ifeq ($(shell uname),Linux) CPPUTEST_WARNINGFLAGS += \ -fsanitize=leak CPPUTEST_LDFLAGS += \ -fsanitize=leak endif # Enable sanitizers, and crash on error (don't attempt to recover sanely) so the # test fails on sanitizer violation CPPUTEST_WARNINGFLAGS += \ -fsanitize=address \ -fsanitize=undefined \ -fno-sanitize-recover=all CPPUTEST_LDFLAGS += \ -fsanitize=address \ -fsanitize=undefined \ -fno-sanitize-recover=all endif CPPUTEST_WARNINGFLAGS += $(COMPILER_SPECIFIC_WARNINGS) CPPUTEST_C_WARNINGFLAGS += $(COMPILER_SPECIFIC_C_WARNINGS) export CPPUTEST_WARNINGFLAGS export CPPUTEST_C_WARNINGFLAGS export CPPUTEST_LDFLAGS MEMFAULT_RESULT_DIR=$(BUILD_DIR)/$(COMPONENT_NAME) export TEST_TARGET=$(MEMFAULT_RESULT_DIR)/$(COMPONENT_NAME)_tests export CPPUTEST_OBJS_DIR=$(MEMFAULT_RESULT_DIR)/objs export CPPUTEST_LIB_DIR=$(MEMFAULT_RESULT_DIR)/lib COV_INCLUDE_FILES = $(notdir $(SRC_FILES)) COV_INCLUDE_ARG = $(patsubst %.c, --include *%.c, $(COV_INCLUDE_FILES)) # run MakefileWorker.mk with the variables defined here include ${CURRENT_DIR}/MakefileWorker.mk ================================================ FILE: tests/unit/README.md ================================================ # Embedded unit tests ## Setup You'll need a c/c++ compiler and Make, as well as [CPPUTest](https://cpputest.github.io) installed. 1. Install the CppUtest library and the lcov tool: - Linux - `sudo apt-get install cpputest lcov` - OSX - `brew install cpputest && brew install lcov` 2. Optionally, install the python packages with `pip`: - `pip install -r requirements.txt` ## Running tests If the python packages were installed, you can use invoke to run the tests: `inv test` You can also call `make` directly from within this directory: `make` ## Directory structure ```plaintext ├── Makefile // Invokes all the unit tests ├── MakefileWorker.mk // Comes from CppUTest itself ├── MakefileWorkerOverrides.mk // memfault injected overrides ├── build │   [...] // Where all the tests wind up ├── fakes │   // fakes for unit tests ├── mocks │   // mocks for unit tests ├── makefiles // Each c file you unit test has a makefile here │   └── Makefile_.mk | [...] └── src // test source files └── test_* ``` ## Adding a test - Add a new test makefile under test/makefiles/. These just list the sources you will compile - Add a new test file under tests/src for the module you want to test - `inv test` ================================================ FILE: tests/unit/comparators/comparator_memfault_fault_handling.hpp ================================================ //! @file //! //! Comparators for fault_handling.h types. #pragma once #include "CppUTestExt/MockSupport.h" extern "C" { #include "memfault/panics/fault_handling.h" } class Mflt_sMemfaultAssertInfo_Comparator : public MockNamedValueComparator { public: virtual bool isEqual(const void *object1, const void *object2) { const sMemfaultAssertInfo *a = (const sMemfaultAssertInfo *)object1; const sMemfaultAssertInfo *b = (const sMemfaultAssertInfo *)object2; return (a->extra == b->extra) && (a->assert_reason == b->assert_reason); } virtual SimpleString valueToString(const void *object) { const sMemfaultAssertInfo *o = (const sMemfaultAssertInfo *)object; return StringFromFormat("sMemfaultAssertInfo:") + StringFromFormat(" extra: 0x%08" PRIx32, o->extra) + StringFromFormat(" assert_reason: %d", o->assert_reason); } }; ================================================ FILE: tests/unit/comparators/comparator_memfault_metric_ids.hpp ================================================ #pragma once #include "CppUTestExt/MockSupport.h" extern "C" { #include "memfault/metrics/metrics.h" } class MemfaultMetricIdsComparator : public MockNamedValueComparator { public: virtual bool isEqual(const void *object1, const void *object2) { const MemfaultMetricId *a = (const MemfaultMetricId *)object1; const MemfaultMetricId *b = (const MemfaultMetricId *)object2; return a->_impl == b->_impl; } virtual SimpleString valueToString(const void *object) { const MemfaultMetricId *o = (const MemfaultMetricId *)object; return StringFromFormat("MemfaultMetricId: _impl: %d", o->_impl); } }; ================================================ FILE: tests/unit/fakes/fake_memfault_buffered_coredump_storage.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! #pragma once #ifdef __cplusplus extern "C" { #endif #define MEMFAULT_STORAGE_SIZE (256) extern uint8_t g_ram_backed_storage[MEMFAULT_STORAGE_SIZE]; void fake_buffered_coredump_storage_reset(void); void fake_buffered_coredump_storage_set_size(size_t size); void fake_buffered_coredump_inject_write_failure(void); #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/fakes/fake_memfault_build_id.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fake memfault_build_info_read implementation #include "fake_memfault_build_id.h" #include #define INITIAL_FAKE_BUILD_ID \ (sMemfaultBuildInfo) { \ .build_id = { \ 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, \ 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, \ }, \ } eMemfaultBuildIdType g_fake_memfault_build_id_type = kMemfaultBuildIdType_None; sMemfaultBuildInfo g_fake_memfault_build_id_info = INITIAL_FAKE_BUILD_ID; void fake_memfault_build_id_reset(void) { g_fake_memfault_build_id_type = kMemfaultBuildIdType_None; g_fake_memfault_build_id_info = INITIAL_FAKE_BUILD_ID; } bool memfault_build_info_read(sMemfaultBuildInfo *info) { if (g_fake_memfault_build_id_type == kMemfaultBuildIdType_None) { return false; } memcpy(info->build_id, g_fake_memfault_build_id_info.build_id, sizeof(g_fake_memfault_build_id_info.build_id)); return true; } bool memfault_build_id_get_string(char *out_buf, size_t buf_len) { if (g_fake_memfault_build_id_type == kMemfaultBuildIdType_None) { return false; } const char *fake_build_id = "0123456789abcdef0123456789abcdef01234567"; strncpy(out_buf, fake_build_id, buf_len); return true; } ================================================ FILE: tests/unit/fakes/fake_memfault_build_id.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "memfault/core/build_info.h" #include "memfault_build_id_private.h" #ifdef __cplusplus extern "C" { #endif extern eMemfaultBuildIdType g_fake_memfault_build_id_type; extern sMemfaultBuildInfo g_fake_memfault_build_id_info; void fake_memfault_build_id_reset(void); #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/fakes/fake_memfault_coredump_utils.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/panics/coredump.h" void memfault_coredump_size_and_storage_capacity(size_t *total_size, size_t *capacity) { *total_size = 0; *capacity = 0; } ================================================ FILE: tests/unit/fakes/fake_memfault_event_storage.cpp ================================================ //! @file //! //! @brief //! A very simple fake storage implementation #include "fakes/fake_memfault_event_storage.h" #include #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/event_storage.h" #include "memfault/core/event_storage_implementation.h" typedef struct FakeEventStorageState { uint8_t *buf; size_t total_size; size_t space_available; size_t curr_offset; size_t start_offset; } sFakeEventStorageState; static sFakeEventStorageState s_event_storage_state; static size_t prv_begin_write(void) { mock().actualCall(__func__); s_event_storage_state.start_offset = s_event_storage_state.curr_offset; uint8_t *startp = &s_event_storage_state.buf[s_event_storage_state.curr_offset]; const size_t total_size = s_event_storage_state.total_size - s_event_storage_state.curr_offset; memset(startp, 0x0, total_size); return s_event_storage_state.space_available; } // offset not really needed by encoder static bool prv_append_data(const void *bytes, size_t num_bytes) { const uint32_t offset = s_event_storage_state.curr_offset; CHECK((offset + num_bytes) <= s_event_storage_state.space_available); uint8_t *buf = s_event_storage_state.buf; memcpy(&buf[offset], bytes, num_bytes); s_event_storage_state.curr_offset += num_bytes; return true; } static void prv_finish_write(bool rollback) { if (rollback) { s_event_storage_state.curr_offset = s_event_storage_state.start_offset; } mock().actualCall(__func__).withParameter("rollback", rollback); } static size_t prv_get_size(void) { return s_event_storage_state.space_available; } void fake_event_storage_assert_contents_match(const void *buf, size_t buf_len) { LONGS_EQUAL(buf_len, s_event_storage_state.curr_offset); MEMCMP_EQUAL(buf, s_event_storage_state.buf, buf_len); } void fake_memfault_event_storage_clear(void) { s_event_storage_state.curr_offset = 0; s_event_storage_state.space_available = s_event_storage_state.total_size; } void fake_memfault_event_storage_set_available_space(size_t space_available) { CHECK(space_available < s_event_storage_state.total_size); s_event_storage_state.space_available = space_available; } const sMemfaultEventStorageImpl *memfault_events_storage_boot(void *buf, size_t buf_len) { s_event_storage_state = (sFakeEventStorageState){ .buf = (uint8_t *)buf, .total_size = buf_len, .space_available = buf_len, .curr_offset = 0, }; static const sMemfaultEventStorageImpl s_fake_storage_impl = { .begin_write_cb = &prv_begin_write, .append_data_cb = &prv_append_data, .finish_write_cb = &prv_finish_write, .get_storage_size_cb = &prv_get_size, }; return &s_fake_storage_impl; } ================================================ FILE: tests/unit/fakes/fake_memfault_event_storage.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #ifdef __cplusplus extern "C" { #endif void fake_memfault_event_storage_clear(void); void fake_memfault_event_storage_set_available_space(size_t space_available); void fake_event_storage_assert_contents_match(const void *buf, size_t buf_len); #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/fakes/fake_memfault_metrics_platform_locking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fake implementation of memfault_metrics_platform_locking APIs #include #include "memfault/metrics/platform/overrides.h" typedef struct { uint32_t lock_count; uint32_t unlock_count; } sMetricLockStats; static sMetricLockStats s_metric_lock_stats; void memfault_lock(void) { s_metric_lock_stats.lock_count++; } void memfault_unlock(void) { s_metric_lock_stats.unlock_count++; } void fake_memfault_metrics_platform_locking_reboot(void) { s_metric_lock_stats = (sMetricLockStats){ 0 }; } bool fake_memfault_platform_metrics_lock_calls_balanced(void) { return s_metric_lock_stats.lock_count == s_metric_lock_stats.unlock_count; } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_boot_time.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/platform/core.h" uint64_t memfault_platform_get_time_since_boot_ms(void) { static uint64_t boot_time = 1; return boot_time++; } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_coredump_storage.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! Ram backed memfault storage implementation #include "fake_memfault_platform_coredump_storage.h" #include #include #include "memfault/panics/platform/coredump.h" typedef struct FakeMfltStorage { uint8_t *buf; size_t size; size_t sector_size; } sFakeMfltStorage; static sFakeMfltStorage s_fake_mflt_storage_ctx; void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = s_fake_mflt_storage_ctx.size, .sector_size = s_fake_mflt_storage_ctx.sector_size, }; } void fake_memfault_platform_coredump_storage_setup(void *storage_buf, size_t storage_size, size_t sector_size) { s_fake_mflt_storage_ctx = (sFakeMfltStorage){ .buf = (uint8_t *)storage_buf, .size = storage_size, .sector_size = sector_size, }; } bool fake_memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { assert(s_fake_mflt_storage_ctx.buf != NULL); if ((offset + read_len) > s_fake_mflt_storage_ctx.size) { return false; } uint8_t *read_ptr = &s_fake_mflt_storage_ctx.buf[offset]; memcpy(data, read_ptr, read_len); return true; } bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { assert(s_fake_mflt_storage_ctx.buf != NULL); if ((offset + data_len) > s_fake_mflt_storage_ctx.size) { return false; } uint8_t *write_ptr = &s_fake_mflt_storage_ctx.buf[offset]; memcpy(write_ptr, data, data_len); return true; } bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { const size_t sector_size = s_fake_mflt_storage_ctx.sector_size; assert((erase_size % sector_size) == 0); assert((offset % sector_size) == 0); for (size_t i = offset; i < erase_size; i += sector_size) { uint8_t erase_pattern[sector_size]; memset(erase_pattern, 0xff, sizeof(erase_pattern)); if (!memfault_platform_coredump_storage_write(i + offset, erase_pattern, sizeof(erase_pattern))) { return false; } } return true; } void memfault_platform_coredump_storage_clear(void) { uint8_t clear_byte = 0x0; bool success = memfault_platform_coredump_storage_write(0, &clear_byte, sizeof(clear_byte)); assert(success); } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_coredump_storage.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! Fake implementation of memfault storage #include #include #include void fake_memfault_platform_coredump_storage_setup(void *storage_buf, size_t storage_size, size_t sector_size); bool fake_memfault_platform_coredump_storage_read(uint32_t offset, void *buf, size_t buf_len); ================================================ FILE: tests/unit/fakes/fake_memfault_platform_crc32.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Simple example implementation of standard Zip/Ethernet CRC polynomial. In real life, you //! probably want to use a lookup table to speed up the computation #include "memfault/core/platform/crc32.h" static uint32_t prv_crc32_for_byte(uint32_t b) { for (size_t i = 0; i < 8; i++) { b = (((b & 0x1) != 0) ? 0 : (uint32_t)0xEDB88320L) ^ b >> 1; } return b ^ (uint32_t)0xFF000000L; } uint32_t memfault_platform_crc32(const void *data, size_t data_len) { const uint8_t *byte = data; uint32_t crc = 0; for (size_t i = 0; i < data_len; i++) { uint32_t idx = ((crc & 0xff) ^ byte[i]); crc = prv_crc32_for_byte(idx) ^ crc >> 8; } return crc; } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_debug_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A fake implementation simulating platform logs which can be used for unit tests #include #include #include #include "memfault/core/compiler.h" #include "memfault/core/log.h" #include "memfault/core/platform/debug_log.h" static const char *prv_severity_level_to_str(eMemfaultPlatformLogLevel level) { switch (level) { case kMemfaultPlatformLogLevel_Debug: return "D"; case kMemfaultPlatformLogLevel_Info: return "I"; case kMemfaultPlatformLogLevel_Warning: return "W"; case kMemfaultPlatformLogLevel_Error: return "E"; case kMemfaultPlatformLogLevel_NumLevels: // silence error with -Wswitch-enum default: return "U"; } } void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { va_list args; va_start(args, fmt); char log_buf[128]; strcpy(log_buf, "MFLT: "); char *write_ptr = &log_buf[0] + strlen(log_buf); vsnprintf(write_ptr, sizeof(log_buf) - strlen(log_buf), fmt, args); printf("[%s] %s\n", prv_severity_level_to_str(level), log_buf); } void memfault_platform_log_raw(const char *fmt, ...) { va_list args; va_start(args, fmt); char log_buf[128]; vsnprintf(log_buf, sizeof(log_buf), fmt, args); printf("%s", log_buf); } void memfault_platform_hexdump(MEMFAULT_UNUSED eMemfaultPlatformLogLevel level, MEMFAULT_UNUSED const void *data, MEMFAULT_UNUSED size_t data_len) { // No fake impl yet! } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_get_device_info.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fake implementation of the memfault_platform_get_device_info platform API #include "fake_memfault_platform_get_device_info.h" #include "memfault/core/platform/device_info.h" struct MemfaultDeviceInfo g_fake_device_info = { .device_serial = "DAABBCCDD", .software_version = "1.2.3", .software_type = "main", .hardware_version = "evt_24", }; void memfault_platform_get_device_info(struct MemfaultDeviceInfo *info) { *info = g_fake_device_info; } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_get_device_info.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fake implementation of the memfault_platform_get_device_info platform API #include "memfault/core/platform/core.h" extern struct MemfaultDeviceInfo g_fake_device_info; ================================================ FILE: tests/unit/fakes/fake_memfault_platform_http_client.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fake implementation of the http client platform API #include "memfault/core/compiler.h" #include "memfault/http/platform/http_client.h" int memfault_platform_http_response_get_status(MEMFAULT_UNUSED const sMfltHttpResponse *response, uint32_t *status_out) { if (status_out) { *status_out = 200; } return 0; } static sMfltHttpClient *s_client = (sMfltHttpClient *)~0; sMfltHttpClient *memfault_platform_http_client_create(void) { return s_client; } int memfault_platform_http_client_post_data( MEMFAULT_UNUSED sMfltHttpClient *client, MEMFAULT_UNUSED MemfaultHttpClientResponseCallback callback, MEMFAULT_UNUSED void *ctx) { return 0; } int memfault_platform_http_client_wait_until_requests_completed( MEMFAULT_UNUSED sMfltHttpClient *client, MEMFAULT_UNUSED uint32_t timeout_ms) { return 0; } int memfault_platform_http_client_destroy(MEMFAULT_UNUSED sMfltHttpClient *client) { return 0; } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_locking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fake implementation of memfault_metrics_platform_locking APIs #include #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/core/platform/overrides.h" typedef struct { uint32_t lock_count; uint32_t unlock_count; } sMetricLockStats; static sMetricLockStats s_metric_lock_stats; void memfault_lock(void) { s_metric_lock_stats.lock_count++; } void memfault_unlock(void) { s_metric_lock_stats.unlock_count++; } void fake_memfault_metrics_platform_locking_reboot(void) { s_metric_lock_stats = (sMetricLockStats){ 0 }; } bool fake_memfault_platform_metrics_lock_calls_balanced(void) { return s_metric_lock_stats.lock_count == s_metric_lock_stats.unlock_count; } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_metrics_locking.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Fake implementation of memfault_metrics_platform_locking APIs #include #ifdef __cplusplus extern "C" { #endif //! @return true if there has been an equivalent number of lock and unlock calls, else false bool fake_memfault_platform_metrics_lock_calls_balanced(void); //! Reset the state of the fake locking tracker void fake_memfault_metrics_platform_locking_reboot(void); #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/fakes/fake_memfault_platform_time.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "fake_memfault_platform_time.h" #include typedef struct { bool enabled; sMemfaultCurrentTime time; } sMemfaultFakeTimerState; static sMemfaultFakeTimerState s_fake_timer_state; void fake_memfault_platform_time_enable(bool enable) { s_fake_timer_state.enabled = enable; } void fake_memfault_platform_time_set(const sMemfaultCurrentTime *time) { s_fake_timer_state.time = *time; } // // fake implementation // bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { if (!s_fake_timer_state.enabled) { return false; } *time = s_fake_timer_state.time; return true; } ================================================ FILE: tests/unit/fakes/fake_memfault_platform_time.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/platform/system_time.h" #ifdef __cplusplus extern "C" { #endif void fake_memfault_platform_time_enable(bool enable); void fake_memfault_platform_time_set(const sMemfaultCurrentTime *time); #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/fakes/fake_memfault_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/reboot_tracking.h" void memfault_reboot_tracking_metrics_session(bool active, uint32_t index) { (void)active; (void)index; } bool memfault_reboot_tracking_metrics_session_was_active(uint32_t index) { (void)index; return false; } void memfault_reboot_tracking_clear_metrics_sessions(void) { } ================================================ FILE: tests/unit/fakes/fake_memfault_sdk_assert.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "memfault/core/compiler.h" #include "memfault/core/sdk_assert.h" MEMFAULT_NORETURN void memfault_sdk_assert_func(void) { assert(0); } ================================================ FILE: tests/unit/llvm-cov-wrapper.sh ================================================ #!/usr/bin/env bash # Fastcov wants a single executable when issuing gcov commands, but when using # clang/llvm, we need to call llvm-cov with the 'gcov' command, so hence this # little wrapper ${LLVM_COV:-llvm-cov} gcov "$@" ================================================ FILE: tests/unit/makefiles/Makefile_assert.mk ================================================ # need to provide a stub object for cpputest to pack into an archive, to work # around the behavior of the old 2005 version of 'ar' present on mac SRC_FILES = \ $(MFLT_TEST_STUB_DIR)/stub_assert.c MOCK_AND_FAKE_SRC_FILES += \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_assert.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Blank out noreturn for this test CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_batched_events.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_batched_events.c TEST_SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_SRC_DIR)/test_memfault_batched_events.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_circular_buffer.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_circular_buffer.c include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_base64.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_base64.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_buffered_coredump_storage.mk ================================================ SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_buffered_coredump_storage_impl.c MOCK_AND_FAKE_SRC_FILES += TEST_SRC_FILES = \ $(MOCK_AND_FAKE_SRC_FILES) \ $(MFLT_TEST_SRC_DIR)/test_memfault_buffered_coredump_storage.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_build_id_gnu.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_build_id.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_core_utils.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_build_id.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_USE_GNU_BUILD_ID=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_build_id_memfault.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_build_id.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_core_utils.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_build_id.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_USE_GNU_BUILD_ID=0 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_cdr_source.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_custom_data_recording.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_custom_data_recording.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_CDR_ENABLE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_chunking_transport.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_chunk_transport.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_varint.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_chunk_transport.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_compact_log.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_log.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_log.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_COMPACT_LOG_ENABLE=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_EXPORT_CHUNK_MAX_LEN=10 CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_TIMESTAMPS_ENABLE=0 CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_RESTORE_STATE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_compact_log_macros.mk ================================================ SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_compact_log_c.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_compact_log_macros.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_COMPACT_LOG_ENABLE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_compact_log_save_truncation.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_log.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_compact_log_serializer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_compact_log_save_truncation.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_COMPACT_LOG_ENABLE=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_EXPORT_CHUNK_MAX_LEN=10 # Use a small max line save length to easily trigger truncation CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_MAX_LINE_SAVE_LEN=32 CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_TIMESTAMPS_ENABLE=0 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_compact_log_serializer.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_compact_log_serializer.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_compact_log_serializer.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_COMPACT_LOG_ENABLE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_coredump.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/panics/src/memfault_coredump.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_coredump_storage.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_coredump.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_coredump_sdk_regions.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/panics/src/memfault_coredump_sdk_regions.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_coredump_sdk_regions.cpp CPPUTEST_CPPFLAGS += -DMEMFAULT_COREDUMP_COLLECT_LOG_REGIONS=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_COREDUMP_COLLECT_HEAP_STATS=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_coredump_utils.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/panics/src/memfault_coredump_utils.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_coredump_utils.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_coredump_with_serial.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/panics/src/memfault_coredump.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_coredump_storage.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_coredump.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_crc16_ccitt.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_crc16_ccitt.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_crc16_ccitt_no_lut.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_crc16_ccitt.cpp CPPUTEST_CPPFLAGS += -DMEMFAULT_CRC16_LOOKUP_TABLE_ENABLE=0 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_data_export.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_data_export.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MOCK_AND_FAKE_SRC_FILES) \ $(MFLT_TEST_SRC_DIR)/test_memfault_data_export.cpp \ include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_data_packetizer.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_data_packetizer.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_data_packetizer.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_data_packetizer_with_project_key.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_data_packetizer.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_data_packetizer.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += \ -DMEMFAULT_PROJECT_KEY="\"1234567890abcdef1234567890abcdef\"" \ -DMEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY=1 \ include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_data_source_rle.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_data_source_rle.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_rle.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_varint.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_data_source_rle.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_demo_shell.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/demo/src/memfault_demo_shell.c MOCK_AND_FAKE_SRC_FILES += TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_demo_shell.c \ $(MOCK_AND_FAKE_SRC_FILES) # add the -DMEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS=1 define to enable the # extended commands CPPUTEST_CPPFLAGS += -DMEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_event_storage.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_event_storage.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_event_storage.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_TEST_PERSISTENT_EVENT_STORAGE_DISABLE=0 CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED=0 CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_STORAGE_NV_SUPPORT_ENABLED=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_event_storage_batch_read.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_event_storage.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_event_storage.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_TEST_PERSISTENT_EVENT_STORAGE_DISABLE=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_STORAGE_READ_BATCHING_MAX_BYTES=4 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_event_storage_no_persistent_storage.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_event_storage.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_event_storage.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_TEST_PERSISTENT_EVENT_STORAGE_DISABLE=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED=0 CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_STORAGE_RESTORE_STATE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_heap_stats.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_heap_stats.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_heap_stats.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_heartbeat_metrics.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics_reliability.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_reboot_tracking.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_reboot_tracking.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_heartbeat_metrics.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Set -DMEMFAULT_METRICS_SESSIONS_ENABLED=0 to disable session metrics, to # reduce the number of mocks required CPPUTEST_CPPFLAGS += -DMEMFAULT_METRICS_SESSIONS_ENABLED=0 CPPUTEST_CPPFLAGS += -DMEMFAULT_METRICS_RESTORE_STATE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_heartbeat_metrics_debug.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_reboot_tracking.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_reboot_tracking.cpp \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics_reliability.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_heartbeat_metrics_debug.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Set -DMEMFAULT_METRICS_SESSIONS_ENABLED=0 to disable session metrics, to # reduce the number of mocks required CPPUTEST_CPPFLAGS += -DMEMFAULT_METRICS_SESSIONS_ENABLED=0 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_heartbeat_metrics_nocustom.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_reboot_tracking.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_reboot_tracking.cpp \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics_reliability.cpp \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_heartbeat_metrics_nocustom.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Set flag to compile with only built-in memfault metrics, to confirm # compilation is OK CPPUTEST_CPPFLAGS += -DTEST_NO_CUSTOM_METRICS=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_http_utils.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/http/src/memfault_http_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_http_utils.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_log.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_log.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_log.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += \ -DMEMFAULT_LOG_TIMESTAMPS_ENABLE=0 \ -DMEMFAULT_LOG_RESTORE_STATE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_log_data_source.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_log.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_log_data_source.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_log_data_source.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Makefile_memfault_log_data_source_timestamps.mk tests with timestamps enabled CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_TIMESTAMPS_ENABLE=0 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_log_data_source_timestamps.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_log.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_log_data_source.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_log_data_source.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_TIMESTAMPS_ENABLE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_log_with_timestamps.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_log.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_circular_buffer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_crc16_ccitt.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_system_time.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_log_with_timestamps.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_LOG_TIMESTAMPS_ENABLE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_metrics_battery.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics_battery.c \ MOCK_AND_FAKE_SRC_FILES = \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_metrics_battery.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_METRICS_BATTERY_ENABLE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_metrics_connectivity.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics_connectivity.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_metrics_connectivity.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += \ -DMEMFAULT_METRICS_SYNC_SUCCESS=1 \ -DMEMFAULT_METRICS_MEMFAULT_SYNC_SUCCESS=1 \ -DMEMFAULT_METRICS_CONNECTIVITY_CONNECTED_TIME=1 \ include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_metrics_reliability.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics_reliability.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics.cpp \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_reboot_tracking.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_metrics_reliability.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_metrics_serializer.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics_serializer.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_metrics_serializer.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_port_lwip_metrics.mk ================================================ SRC_FILES = \ $(MFLT_PORTS_DIR)/lwip/memfault_lwip_metrics.c \ INCLUDE_DIRS = \ $(MFLT_PORTS_DIR)/lwip/config \ MOCK_AND_FAKE_SRC_FILES = \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics.cpp TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_port_lwip_metrics.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DTEST_LWIP_METRICS=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_port_mbedtls_metrics.mk ================================================ SRC_FILES = \ $(MFLT_PORTS_DIR)/mbedtls/memfault_mbedtls_metrics.c \ $(MFLT_TEST_STUB_DIR)/stub_mbedtls_mem.c \ INCLUDE_DIRS = \ $(MFLT_PORTS_DIR)/mbedtls/config \ MOCK_AND_FAKE_SRC_FILES = \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics.cpp \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_port_mbedtls_metrics.cpp \ $(MOCK_AND_FAKE_SRC_FILES) \ CPPUTEST_CPPFLAGS += \ -DTEST_MBEDTLS_METRICS=1 \ CPPUTEST_CFLAGS += \ -include $(MFLT_TEST_ROOT)/stub_includes/mbedtls_mem.h include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_port_nrf5_coredump_regions.mk ================================================ SRC_FILES = \ $(MFLT_PORTS_DIR)/nrf5_sdk/nrf5_coredump_regions.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_port_nrf5_coredump_regions.cpp CPPUTEST_CPPFLAGS += \ -Werror=unused-macros \ -DMEMFAULT_PLATFORM_COREDUMP_CUSTOM_REGIONS=1 \ include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_printf_attribute.mk ================================================ # Add a file to create dummy TARGET_LIB # We don't actually call anything from this lib so just add # the current source SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_printf_attribute.cpp TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_printf_attribute.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_ram_backed_coredump_port.mk ================================================ SRC_FILES = \ $(MFLT_PORTS_DIR)/panics/src/memfault_platform_ram_backed_coredump.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_ram_backed_coredump_port.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_reboot_tracking_serializer.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_reboot_tracking_serializer.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_reboot_tracking_serializer.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_rle.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_rle.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_varint.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_rle.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_root_cert.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/http/src/memfault_root_certs_der.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_root_cert.cpp \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_base64.c include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_sdk_assert.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_sdk_assert.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_sdk_assert.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_sdk_metrics_freertos.mk ================================================ SRC_FILES = \ $(MFLT_PORTS_DIR)/freertos/src/memfault_sdk_metrics_freertos.c \ INCLUDE_DIRS = \ $(MFLT_PORTS_DIR)/freertos/config \ MOCK_AND_FAKE_SRC_FILES = \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics.cpp TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_sdk_freertos_metrics.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DTEST_FREERTOS_METRICS=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Add flag to remove subtest definitions CPPUTEST_CPPFLAGS += -DMEMFAULT_UNITTEST_SELF_TEST \ -DMEMFAULT_NORETURN="" \ -DMEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE=1 \ include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test_component_boot_check.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_coredump_utils.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_boot_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_regions.c \ $(MFLT_TEST_STUB_DIR)/stub_platform.c \ $(MFLT_TEST_STUB_DIR)/stub_reboot_tracking.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_storage_debug.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test_component_boot_check.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Needed to prevent error on memfault_platform_reboot stub returning CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test_coredump_regions.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_coredump_utils.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_boot_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_component_booted.c \ $(MFLT_TEST_STUB_DIR)/stub_platform.c \ $(MFLT_TEST_STUB_DIR)/stub_reboot_tracking.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_storage_debug.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test_coredump_regions.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Needed to prevent error on memfault_platform_reboot stub returning CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test_coredump_storage.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_coredump_utils.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_boot_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_STUB_DIR)/stub_component_booted.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_regions.c \ $(MFLT_TEST_STUB_DIR)/stub_platform.c \ $(MFLT_TEST_STUB_DIR)/stub_reboot_tracking.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test_coredump_storage.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Needed to prevent error on memfault_platform_reboot stub returning CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test_data_export.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_coredump_utils.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_boot_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_component_booted.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_regions.c \ $(MFLT_TEST_STUB_DIR)/stub_platform.c \ $(MFLT_TEST_STUB_DIR)/stub_reboot_tracking.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_storage_debug.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test_data_export.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Needed to prevent error on memfault_platform_reboot stub returning CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test_device_info.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_coredump_utils.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_boot_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_component_booted.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_regions.c \ $(MFLT_TEST_STUB_DIR)/stub_platform.c \ $(MFLT_TEST_STUB_DIR)/stub_reboot_tracking.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_storage_debug.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test_device_info.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Needed to prevent error on memfault_platform_reboot stub returning CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test_reboot_reason.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_coredump_utils.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_boot_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_reboot_tracking.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_component_booted.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_regions.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_storage_debug.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test_reboot_reason.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Needed to prevent error on memfault_platform_reboot stub returning CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test_time.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_coredump_utils.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_sdk_assert.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_system_time.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_component_booted.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_regions.c \ $(MFLT_TEST_STUB_DIR)/stub_platform.c \ $(MFLT_TEST_STUB_DIR)/stub_reboot_tracking.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump_storage_debug.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_coredump.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test_time.cpp \ $(MOCK_AND_FAKE_SRC_FILES) # Needed to prevent error on memfault_platform_reboot stub returning CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_self_test_utils.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_self_test_utils.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_self_test_utils.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_serializer_helper.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_serializer_helper.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL=0 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_serializer_helper_with_device_serial.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_serializer_helper.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_serializer_helper_without_build_id.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_serializer_helper.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_INCLUDE_BUILD_ID=0 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_session_metrics.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_reboot_tracking.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_reboot_tracking.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_session_metrics.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_session_metrics_debug.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_reboot_tracking.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_platform_debug_log.cpp \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_reboot_tracking.cpp \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_metrics_reliability.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_session_metrics_debug.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_session_vitals.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/metrics/src/memfault_metrics.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_reboot_tracking.cpp \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_session_vitals.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_task_watchdog.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_task_watchdog.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_locking.c \ $(MFLT_TEST_MOCK_DIR)/mock_memfault_fault_handling.cpp TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_task_watchdog.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_TASK_WATCHDOG_ENABLE=1 # required for mock memfault_fault_handling_assert_extra CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_test_coredump_storage_debug.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/panics/src/memfault_coredump_storage_debug.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_coredump_storage_debug.cpp \ $(MOCK_AND_FAKE_SRC_FILES) include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_trace_event.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_trace_event.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_trace_event.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_TRACE_EVENT_MAX_LOG_LEN=15 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_trace_event_compact_log.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_trace_event.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_trace_event.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_TRACE_EVENT_MAX_LOG_LEN=15 CPPUTEST_CPPFLAGS += -DMEMFAULT_COMPACT_LOG_ENABLE=1 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_trace_event_no_isr_log.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_serializer_helper.c \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_trace_event.c \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c MOCK_AND_FAKE_SRC_FILES += \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_build_id.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_event_storage.cpp \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_debug_log.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_get_device_info.c \ $(MFLT_TEST_FAKE_DIR)/fake_memfault_platform_time.c \ $(MFLT_TEST_STUB_DIR)/stub_memfault_log_save.c \ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_trace_event.cpp \ $(MOCK_AND_FAKE_SRC_FILES) CPPUTEST_CPPFLAGS += -DMEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL=1 CPPUTEST_CPPFLAGS += -DMEMFAULT_TRACE_EVENT_MAX_LOG_LEN=15 CPPUTEST_CPPFLAGS += -DMEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED=0 include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_memfault_user_reboot_reasons.mk ================================================ TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_user_reboot_reasons.cpp # macos ar does not allow empty archives, add a stub as the source to work around SRC_FILES = \ $(MFLT_TEST_STUB_DIR)/stub_assert.c include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_minimal_cbor.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_minimal_cbor.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_minimal_cbor.c include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_ram_reboot_tracking.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/core/src/memfault_ram_reboot_info_tracking.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_ram_reboot_tracking.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/makefiles/Makefile_varint.mk ================================================ SRC_FILES = \ $(MFLT_COMPONENTS_DIR)/util/src/memfault_varint.c TEST_SRC_FILES = \ $(MFLT_TEST_SRC_DIR)/test_memfault_varint.cpp include $(CPPUTEST_MAKFILE_INFRA) ================================================ FILE: tests/unit/mocks/mock_memfault_coredump.cpp ================================================ #include "mock_memfault_coredump.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include #include #include "memfault/core/data_packetizer_source.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" } uint32_t g_mock_memfault_coredump_total_size = COREDUMP_TOTAL_SIZE; bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { const bool rv = mock() .actualCall(__func__) .withUnsignedIntParameter("offset", offset) .withUnsignedIntParameter("read_len", (unsigned int)read_len) .returnBoolValueOrDefault(true); if (rv) { memset(data, 0xff, read_len); } return rv; } bool memfault_coredump_has_valid_coredump(size_t *total_size_out) { const bool rv = mock() .actualCall(__func__) .withOutputParameter("total_size_out", total_size_out) .returnBoolValueOrDefault(true); if (rv && total_size_out) { *total_size_out = g_mock_memfault_coredump_total_size; } return rv; } void memfault_platform_coredump_storage_clear(void) { mock().actualCall(__func__); } const sMemfaultDataSourceImpl g_memfault_coredump_data_source = { .has_more_msgs_cb = memfault_coredump_has_valid_coredump, .read_msg_cb = memfault_platform_coredump_storage_read, .mark_msg_read_cb = memfault_platform_coredump_storage_clear, }; ================================================ FILE: tests/unit/mocks/mock_memfault_coredump.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #define COREDUMP_TOTAL_SIZE 32 extern uint32_t g_mock_memfault_coredump_total_size; ================================================ FILE: tests/unit/mocks/mock_memfault_fault_handling.cpp ================================================ //! @file //! //! Mock implementation of memfault fault handling functions #include "CppUTestExt/MockSupport.h" #include "memfault/panics/fault_handling.h" void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { mock() .actualCall(__func__) .withPointerParameter("pc", pc) .withPointerParameter("lr", lr) .withParameterOfType("sMemfaultAssertInfo", "extra_info", extra_info); } ================================================ FILE: tests/unit/mocks/mock_memfault_metrics.cpp ================================================ #include "CppUTestExt/MockSupport.h" #include "memfault/metrics/metrics.h" int memfault_metrics_heartbeat_set_unsigned(MemfaultMetricId key, uint32_t unsigned_value) { return mock() .actualCall(__func__) .withParameterOfType("MemfaultMetricId", "key", &key) .withParameter("unsigned_value", unsigned_value) .returnIntValue(); } int memfault_metrics_heartbeat_set_signed(MemfaultMetricId key, int32_t signed_value) { return mock() .actualCall(__func__) .withParameterOfType("MemfaultMetricId", "key", &key) .withParameter("signed_value", signed_value) .returnIntValue(); } int memfault_metrics_heartbeat_timer_start(MemfaultMetricId key) { return mock() .actualCall(__func__) .withParameterOfType("MemfaultMetricId", "key", &key) .returnIntValue(); } int memfault_metrics_heartbeat_timer_stop(MemfaultMetricId key) { return mock() .actualCall(__func__) .withParameterOfType("MemfaultMetricId", "key", &key) .returnIntValue(); } int memfault_metrics_heartbeat_add(MemfaultMetricId key, int32_t amount) { return mock() .actualCall(__func__) .withParameterOfType("MemfaultMetricId", "key", &key) .withParameter("amount", amount) .returnIntValue(); } ================================================ FILE: tests/unit/mocks/mock_memfault_metrics_reliability.cpp ================================================ #include "CppUTestExt/MockSupport.h" #include "memfault/metrics/reliability.h" void memfault_metrics_reliability_boot(sMemfaultMetricsReliabilityCtx *ctx) { mock().actualCall("memfault_metrics_reliability_boot").withParameter("ctx", ctx); } sMemfaultMetricsReliabilityCtx *memfault_metrics_reliability_get_ctx(void) { mock().actualCall("memfault_metrics_reliability_get_ctx"); // Return a pointer to a static context for testing purposes static sMemfaultMetricsReliabilityCtx ctx; return &ctx; } void memfault_metrics_reliability_collect(void) { mock().actualCall("memfault_metrics_reliability_collect"); } ================================================ FILE: tests/unit/mocks/mock_memfault_platform_debug_log.cpp ================================================ //! @file //! //! Mock implementation of memfault logging subsystem which can be used //! when asserting that logs of certain formats are generated from unit tests #include "mock_memfault_platform_debug_log.h" #include #include #include #include #include "CppUTestExt/MockSupport.h" #include "memfault/core/compiler.h" #include "memfault/core/log.h" #include "memfault/core/platform/debug_log.h" #define LOG_BUFFER_SIZE (512) void memfault_platform_log(eMemfaultPlatformLogLevel level, const char *fmt, ...) { char log_buf[LOG_BUFFER_SIZE]; va_list args; va_start(args, fmt); vsnprintf(log_buf, sizeof(log_buf), fmt, args); va_end(args); // Let's avoid dealing with the va_list, we usually really care only about the resulting string // anyway: mock() .actualCall(__func__) .withLongIntParameter("level", level) .withStringParameter("output", log_buf); } void memfault_platform_log_raw(const char *fmt, ...) { char log_buf[LOG_BUFFER_SIZE]; va_list args; va_start(args, fmt); vsnprintf(log_buf, sizeof(log_buf), fmt, args); va_end(args); // Let's avoid dealing with the va_list, we usually really care only about the resulting string // anyway: mock().actualCall(__func__).withStringParameter("output", log_buf); } void memfault_platform_log_set_mock(eMemfaultPlatformLogLevel level, const char *const lines[], size_t num_lines) { for (unsigned int i = 0; i < num_lines; ++i) { mock() .expectOneCall("memfault_platform_log") .withIntParameter("level", level) .withStringParameter("output", lines[i]); } } ================================================ FILE: tests/unit/mocks/mock_memfault_platform_debug_log.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once extern "C" { #include #include "memfault/core/platform/debug_log.h" } void memfault_platform_log_set_mock(eMemfaultPlatformLogLevel level, const char *const lines[], size_t num_lines); ================================================ FILE: tests/unit/mocks/mock_memfault_platform_system_time.cpp ================================================ //! @file #include #include "CppUTestExt/MockSupport.h" #include "memfault/core/platform/system_time.h" bool memfault_platform_time_get_current(sMemfaultCurrentTime *time) { return mock().actualCall(__func__).withOutputParameter("time", time).returnBoolValue(); } ================================================ FILE: tests/unit/mocks/mock_memfault_reboot_tracking.cpp ================================================ //! @file //! //! Mock for use testing components which depend on reboot tracking #include "CppUTestExt/MockSupport.h" #include "memfault/core/reboot_tracking.h" int memfault_reboot_tracking_get_reboot_reason(sMfltRebootReason *reboot_reason) { return mock() .actualCall(__func__) .withOutputParameter("reboot_reason", reboot_reason) .returnIntValue(); } int memfault_reboot_tracking_get_unexpected_reboot_occurred(bool *unexpected_reboot_occurred) { return mock() .actualCall(__func__) .withOutputParameter("unexpected_reboot_occurred", unexpected_reboot_occurred) .returnIntValue(); } ================================================ FILE: tests/unit/scripts/filterGcov.sh ================================================ #!/bin/bash # shellcheck disable=SC2034,SC2162,SC2086,SC2140 # NOTE: This was copied from: # https://github.com/cpputest/cpputest/blob/master/scripts/filterGcov.sh # It's supposed to be part of a CppUTest install but isn't part of what you get from brew :( INPUT_FILE=$1 TEMP_FILE1=${INPUT_FILE}1.tmp TEMP_FILE2=${INPUT_FILE}2.tmp TEMP_FILE3=${INPUT_FILE}3.tmp ERROR_FILE=$2 OUTPUT_FILE=$3 HTML_OUTPUT_FILE=$3.html TEST_RESULTS=$4 flattenGcovOutput() { while read line1; do read line2 echo $line2 " " $line1 read junk read junk done < ${INPUT_FILE} } getRidOfCruft() { sed '-e s/^Lines.*://g' \ '-e s/^[0-9]\./ &/g' \ '-e s/^[0-9][0-9]\./ &/g' \ '-e s/of.*File/ /g' \ "-e s/'//g" \ '-e s/^.*\/usr\/.*$//g' \ '-e s/^.*\.$//g' } flattenPaths() { sed \ -e 's/\/[^/][^/]*\/[^/][^/]*\/\.\.\/\.\.\//\//g' \ -e 's/\/[^/][^/]*\/[^/][^/]*\/\.\.\/\.\.\//\//g' \ -e 's/\/[^/][^/]*\/[^/][^/]*\/\.\.\/\.\.\//\//g' \ -e 's/\/[^/][^/]*\/\.\.\//\//g' } getFileNameRootFromErrorFile() { sed '-e s/gc..:cannot open .* file//g' ${ERROR_FILE} } writeEachNoTestCoverageFile() { while read line; do echo " 0.00% " ${line} done } createHtmlOutput() { echo "" echo "" sed "-e s/.*% /
CoverageFile
&<\/td>/" \ "-e s/[a-zA-Z0-9_]*\.[ch][a-z]*/&<\/a><\/td><\/tr>/" echo "
" sed "-e s/.*/&
/g" < ${TEST_RESULTS} } flattenGcovOutput | getRidOfCruft | flattenPaths > ${TEMP_FILE1} getFileNameRootFromErrorFile | writeEachNoTestCoverageFile | flattenPaths > ${TEMP_FILE2} cat ${TEMP_FILE1} ${TEMP_FILE2} | sort | uniq > ${OUTPUT_FILE} createHtmlOutput < ${OUTPUT_FILE} > ${HTML_OUTPUT_FILE} rm -f ${TEMP_FILE1} ${TEMP_FILE2} ================================================ FILE: tests/unit/src/AllTests.cpp ================================================ /* * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "CppUTest/CommandLineTestRunner.h" int main(int argc, char **argv) { return CommandLineTestRunner::RunAllTests(argc, argv); } ================================================ FILE: tests/unit/src/memfault_test_compact_log_c.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! Functions for testing compact log expansion from C code #ifdef __cplusplus extern "C" { #endif void test_compact_log_c_argument_expansion(void); void test_compact_log_c_multi_arg(void); void test_compact_log_c_multi_complex(void); #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/src/test_assert.cpp ================================================ #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/panics/assert.h" static sMemfaultAssertInfo p_assert_info = { 0 }; void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) { (void)pc, (void)lr; LONGS_EQUAL(p_assert_info.extra, extra_info->extra); LONGS_EQUAL(p_assert_info.assert_reason, extra_info->assert_reason); mock().actualCall(__func__); } void memfault_fault_handling_assert(void *pc, void *lr) { (void)pc, (void)lr; mock().actualCall(__func__); } TEST_GROUP(Assert) { void setup() { mock().strictOrder(); memset(&p_assert_info, 0, sizeof(p_assert_info)); } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(Assert, Test_AssertMacrosOK) { mock().expectOneCall("memfault_fault_handling_assert_extra"); p_assert_info.extra = 0x12345678; p_assert_info.assert_reason = kMfltRebootReason_BusFault; MEMFAULT_ASSERT_EXTRA_AND_REASON(p_assert_info.extra, kMfltRebootReason_BusFault); mock().expectOneCall("memfault_fault_handling_assert_extra"); p_assert_info.extra++; p_assert_info.assert_reason = kMfltRebootReason_Assert; MEMFAULT_ASSERT_RECORD(p_assert_info.extra); mock().expectOneCall("memfault_fault_handling_assert_extra"); p_assert_info.extra++; MEMFAULT_ASSERT_EXTRA(0, p_assert_info.extra); mock().expectOneCall("memfault_fault_handling_assert_extra"); p_assert_info.extra = 0; p_assert_info.assert_reason = kMfltRebootReason_SoftwareWatchdog; MEMFAULT_SOFTWARE_WATCHDOG(); mock().expectOneCall("memfault_fault_handling_assert"); p_assert_info.extra++; MEMFAULT_ASSERT(0); } ================================================ FILE: tests/unit/src/test_memfault_base64.cpp ================================================ //! @file //! //! @brief #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/util/base64.h" TEST_GROUP(MemfaultMinimalCbor) { void setup() { } void teardown() { } }; static void prv_test_basic_encoder(const char *input_str, const char *expected_out_str) { const size_t in_len = strlen(input_str); const size_t encode_len = MEMFAULT_BASE64_ENCODE_LEN(in_len); char result[encode_len + 1]; memset(result, 0xA5, sizeof(result)); result[encode_len] = '\0'; memfault_base64_encode(input_str, in_len, result); STRCMP_EQUAL(expected_out_str, result); } static void prv_test_inplace_encoder(const char *input_str, const char *expected_out_str) { const size_t in_len = strlen(input_str); const size_t encode_len = MEMFAULT_BASE64_ENCODE_LEN(in_len); char in_out_buf[encode_len + 1]; memset(in_out_buf, 0xA5, sizeof(in_out_buf)); in_out_buf[encode_len] = '\0'; strncpy(in_out_buf, input_str, sizeof(in_out_buf) - 1); memfault_base64_encode_inplace(in_out_buf, in_len); STRCMP_EQUAL(expected_out_str, in_out_buf); } static void prv_run_test_cases(const char *input_str, const char *expected_out_str) { prv_test_basic_encoder(input_str, expected_out_str); prv_test_inplace_encoder(input_str, expected_out_str); } // Test vectors for base64 from: // https://tools.ietf.org/html/rfc4648#page-12 TEST(MemfaultMinimalCbor, Test_RfcTestVectors) { prv_run_test_cases("", ""); prv_run_test_cases("f", "Zg=="); prv_run_test_cases("fo", "Zm8="); prv_run_test_cases("foo", "Zm9v"); prv_run_test_cases("foob", "Zm9vYg=="); prv_run_test_cases("fooba", "Zm9vYmE="); prv_run_test_cases("foobar", "Zm9vYmFy"); } // Run encoding over all possible values TEST(MemfaultMinimalCbor, Test_All_BasicEncoder) { uint8_t bin_in[256]; for (size_t i = 0; i < sizeof(bin_in); i++) { bin_in[i] = i & 0xff; } const size_t bin_len = sizeof(bin_in); const size_t encode_len = MEMFAULT_BASE64_ENCODE_LEN(bin_len); char result[encode_len + 1]; memset(result, 0xA5, sizeof(result)); result[encode_len] = '\0'; const char *expected_out_str = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUm" "JygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNT" "k9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dX" "Z3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2" "en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TF" "xsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7" "e7v8PHy8/T19vf4+fr7/P3+/w=="; memfault_base64_encode(bin_in, bin_len, result); STRCMP_EQUAL(expected_out_str, result); } TEST(MemfaultMinimalCbor, Test_All_InPlace) { const size_t bin_len = 256; uint8_t in_out_buf[MEMFAULT_BASE64_ENCODE_LEN(bin_len) + 1]; const size_t encode_len = sizeof(in_out_buf) - 1; memset(in_out_buf, 0xA5, sizeof(in_out_buf)); in_out_buf[encode_len] = '\0'; // Fill in-out buffer with binary data to encode for (size_t i = 0; i < bin_len; i++) { in_out_buf[i] = i & 0xff; } const char *expected_out_str = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUm" "JygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNT" "k9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dX" "Z3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2" "en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TF" "xsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7" "e7v8PHy8/T19vf4+fr7/P3+/w=="; memfault_base64_encode_inplace(in_out_buf, bin_len); MEMCMP_EQUAL(expected_out_str, in_out_buf, encode_len); } TEST(MemfaultMinimalCbor, Test_ConversionMacros) { // multiples of 3 should always match size_t encode_len = MEMFAULT_BASE64_ENCODE_LEN(3); LONGS_EQUAL(3, MEMFAULT_BASE64_MAX_DECODE_LEN(encode_len)); encode_len = MEMFAULT_BASE64_ENCODE_LEN(300); LONGS_EQUAL(300, MEMFAULT_BASE64_MAX_DECODE_LEN(encode_len)); // for non multiples of 3, there will be up to 2 padding characters encode_len = MEMFAULT_BASE64_ENCODE_LEN(4); LONGS_EQUAL(6, MEMFAULT_BASE64_MAX_DECODE_LEN(encode_len)); encode_len = MEMFAULT_BASE64_ENCODE_LEN(5); LONGS_EQUAL(6, MEMFAULT_BASE64_MAX_DECODE_LEN(encode_len)); } ================================================ FILE: tests/unit/src/test_memfault_batched_events.cpp ================================================ //! @file //! //! @brief #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/batched_events.h" TEST_GROUP(MemfaultBatchedEvents) { void setup() { } void teardown() { } }; TEST(MemfaultBatchedEvents, Test_MemfaultBatchedHeader) { sMemfaultBatchedEventsHeader header; memfault_batched_events_build_header(1000000, &header); LONGS_EQUAL(5, header.length); const uint8_t cbor_enc_1000000[] = { 0x9a, 0x00, 0x0f, 0x42, 0x40 }; MEMCMP_EQUAL(cbor_enc_1000000, header.data, header.length); memfault_batched_events_build_header(2, &header); LONGS_EQUAL(1, header.length); LONGS_EQUAL(0x82, header.data[0]); memfault_batched_events_build_header(0, &header); LONGS_EQUAL(0, header.length); memfault_batched_events_build_header(1, &header); LONGS_EQUAL(0, header.length); } ================================================ FILE: tests/unit/src/test_memfault_buffered_coredump_storage.cpp ================================================ //! @file //! #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "fakes/fake_memfault_buffered_coredump_storage.h" #include "memfault/core/math.h" #include "memfault/panics/platform/coredump.h" TEST_GROUP(MemfaultBufferedCoredumpStorage) { void setup() { fake_buffered_coredump_storage_reset(); } void teardown() { } }; static void prv_validate_pattern_written(size_t bytes_to_check) { for (size_t i = 0; i < bytes_to_check; i++) { LONGS_EQUAL(i, g_ram_backed_storage[i]); } } TEST(MemfaultBufferedCoredumpStorage, Test_BufferedStorageSingleByte) { const size_t size_to_write = (MEMFAULT_STORAGE_SIZE / 2) + 1; uint32_t start_offset = 2; for (size_t i = 0; i < size_to_write; i++) { const uint32_t addr = (i + start_offset) % size_to_write; uint8_t data = addr & 0xff; const bool success = memfault_platform_coredump_storage_write(addr, &data, sizeof(data)); CHECK(success); } prv_validate_pattern_written(size_to_write); } TEST(MemfaultBufferedCoredumpStorage, Test_BufferedStorageWord) { uint32_t start_offset = 3; uint8_t data[4]; for (size_t i = 0; i < MEMFAULT_STORAGE_SIZE; i += sizeof(data)) { const uint32_t addr = (i + start_offset) % MEMFAULT_STORAGE_SIZE; const size_t write_len = MEMFAULT_MIN(MEMFAULT_STORAGE_SIZE - addr, sizeof(data)); if (write_len != sizeof(data)) { // we are wrapping around so do single byte writes for (size_t j = 0; j < sizeof(data); j++) { const uint32_t byte_addr = (addr + j) % MEMFAULT_STORAGE_SIZE; data[0] = byte_addr & 0xff; const bool success = memfault_platform_coredump_storage_write(byte_addr, &data, 1); CHECK(success); } } else { // write a word for (size_t j = 0; j < sizeof(data); j++) { data[j] = (addr + j) & 0xff; } const bool success = memfault_platform_coredump_storage_write(addr, &data[0], sizeof(data)); CHECK(success); } } prv_validate_pattern_written(MEMFAULT_STORAGE_SIZE); } TEST(MemfaultBufferedCoredumpStorage, Test_LargeWrite) { uint32_t start_offset = 1; uint8_t data[MEMFAULT_STORAGE_SIZE - start_offset]; for (size_t i = 0; i < sizeof(data); i++) { data[i] = (start_offset + i) & 0xff; } bool success = memfault_platform_coredump_storage_write(start_offset, &data, sizeof(data)); CHECK(success); // write final byte to complete update data[0] = 0; success = memfault_platform_coredump_storage_write(0, &data, 1); CHECK(success); prv_validate_pattern_written(MEMFAULT_STORAGE_SIZE); } TEST(MemfaultBufferedCoredumpStorage, Test_BadSectorSize) { fake_buffered_coredump_storage_set_size(7); char data[32] = { 0 }; const bool success = memfault_platform_coredump_storage_write(0, data, sizeof(data)); CHECK(!success); } TEST(MemfaultBufferedCoredumpStorage, Test_BadBlockWrite) { char data[32] = { 0 }; fake_buffered_coredump_inject_write_failure(); bool success = memfault_platform_coredump_storage_write(32, data, sizeof(data)); CHECK(!success); fake_buffered_coredump_inject_write_failure(); success = memfault_platform_coredump_storage_write(0, data, sizeof(data)); CHECK(!success); } ================================================ FILE: tests/unit/src/test_memfault_buffered_coredump_storage_impl.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! A fake implementation of coredump storage which makes use of the //! implementation in memfault/ports/buffered_coredump_storage.h //! We use this in testing to verify that the buffered writer is working //! as expected #include #include #include #include "fakes/fake_memfault_buffered_coredump_storage.h" #include "memfault/ports/buffered_coredump_storage.h" uint8_t g_ram_backed_storage[MEMFAULT_STORAGE_SIZE]; static size_t s_active_storage_size = MEMFAULT_STORAGE_SIZE; static bool s_injected_write_failure = false; void fake_buffered_coredump_inject_write_failure(void) { s_injected_write_failure = true; } void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = (sMfltCoredumpStorageInfo){ .size = s_active_storage_size }; } bool memfault_platform_coredump_storage_buffered_write(sCoredumpWorkingBuffer *block) { if (s_injected_write_failure) { s_injected_write_failure = false; return false; } assert(block != NULL); // writes should always be at an aligned offset assert((block->write_offset % MEMFAULT_COREDUMP_STORAGE_WRITE_SIZE) == 0); assert(block->write_offset < s_active_storage_size); assert((block->write_offset + sizeof(block->data)) <= s_active_storage_size); memcpy(&g_ram_backed_storage[block->write_offset], block->data, sizeof(block->data)); return true; } void fake_buffered_coredump_storage_reset(void) { memset(g_ram_backed_storage, 0xff, MEMFAULT_STORAGE_SIZE); s_active_storage_size = MEMFAULT_STORAGE_SIZE; s_injected_write_failure = false; memset(&s_working_buffer_header, 0x0, sizeof(s_working_buffer_header)); memset(&s_working_buffer, 0x0, sizeof(s_working_buffer)); } void fake_buffered_coredump_storage_set_size(size_t size) { s_active_storage_size = size; } ================================================ FILE: tests/unit/src/test_memfault_build_id.cpp ================================================ //! @file #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/build_info.h" #include "memfault/core/device_info.h" #include "memfault/core/platform/debug_log.h" #include "memfault/core/platform/device_info.h" #include "memfault_build_id_private.h" static bool s_valid_device_info = false; void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { if (s_valid_device_info) { static const sMemfaultDeviceInfo devinfo = { .device_serial = "1234567890", .software_type = "test", .software_version = "1.0.0", .hardware_version = "1.0.0", }; *info = devinfo; } else { memset(info, 0x0, sizeof(*info)); } } TEST_GROUP(MemfaultBuildInfo) { void setup() { s_valid_device_info = false; } void teardown() { mock().checkExpectations(); mock().clear(); } }; #if MEMFAULT_USE_GNU_BUILD_ID extern "C" { // Emulate Symbol that would be emitted by Linker extern uint8_t __start_gnu_build_id_start[]; // A dump from a real note section in an ELF: // arm-none-eabi-objdump -s -j .note.gnu.build-id nrf52.elf // // Contents of section .note.gnu.build-id: // 0050 04000000 14000000 03000000 474e5500 ............GNU. // 0060 58faeeb0 1696afbf b4f790c6 3b77c80f X...........;w.. // 0070 89972989 ..). uint8_t __start_gnu_build_id_start[] = { 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x58, 0xfa, 0xee, 0xb0, 0x16, 0x96, 0xaf, 0xbf, 0xb4, 0xf7, 0x90, 0xc6, 0x3b, 0x77, 0xc8, 0x0f, 0x89, 0x97, 0x29, 0x89 }; // arm-none-eabi-readelf -n nrf52.elf // // Displaying notes found in: .note.gnu.build-id // Owner Data size Description // GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) // Build ID: 58faeeb01696afbfb4f790c63b77c80f89972989 static const uint8_t s_expected_gnu_build_id[] = { 0x58, 0xfa, 0xee, 0xb0, 0x16, 0x96, 0xaf, 0xbf, 0xb4, 0xf7, 0x90, 0xc6, 0x3b, 0x77, 0xc8, 0x0f, 0x89, 0x97, 0x29, 0x89 }; static const char *s_expected_gnu_build_id_dump = "GNU Build ID: 58faeeb01696afbfb4f790c63b77c80f89972989"; } TEST(MemfaultBuildInfo, Test_GnuBuildId) { sMemfaultBuildInfo info = { 0 }; const bool found = memfault_build_info_read(&info); CHECK(found); MEMCMP_EQUAL(info.build_id, &s_expected_gnu_build_id, sizeof(s_expected_gnu_build_id)); mock() .expectOneCall("memfault_platform_log") .withIntParameter("level", kMemfaultPlatformLogLevel_Info) .withStringParameter("output", s_expected_gnu_build_id_dump); memfault_build_info_dump(); mock().checkExpectations(); } #else /* Default - Memfault Generated Build Id is In Use */ TEST(MemfaultBuildInfo, Test_MemfaultCreateUniqueVersionString) { const uint8_t fake_memfault_build_id[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0f, 0x10, 0x11, 0x12, 0x13 }; memcpy(g_memfault_sdk_derived_build_id, fake_memfault_build_id, sizeof(fake_memfault_build_id)); g_memfault_build_id.type = kMemfaultBuildIdType_MemfaultBuildIdSha1; // Test order is important. // 1. NULL at genesis const char *unique = memfault_create_unique_version_string(NULL); CHECK(!unique); // 2. Too big at genesis // Instead of pulling in default_config.h just use an // unrealistically large size. char too_big[1024]; memset(too_big, 'v', sizeof(too_big) - 1); too_big[sizeof(too_big) - 1] = '\0'; unique = memfault_create_unique_version_string(too_big); CHECK(!unique); // 3. Good user version unique = memfault_create_unique_version_string("1.2.3"); CHECK(unique); STRCMP_EQUAL("1.2.3+000102", unique); // 4a. Immutable checks unique = memfault_create_unique_version_string("changed"); CHECK(unique); STRCMP_EQUAL("1.2.3+000102", unique); // 4b. Null should degrade into memfault_get_unique_version_string() // after a good version has been created. unique = memfault_create_unique_version_string(NULL); CHECK(unique); STRCMP_EQUAL("1.2.3+000102", unique); // 5. Getter check (for completeness) unique = memfault_get_unique_version_string(); CHECK(unique); STRCMP_EQUAL("1.2.3+000102", unique); } TEST(MemfaultBuildInfo, Test_MemfaultBuildIdGetString) { const uint8_t fake_memfault_build_id[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0f, 0x10, 0x11, 0x12, 0x13 }; memcpy(g_memfault_sdk_derived_build_id, fake_memfault_build_id, sizeof(fake_memfault_build_id)); g_memfault_build_id.type = kMemfaultBuildIdType_MemfaultBuildIdSha1; char build_id_str[41]; bool success = memfault_build_id_get_string(build_id_str, 1); CHECK(!success); success = memfault_build_id_get_string(build_id_str, 6); CHECK(success); STRCMP_EQUAL("00010", build_id_str); success = memfault_build_id_get_string(build_id_str, 7); CHECK(success); STRCMP_EQUAL("000102", build_id_str); success = memfault_build_id_get_string(build_id_str, sizeof(build_id_str)); CHECK(success); STRCMP_EQUAL("000102030405060708090a0b0c0d0e0f10111213", build_id_str); } TEST(MemfaultBuildInfo, Test_MemfaultBuildId) { // If a user hasn't run script to patch in build id, the value should be none sMemfaultBuildInfo info = { 0 }; bool found = memfault_build_info_read(&info); CHECK(!found); mock() .expectOneCall("memfault_platform_log") .withIntParameter("level", kMemfaultPlatformLogLevel_Info) .withStringParameter("output", "No Build ID available"); memfault_build_info_dump(); mock().checkExpectations(); const uint8_t fake_memfault_build_id[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0f, 0x10, 0x11, 0x12, 0x13 }; memcpy(g_memfault_sdk_derived_build_id, fake_memfault_build_id, sizeof(fake_memfault_build_id)); g_memfault_build_id.type = kMemfaultBuildIdType_MemfaultBuildIdSha1; found = memfault_build_info_read(&info); CHECK(found); MEMCMP_EQUAL(info.build_id, fake_memfault_build_id, sizeof(fake_memfault_build_id)); mock() .expectOneCall("memfault_platform_log") .withIntParameter("level", kMemfaultPlatformLogLevel_Info) .withStringParameter("output", "Memfault Build ID: 000102030405060708090a0b0c0d0e0f10111213"); memfault_build_info_dump(); mock().checkExpectations(); } #endif /* MEMFAULT_USE_GNU_BUILD_ID */ TEST(MemfaultBuildInfo, Test_MfltCoredumpUtil_DeviceInfoDump) { mock().ignoreOtherCalls(); memfault_device_info_dump(); s_valid_device_info = true; memfault_device_info_dump(); } ================================================ FILE: tests/unit/src/test_memfault_chunk_transport.cpp ================================================ //! @file //! //! @brief #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include #include #include #include #include "memfault/core/math.h" #include "memfault/util/chunk_transport.h" static const uint8_t s_test_msg[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa }; static const uint8_t *s_active_msg = NULL; static sMfltChunkTransportCtx s_chunk_ctx; typedef struct { size_t total_bytes_read; size_t last_offset; } sMfltChunkReadStats; static sMfltChunkReadStats s_chunk_read_stats; static void prv_chunk_msg(uint32_t offset, void *out_buf, size_t out_buf_len) { // One property enforced on the chunker is that all the reads are performed sequentially. This // way the offsets which will be accessed prior to invoking the chunker are known. CHECK(offset >= s_chunk_read_stats.last_offset); s_chunk_read_stats.last_offset = offset; s_chunk_read_stats.total_bytes_read += out_buf_len; CHECK(s_active_msg != NULL); memcpy(out_buf, &s_active_msg[offset], out_buf_len); } } TEST_GROUP(MemfaultChunkTransport) { void setup() { s_active_msg = &s_test_msg[0]; memset(&s_chunk_read_stats, 0x0, sizeof(s_chunk_read_stats)); memset(&s_chunk_ctx, 0x0, sizeof(s_chunk_ctx)); // restore defaults s_chunk_ctx.total_size = MEMFAULT_ARRAY_SIZE(s_test_msg); s_chunk_ctx.read_msg = &prv_chunk_msg; } void teardown() { if (s_chunk_read_stats.total_bytes_read == 0) { return; // a failure test case where no data got read } // Another property we impose on the chunker is that it should only need to read the message // being sent once. This is a performance optimization for situations where the backing storage // medium (i.e flash) can be slow to access. LONGS_EQUAL(s_chunk_ctx.total_size, s_chunk_read_stats.total_bytes_read); } }; static void prv_check_chunk(sMfltChunkTransportCtx *ctx, bool expect_md, size_t receive_buf_len, const void *expected_chunk, size_t expected_chunk_len) { uint8_t actual_chunk[receive_buf_len]; memset(actual_chunk, 0x0, receive_buf_len); bool md = memfault_chunk_transport_get_next_chunk(ctx, &actual_chunk[0], &receive_buf_len); LONGS_EQUAL(ctx->total_size + 1 + 2, ctx->single_chunk_message_length); LONGS_EQUAL(expect_md, md); LONGS_EQUAL(expected_chunk_len, receive_buf_len); MEMCMP_EQUAL(expected_chunk, actual_chunk, expected_chunk_len); } TEST(MemfaultChunkTransport, Test_ChunkerMultiPart) { const size_t receive_buf_size = 9; const bool md = true; const uint8_t expected_msg_1[] = { 0x48, 0x09, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; prv_check_chunk(&s_chunk_ctx, md, receive_buf_size, &expected_msg_1, sizeof(expected_msg_1)); const uint8_t expected_msg_2[] = { 0x80, 0x7, 0x8, 0xa, 0x1b, 0x13 }; prv_check_chunk(&s_chunk_ctx, !md, receive_buf_size, &expected_msg_2, sizeof(expected_msg_2)); } TEST(MemfaultChunkTransport, Test_ChunkerMultiPartLastMessageJustCrc) { static const uint8_t test_msg_long[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }; s_chunk_ctx.total_size = sizeof(test_msg_long); s_active_msg = &test_msg_long[0]; const bool md = true; const uint8_t expected_msg_1[] = { 0x48, 0x0E, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; size_t receive_buf_size = sizeof(expected_msg_1); prv_check_chunk(&s_chunk_ctx, md, receive_buf_size, &expected_msg_1, sizeof(expected_msg_1)); const uint8_t expected_msg_2[] = { 0xC0, 0x07, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }; // The CRC is two bytes so make sure if there is only 1 extra byte, the crc is not written and // one more chunk needs to be sent receive_buf_size = sizeof(expected_msg_2) + 1; prv_check_chunk(&s_chunk_ctx, md, receive_buf_size, &expected_msg_2, sizeof(expected_msg_2)); const uint8_t expected_msg_3[] = { 0x80, 0xE, 0x4c, 0xdc }; receive_buf_size = MEMFAULT_MIN_CHUNK_BUF_LEN; prv_check_chunk(&s_chunk_ctx, !md, receive_buf_size, &expected_msg_3, sizeof(expected_msg_3)); } TEST(MemfaultChunkTransport, Test_ChunkerMultiCallChunkLastMessageJustCrc) { static const uint8_t test_msg_long[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11 }; s_chunk_ctx.total_size = sizeof(test_msg_long); s_active_msg = &test_msg_long[0]; s_chunk_ctx.enable_multi_call_chunk = true; const bool md = true; const uint8_t expected_msg_1[] = { 0x08, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 }; size_t receive_buf_size = sizeof(expected_msg_1); prv_check_chunk(&s_chunk_ctx, md, receive_buf_size, &expected_msg_1, sizeof(expected_msg_1)); const uint8_t expected_msg_2[] = { 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11 }; // The CRC is two bytes so make sure if there is only 1 extra byte, the crc is not written and // one more chunk needs to be sent receive_buf_size = sizeof(expected_msg_2) + 1; prv_check_chunk(&s_chunk_ctx, md, receive_buf_size, &expected_msg_2, sizeof(expected_msg_2)); const uint8_t expected_msg_3[] = { 0x13, 0xdb }; receive_buf_size = MEMFAULT_MIN_CHUNK_BUF_LEN; prv_check_chunk(&s_chunk_ctx, !md, receive_buf_size, &expected_msg_3, sizeof(expected_msg_3)); } TEST(MemfaultChunkTransport, Test_BufTooSmall) { size_t receive_buf_len = 8; uint8_t receive_buf[receive_buf_len]; bool md = memfault_chunk_transport_get_next_chunk(&s_chunk_ctx, &receive_buf[0], &receive_buf_len); CHECK(md); LONGS_EQUAL(0, receive_buf_len); } TEST(MemfaultChunkTransport, Test_ChunkerSingleMsg) { const bool md = true; const uint8_t expected_msg_all[] = { 0x08, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa, 0x1b, 0x13 }; prv_check_chunk(&s_chunk_ctx, !md, sizeof(expected_msg_all), &expected_msg_all, sizeof(expected_msg_all)); } TEST(MemfaultChunkTransport, Test_ChunkerSingleMsgOversizeBuffer) { const uint8_t expected_msg_all[] = { 0x08, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa, 0x1b, 0x13 }; const size_t expected_chunk_len = sizeof(expected_msg_all); const size_t known_pattern_len = 10; uint8_t actual_chunk[expected_chunk_len + known_pattern_len]; memset(actual_chunk, 0x0, sizeof(actual_chunk)); size_t buf_len = sizeof(actual_chunk); const bool md = memfault_chunk_transport_get_next_chunk(&s_chunk_ctx, &actual_chunk[0], &buf_len); CHECK(!md); LONGS_EQUAL(s_chunk_ctx.total_size + 1 + 2, s_chunk_ctx.single_chunk_message_length); LONGS_EQUAL(expected_chunk_len, buf_len); MEMCMP_EQUAL(expected_msg_all, actual_chunk, expected_chunk_len); // the remainder of the buffer should have been scrubbed with a known pattern uint8_t pattern[known_pattern_len]; memset(pattern, 0xBA, sizeof(pattern)); MEMCMP_EQUAL(&actual_chunk[expected_chunk_len], pattern, sizeof(pattern)); } TEST(MemfaultChunkTransport, Test_ChunkerBigMessage) { uint8_t s_big_msg[4072] = { 0x1, 0x2, 0x3 }; s_active_msg = &s_big_msg[0]; s_chunk_ctx.total_size = MEMFAULT_ARRAY_SIZE(s_big_msg); // get info before sending the message memfault_chunk_transport_get_chunk_info(&s_chunk_ctx); LONGS_EQUAL(s_chunk_ctx.total_size + 1 + 2, s_chunk_ctx.single_chunk_message_length); LONGS_EQUAL(0, s_chunk_ctx.read_offset); // Let's just make sure the sequencer does okay when it takes more than 1 byte to encode a varint const bool md = true; const uint8_t expected_msg_1[] = { 0x48, 0xe8, 0x1f, 0x1, 0x2, 0x3, 0x0, 0x0, 0x0 }; prv_check_chunk(&s_chunk_ctx, md, sizeof(expected_msg_1), &expected_msg_1, sizeof(expected_msg_1)); // read 4000 bytes (& 1 byte hdr & 1 byte varint) uint8_t read_buffer[4067]; size_t read_buffer_size = sizeof(read_buffer); bool md_avail = memfault_chunk_transport_get_next_chunk(&s_chunk_ctx, &read_buffer[0], &read_buffer_size); LONGS_EQUAL(read_buffer[0], 0xC0); // should be a continuation header CHECK(md_avail); LONGS_EQUAL(sizeof(read_buffer), read_buffer_size); // size info should not change if we are in the middle of sending a message memfault_chunk_transport_get_chunk_info(&s_chunk_ctx); LONGS_EQUAL(s_chunk_ctx.total_size + 1 + 2, s_chunk_ctx.single_chunk_message_length); LONGS_EQUAL(4071, s_chunk_ctx.read_offset); const uint8_t expected_msg_2[] = { 0x80, 0xe7, 0x1f, 0x0, 0xF4, 0x79 }; prv_check_chunk(&s_chunk_ctx, !md, 20 /* oversize buffer */, &expected_msg_2, sizeof(expected_msg_2)); } TEST(MemfaultChunkTransport, Test_ChunkerSingleMsgMultiChunkEnabled) { s_chunk_ctx.enable_multi_call_chunk = true; const bool md = true; const uint8_t expected_msg_all[] = { 0x08, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa, 0x1b, 0x13 }; prv_check_chunk(&s_chunk_ctx, !md, sizeof(expected_msg_all), &expected_msg_all, sizeof(expected_msg_all)); } TEST(MemfaultChunkTransport, Test_ChunkerMultiCallSingleChunk) { uint8_t s_big_msg[4072] = { 0x1, 0x2, 0x3 }; s_active_msg = &s_big_msg[0]; s_chunk_ctx.total_size = MEMFAULT_ARRAY_SIZE(s_big_msg); s_chunk_ctx.enable_multi_call_chunk = true; // Let's just make sure the sequencer does okay when it takes more than 1 byte to encode a varint const bool md = true; const uint8_t expected_msg_1[] = { 0x08, 0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0 }; prv_check_chunk(&s_chunk_ctx, md, sizeof(expected_msg_1), &expected_msg_1, sizeof(expected_msg_1)); // read all bytes except for the last one uint8_t read_buffer[4063]; size_t read_buffer_size = sizeof(read_buffer); bool md_avail = memfault_chunk_transport_get_next_chunk(&s_chunk_ctx, &read_buffer[0], &read_buffer_size); CHECK(md_avail); LONGS_EQUAL(sizeof(read_buffer), read_buffer_size); const uint8_t expected_msg_2[] = { 0x0, /* crc */ 0xF4, 0x79 }; prv_check_chunk(&s_chunk_ctx, !md, 20 /* oversize buffer */, &expected_msg_2, sizeof(expected_msg_2)); } ================================================ FILE: tests/unit/src/test_memfault_circular_buffer.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include #include #include "memfault/util/circular_buffer.h" } TEST_GROUP(MfltCircularBufferTestGroup) { void setup() { } void teardown() { } }; TEST(MfltCircularBufferTestGroup, Test_MfltCircularBufferInit) { bool success = memfault_circular_buffer_init(NULL, NULL, 0); CHECK(!success); sMfltCircularBuffer buffer; success = memfault_circular_buffer_init(&buffer, NULL, 0); CHECK(!success); uint8_t storage_buf[10]; success = memfault_circular_buffer_init(&buffer, storage_buf, 0); CHECK(!success); success = memfault_circular_buffer_init(&buffer, storage_buf, sizeof(storage_buf)); CHECK(success); size_t bytes_to_read = memfault_circular_buffer_get_read_size(&buffer); LONGS_EQUAL(0, bytes_to_read); bytes_to_read = memfault_circular_buffer_get_read_size(NULL); LONGS_EQUAL(0, bytes_to_read); size_t space_available = memfault_circular_buffer_get_write_size(&buffer); LONGS_EQUAL(sizeof(storage_buf), space_available); space_available = memfault_circular_buffer_get_write_size(NULL); LONGS_EQUAL(0, space_available); } TEST(MfltCircularBufferTestGroup, Test_MfltCircularWriteAtOffsetBasic) { const uint8_t buffer_size = 4; uint8_t storage_buf[buffer_size]; sMfltCircularBuffer buffer; bool success = memfault_circular_buffer_init(&buffer, storage_buf, sizeof(storage_buf)); CHECK(success); const uint8_t seq1[] = { 0x1, 0x2 }; success = memfault_circular_buffer_write_at_offset(&buffer, 0, seq1, sizeof(seq1)); CHECK(success); // overwrite first two bytes and fill rest of buffer const uint8_t seq2[] = { 0x3, 0x4, 0x5, 0x6 }; success = memfault_circular_buffer_write_at_offset(&buffer, 2, seq2, sizeof(seq2)); CHECK(success); // buffer should be full uint8_t result[buffer_size]; memset(result, 0x0, sizeof(result)); success = memfault_circular_buffer_read(&buffer, 0, &result, sizeof(result)); CHECK(success); MEMCMP_EQUAL(seq2, result, sizeof(result)); } TEST(MfltCircularBufferTestGroup, Test_MfltCircularWriteAndReadBasic) { const uint8_t buffer_size = 16; uint8_t storage_buf[buffer_size]; sMfltCircularBuffer buffer; bool success = memfault_circular_buffer_init(&buffer, storage_buf, sizeof(storage_buf)); CHECK(success); size_t bytes_to_read = memfault_circular_buffer_get_read_size(&buffer); LONGS_EQUAL(0, bytes_to_read); size_t space_available = memfault_circular_buffer_get_write_size(&buffer); LONGS_EQUAL(buffer_size, space_available); // should be nothing to read yet uint8_t bytes_read[buffer_size]; success = memfault_circular_buffer_read(&buffer, 0, bytes_read, sizeof(bytes_read)); CHECK(!success); uint8_t bytes_to_write[buffer_size - 3]; for (uint8_t i = 0; i < sizeof(bytes_to_write); i++) { bytes_to_write[i] = i; } // let's write some bytes for (int i = 0; i < 2; i++) { // can't write to a NULL buffer success = memfault_circular_buffer_write(NULL, bytes_to_write, sizeof(bytes_to_write)); CHECK(!success); success = memfault_circular_buffer_write(&buffer, NULL, 0); CHECK(!success); success = memfault_circular_buffer_write(&buffer, bytes_to_write, sizeof(bytes_to_write)); // 2nd iteration should fail because we are out of space LONGS_EQUAL(i == 0, success); bytes_to_read = memfault_circular_buffer_get_read_size(&buffer); LONGS_EQUAL(sizeof(bytes_to_write), bytes_to_read); space_available = memfault_circular_buffer_get_write_size(&buffer); LONGS_EQUAL(buffer_size - sizeof(bytes_to_write), space_available); } // trying to read too many bytes should fail success = memfault_circular_buffer_read(&buffer, 0, bytes_read, sizeof(bytes_read)); CHECK(!success); // an attempt to overwrite past the beginning of the buffer should fail success = memfault_circular_buffer_write_at_offset(&buffer, sizeof(bytes_to_write) + 1, bytes_to_write, sizeof(bytes_to_write)); CHECK(!success); // trying a read with a NULL buffer should fail success = memfault_circular_buffer_read(NULL, 0, bytes_read, sizeof(bytes_read)); CHECK(!success); success = memfault_circular_buffer_read(&buffer, 0, NULL, 0); CHECK(!success); success = memfault_circular_buffer_read(&buffer, 0, bytes_read, sizeof(bytes_to_write)); CHECK(success); MEMCMP_EQUAL(bytes_to_write, bytes_read, sizeof(bytes_to_write)); // trying to consume too many bytes should fail success = memfault_circular_buffer_consume(&buffer, sizeof(bytes_read)); CHECK(!success); success = memfault_circular_buffer_consume_from_end(&buffer, sizeof(bytes_read)); CHECK(!success); // trying to consume without a buffer should fail success = memfault_circular_buffer_consume(NULL, sizeof(bytes_to_write)); CHECK(!success); success = memfault_circular_buffer_consume_from_end(NULL, sizeof(bytes_to_write)); CHECK(!success); success = memfault_circular_buffer_consume(&buffer, sizeof(bytes_to_write)); CHECK(success); // should be nothing left bytes_to_read = memfault_circular_buffer_get_read_size(&buffer); LONGS_EQUAL(0, bytes_to_read); space_available = memfault_circular_buffer_get_write_size(&buffer); LONGS_EQUAL(buffer_size, space_available); } static void prv_fill_write_buf(uint8_t start_idx, uint8_t *buf, size_t buf_len) { for (size_t i = 0; i < buf_len; i++) { buf[i] = (uint8_t)(start_idx + i); } } static void prv_read_buf_and_check(sMfltCircularBuffer *buffer, uint8_t start_idx, size_t total_len) { uint8_t read_buf[total_len]; bool success; // as tiny reads memset(read_buf, 0x0, sizeof(read_buf)); for (uint8_t i = 0; i < sizeof(read_buf); i++) { success = memfault_circular_buffer_read(buffer, i, &read_buf[i], 1); CHECK(success); LONGS_EQUAL((uint8_t)(start_idx + i), read_buf[i]); } // as one big read memset(read_buf, 0x0, sizeof(read_buf)); success = memfault_circular_buffer_read(buffer, 0, read_buf, sizeof(read_buf)); CHECK(success); for (uint8_t i = 0; i < sizeof(read_buf); i++) { LONGS_EQUAL((uint8_t)(start_idx + i), read_buf[i]); } // we are done, consume the data success = memfault_circular_buffer_consume(buffer, total_len); CHECK(success); } TEST(MfltCircularBufferTestGroup, Test_MfltCircularWriteAndReadWrapAround) { const uint8_t buffer_size = 14; uint8_t storage_buf[buffer_size]; sMfltCircularBuffer buffer; bool success = memfault_circular_buffer_init(&buffer, storage_buf, sizeof(storage_buf)); CHECK(success); // write 3 bytes at a time & read 9 bytes every 3 iterations uint8_t write_buf[3]; uint8_t expected_val = 0; uint8_t curr_write_val = 0; for (size_t i = 0; i < 99; i++) { prv_fill_write_buf(curr_write_val, write_buf, sizeof(write_buf)); curr_write_val += 3; // write an initial pattern that we will overwrite uint8_t init_pattern_buf[sizeof(write_buf)]; memset(init_pattern_buf, 0xfe, sizeof(init_pattern_buf)); success = memfault_circular_buffer_write(&buffer, init_pattern_buf, sizeof(init_pattern_buf)); CHECK(success); // re-write first byte success = memfault_circular_buffer_write_at_offset(&buffer, sizeof(write_buf), write_buf, 1); CHECK(success); // consume one byte success = memfault_circular_buffer_consume_from_end(&buffer, 1); CHECK(success); // re-write remaining success = memfault_circular_buffer_write_at_offset(&buffer, sizeof(write_buf) - 2, &write_buf[1], sizeof(write_buf) - 1); CHECK(success); if ((i == 0) || ((i % 3) != 0)) { continue; } const size_t read_size = 9; prv_read_buf_and_check(&buffer, expected_val, read_size); expected_val += read_size; } } TEST(MfltCircularBufferTestGroup, Test_MfltCircularWriteAndGetReadPointerBadInput) { const uint8_t buffer_size = 20; uint8_t storage_buf[buffer_size]; sMfltCircularBuffer buffer; bool success = memfault_circular_buffer_init(&buffer, storage_buf, sizeof(storage_buf)); CHECK(success); uint8_t data = 0xab; success = memfault_circular_buffer_write(&buffer, &data, sizeof(data)); CHECK(success); uint8_t *read_ptr; size_t read_ptr_len; success = memfault_circular_buffer_get_read_pointer(NULL, 0, &read_ptr, &read_ptr_len); CHECK(!success); success = memfault_circular_buffer_get_read_pointer(&buffer, 0, NULL, &read_ptr_len); CHECK(!success); success = memfault_circular_buffer_get_read_pointer(&buffer, 0, &read_ptr, NULL); CHECK(!success); success = memfault_circular_buffer_get_read_pointer(&buffer, 2, &read_ptr, &read_ptr_len); CHECK(!success); } TEST(MfltCircularBufferTestGroup, Test_MfltCircularWriteAndGetReadPointer) { const uint8_t buffer_size = 20; uint8_t storage_buf[buffer_size]; sMfltCircularBuffer buffer; bool success = memfault_circular_buffer_init(&buffer, storage_buf, sizeof(storage_buf)); CHECK(success); // fill the buffer up, our read size should keep increasing by 1 for (uint8_t i = 0; i < buffer_size; i++) { uint8_t data = i; success = memfault_circular_buffer_write(&buffer, &data, sizeof(data)); CHECK(success); uint8_t *read_ptr; size_t read_ptr_len; success = memfault_circular_buffer_get_read_pointer(&buffer, 0, &read_ptr, &read_ptr_len); CHECK(success); LONGS_EQUAL(read_ptr_len, i + 1); for (uint8_t j = 0; j < read_ptr_len; j++) { LONGS_EQUAL(j, read_ptr[j]); } } // consume & add one byte at a time for (uint8_t i = 0; i < buffer_size; i++) { success = memfault_circular_buffer_consume(&buffer, 1); CHECK(success); uint8_t data = i + buffer_size; success = memfault_circular_buffer_write(&buffer, &data, sizeof(data)); CHECK(success); uint8_t *read_ptr; size_t read_ptr_len; success = memfault_circular_buffer_get_read_pointer(&buffer, 0, &read_ptr, &read_ptr_len); CHECK(success); const uint8_t base = i + 1; size_t read_size = buffer_size - base; // once we have wrapped around, the read size will be the full buffer again LONGS_EQUAL(read_size == 0 ? buffer_size : read_size, read_ptr_len); for (uint8_t j = 0; j < read_ptr_len; j++) { LONGS_EQUAL(j + base, read_ptr[j]); } } } static uint8_t s_storage_buf[10]; static sMfltCircularBuffer s_buffer; static int s_ctx; TEST_GROUP(MfltCircularBufferReadWithCallbackTestGroup) { void setup() { memset(s_storage_buf, 0, sizeof(s_storage_buf)); const bool success = memfault_circular_buffer_init(&s_buffer, s_storage_buf, sizeof(s_storage_buf)); CHECK(success); } void teardown() { mock().checkExpectations(); mock().clear(); } }; static bool prv_read_callback(void *ctx, size_t offset, const void *buf, size_t buf_len) { return mock() .actualCall(__func__) .withPointerParameter("ctx", ctx) .withUnsignedIntParameter("offset", offset) .withConstPointerParameter("buf", buf) .withUnsignedIntParameter("buf_len", buf_len) .returnBoolValueOrDefault(true); } TEST(MfltCircularBufferReadWithCallbackTestGroup, Test_InvalidParamNullBuffer) { CHECK_FALSE(memfault_circular_buffer_read_with_callback(NULL, 0, 1, NULL, prv_read_callback)); } TEST(MfltCircularBufferReadWithCallbackTestGroup, Test_InvalidParamNullCallback) { CHECK_FALSE(memfault_circular_buffer_read_with_callback(&s_buffer, 0, 1, NULL, NULL)); } TEST(MfltCircularBufferReadWithCallbackTestGroup, Test_InvalidParamOverReadSize) { CHECK_FALSE(memfault_circular_buffer_read_with_callback(&s_buffer, 0, 1, NULL, prv_read_callback)); } TEST(MfltCircularBufferReadWithCallbackTestGroup, Test_NothingToRead) { CHECK(memfault_circular_buffer_write(&s_buffer, "hi", 2)); CHECK(memfault_circular_buffer_read_with_callback(&s_buffer, 2, 0, &s_ctx, prv_read_callback)); } TEST(MfltCircularBufferReadWithCallbackTestGroup, Test_Contiguous) { const size_t read_size = 2; const size_t read_offset = 1; CHECK(memfault_circular_buffer_write(&s_buffer, "hi", read_size)); mock() .expectOneCall("prv_read_callback") .withPointerParameter("ctx", &s_ctx) .withUnsignedIntParameter("offset", 0) .withConstPointerParameter("buf", &s_storage_buf[read_offset]) .withUnsignedIntParameter("buf_len", read_size - read_offset); CHECK(memfault_circular_buffer_read_with_callback(&s_buffer, read_offset, read_size - read_offset, &s_ctx, prv_read_callback)); } TEST(MfltCircularBufferReadWithCallbackTestGroup, Test_NonContiguous) { CHECK(memfault_circular_buffer_write(&s_buffer, "0123456789", sizeof(s_storage_buf))); CHECK(memfault_circular_buffer_consume(&s_buffer, 5)); CHECK(memfault_circular_buffer_write(&s_buffer, "hello", 5)); mock() .expectOneCall("prv_read_callback") .withPointerParameter("ctx", &s_ctx) .withUnsignedIntParameter("offset", 0) .withConstPointerParameter("buf", &s_storage_buf[5]) .withUnsignedIntParameter("buf_len", 5); mock() .expectOneCall("prv_read_callback") .withPointerParameter("ctx", &s_ctx) .withUnsignedIntParameter("offset", 5) .withConstPointerParameter("buf", &s_storage_buf[0]) .withUnsignedIntParameter("buf_len", 5); CHECK(memfault_circular_buffer_read_with_callback(&s_buffer, 0, 10, &s_ctx, prv_read_callback)); } TEST(MfltCircularBufferReadWithCallbackTestGroup, Test_CallbackReturningFalse) { const size_t read_size = 2; const size_t read_offset = 1; CHECK(memfault_circular_buffer_write(&s_buffer, "hi", read_size)); mock() .expectOneCall("prv_read_callback") .withPointerParameter("ctx", &s_ctx) .withUnsignedIntParameter("offset", 0) .withConstPointerParameter("buf", &s_storage_buf[read_offset]) .withUnsignedIntParameter("buf_len", read_size - read_offset) .andReturnValue(false); CHECK_FALSE(memfault_circular_buffer_read_with_callback(&s_buffer, read_offset, read_size - read_offset, &s_ctx, prv_read_callback)); } ================================================ FILE: tests/unit/src/test_memfault_compact_log_c.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Note: These tests are in a C (instead of CPP) file because they //! test the use of the _Generic() macro which is not available in CPP. #include #include #include "CppUTestExt/MockSupport_c.h" #include "memfault/core/compact_log_compile_time_checks.h" #include "memfault/core/compact_log_helpers.h" #include "memfault_test_compact_log_c.h" static void prv_compact_log_mock(uint32_t compressed_fmt, ...) { mock_c()->actualCall(__func__)->withUnsignedIntParameters("compressed_fmt", compressed_fmt); } static void prv_expect_compact_log_call(uint32_t compressed_fmt) { mock_c() ->expectOneCall("prv_compact_log_mock") ->withUnsignedIntParameters("compressed_fmt", compressed_fmt); } #define MOCK_CHECK_EXPECTATIONS_AND_CLEAR() \ do { \ mock_c()->checkExpectations(); \ mock_c()->clear(); \ } while (0) #include "test_memfault_compact_log_cxx.c" void test_compact_log_c_argument_expansion(void) { test_compact_log_cxx_argument_expansion(); } void test_compact_log_c_multi_arg(void) { test_compact_log_cxx_multi_arg(); } void test_compact_log_c_multi_complex(void) { test_compact_log_cxx_multi_complex(); } ================================================ FILE: tests/unit/src/test_memfault_compact_log_cxx.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Note: These tests are reused in C++ and C tests. See the files including //! this one for how they're meant to be used. #include #include #include "CppUTestExt/MockSupport_c.h" #include "memfault/core/compact_log_compile_time_checks.h" #include "memfault/core/compact_log_helpers.h" #include "memfault_test_compact_log_c.h" #define MEMFAULT_COMPACT_LOG_CHECK(format, ...) \ do { \ MEMFAULT_LOGGING_RUN_COMPILE_TIME_CHECKS(format, ##__VA_ARGS__); \ prv_compact_log_mock(MFLT_GET_COMPRESSED_LOG_FMT(__VA_ARGS__), ##__VA_ARGS__); \ } while (0) static void test_compact_log_cxx_argument_expansion(void) { // Up to 15 arguments should get serialized correctly // compressed format code for integers is 0b00 for (int i = 0; i <= 15; i++) { const uint32_t compressed_fmt = 0x1 << (i * 2); prv_expect_compact_log_call(compressed_fmt); } MEMFAULT_COMPACT_LOG_CHECK("0 args"); MEMFAULT_COMPACT_LOG_CHECK("1 arg %d", 1); MEMFAULT_COMPACT_LOG_CHECK("2 arg %d %d", 1, 2); MEMFAULT_COMPACT_LOG_CHECK("3 arg %d %d %d", 1, 2, 3); MEMFAULT_COMPACT_LOG_CHECK("4 arg %d %d %d %d", 1, 2, 3, 4); MEMFAULT_COMPACT_LOG_CHECK("5 arg %d %d %d %d %d", 1, 2, 3, 4, 5); MEMFAULT_COMPACT_LOG_CHECK("6 arg %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6); MEMFAULT_COMPACT_LOG_CHECK("7 arg %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7); MEMFAULT_COMPACT_LOG_CHECK("8 arg %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8); MEMFAULT_COMPACT_LOG_CHECK("9 arg %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9); MEMFAULT_COMPACT_LOG_CHECK("10 arg %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); MEMFAULT_COMPACT_LOG_CHECK("11 arg %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); MEMFAULT_COMPACT_LOG_CHECK("12 arg %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); MEMFAULT_COMPACT_LOG_CHECK("13 arg %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); MEMFAULT_COMPACT_LOG_CHECK("14 arg %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); MEMFAULT_COMPACT_LOG_CHECK("15 arg %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); } static void test_compact_log_cxx_multi_arg(void) { MOCK_CHECK_EXPECTATIONS_AND_CLEAR(); prv_expect_compact_log_call(0x1C9); const uint8_t u8 = 8; const uint64_t u64 = 0x7fffeee10001234; MEMFAULT_COMPACT_LOG_CHECK("Assorted Types: %s %" PRIx8 " %f %" PRIx64, "hello", u8, (double)2.1f, u64); MOCK_CHECK_EXPECTATIONS_AND_CLEAR(); const char *s1 = NULL; const char s2[] = "s2"; const char *s3 = "s3"; char s4[] = "s4"; prv_expect_compact_log_call(0x1FF); MEMFAULT_COMPACT_LOG_CHECK("Strings %s %s %s %s", s1, s2, s3, s4); } static void test_compact_log_cxx_multi_complex(void) { // a variety of different argument types const char *s = "s"; uint64_t llx = 0xdeadbeef1; double f = 1.2; int d = 10; int64_t lld = 1; // A complex formatter with all the different types interleaved // 0b111.0010.0101.1000.1110.0111.0001.1011 prv_expect_compact_log_call(0x7258E71B); MEMFAULT_COMPACT_LOG_CHECK("%s %d %f %" PRIx64 ": " "%" PRId64 " %f %d %s: " "%f %" PRId64 " %s %d: " "%" PRId64 " %f %s", s, d, f, llx, lld, f, d, s, f, lld, s, d, lld, f, s); } ================================================ FILE: tests/unit/src/test_memfault_compact_log_macros.cpp ================================================ //! @file //! #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/compact_log_compile_time_checks.h" #include "memfault/core/compact_log_helpers.h" #include "memfault_test_compact_log_c.h" TEST_GROUP(MfltCompactLogMacros) { void setup() { mock().strictOrder(); } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MfltCompactLogMacros, Test_MfltCompactLog_ArgExpansion) { test_compact_log_c_argument_expansion(); } TEST(MfltCompactLogMacros, Test_MfltCompactLog_MultiArg) { test_compact_log_c_multi_arg(); } TEST(MfltCompactLogMacros, Test_MfltCompactLog_Complex) { test_compact_log_c_multi_complex(); } //! Mocks and helper for the Cxx->C++ generic tests below static void prv_compact_log_mock(uint32_t compressed_fmt, ...) { mock().actualCall(__func__).withUnsignedIntParameter("compressed_fmt", compressed_fmt); } static void prv_expect_compact_log_call(uint32_t compressed_fmt) { mock() .expectOneCall("prv_compact_log_mock") .withUnsignedIntParameter("compressed_fmt", compressed_fmt); } #define MOCK_CHECK_EXPECTATIONS_AND_CLEAR() \ do { \ mock().checkExpectations(); \ mock().clear(); \ } while (0) #include "test_memfault_compact_log_cxx.c" TEST(MfltCompactLogMacros, Test_MfltCompactLog_Cpp_ArgExpansion) { test_compact_log_cxx_argument_expansion(); } TEST(MfltCompactLogMacros, Test_MfltCompactLog_Cpp_MultiArg) { test_compact_log_cxx_multi_arg(); } TEST(MfltCompactLogMacros, Test_MfltCompactLog_Cpp_Complex) { test_compact_log_cxx_multi_complex(); } ================================================ FILE: tests/unit/src/test_memfault_compact_log_save_truncation.cpp ================================================ //! @file //! //! Unit tests for memfault_compact_log_save() when serialized entry exceeds //! MEMFAULT_LOG_MAX_LINE_SAVE_LEN #include #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/core/compact_log_serializer.h" #include "memfault/core/log.h" #include "memfault/core/log_impl.h" #include "memfault/core/sdk_assert.h" #include "memfault_log_data_source_private.h" #include "memfault_log_private.h" extern "C" { // emulated linker symbol expected by the serializer extern uint32_t __start_log_fmt; uint32_t __start_log_fmt; bool memfault_log_data_source_has_been_triggered(void) { return false; } void memfault_sdk_assert_func(void) { } } TEST_GROUP(MemfaultCompactLogSaveTruncation) { void setup() { fake_memfault_metrics_platform_locking_reboot(); // we selectively enable memfault_log_handle_saved_callback() for certain tests mock().disable(); } void teardown() { CHECK(fake_memfault_platform_metrics_lock_calls_balanced()); memfault_log_reset(); mock().checkExpectations(); mock().clear(); } }; static uint32_t prv_get_fake_log_id(void) { // We'll just force all our log ids to be the same for test purposes. We just want to verify that // its computed relative to the "__start_log_fmt" linker symbol return (uint32_t)(uintptr_t)&__start_log_fmt + 0xA; } static eMemfaultPlatformLogLevel prv_record_oversized_compact_log(void) { const eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; const uint32_t log_id = prv_get_fake_log_id(); // Create a compact log format that will result in a very long serialized output // This uses many string arguments to exceed MEMFAULT_LOG_MAX_LINE_SAVE_LEN (32 bytes) const uint32_t compressed_fmt = 0x1FFF; // 0b1.1111.1111.1111 (12 string args) #define LONG_STRING "This is a very long string that will make the compact log exceed the limit" // Call memfault_compact_log_save with many long string arguments memfault_compact_log_save(level, log_id, compressed_fmt, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING, LONG_STRING); return level; } TEST(MemfaultCompactLogSaveTruncation, Test_CompactLogSaveWithFallback) { // Set up a buffer large enough to hold the fallback entry but not the original oversized entry uint8_t s_ram_log_store[128]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); eMemfaultPlatformLogLevel level = prv_record_oversized_compact_log(); // The function should have created a fallback entry instead of the original log sMemfaultLog log; const bool found_log = memfault_log_read(&log); CHECK(found_log); // Verify it's a compact log type LONGS_EQUAL(kMemfaultLogRecordType_Compact, log.type); LONGS_EQUAL(level, log.level); // The fallback entry should contain: // [, {0:1, 1:serialized_len}] const uint8_t expected_cbor[] = { 0x82, // array of 2 elements 0x0A, // log_id offset (0xA) 0xA2, // map of 2 elements 0x00, 0x01, // key 0 -> value 1 (fallback reason) 0x01, 0x19, 0x01, 0xCA // key 1 -> value 458 (serialized_len) }; LONGS_EQUAL(sizeof(expected_cbor), log.msg_len); // array of 2 elements MEMCMP_EQUAL(expected_cbor, log.msg, sizeof(expected_cbor)); // Verify there are no more logs (original oversized log should not be saved) const bool found_second_log = memfault_log_read(&log); CHECK_FALSE(found_second_log); } // test logging when the total ram log storage is too small to hold even the fallback entry TEST(MemfaultCompactLogSaveTruncation, Test_CompactLogSaveWithFallback_TooSmallStorage) { // Set up a buffer too small to hold even the fallback entry uint8_t s_ram_log_store[5]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); (void)prv_record_oversized_compact_log(); // We should see the dropped log message response sMemfaultLog log; const bool found_log = memfault_log_read(&log); CHECK(found_log); LONGS_EQUAL(kMemfaultLogRecordType_Preformatted, log.type); LONGS_EQUAL(kMemfaultPlatformLogLevel_Warning, log.level); STRCMP_EQUAL("... 1 messages dropped ...", log.msg); } ================================================ FILE: tests/unit/src/test_memfault_compact_log_serializer.cpp ================================================ //! @file //! #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/config.h" #include "memfault/core/compact_log_serializer.h" extern "C" { // emulated linker symbol expected by the serializer extern uint32_t __start_log_fmt; uint32_t __start_log_fmt; static void prv_write_cb(void *ctx, uint32_t offset, const void *buf, size_t buf_len) { CHECK(ctx != NULL); uint8_t *result_buf = (uint8_t *)ctx; memcpy(&result_buf[offset], buf, buf_len); } } TEST_GROUP(MfltCompactLog) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; static uint32_t prv_get_fake_log_id(void) { // We'll just force all our log ids to be the same for test purposes. We just want to verify that // its computed relative to the "__start_log_fmt" linker symbol return (uint32_t)(uintptr_t)&__start_log_fmt + 0xA; } static char *prv_setup_encoder(sMemfaultCborEncoder *encoder, size_t expected_size) { char *buf = (char *)calloc(expected_size, 1); memfault_cbor_encoder_init(encoder, prv_write_cb, buf, expected_size); return buf; } static void prv_check_result(sMemfaultCborEncoder *encoder, char *result, const void *expected_buf, size_t expected_buf_len) { const size_t encoded_length = memfault_cbor_encoder_deinit(encoder); LONGS_EQUAL(expected_buf_len, encoded_length); MEMCMP_EQUAL(expected_buf, result, expected_buf_len); free(result); } TEST(MfltCompactLog, Test_MfltCompactLog_NoArg) { sMemfaultCborEncoder encoder; uint8_t expected_seq[] = { 0x81, 0x0A }; const size_t expected_seq_len = sizeof(expected_seq); char *result_buf = prv_setup_encoder(&encoder, expected_seq_len); const uint32_t compressed_fmt = 1; // no args memfault_log_compact_serialize(&encoder, prv_get_fake_log_id(), compressed_fmt); prv_check_result(&encoder, result_buf, expected_seq, expected_seq_len); } TEST(MfltCompactLog, Test_MfltCompactLog_Int32) { sMemfaultCborEncoder encoder; uint8_t expected_seq[] = { 0x82, 0x0A, 0xB }; const size_t expected_seq_len = sizeof(expected_seq); char *result_buf = prv_setup_encoder(&encoder, expected_seq_len); const uint32_t compressed_fmt = 0x4; // 0b100 const bool success = memfault_log_compact_serialize(&encoder, prv_get_fake_log_id(), compressed_fmt, 11); CHECK(success); prv_check_result(&encoder, result_buf, expected_seq, expected_seq_len); } TEST(MfltCompactLog, Test_MfltCompactLog_Int64) { sMemfaultCborEncoder encoder; uint8_t expected_seq[] = { 0x82, 0x0A, 0x81, 0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00 }; const size_t expected_seq_len = sizeof(expected_seq); char *result_buf = prv_setup_encoder(&encoder, expected_seq_len); const uint32_t compressed_fmt = 0x5; // 0b101 const int64_t bigint = 1000000000000; const bool success = memfault_log_compact_serialize(&encoder, prv_get_fake_log_id(), compressed_fmt, bigint); CHECK(success); prv_check_result(&encoder, result_buf, expected_seq, expected_seq_len); } TEST(MfltCompactLog, Test_MfltCompactLog_Double) { sMemfaultCborEncoder encoder; uint8_t expected_seq[] = { 0x82, 0x0A, 0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c }; const size_t expected_seq_len = sizeof(expected_seq); char *result_buf = prv_setup_encoder(&encoder, expected_seq_len); const uint32_t compressed_fmt = 0x6; // 0b110 const double a_double = 1.0e+300; const bool success = memfault_log_compact_serialize(&encoder, prv_get_fake_log_id(), compressed_fmt, a_double); CHECK(success); prv_check_result(&encoder, result_buf, expected_seq, expected_seq_len); } TEST(MfltCompactLog, Test_MfltCompactLog_String) { sMemfaultCborEncoder encoder; uint8_t expected_seq[] = { 0x82, 0x0A, 0x65, 'h', 'e', 'l', 'l', 'o' }; const size_t expected_seq_len = sizeof(expected_seq); char *result_buf = prv_setup_encoder(&encoder, expected_seq_len); const uint32_t compressed_fmt = 0x7; // 0b111 const char *str_arg = "hello"; const bool success = memfault_log_compact_serialize(&encoder, prv_get_fake_log_id(), compressed_fmt, str_arg); CHECK(success); prv_check_result(&encoder, result_buf, expected_seq, expected_seq_len); } TEST(MfltCompactLog, Test_MfltCompactLog_StringTooLong) { sMemfaultCborEncoder encoder; #define OVERLONG_STRING \ "This is a very long string that is intended to make the compact log " \ "serialization exceed the maximum allowed log size for testing purposes." static_assert(sizeof(OVERLONG_STRING) - 1 > MEMFAULT_LOG_MAX_LINE_SAVE_LEN, "Test string is not long enough to trigger truncation"); char *result_buf = prv_setup_encoder(&encoder, MEMFAULT_LOG_MAX_LINE_SAVE_LEN); const uint32_t compressed_fmt = 0x7; // 0b111 const bool success = memfault_log_compact_serialize(&encoder, prv_get_fake_log_id(), compressed_fmt, OVERLONG_STRING); free(result_buf); CHECK(success == false); int status = memfault_cbor_encoder_get_status(&encoder); LONGS_EQUAL(MEMFAULT_CBOR_ENCODER_STATUS_ENOMEM, status); } TEST(MfltCompactLog, Test_MfltCompactLog_MultiArg) { sMemfaultCborEncoder encoder; uint8_t expected_seq[] = { 0x85, 0x0A, 0xB, 0x81, 0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00, 0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c, 0x65, 'h', 'e', 'l', 'l', 'o' }; const size_t expected_seq_len = sizeof(expected_seq); const uint32_t compressed_fmt = 0x11B; // 0b1.0001.1011 for (size_t i = 0; i <= expected_seq_len; i++) { char *result_buf = prv_setup_encoder(&encoder, i); // all together const uint8_t hhx = 11; const int64_t lld = 1000000000000; const double g = 1.0e+300; const char *str_arg = "hello"; const bool success = memfault_log_compact_serialize(&encoder, prv_get_fake_log_id(), compressed_fmt, hhx, lld, g, str_arg); const bool success_expected = (i == expected_seq_len); CHECK(success == success_expected); if (success) { prv_check_result(&encoder, result_buf, expected_seq, expected_seq_len); } else { free(result_buf); } } } ================================================ FILE: tests/unit/src/test_memfault_coredump.cpp ================================================ #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include "fakes/fake_memfault_platform_coredump_storage.h" #include "memfault/core/build_info.h" #include "memfault/core/compiler.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/device_info.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" #include "memfault/panics/platform/coredump.h" MEMFAULT_ALIGNED(0x8) static uint8_t s_storage_buf[4 * 1024]; static sMfltCoredumpRegion s_fake_memory_region[2]; static size_t s_num_fake_regions; static sMfltCoredumpRegion s_fake_arch_region[3]; static size_t s_num_fake_arch_regions; static sMfltCoredumpRegion s_fake_sdk_region[2]; static size_t s_num_fake_sdk_regions; static const uint8_t s_fake_memfault_build_id[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0f, 0x10, 0x11, 0x12, 0x13 }; void memfault_platform_get_device_info(struct MemfaultDeviceInfo *info) { *info = (struct MemfaultDeviceInfo){ .device_serial = "1", .software_type = "main", .software_version = "22", .hardware_version = "333", }; } bool memfault_coredump_read(uint32_t offset, void *buf, size_t buf_len) { mock().actualCall(__func__); return fake_memfault_platform_coredump_storage_read(offset, buf, buf_len); } bool memfault_platform_coredump_storage_read(uint32_t offset, void *buf, size_t buf_len) { mock().actualCall(__func__); return fake_memfault_platform_coredump_storage_read(offset, buf, buf_len); } bool memfault_build_info_read(sMemfaultBuildInfo *info) { const bool build_info_available = mock().actualCall(__func__).returnBoolValueOrDefault(false); if (build_info_available) { memcpy(info->build_id, s_fake_memfault_build_id, sizeof(s_fake_memfault_build_id)); } return build_info_available; } } const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( MEMFAULT_UNUSED const sCoredumpCrashInfo *crash_info, size_t *num_regions) { *num_regions = s_num_fake_regions; return &s_fake_memory_region[0]; } const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) { *num_regions = s_num_fake_arch_regions; return &s_fake_arch_region[0]; } const sMfltCoredumpRegion *memfault_coredump_get_sdk_regions(size_t *num_regions) { *num_regions = s_num_fake_sdk_regions; return &s_fake_sdk_region[0]; } TEST_GROUP(MfltCoredumpTestGroup) { void setup() { mock().enable(); fake_memfault_platform_coredump_storage_setup(s_storage_buf, sizeof(s_storage_buf), 1024); memfault_platform_coredump_storage_erase(0, sizeof(s_storage_buf)); static uint8_t s_fake_core_region0[] = { 'h', 'e', 'l', 'l', 'o', '!', '!', '!' }; static uint32_t s_fake_core_region1 = 0x61626364; s_fake_memory_region[0].type = kMfltCoredumpRegionType_Memory; s_fake_memory_region[0].region_start = &s_fake_core_region0; s_fake_memory_region[0].region_size = sizeof(s_fake_core_region0); s_fake_memory_region[1].type = kMfltCoredumpRegionType_MemoryWordAccessOnly; s_fake_memory_region[1].region_start = &s_fake_core_region1; s_fake_memory_region[1].region_size = sizeof(s_fake_core_region1); s_num_fake_regions = 2; static uint8_t s_fake_arch_region1[] = { 'e', 'f', 'g' }; s_fake_arch_region[0].type = kMfltCoredumpRegionType_Memory; s_fake_arch_region[0].region_start = &s_fake_arch_region1; s_fake_arch_region[0].region_size = sizeof(s_fake_arch_region1); // Valid cache of "registers" starting at 0xE0000000. static uint32_t s_fake_arch_cached_block_region[(sizeof(sMfltCachedBlock) + 3 * 4) / 4]; sMfltCachedBlock *blk = (sMfltCachedBlock *)&s_fake_arch_cached_block_region[0]; blk->blk_size = sizeof(s_fake_arch_cached_block_region) - sizeof(sMfltCachedBlock); blk->cached_address = 0xE0000000; blk->blk[0] = 0x12345678; blk->blk[1] = 0xAABBCCDD; blk->blk[2] = 0x9999EEEE; blk->valid_cache = 1; s_fake_arch_region[1].type = kMfltCoredumpRegionType_CachedMemory; s_fake_arch_region[1].region_start = &s_fake_arch_cached_block_region; s_fake_arch_region[1].region_size = sizeof(s_fake_arch_cached_block_region); // Invalid cache of "registers" starting at 0xE000E000. static uint32_t s_fake_arch_cached_block_region_invalid[(sizeof(sMfltCachedBlock) + 3 * 4) / 4]; blk = (sMfltCachedBlock *)&s_fake_arch_cached_block_region_invalid[0]; blk->blk_size = sizeof(s_fake_arch_cached_block_region_invalid) - sizeof(sMfltCachedBlock); blk->cached_address = 0xE000E000; blk->blk[0] = 0x77777777; blk->blk[1] = 0x88888888; blk->blk[2] = 0xFEEDFACE; blk->valid_cache = 0; s_fake_arch_region[2].type = kMfltCoredumpRegionType_CachedMemory; s_fake_arch_region[2].region_start = &s_fake_arch_cached_block_region_invalid; s_fake_arch_region[2].region_size = sizeof(s_fake_arch_cached_block_region_invalid); s_num_fake_arch_regions = 3; static uint8_t s_fake_sdk_region1[] = { 'a', 'b', 'c' }; s_fake_sdk_region[0].type = kMfltCoredumpRegionType_Memory; s_fake_sdk_region[0].region_start = &s_fake_sdk_region1; s_fake_sdk_region[0].region_size = sizeof(s_fake_sdk_region1); static uint8_t s_fake_sdk_region2[] = { 'd', 'e', 'f' }; s_fake_sdk_region[1].type = kMfltCoredumpRegionType_Memory; s_fake_sdk_region[1].region_start = &s_fake_sdk_region2; s_fake_sdk_region[1].region_size = sizeof(s_fake_sdk_region2); s_num_fake_sdk_regions = 2; } void teardown() { mock().checkExpectations(); mock().clear(); } }; static bool prv_check_coredump_validity_and_get_size(size_t *total_coredump_size) { // NB: This should only be called while the system is running so memfault_coredump_read // should always be used. Let's use a mock to verify that. mock().expectOneCall("memfault_coredump_read"); const bool has_coredump = memfault_coredump_has_valid_coredump(total_coredump_size); mock().checkExpectations(); return has_coredump; } static void prv_assert_storage_empty(void) { size_t total_coredump_size; bool has_coredump = prv_check_coredump_validity_and_get_size(&total_coredump_size); CHECK(!has_coredump); uint8_t empty_storage[sizeof(s_storage_buf)]; memset(empty_storage, 0xff, sizeof(empty_storage)); MEMCMP_EQUAL(empty_storage, s_storage_buf, sizeof(s_storage_buf)); } static bool prv_collect_regions_and_save(void *regs, size_t size, uint32_t trace_reason) { size_t num_regions = 0; const sMfltCoredumpRegion *regions = memfault_platform_coredump_get_regions(NULL, &num_regions); if (num_regions != 0) { // we are going to try and write a coredump so this should trigger a header read mock().expectOneCall("memfault_platform_coredump_storage_read"); mock().ignoreOtherCalls(); // We will test handling memfault_build_info_read() explicitly } sMemfaultCoredumpSaveInfo info = { .regs = regs, .regs_size = size, .trace_reason = (eMemfaultRebootReason)trace_reason, .regions = regions, .num_regions = num_regions, }; const bool success = memfault_coredump_save(&info); mock().checkExpectations(); return success; } static size_t prv_compute_space_needed_with_build_id(void *regs, size_t size, uint32_t trace_reason, bool include_build_id) { mock().checkExpectations(); // computing the space needed to save a coredump shouldn't require any access to the storage // medium itself mock().expectNCalls(0, "memfault_coredump_read"); mock().expectNCalls(0, "memfault_platform_coredump_storage_read"); // if there are no regions, no coredump will be saved so the build id call will never be invoked if (s_num_fake_regions != 0) { mock().expectOneCall("memfault_build_info_read").andReturnValue(include_build_id); } size_t num_regions = 0; const sMfltCoredumpRegion *regions = memfault_platform_coredump_get_regions(NULL, &num_regions); sMemfaultCoredumpSaveInfo info = { .regs = regs, .regs_size = size, .trace_reason = (eMemfaultRebootReason)trace_reason, .regions = regions, .num_regions = num_regions, }; const size_t size_needed = memfault_coredump_get_save_size(&info); mock().checkExpectations(); mock().clear(); return size_needed; } static size_t prv_compute_space_needed(void *regs, size_t size, uint32_t trace_reason) { const bool include_build_id = false; return prv_compute_space_needed_with_build_id(regs, size, trace_reason, include_build_id); } TEST(MfltCoredumpTestGroup, Test_MfltCoredumpNoRegions) { s_num_fake_regions = 0; s_num_fake_arch_regions = 0; s_num_fake_sdk_regions = 0; prv_collect_regions_and_save(NULL, 0, 0); size_t size = prv_compute_space_needed(NULL, 0, 0); LONGS_EQUAL(0, size); prv_assert_storage_empty(); } TEST(MfltCoredumpTestGroup, Test_MfltCoredumpEmptyStorage) { fake_memfault_platform_coredump_storage_setup(NULL, 0, 1024); size_t total_size; const bool has_coredump = memfault_coredump_has_valid_coredump(&total_size); CHECK(!has_coredump); } TEST(MfltCoredumpTestGroup, Test_MfltCoredumpNothingWritten) { prv_assert_storage_empty(); } TEST(MfltCoredumpTestGroup, Test_MfltCoredumpStorageTooSmall) { const uint32_t regs[] = { 0x10111213, 0x20212223, 0x30313233, 0x40414243, 0x50515253 }; const uint32_t trace_reason = 0xdeadbeef; // update coredump_size_without_build_id if you add/remove regions. #if MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL const size_t coredump_size_without_build_id = 308; const size_t coredump_metadata_size = 182; #else const size_t coredump_size_without_build_id = 296; const size_t coredump_metadata_size = 169; #endif (void)coredump_metadata_size; const size_t coredump_size_with_build_id = coredump_size_without_build_id + 20 /* sha1 */ + 12 /* sMfltCoredumpBlock */; for (size_t i = 1; i <= coredump_size_with_build_id; i++) { uint8_t storage[i]; memset(storage, 0x0, sizeof(storage)); fake_memfault_platform_coredump_storage_setup(storage, i, i); memfault_platform_coredump_storage_clear(); // build id is written after the header and regs const uint32_t segment_hdr_sz = 12; const size_t build_id_start_offset = (12 + sizeof(regs) + segment_hdr_sz + 16 /* reserved for footer */); const bool do_collect_build_id = (i >= (build_id_start_offset + 3)); if (i >= build_id_start_offset) { mock().expectOneCall("memfault_build_info_read").andReturnValue(do_collect_build_id); } bool success = prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); // even if storage itself is too small we should always be able to compute the amount of space // needed! const size_t space_needed = prv_compute_space_needed_with_build_id( (void *)®s, sizeof(regs), trace_reason, do_collect_build_id); const size_t expected_coredump_size = do_collect_build_id ? coredump_size_with_build_id : coredump_size_without_build_id; LONGS_EQUAL(expected_coredump_size, space_needed); // once we get to the memory regions section, the save can succeed, we just won't get all the // memory regions! CHECK(success == (i >= coredump_metadata_size)); } } TEST(MfltCoredumpTestGroup, Test_MfltCoredumpTruncated) { const uint32_t regs[] = { 0xabababab }; const uint32_t trace_reason = 0xdeadbeef; // get the total size of the coredump, update space_needed // check if you add/remove regions. const bool do_collect_build_id = false; fake_memfault_platform_coredump_storage_setup(NULL, 0, 0); const size_t space_needed = prv_compute_space_needed_with_build_id( (void *)®s, sizeof(regs), trace_reason, do_collect_build_id); #if MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL LONGS_EQUAL(292, space_needed); #else LONGS_EQUAL(280, space_needed); #endif size_t num_regions; const sMfltCoredumpRegion *regions = memfault_platform_coredump_get_regions(NULL, &num_regions); // this should match the number of fake regions we have in s_num_fake_regions LONGS_EQUAL(2, num_regions); const sMfltCoredumpRegion *last_region = ®ions[num_regions - 1]; const size_t block_hdr_len = 12; uint8_t storage[space_needed]; memset(&storage, 0x0, sizeof(storage)); // size such that the second to last region gets truncated size_t storage_size = space_needed - last_region->region_size - block_hdr_len - 1; fake_memfault_platform_coredump_storage_setup(storage, storage_size, storage_size); bool success = prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); CHECK(success); size_t coredump_size; bool has_coredump = prv_check_coredump_validity_and_get_size(&coredump_size); CHECK(has_coredump); // second to last region should be truncated by one word LONGS_EQUAL(storage_size - 3, coredump_size); memfault_platform_coredump_storage_clear(); // size with not enough space for the last region storage_size = space_needed - last_region->region_size - block_hdr_len; for (size_t i = 0; i < block_hdr_len + 3; i++) { fake_memfault_platform_coredump_storage_setup(storage, storage_size + i, storage_size + i); success = prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); CHECK(success); has_coredump = prv_check_coredump_validity_and_get_size(&coredump_size); CHECK(has_coredump); LONGS_EQUAL(storage_size, coredump_size); memfault_platform_coredump_storage_clear(); } // space for beginning of last region so space used should increase storage_size += block_hdr_len + 4; fake_memfault_platform_coredump_storage_setup(storage, storage_size, storage_size); success = prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); CHECK(success); has_coredump = prv_check_coredump_validity_and_get_size(&coredump_size); CHECK(has_coredump); LONGS_EQUAL(storage_size, coredump_size); memfault_platform_coredump_storage_clear(); } TEST(MfltCoredumpTestGroup, Test_MfltCoredumpNoOverwrite) { const uint32_t regs[] = { 0x10111213, 0x20212223, 0x30313233, 0x40414243, 0x50515253 }; const uint32_t trace_reason = 0xdeadbeef; bool success = prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); CHECK(success); // since we already have an unread core, it shouldn't be over success = prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); CHECK(!success); } TEST(MfltCoredumpTestGroup, Test_BadMagic) { const uint32_t regs[] = { 0x10111213, 0x20212223, 0x30313233, 0x40414243, 0x50515253 }; const uint32_t trace_reason = 0xdeadbeef; const bool success = prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); CHECK(success); size_t coredump_size; bool has_coredump = prv_check_coredump_validity_and_get_size(&coredump_size); CHECK(has_coredump); // now corrupt the magic memset(&s_storage_buf[1], 0xAB, 8); has_coredump = prv_check_coredump_validity_and_get_size(&coredump_size); CHECK(!has_coredump); } TEST(MfltCoredumpTestGroup, Test_InvalidHeader) { const uint32_t regs[] = { 0x10111213, 0x20212223, 0x30313233, 0x40414243, 0x50515253 }; const uint32_t trace_reason = 0xdeadbeef; const bool success = prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); CHECK(success); size_t coredump_size; bool has_coredump = prv_check_coredump_validity_and_get_size(&coredump_size); CHECK(has_coredump); memfault_platform_coredump_storage_clear(); has_coredump = prv_check_coredump_validity_and_get_size(&coredump_size); CHECK(!has_coredump); } TEST(MfltCoredumpTestGroup, Test_ShortHeaderRead) { uint8_t storage; fake_memfault_platform_coredump_storage_setup(&storage, sizeof(storage), sizeof(storage)); // A misconfiguration where coredump storage isn't even large enough to hold a header size_t coredump_size; const bool has_coredump = prv_check_coredump_validity_and_get_size(&coredump_size); CHECK(!has_coredump); } TEST(MfltCoredumpTestGroup, Test_CoredumpReadHeaderMagic) { const uint32_t regs[] = { 0x1, 0x2, 0x3, 0x4, 0x5 }; const uint32_t trace_reason = 0xdead; prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); uint32_t word = 0; const bool success = fake_memfault_platform_coredump_storage_read(0, &word, sizeof(word)); CHECK(success); LONGS_EQUAL(0x45524f43, word); } // Returns correct region size for any region even if cached. Invalid // cached blocks result in zero length so they are properly ignored. static size_t prv_get_region_size(const sMfltCoredumpRegion *const region) { if (region->type != kMfltCoredumpRegionType_CachedMemory) { return region->region_size; } const sMfltCachedBlock *cached_block = (const sMfltCachedBlock *)region->region_start; return cached_block->valid_cache ? cached_block->blk_size : 0; } // Returns correct region start for any region even if cached. static const void *prv_get_region_start(const sMfltCoredumpRegion *const region) { if (region->type != kMfltCoredumpRegionType_CachedMemory) { return region->region_start; } const sMfltCachedBlock *cached_block = (const sMfltCachedBlock *)region->region_start; return cached_block->blk; } static size_t prv_compute_padding_needed(uint64_t const offset) { return (offset % 4) ? 4 - (offset % 4) : 0; } // Test the basics ... make sure the coredump is flushed out in the order we expect TEST(MfltCoredumpTestGroup, Test_MfltCoredumpSaveCore) { const uint32_t regs[] = { 0x1, 0x2, 0x3, 0x4, 0x5 }; const uint32_t trace_reason = 0xdead; mock().expectOneCall("memfault_build_info_read").andReturnValue(true); prv_collect_regions_and_save((void *)®s, sizeof(regs), trace_reason); uint8_t *coredump_buf = &s_storage_buf[0]; const uint32_t expected_header_magic = 0x45524f43; MEMCMP_EQUAL(coredump_buf, &expected_header_magic, sizeof(expected_header_magic)); coredump_buf += sizeof(expected_header_magic); const uint32_t expected_version = 2; MEMCMP_EQUAL(coredump_buf, &expected_version, sizeof(expected_version)); coredump_buf += sizeof(expected_version); const uint32_t segment_hdr_sz = 12; uint32_t total_length = 12 + sizeof(regs) + segment_hdr_sz; struct MemfaultDeviceInfo info; memfault_platform_get_device_info(&info); total_length += (5 * segment_hdr_sz) + sizeof(s_fake_memfault_build_id) + strlen(info.hardware_version) + strlen(info.software_version) + strlen(info.software_type) + sizeof(uint32_t); #if MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL total_length += segment_hdr_sz + strlen(info.device_serial); #endif const uint32_t pad_needed = prv_compute_padding_needed(total_length); total_length += pad_needed ? pad_needed + segment_hdr_sz : 0; total_length += sizeof(trace_reason) + segment_hdr_sz; for (size_t i = 0; i < s_num_fake_arch_regions; i++) { const size_t arch_region_size = prv_get_region_size(&s_fake_arch_region[i]); if (arch_region_size == 0) { // Skip invalid cached regions. continue; } total_length += arch_region_size + segment_hdr_sz; const uint32_t arch_padding = prv_compute_padding_needed(total_length); total_length += arch_padding ? arch_padding + segment_hdr_sz : 0; } for (size_t i = 0; i < s_num_fake_sdk_regions; i++) { const size_t sdk_region_size = prv_get_region_size(&s_fake_sdk_region[i]); if (sdk_region_size == 0) { continue; } total_length += sdk_region_size + segment_hdr_sz; const uint32_t sdk_padding = prv_compute_padding_needed(total_length); total_length += sdk_padding ? sdk_padding + segment_hdr_sz : 0; } for (size_t i = 0; i < s_num_fake_regions; i++) { const size_t mem_region_size = prv_get_region_size(&s_fake_memory_region[i]); if (mem_region_size == 0) { continue; } total_length += mem_region_size + segment_hdr_sz; } total_length += 16; // footer MEMCMP_EQUAL(&total_length, coredump_buf, sizeof(total_length)); coredump_buf += sizeof(total_length); // registers are written first coredump_buf += segment_hdr_sz; MEMCMP_EQUAL(®s[0], coredump_buf, sizeof(regs)); coredump_buf += sizeof(regs); // device info uint32_t actual_value; // memcpy for possibly unaligned address memcpy(&actual_value, coredump_buf, sizeof(actual_value)); LONGS_EQUAL(12, actual_value); coredump_buf += segment_hdr_sz; MEMCMP_EQUAL(s_fake_memfault_build_id, coredump_buf, sizeof(s_fake_memfault_build_id)); coredump_buf += sizeof(s_fake_memfault_build_id); #if MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL memcpy(&actual_value, coredump_buf, sizeof(actual_value)); LONGS_EQUAL(2, actual_value); coredump_buf += segment_hdr_sz; MEMCMP_EQUAL(info.device_serial, coredump_buf, strlen(info.device_serial)); coredump_buf += strlen(info.device_serial); #endif memcpy(&actual_value, coredump_buf, sizeof(actual_value)); LONGS_EQUAL(10, actual_value); coredump_buf += segment_hdr_sz; MEMCMP_EQUAL(info.software_version, coredump_buf, strlen(info.software_version)); coredump_buf += strlen(info.software_version); memcpy(&actual_value, coredump_buf, sizeof(actual_value)); LONGS_EQUAL(11, actual_value); coredump_buf += segment_hdr_sz; MEMCMP_EQUAL(info.software_type, coredump_buf, strlen(info.software_type)); coredump_buf += strlen(info.software_type); memcpy(&actual_value, coredump_buf, sizeof(actual_value)); LONGS_EQUAL(4, actual_value); coredump_buf += segment_hdr_sz; MEMCMP_EQUAL(info.hardware_version, coredump_buf, strlen(info.hardware_version)); coredump_buf += strlen(info.hardware_version); memcpy(&actual_value, coredump_buf, sizeof(actual_value)); LONGS_EQUAL(7, actual_value); coredump_buf += segment_hdr_sz; const uint32_t machine_type = 0; MEMCMP_EQUAL(&machine_type, coredump_buf, sizeof(machine_type)); coredump_buf += sizeof(machine_type); // trace reason memcpy(&actual_value, coredump_buf, sizeof(actual_value)); LONGS_EQUAL(5, actual_value); coredump_buf += segment_hdr_sz; MEMCMP_EQUAL(&trace_reason, coredump_buf, sizeof(trace_reason)); coredump_buf += sizeof(trace_reason); // expected padding if (pad_needed) { memcpy(&actual_value, coredump_buf, sizeof(actual_value)); LONGS_EQUAL(6, actual_value); coredump_buf += segment_hdr_sz; coredump_buf += pad_needed; } // now we should find the architecture specific regions for (size_t i = 0; i < s_num_fake_arch_regions; i++) { // make sure regions are aligned on 4 byte boundaries LONGS_EQUAL(0, (uint64_t)coredump_buf % 4); size_t segment_size = prv_get_region_size(&s_fake_arch_region[i]); if (segment_size == 0) { // Invalid cached block, skip it. continue; } coredump_buf += segment_hdr_sz; const void *memory = prv_get_region_start(&s_fake_arch_region[i]); MEMCMP_EQUAL(memory, coredump_buf, segment_size); coredump_buf += segment_size; if (((uint64_t)coredump_buf % 4) != 0) { const uint32_t arch_padding = (4 - ((uint64_t)coredump_buf % 4)); coredump_buf += arch_padding + segment_hdr_sz; } } for (size_t i = 0; i < s_num_fake_sdk_regions; i++) { // make sure regions are aligned on 4 byte boundaries LONGS_EQUAL(0, (uint64_t)coredump_buf % 4); size_t segment_size = prv_get_region_size(&s_fake_sdk_region[i]); if (segment_size == 0) { continue; } coredump_buf += segment_hdr_sz; const void *memory = prv_get_region_start(&s_fake_sdk_region[i]); MEMCMP_EQUAL(memory, coredump_buf, segment_size); coredump_buf += segment_size; const uint32_t sdk_padding = prv_compute_padding_needed((uint64_t)coredump_buf); coredump_buf += sdk_padding + segment_hdr_sz; } for (size_t i = 0; i < s_num_fake_regions; i++) { // make sure regions are aligned on 4 byte boundaries LONGS_EQUAL(0, (uint64_t)coredump_buf % 4); size_t segment_size = prv_get_region_size(&s_fake_memory_region[i]); if (segment_size == 0) { continue; } coredump_buf += segment_hdr_sz; const void *memory = prv_get_region_start(&s_fake_memory_region[i]); MEMCMP_EQUAL(memory, coredump_buf, segment_size); coredump_buf += segment_size; } // check the footer const uint32_t expected_footer_magic = 0x504d5544; MEMCMP_EQUAL(coredump_buf, &expected_footer_magic, sizeof(expected_footer_magic)); coredump_buf += sizeof(expected_footer_magic); uint32_t rsvd[] = { 0x0, 0x0, 0x0 }; MEMCMP_EQUAL(coredump_buf, &rsvd[0], sizeof(rsvd)); // should have been no calls to memfault_coredump_read mock().checkExpectations(); // we should see that a coredump is saved! size_t total_coredump_size = 0; bool has_coredump = prv_check_coredump_validity_and_get_size(&total_coredump_size); CHECK(has_coredump); LONGS_EQUAL(total_length, total_coredump_size); } ================================================ FILE: tests/unit/src/test_memfault_coredump_sdk_regions.cpp ================================================ //! @file //! //! @brief #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/heap_stats.h" #include "memfault/core/heap_stats_impl.h" #include "memfault/core/log_impl.h" #include "memfault/panics/coredump_impl.h" // fakes for populating heap_stats in coredump regions sMfltHeapStats g_memfault_heap_stats; sMfltHeapStatEntry g_memfault_heap_stats_pool[MEMFAULT_HEAP_STATS_MAX_COUNT]; TEST_GROUP(MemfaultSdkRegions) { void setup() { mock().strictOrder(); } void teardown() { mock().checkExpectations(); mock().clear(); } }; bool memfault_log_get_regions(sMemfaultLogRegions *regions) { const bool enabled = mock().actualCall(__func__).returnBoolValueOrDefault(true); if (!enabled) { return false; } for (size_t i = 0; i < MEMFAULT_LOG_NUM_RAM_REGIONS; i++) { sMemfaultLogMemoryRegion *region = ®ions->region[i]; region->region_start = (void *)i; region->region_size = i + 1; } return true; } bool memfault_heap_stats_empty(void) { return mock().actualCall(__func__).returnBoolValueOrDefault(false); } static void prv_check_heap_stats_region(const sMfltCoredumpRegion *region) { LONGS_EQUAL(kMfltCoredumpRegionType_Memory, region->type); LONGS_EQUAL((uintptr_t)&g_memfault_heap_stats, (uintptr_t)region->region_start); LONGS_EQUAL(sizeof(g_memfault_heap_stats), (uint32_t)region->region_size); } static void prv_check_heap_stats_pool_region(const sMfltCoredumpRegion *region) { LONGS_EQUAL(kMfltCoredumpRegionType_Memory, region->type); LONGS_EQUAL((uintptr_t)&g_memfault_heap_stats_pool, (uintptr_t)region->region_start); LONGS_EQUAL(sizeof(g_memfault_heap_stats_pool), (uint32_t)region->region_size); } TEST(MemfaultSdkRegions, Test_MemfaultSdkRegions_Enabled) { mock().expectOneCall("memfault_log_get_regions"); mock().expectOneCall("memfault_heap_stats_empty"); size_t num_regions; const sMfltCoredumpRegion *regions = memfault_coredump_get_sdk_regions(&num_regions); LONGS_EQUAL(4, num_regions); const sMfltCoredumpRegion *region = nullptr; for (size_t i = 0; i < MEMFAULT_LOG_NUM_RAM_REGIONS; i++) { region = ®ions[i]; LONGS_EQUAL(kMfltCoredumpRegionType_Memory, region->type); LONGS_EQUAL(i, (uint32_t)(uintptr_t)region->region_start); LONGS_EQUAL(i + 1, (uint32_t)region->region_size); } region++; prv_check_heap_stats_region(region); region++; prv_check_heap_stats_pool_region(region); } TEST(MemfaultSdkRegions, Test_MemfaultSdkRegions_Disabled) { mock().expectOneCall("memfault_log_get_regions").andReturnValue(false); mock().expectOneCall("memfault_heap_stats_empty").andReturnValue(true); size_t num_regions; const sMfltCoredumpRegion *regions = memfault_coredump_get_sdk_regions(&num_regions); LONGS_EQUAL(0, num_regions); CHECK(regions == NULL); } ================================================ FILE: tests/unit/src/test_memfault_coredump_storage_debug.cpp ================================================ //! @file #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/panics/coredump.h" #include "memfault/panics/platform/coredump.h" // 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; } return true; } static uint8_t s_ram_backed_coredump_region[200]; #define COREDUMP_REGION_SIZE sizeof(s_ram_backed_coredump_region) static bool s_inject_get_info_failure = false; void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { if (s_inject_get_info_failure) { return; // fail to populate information } const size_t coredump_region_size = sizeof(s_ram_backed_coredump_region); *info = (sMfltCoredumpStorageInfo){ .size = coredump_region_size, .sector_size = coredump_region_size }; } static int s_inject_read_failure_offset; bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, size_t read_len) { if ((offset + read_len) > COREDUMP_REGION_SIZE) { return false; } const uint8_t *read_ptr = &s_ram_backed_coredump_region[offset]; if ((s_inject_read_failure_offset >= 0) && (s_inject_read_failure_offset < (int)COREDUMP_REGION_SIZE)) { s_ram_backed_coredump_region[s_inject_read_failure_offset] = 0xff; } memcpy(data, read_ptr, read_len); // let's have most of the failures be fake successes and for one case // just return a driver read failure return (s_inject_read_failure_offset != 0); } bool memfault_coredump_read(uint32_t offset, void *data, size_t read_len) { return memfault_platform_coredump_storage_read(offset, data, read_len); } typedef enum { kMemfaultEraseFailureMode_None = 0, kMemfaultEraseFailureMode_ClearFailure, kMemfaultEraseFailureMode_EraseDriverFailure, kMemfaultEraseFailureMode_NumModes, } eMemfaultEraseFailureMode; static eMemfaultEraseFailureMode s_inject_erase_failure; bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { if ((offset + erase_size) > COREDUMP_REGION_SIZE) { return false; } uint8_t *erase_ptr = &s_ram_backed_coredump_region[offset]; memset(erase_ptr, 0x0, erase_size); switch ((int)s_inject_erase_failure) { case kMemfaultEraseFailureMode_ClearFailure: s_ram_backed_coredump_region[COREDUMP_REGION_SIZE / 2] = 0xa5; break; case kMemfaultEraseFailureMode_EraseDriverFailure: return false; default: break; } return true; } typedef enum { kMemfaultWriteFailureMode_None = 0, kMemfaultWriteFailureMode_WriteDriverFailure, kMemfaultWriteFailureMode_WordUnaligned, kMemfaultWriteFailureMode_PartialWriteFail, kMemfaultWriteFailureMode_LastByte, kMemfaultWriteFailureMode_WriteFailureAtOffset0, kMemfaultWriteFailureMode_ReadAfterWrite, kMemfaultWriteFailureMode_NumModes, } eMemfaultWriteFailureMode; static eMemfaultWriteFailureMode s_inject_write_failure; bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, size_t data_len) { if ((offset + data_len) > COREDUMP_REGION_SIZE) { return false; } switch ((int)s_inject_write_failure) { case kMemfaultWriteFailureMode_WriteDriverFailure: return false; case kMemfaultWriteFailureMode_WriteFailureAtOffset0: if (offset == 0) { return false; } break; case kMemfaultWriteFailureMode_WordUnaligned: if ((offset % 4) != 0) { return true; // silent fail } break; case kMemfaultWriteFailureMode_PartialWriteFail: // fail to write 1 byte data_len -= 1; break; case kMemfaultWriteFailureMode_LastByte: if ((offset + data_len) == COREDUMP_REGION_SIZE) { data_len -= 1; } break; case kMemfaultWriteFailureMode_ReadAfterWrite: s_inject_read_failure_offset = 0; break; default: break; } uint8_t *write_ptr = &s_ram_backed_coredump_region[offset]; memcpy(write_ptr, data, data_len); return true; } void memfault_platform_coredump_storage_clear(void) { const uint8_t clear_byte = 0x0; memfault_platform_coredump_storage_write(0, &clear_byte, sizeof(clear_byte)); } TEST_GROUP(MfltCoredumpStorageTestGroup) { void setup() { s_inject_write_failure = kMemfaultWriteFailureMode_None; s_inject_read_failure_offset = -1; s_inject_erase_failure = kMemfaultEraseFailureMode_None; s_inject_get_info_failure = false; s_inject_prepare_failure = false; } void teardown() { } }; TEST(MfltCoredumpStorageTestGroup, Test_StorageImplementationGood) { bool success = memfault_coredump_storage_debug_test_begin(); success &= memfault_coredump_storage_debug_test_finish(); CHECK(success); } TEST(MfltCoredumpStorageTestGroup, Test_WriteFailureModes) { for (int i = 1; i < kMemfaultWriteFailureMode_NumModes; i++) { s_inject_write_failure = (eMemfaultWriteFailureMode)i; s_inject_read_failure_offset = -1; bool success = memfault_coredump_storage_debug_test_begin(); success &= memfault_coredump_storage_debug_test_finish(); CHECK(!success); } } TEST(MfltCoredumpStorageTestGroup, Test_ReadCompareFailure) { for (size_t i = 0; i < COREDUMP_REGION_SIZE; i++) { s_inject_read_failure_offset = i; bool success = memfault_coredump_storage_debug_test_begin(); success &= memfault_coredump_storage_debug_test_finish(); CHECK(!success); } } TEST(MfltCoredumpStorageTestGroup, Test_EraseFailure) { for (int i = 1; i < kMemfaultEraseFailureMode_NumModes; i++) { s_inject_erase_failure = (eMemfaultEraseFailureMode)i; bool success = memfault_coredump_storage_debug_test_begin(); success &= memfault_coredump_storage_debug_test_finish(); CHECK(!success); } } TEST(MfltCoredumpStorageTestGroup, Test_ClearFailureDueToWriteFailure) { bool success = memfault_coredump_storage_debug_test_begin(); CHECK(success); s_inject_write_failure = (eMemfaultWriteFailureMode)kMemfaultWriteFailureMode_PartialWriteFail; success = memfault_coredump_storage_debug_test_finish(); CHECK(!success); } TEST(MfltCoredumpStorageTestGroup, Test_ClearFailureDueToReadFailure) { bool success = memfault_coredump_storage_debug_test_begin(); CHECK(success); s_inject_write_failure = (eMemfaultWriteFailureMode)kMemfaultWriteFailureMode_ReadAfterWrite; success = memfault_coredump_storage_debug_test_finish(); CHECK(!success); } TEST(MfltCoredumpStorageTestGroup, Test_GetInfoFail) { s_inject_get_info_failure = true; bool success = memfault_coredump_storage_debug_test_begin(); success &= memfault_coredump_storage_debug_test_finish(); CHECK(!success); } TEST(MfltCoredumpStorageTestGroup, Test_PrepareFail) { s_inject_prepare_failure = true; bool success = memfault_coredump_storage_debug_test_begin(); success &= memfault_coredump_storage_debug_test_finish(); CHECK(!success); } ================================================ FILE: tests/unit/src/test_memfault_coredump_utils.cpp ================================================ //! @file //! #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/panics/coredump.h" static sMfltCoredumpStorageInfo s_fake_coredump_info; void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { *info = s_fake_coredump_info; } size_t memfault_coredump_storage_compute_size_required(void) { return 10; } TEST_GROUP(MfltCoredumpUtilTestGroup) { void setup() { } void teardown() { memset(&s_fake_coredump_info, 0x0, sizeof(s_fake_coredump_info)); } }; TEST(MfltCoredumpUtilTestGroup, Test_MfltCoredumpUtilSizeCheck) { bool check_passed = memfault_coredump_storage_check_size(); CHECK(!check_passed); s_fake_coredump_info.size = memfault_coredump_storage_compute_size_required() - 1; check_passed = memfault_coredump_storage_check_size(); CHECK(!check_passed); s_fake_coredump_info.size++; check_passed = memfault_coredump_storage_check_size(); CHECK(check_passed); } ================================================ FILE: tests/unit/src/test_memfault_crc16_ccitt.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" extern "C" { #include #include #include "memfault/util/crc16.h" } TEST_GROUP(MfltCrc16CcittGroup) { void setup() { } void teardown() { } }; TEST(MfltCrc16CcittGroup, Test_MfltCrc16CcittGroupComputeCodewords) { const uint8_t codeword1[] = { 0x54, 0x1A, 0x71 }; uint16_t crc = memfault_crc16_compute(MEMFAULT_CRC16_INITIAL_VALUE, codeword1, sizeof(codeword1)); LONGS_EQUAL(0x0, crc); const uint8_t codeword2[] = { 0x43, 0x61, 0x74, 0x4D, 0x6F, 0x75, 0x73, 0x65, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0xE5, 0x56 }; crc = memfault_crc16_compute(MEMFAULT_CRC16_INITIAL_VALUE, codeword2, sizeof(codeword2)); LONGS_EQUAL(0x0, crc); } TEST(MfltCrc16CcittGroup, Test_MfltCrc16CcittGroupComputeTestPattern) { const char *test_pattern = "123456789"; uint16_t crc = memfault_crc16_compute(MEMFAULT_CRC16_INITIAL_VALUE, test_pattern, strlen(test_pattern)); LONGS_EQUAL(0x31C3, crc); } TEST(MfltCrc16CcittGroup, Test_MfltCrc16CcittGroupComputeTestPatternOneByte) { const char *test_pattern = "A"; uint16_t crc = memfault_crc16_compute(MEMFAULT_CRC16_INITIAL_VALUE, test_pattern, strlen(test_pattern)); LONGS_EQUAL(0x58E5, crc); } TEST(MfltCrc16CcittGroup, Test_MfltCrc16CcittGroupComputeTestPatternIncremental) { uint16_t crc = MEMFAULT_CRC16_INITIAL_VALUE; for (char i = '1'; i <= '9'; i++) { crc = memfault_crc16_compute(crc, &i, sizeof(i)); } LONGS_EQUAL(0x31C3, crc); } ================================================ FILE: tests/unit/src/test_memfault_custom_data_recording.cpp ================================================ //! @file //! //! @brief Tests for CDR implementation #include #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "fakes/fake_memfault_platform_time.h" #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/core/custom_data_recording.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/math.h" #include "memfault_custom_data_recording_private.h" typedef struct { bool has_data; sMemfaultCdrMetadata metadata; const uint8_t *recording; size_t recording_len; } sCdrFakeInfo; static sCdrFakeInfo s_cdr_fake_info; static void prv_set_fake_info(const sMemfaultCdrMetadata *metadata, const void *recording, size_t recording_len) { s_cdr_fake_info = (sCdrFakeInfo){ .has_data = true, .metadata = *metadata, .recording = (const uint8_t *)recording, .recording_len = recording_len, }; } static bool prv_has_cdr_cb(sMemfaultCdrMetadata *metadata) { if (!s_cdr_fake_info.has_data) { return false; } *metadata = s_cdr_fake_info.metadata; return true; } static bool prv_read_data_cb(uint32_t offset, void *buf, size_t buf_len) { assert((offset + buf_len) <= s_cdr_fake_info.recording_len); uint8_t *bufp = (uint8_t *)buf; memcpy(bufp, &s_cdr_fake_info.recording[offset], buf_len); return true; } static void prv_mark_cdr_read_cb(void) { s_cdr_fake_info = (sCdrFakeInfo){ .has_data = false, }; } static bool prv_stub_has_cdr_cb(MEMFAULT_UNUSED sMemfaultCdrMetadata *metadata) { return false; } static bool prv_stub_read_data_cb(MEMFAULT_UNUSED uint32_t offset, MEMFAULT_UNUSED void *buf, MEMFAULT_UNUSED size_t buf_len) { return false; } static void prv_stub_mark_cdr_read_cb(void) { } static sMemfaultCdrSourceImpl s_memfault_cdr_fake_source = { .has_cdr_cb = prv_has_cdr_cb, .read_data_cb = prv_read_data_cb, .mark_cdr_read_cb = prv_mark_cdr_read_cb, }; static sMemfaultCdrSourceImpl s_memfault_cdr_stub_source = { .has_cdr_cb = prv_stub_has_cdr_cb, .read_data_cb = prv_stub_read_data_cb, .mark_cdr_read_cb = prv_stub_mark_cdr_read_cb, }; static const uint8_t s_expected_encoded_buffer[] = { 0xA7, 0x02, 0x05, 0x03, 0x01, 0x0A, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x01, 0x14, 0x04, 0xa4, // kMemfaultCdrInfoKey_DurationMs 0x01, 0x00, // kMemfaultCdrInfoKey_Mimetypes 0x02, 0x82, 0x6A, 't', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n', 0x68, 't', 'e', 'x', 't', '/', 'c', 's', 'v', // kMemfaultCdrInfoKey_Reason 0x03, 0x65, 'e', 'r', 'r', 'o', 'r', // kMemfaultCdrInfoKey_Data 0x04, 0x4f, 0x6d, 0x65, 0x6d, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x21, 0x0a }; const size_t s_expected_encoded_size = sizeof(s_expected_encoded_buffer); static const char *s_cdr_mimetypes[] = { MEMFAULT_CDR_TEXT, MEMFAULT_CDR_CSV }; static const uint8_t s_cdr_bin[] = { 0x6d, 0x65, 0x6d, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x21, 0x0a }; static const sMemfaultCdrMetadata s_cdr_metadata = { .start_time = { .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = 20, }, }, .mimetypes = s_cdr_mimetypes, .num_mimetypes = MEMFAULT_ARRAY_SIZE(s_cdr_mimetypes), .data_size_bytes = sizeof(s_cdr_bin), .duration_ms = 0, .collection_reason = "error", }; TEST_GROUP(MemfaultCdrSource) { void setup() { fake_memfault_platform_time_enable(true); } void teardown() { memfault_cdr_source_reset(); mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultCdrSource, Test_NoSources) { size_t total_size; const bool has_data = g_memfault_cdr_source.has_more_msgs_cb(&total_size); CHECK_FALSE(has_data); uint8_t buf[16]; const bool success = g_memfault_cdr_source.read_msg_cb(0, &buf, sizeof(buf)); CHECK_FALSE(success); // no-op g_memfault_cdr_source.mark_msg_read_cb(); } static void prv_run_encoder_test(void) { prv_set_fake_info(&s_cdr_metadata, s_cdr_bin, sizeof(s_cdr_bin)); // it should be safe to call this function multiple times size_t total_size = 0; bool has_data; for (int i = 0; i < 2; i++) { has_data = g_memfault_cdr_source.has_more_msgs_cb(&total_size); CHECK_TRUE(has_data); LONGS_EQUAL(s_expected_encoded_size, total_size); } uint8_t cbor_buffer[total_size]; memset(cbor_buffer, 0x0, total_size); bool success = g_memfault_cdr_source.read_msg_cb(0, &cbor_buffer[0], total_size); CHECK_TRUE(success); MEMCMP_EQUAL(s_expected_encoded_buffer, cbor_buffer, s_expected_encoded_size); // same check but now do byte-wise reading memset(cbor_buffer, 0x0, total_size); for (size_t i = 0; i < total_size; i++) { success = g_memfault_cdr_source.read_msg_cb(i, &cbor_buffer[i], 1); CHECK_TRUE(success); } MEMCMP_EQUAL(s_expected_encoded_buffer, cbor_buffer, s_expected_encoded_size); // same check but now do 2 bytes at a time memset(cbor_buffer, 0x0, total_size); for (size_t i = 0; i < total_size; i += 2) { const size_t bytes_to_read = MEMFAULT_MIN(2, total_size - i); success = g_memfault_cdr_source.read_msg_cb(i, &cbor_buffer[i], bytes_to_read); CHECK_TRUE(success); } MEMCMP_EQUAL(s_expected_encoded_buffer, cbor_buffer, s_expected_encoded_size); g_memfault_cdr_source.mark_msg_read_cb(); // there should no longer be data has_data = g_memfault_cdr_source.has_more_msgs_cb(&total_size); CHECK_FALSE(has_data); } TEST(MemfaultCdrSource, Test_BasicCapture) { memfault_cdr_register_source(&s_memfault_cdr_fake_source); prv_run_encoder_test(); } TEST(MemfaultCdrSource, Test_MultipleSources) { memfault_cdr_register_source(&s_memfault_cdr_stub_source); memfault_cdr_register_source(&s_memfault_cdr_fake_source); prv_run_encoder_test(); } TEST(MemfaultCdrSource, Test_SourceRegistryFull) { for (size_t i = 0; i < MEMFAULT_CDR_MAX_DATA_SOURCES; i++) { memfault_cdr_register_source(&s_memfault_cdr_stub_source); } // shouldn't get registered so there should be no data memfault_cdr_register_source(&s_memfault_cdr_fake_source); prv_set_fake_info(&s_cdr_metadata, s_cdr_bin, sizeof(s_cdr_bin)); size_t total_size; const bool has_data = g_memfault_cdr_source.has_more_msgs_cb(&total_size); CHECK_FALSE(has_data); } ================================================ FILE: tests/unit/src/test_memfault_data_export.cpp ================================================ #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/data_export.h" #include "memfault/core/data_packetizer.h" #include "memfault/core/math.h" static uint8_t s_fake_chunk1[] = { 0x1 }; static uint8_t s_fake_chunk2[MEMFAULT_DATA_EXPORT_CHUNK_MAX_LEN]; typedef struct { void *data; size_t length; } sMemfaultChunkTestData; static const sMemfaultChunkTestData s_fake_chunks[] = { [0] = { .data = s_fake_chunk1, .length = sizeof(s_fake_chunk1), }, [1] = { .data = s_fake_chunk2, .length = sizeof(s_fake_chunk2), } }; static int s_current_chunk_idx = 0; TEST_GROUP(MemfaultDataExport) { void setup() { for (size_t i = 0; i < sizeof(s_fake_chunk2); i++) { s_fake_chunk2[i] = i & 0xff; } s_current_chunk_idx = 0; mock().strictOrder(); } void teardown() { mock().clear(); } }; void memfault_data_export_base64_encoded_chunk(const char *chunk_str) { mock().actualCall(__func__).withStringParameter("chunk_str", chunk_str); CHECK(strlen(chunk_str) < MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN); } bool memfault_packetizer_get_chunk(void *buf, size_t *buf_len) { const size_t num_chunks = MEMFAULT_ARRAY_SIZE(s_fake_chunks); if (s_current_chunk_idx == num_chunks) { return false; } const sMemfaultChunkTestData *chunk = &s_fake_chunks[s_current_chunk_idx]; s_current_chunk_idx++; LONGS_EQUAL(MEMFAULT_DATA_EXPORT_CHUNK_MAX_LEN, *buf_len); CHECK(chunk->length <= *buf_len); memcpy(buf, chunk->data, chunk->length); *buf_len = chunk->length; return true; } TEST(MemfaultDataExport, Test_MemfaultDataExport) { mock() .expectOneCall("memfault_data_export_base64_encoded_chunk") .withStringParameter("chunk_str", "MC:AQ==:"); mock() .expectOneCall("memfault_data_export_base64_encoded_chunk") .withStringParameter( "chunk_str", "MC:AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+" "P0BBQkNERUZHSElKS0xNTk8=:"); memfault_data_export_dump_chunks(); mock().checkExpectations(); } ================================================ FILE: tests/unit/src/test_memfault_data_packetizer.cpp ================================================ //! @file //! //! @brief #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/config.h" #include "memfault/core/data_packetizer.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/data_source_rle.h" #include "memfault/core/math.h" #include "memfault/util/chunk_transport.h" static uint8_t s_fake_coredump[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa }; static bool s_multi_call_chunking_enabled = false; static uint8_t s_fake_event[] = { 0xa, 0xb, 0xc, 0xd }; static const sMemfaultDataSourceImpl *s_active_rle_data_source = NULL; // type bits are LS 6 bits of the header #define PACKETIZER_HEADER_TYPE_MASK 0x3f #if MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY #define MEMFAULT_PROJECT_KEY_LEN sizeof(MEMFAULT_PROJECT_KEY) #else #define MEMFAULT_PROJECT_KEY_LEN 0 #endif #define PACKETIZER_HEADER_SIZE_BYTES \ (1 /* hdr */ \ + MEMFAULT_PROJECT_KEY_LEN /* project key */ \ ) // // Mocks & Fakes to exercise packetizer logic // static bool prv_coredump_read_core(uint32_t offset, void *buf, size_t buf_len) { CHECK((offset + buf_len) <= sizeof(s_fake_coredump)); memcpy(buf, &s_fake_coredump[offset], buf_len); return mock().actualCall(__func__).returnBoolValueOrDefault(true); } static void prv_mark_core_read(void) { mock().actualCall(__func__); } static bool prv_coredump_has_core(size_t *total_size_out) { bool has_coredump = mock().actualCall(__func__).returnBoolValueOrDefault(true); if (has_coredump) { *total_size_out = sizeof(s_fake_coredump); } return has_coredump; } const sMemfaultDataSourceImpl g_memfault_coredump_data_source = { .has_more_msgs_cb = prv_coredump_has_core, .read_msg_cb = prv_coredump_read_core, .mark_msg_read_cb = prv_mark_core_read, }; static bool prv_heartbeat_metric_read_event(uint32_t offset, void *buf, size_t buf_len) { CHECK((offset + buf_len) <= sizeof(s_fake_event)); memcpy(buf, &s_fake_event[offset], buf_len); return mock().actualCall(__func__).returnBoolValueOrDefault(true); } static void prv_heartbeat_metric_mark_read(void) { mock().actualCall(__func__); } static bool prv_heartbeat_metric_has_event(size_t *total_size_out) { // by default disable event bool has_coredump = mock().actualCall(__func__).returnBoolValueOrDefault(true); if (has_coredump) { *total_size_out = sizeof(s_fake_event); } return has_coredump; } const sMemfaultDataSourceImpl g_memfault_event_data_source = { .has_more_msgs_cb = prv_heartbeat_metric_has_event, .read_msg_cb = prv_heartbeat_metric_read_event, .mark_msg_read_cb = prv_heartbeat_metric_mark_read, }; bool memfault_data_source_rle_encoder_set_active(const sMemfaultDataSourceImpl *active_source) { s_active_rle_data_source = active_source; return mock().actualCall(__func__).returnBoolValueOrDefault(true); } static bool prv_rle_read_data(uint32_t offset, void *buf, size_t buf_len) { mock().actualCall(__func__); return s_active_rle_data_source->read_msg_cb(offset, buf, buf_len); } static void prv_rle_mark_msg_read(void) { mock().actualCall(__func__); return s_active_rle_data_source->mark_msg_read_cb(); } static bool prv_rle_has_msg(size_t *total_size_out) { mock().actualCall(__func__); return s_active_rle_data_source->has_more_msgs_cb(total_size_out); } const sMemfaultDataSourceImpl g_memfault_data_rle_source = { .has_more_msgs_cb = prv_rle_has_msg, .read_msg_cb = prv_rle_read_data, .mark_msg_read_cb = prv_rle_mark_msg_read, }; // For packetizer test purposes, the data within the chunker should be opaque to us // so just use this fake implementation which simply copies whatever the backing reader // points to bool memfault_chunk_transport_get_next_chunk(sMfltChunkTransportCtx *ctx, void *buf, size_t *buf_len) { LONGS_EQUAL(s_multi_call_chunking_enabled, ctx->enable_multi_call_chunk); const size_t bytes_to_read = MEMFAULT_MIN(*buf_len, ctx->total_size - ctx->read_offset); ctx->read_msg(ctx->read_offset, buf, bytes_to_read); ctx->read_offset += bytes_to_read; *buf_len = bytes_to_read; return (ctx->read_offset != ctx->total_size); } void memfault_chunk_transport_get_chunk_info(sMfltChunkTransportCtx *ctx) { // fake chunker has 0 overhead so total_chunk_size just matches that ctx->single_chunk_message_length = ctx->total_size; } static const char *s_log_scope = "log_data_source"; static const char *s_cdr_scope = "cdr_source"; TEST_GROUP(MemfaultDataPacketizer) { void setup() { // abort any in-progress transactions mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); memfault_packetizer_abort(); mock().checkExpectations(); mock().clear(); s_multi_call_chunking_enabled = false; mock().strictOrder(); mock(s_log_scope).disable(); mock(s_cdr_scope).disable(); } void teardown() { mock().checkExpectations(); mock().clear(); } }; static bool prv_data_available(void) { sPacketizerConfig cfg = { .enable_multi_packet_chunk = s_multi_call_chunking_enabled, }; sPacketizerMetadata metadata; return memfault_packetizer_begin(&cfg, &metadata); } static void prv_setup_expect_coredump_call_expectations(bool has_core) { mock().expectOneCall("memfault_data_source_rle_encoder_set_active").andReturnValue(false); mock().expectOneCall("prv_coredump_has_core").andReturnValue(has_core); } static void prv_begin_transfer(bool data_expected, size_t expected_raw_msg_size) { sPacketizerConfig cfg = { .enable_multi_packet_chunk = s_multi_call_chunking_enabled, }; sPacketizerMetadata metadata; bool md = memfault_packetizer_begin(&cfg, &metadata); LONGS_EQUAL(data_expected, md); const uint32_t expected_data_len = data_expected ? (expected_raw_msg_size + PACKETIZER_HEADER_SIZE_BYTES) : 0; LONGS_EQUAL(expected_data_len, metadata.single_chunk_message_length); CHECK(!metadata.send_in_progress); } TEST(MemfaultDataPacketizer, Test_GetPacketNoActiveMessages) { uint8_t packet[16]; prv_setup_expect_coredump_call_expectations(false); mock().expectOneCall("prv_heartbeat_metric_has_event").andReturnValue(false); const bool data_expected = false; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_NoMoreData, rv); prv_setup_expect_coredump_call_expectations(false); mock().expectOneCall("prv_heartbeat_metric_has_event").andReturnValue(false); CHECK(!prv_data_available()); } static void prv_run_single_packet_test(void) { uint8_t packet[16 + MEMFAULT_PROJECT_KEY_LEN]; prv_setup_expect_coredump_call_expectations(true); mock().expectOneCall("prv_coredump_read_core"); mock().expectOneCall("prv_mark_core_read"); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); // the fake chunker has 0 overhead LONGS_EQUAL(sizeof(s_fake_coredump) + PACKETIZER_HEADER_SIZE_BYTES, buf_len); // packet should be a coredump type. see definition of sMfltPacketizerHdr for // bitmask- filtering only the type bits LONGS_EQUAL(1, packet[0] & PACKETIZER_HEADER_TYPE_MASK); #if MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY // packet header should contain the project key MEMCMP_EQUAL(MEMFAULT_PROJECT_KEY, &packet[1], MEMFAULT_PROJECT_KEY_LEN); #endif // packet payload should be the coredump data MEMCMP_EQUAL(s_fake_coredump, &packet[PACKETIZER_HEADER_SIZE_BYTES], sizeof(s_fake_coredump)); } TEST(MemfaultDataPacketizer, Test_MessageFitsInSinglePacket) { prv_run_single_packet_test(); } TEST(MemfaultDataPacketizer, Test_MessageUsesRle) { // For unit tests we've mocked out rle to return the same data as the active source // What we verify is: // 1. The type bit is correctly set to indicate RLE was used // 2. All data source accesses route through the rle data source uint8_t packet[16 + MEMFAULT_PROJECT_KEY_LEN]; mock().expectOneCall("memfault_data_source_rle_encoder_set_active").andReturnValue(true); mock().expectOneCall("prv_rle_has_msg"); mock().expectOneCall("prv_coredump_has_core").andReturnValue(true); mock().expectOneCall("prv_rle_read_data"); mock().expectOneCall("prv_coredump_read_core"); mock().expectOneCall("prv_rle_mark_msg_read"); mock().expectOneCall("prv_mark_core_read"); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); // the fake chunker has 0 overhead LONGS_EQUAL(sizeof(s_fake_coredump) + PACKETIZER_HEADER_SIZE_BYTES, buf_len); // packet should be a coredump type with RLE bit set const uint8_t expected_type_byte = 1 | 0x80 #if MEMFAULT_MESSAGE_HEADER_CONTAINS_PROJECT_KEY | 0x40 /* project key */ #endif ; LONGS_EQUAL(expected_type_byte, packet[0]); } TEST(MemfaultDataPacketizer, Test_EventMessageFitsInSinglePacket) { uint8_t packet[16 + MEMFAULT_PROJECT_KEY_LEN]; prv_setup_expect_coredump_call_expectations(false); mock().expectOneCall("prv_heartbeat_metric_has_event"); mock().expectOneCall("prv_heartbeat_metric_read_event"); mock().expectOneCall("prv_heartbeat_metric_mark_read"); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_event)); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); // the fake chunker has 0 overhead LONGS_EQUAL(sizeof(s_fake_event) + PACKETIZER_HEADER_SIZE_BYTES, buf_len); // packet should be a heartbeat metric type LONGS_EQUAL(2, packet[0] & PACKETIZER_HEADER_TYPE_MASK); } TEST(MemfaultDataPacketizer, Test_MoreDataAvailable) { for (int i = 0; i < 2; i++) { prv_setup_expect_coredump_call_expectations(true); } CHECK(memfault_packetizer_data_available()); CHECK(prv_data_available()); mock().checkExpectations(); // A message should be batched up after the first check so there // should be no new call to has_valid_coredump CHECK(prv_data_available()); CHECK(memfault_packetizer_data_available()); uint8_t packet[16 + MEMFAULT_PROJECT_KEY_LEN]; mock().expectOneCall("prv_coredump_read_core"); mock().expectOneCall("prv_mark_core_read"); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); // the fake chunker has 0 overhead LONGS_EQUAL(sizeof(s_fake_coredump) + PACKETIZER_HEADER_SIZE_BYTES, buf_len); // packet should be a coredump type LONGS_EQUAL(1, packet[0] & PACKETIZER_HEADER_TYPE_MASK); } static void prv_enable_multi_packet_chunks(void) { s_multi_call_chunking_enabled = true; } TEST(MemfaultDataPacketizer, Test_MultiPacketChunking) { // when multi chunk packets have been configured, we just need // to make sure that the setting is in the chunk context prv_enable_multi_packet_chunks(); prv_run_single_packet_test(); } static void prv_test_msg_fits_in_multiple_packets(void) { uint8_t packet[2]; // calls to the coredump mock data source. note: using integer division ceil trick const size_t num_coredump_calls = (sizeof(s_fake_coredump) + sizeof(packet) / 2) / sizeof(packet); // total number of incremental packetizer calls const size_t num_calls = (sizeof(s_fake_coredump) + sizeof(packet) / 2 + PACKETIZER_HEADER_SIZE_BYTES) / sizeof(packet); printf("num_coredump_calls: %zu\n", num_coredump_calls); printf("num_calls: %zu\n", num_calls); prv_setup_expect_coredump_call_expectations(true); mock().expectNCalls(num_coredump_calls, "prv_coredump_read_core"); mock().expectOneCall("prv_mark_core_read"); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); // the fake chunker has 0 overhead size_t total_packet_length = sizeof(s_fake_coredump) + PACKETIZER_HEADER_SIZE_BYTES; for (size_t i = 0; i < num_calls; i++) { size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); const size_t expected_buf_len = MEMFAULT_MIN(total_packet_length, sizeof(packet)); LONGS_EQUAL(expected_buf_len, buf_len); total_packet_length -= buf_len; if (i == 0) { // packet should be a coredump type LONGS_EQUAL(1, packet[0] & PACKETIZER_HEADER_TYPE_MASK); } if ((i != (num_calls - 1)) && s_multi_call_chunking_enabled) { LONGS_EQUAL(kMemfaultPacketizerStatus_MoreDataForChunk, rv); } else { LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); } } } TEST(MemfaultDataPacketizer, Test_MessageFitsInMultiplePackets) { prv_enable_multi_packet_chunks(); prv_test_msg_fits_in_multiple_packets(); } TEST(MemfaultDataPacketizer, Test_OneChunkMultiplePackets) { prv_test_msg_fits_in_multiple_packets(); } TEST(MemfaultDataPacketizer, Test_SimpleGetChunkApi) { uint8_t packet[2]; bool got_data; // calls to the coredump mock data source. note: uusing integer division ceil trick const size_t num_coredump_calls = (sizeof(s_fake_coredump) + sizeof(packet) / 2) / sizeof(packet); // total number of incremental packetizer calls const size_t num_calls = (sizeof(s_fake_coredump) + sizeof(packet) / 2 + PACKETIZER_HEADER_SIZE_BYTES) / sizeof(packet); prv_setup_expect_coredump_call_expectations(true); mock().expectNCalls(num_coredump_calls, "prv_coredump_read_core"); mock().expectOneCall("prv_mark_core_read"); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); // the fake chunker has 0 overhead size_t total_packet_length = sizeof(s_fake_coredump) + PACKETIZER_HEADER_SIZE_BYTES; for (size_t i = 0; i < num_calls; i++) { size_t buf_len = sizeof(packet); got_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(got_data); const size_t expected_buf_len = MEMFAULT_MIN(total_packet_length, sizeof(packet)); LONGS_EQUAL(expected_buf_len, buf_len); total_packet_length -= buf_len; if (i == 0) { // packet should be a coredump type LONGS_EQUAL(1, packet[0] & PACKETIZER_HEADER_TYPE_MASK); } } mock().checkExpectations(); prv_setup_expect_coredump_call_expectations(false); mock().expectOneCall("prv_heartbeat_metric_has_event").andReturnValue(false); size_t buf_len = sizeof(packet); got_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(!got_data); } TEST(MemfaultDataPacketizer, Test_MessageSendAbort) { // set the destination buffer size sufficient to hold the header and all but // 1 byte of the payload uint8_t packet[sizeof(s_fake_coredump) + PACKETIZER_HEADER_SIZE_BYTES - 1]; // start sending a packet and abort after we have received one packet // we should re-wind and see the entire message transmitted again prv_setup_expect_coredump_call_expectations(true); mock().expectOneCall("prv_coredump_read_core"); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(sizeof(packet), buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); memfault_packetizer_abort(); mock().checkExpectations(); prv_test_msg_fits_in_multiple_packets(); } TEST(MemfaultDataPacketizer, Test_MessageWithCoredumpReadFailure) { uint8_t packet[16 + MEMFAULT_PROJECT_KEY_LEN]; prv_setup_expect_coredump_call_expectations(true); mock().expectOneCall("prv_coredump_read_core").andReturnValue(false); mock().expectOneCall("prv_mark_core_read"); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); // the fake chunker has 0 overhead LONGS_EQUAL(sizeof(s_fake_coredump) + PACKETIZER_HEADER_SIZE_BYTES, buf_len); // packet should be a coredump type LONGS_EQUAL(1, packet[0] & PACKETIZER_HEADER_TYPE_MASK); } TEST(MemfaultDataPacketizer, Test_MessageOnlyHdrFits) { // NOTE: for the fake transport, the chunker has zero overhead uint8_t packet_only_hdr[PACKETIZER_HEADER_SIZE_BYTES]; prv_setup_expect_coredump_call_expectations(true); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); size_t buf_len = sizeof(packet_only_hdr); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet_only_hdr, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); LONGS_EQUAL(sizeof(packet_only_hdr), buf_len); LONGS_EQUAL(1, packet_only_hdr[0] & PACKETIZER_HEADER_TYPE_MASK); mock().checkExpectations(); // if we call memfault_packetizer_begin() while in the middle of sending a // message, we should see that a send is in progress sPacketizerConfig cfg = { .enable_multi_packet_chunk = s_multi_call_chunking_enabled, }; sPacketizerMetadata metadata; bool md = memfault_packetizer_begin(&cfg, &metadata); LONGS_EQUAL(data_expected, md); const uint32_t expected_data_len = sizeof(s_fake_coredump) + PACKETIZER_HEADER_SIZE_BYTES; LONGS_EQUAL(expected_data_len, metadata.single_chunk_message_length); CHECK(metadata.send_in_progress); // now read the actual coredump data mock().expectOneCall("prv_coredump_read_core"); mock().expectOneCall("prv_mark_core_read"); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); uint8_t packet[sizeof(s_fake_coredump)]; buf_len = sizeof(packet); rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); LONGS_EQUAL(sizeof(s_fake_coredump), buf_len); } // exercise the path where a zero length buffer is passed TEST(MemfaultDataPacketizer, Test_ZeroLengthBuffer) { uint8_t packet[0]; // start sending a packet and abort after we have received one packet // we should re-wind and see the entire message transmitted again prv_setup_expect_coredump_call_expectations(true); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_fake_coredump)); size_t buf_len = 0; eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(0, buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_NoMoreData, rv); } TEST(MemfaultDataPacketizer, Test_BadArguments) { uint8_t packet[5]; eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, NULL); LONGS_EQUAL(kMemfaultPacketizerStatus_NoMoreData, rv); size_t buf_len = 0; rv = memfault_packetizer_get_next(NULL, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_NoMoreData, rv); sPacketizerConfig cfg = { 0 }; sPacketizerMetadata metadata; bool md = memfault_packetizer_begin(NULL, &metadata); CHECK(!md); md = memfault_packetizer_begin(&cfg, NULL); CHECK(!md); } static const size_t LOG_SIZE = 1; static bool prv_has_logs(size_t *size) { const bool has_logs = mock(s_log_scope).actualCall(__func__).returnBoolValueOrDefault(false); if (has_logs) { *size = LOG_SIZE; } return has_logs; } static bool prv_logs_read(uint32_t offset, void *buf, size_t buf_len) { (void)offset; memset(buf, 'A', buf_len); mock(s_log_scope).actualCall(__func__); return true; } static void prv_logs_mark_sent(void) { mock(s_log_scope).actualCall(__func__); } const sMemfaultDataSourceImpl g_memfault_log_data_source = { .has_more_msgs_cb = prv_has_logs, .read_msg_cb = prv_logs_read, .mark_msg_read_cb = prv_logs_mark_sent, }; TEST(MemfaultDataPacketizer, Test_LogSourceIsHookedUp) { uint8_t packet[16 + MEMFAULT_PROJECT_KEY_LEN]; mock(s_log_scope).enable(); prv_setup_expect_coredump_call_expectations(false); mock().expectOneCall("prv_heartbeat_metric_has_event").andReturnValue(false); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); mock(s_log_scope).expectOneCall("prv_has_logs").andReturnValue(true); mock(s_log_scope).expectOneCall("prv_logs_read"); mock(s_log_scope).expectOneCall("prv_logs_mark_sent"); const bool data_expected = true; prv_begin_transfer(data_expected, LOG_SIZE); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); // the fake chunker has 0 overhead LONGS_EQUAL(LOG_SIZE + PACKETIZER_HEADER_SIZE_BYTES, buf_len); // packet should be a log type BYTES_EQUAL(3, packet[0] & PACKETIZER_HEADER_TYPE_MASK); BYTES_EQUAL('A', packet[PACKETIZER_HEADER_SIZE_BYTES]); } static const uint8_t s_cdr_payload[] = { 0x1, 0x2, 0x3, 0x4 }; static bool prv_has_cdr(size_t *size) { const bool has_cdr = mock(s_cdr_scope).actualCall(__func__).returnBoolValueOrDefault(false); if (has_cdr) { *size = sizeof(s_cdr_payload); } return has_cdr; } static bool prv_cdr_read(uint32_t offset, void *buf, size_t buf_len) { memcpy(buf, &s_cdr_payload[offset], buf_len); mock(s_cdr_scope).actualCall(__func__); return true; } static void prv_cdr_mark_sent(void) { mock(s_cdr_scope).actualCall(__func__); } const sMemfaultDataSourceImpl g_memfault_cdr_source = { .has_more_msgs_cb = prv_has_cdr, .read_msg_cb = prv_cdr_read, .mark_msg_read_cb = prv_cdr_mark_sent, }; TEST(MemfaultDataPacketizer, Test_CdrSourceIsHookedUp) { uint8_t packet[16 + MEMFAULT_PROJECT_KEY_LEN]; mock(s_cdr_scope).enable(); prv_setup_expect_coredump_call_expectations(false); mock().expectOneCall("prv_heartbeat_metric_has_event").andReturnValue(false); mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); mock(s_cdr_scope).expectOneCall("prv_has_cdr").andReturnValue(true); mock(s_cdr_scope).expectOneCall("prv_cdr_read"); mock(s_cdr_scope).expectOneCall("prv_cdr_mark_sent"); const bool data_expected = true; prv_begin_transfer(data_expected, sizeof(s_cdr_payload)); size_t buf_len = sizeof(packet); eMemfaultPacketizerStatus rv = memfault_packetizer_get_next(packet, &buf_len); LONGS_EQUAL(kMemfaultPacketizerStatus_EndOfChunk, rv); // the fake chunker has 0 overhead LONGS_EQUAL(sizeof(s_cdr_payload) + PACKETIZER_HEADER_SIZE_BYTES, buf_len); // packet should be a log type BYTES_EQUAL(4, packet[0] & PACKETIZER_HEADER_TYPE_MASK); MEMCMP_EQUAL(s_cdr_payload, &packet[PACKETIZER_HEADER_SIZE_BYTES], sizeof(s_cdr_payload)); } TEST(MemfaultDataPacketizer, Test_ActiveSources) { uint8_t packet[16 + MEMFAULT_PROJECT_KEY_LEN]; // changing the sources will abort any in-progress transmissions mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); memfault_packetizer_set_active_sources(kMfltDataSourceMask_None); mock().checkExpectations(); size_t buf_len = sizeof(packet); bool more_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(!more_data); mock().checkExpectations(); // exclusively enable coredumps and we should only see the coredump source get called mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); memfault_packetizer_set_active_sources(kMfltDataSourceMask_Coredump); prv_setup_expect_coredump_call_expectations(false); more_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(!more_data); mock().checkExpectations(); // exclusively enable events and we should only see the coredump source get called mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); memfault_packetizer_set_active_sources(kMfltDataSourceMask_Event); mock().expectOneCall("prv_heartbeat_metric_has_event").andReturnValue(false); more_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(!more_data); mock().checkExpectations(); // exclusively enabled logs and we should only see the log source get called mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); memfault_packetizer_set_active_sources(kMfltDataSourceMask_Log); mock(s_log_scope).expectOneCall("prv_has_logs").andReturnValue(true); more_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(!more_data); mock().checkExpectations(); // exclusively enabled cdr and we should only see the log source get called mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); memfault_packetizer_set_active_sources(kMfltDataSourceMask_Cdr); mock(s_log_scope).expectOneCall("prv_has_cdr").andReturnValue(true); more_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(!more_data); mock().checkExpectations(); // events + logs enabled mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); // note the cast to eMfltDataSourceMask memfault_packetizer_set_active_sources((kMfltDataSourceMask_Event | kMfltDataSourceMask_Log)); mock().expectOneCall("prv_heartbeat_metric_has_event").andReturnValue(false); mock(s_log_scope).expectOneCall("prv_has_logs").andReturnValue(true); more_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(!more_data); mock().checkExpectations(); // if all sources are enabled, they should all be checked mock().expectOneCall("memfault_data_source_rle_encoder_set_active"); memfault_packetizer_set_active_sources(kMfltDataSourceMask_All); prv_setup_expect_coredump_call_expectations(false); mock().expectOneCall("prv_heartbeat_metric_has_event").andReturnValue(false); mock(s_log_scope).expectOneCall("prv_has_logs").andReturnValue(true); more_data = memfault_packetizer_get_chunk(packet, &buf_len); CHECK(!more_data); mock().checkExpectations(); } TEST(MemfaultDataPacketizer, Test_ZeroBufferLength) { // AddressSanitizer will trip on this if anything is written to it uint8_t empty_buf[0]; size_t buf_len = 0; prv_setup_expect_coredump_call_expectations(true); bool rv = memfault_packetizer_get_chunk(empty_buf, &buf_len); LONGS_EQUAL(false, rv); LONGS_EQUAL(0, buf_len); mock().checkExpectations(); } TEST(MemfaultDataPacketizer, Test_MinimalBufferLength) { // the fake chunker has 0 overhead, so the minimal chunk size here is 1 byte. uint8_t small_buf[1]; size_t buf_len = sizeof(small_buf); prv_setup_expect_coredump_call_expectations(true); bool rv = memfault_packetizer_get_chunk(small_buf, &buf_len); LONGS_EQUAL(1, buf_len); LONGS_EQUAL(true, rv); mock().checkExpectations(); } ================================================ FILE: tests/unit/src/test_memfault_data_source_rle.cpp ================================================ //! @file //! //! @brief #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/math.h" extern "C" { #include #include #include #include "memfault/core/data_source_rle.h" static const uint8_t *s_active_data = NULL; static size_t s_active_data_size = 0; } static bool prv_has_msgs(size_t *total_size_out) { *total_size_out = s_active_data_size; return (*total_size_out != 0); } static bool prv_read_msg_data(uint32_t offset, void *buf, size_t buf_len) { memcpy(buf, &s_active_data[offset], buf_len); return true; } static void prv_mark_msg_read(void) { s_active_data = NULL; s_active_data_size = 0; } static const sMemfaultDataSourceImpl s_test_data_source = { .has_more_msgs_cb = prv_has_msgs, .read_msg_cb = prv_read_msg_data, .mark_msg_read_cb = prv_mark_msg_read, }; TEST_GROUP(MemfaultDataSourceRle) { void setup() { s_active_data = NULL; s_active_data_size = 0; memfault_data_source_rle_encoder_set_active(&s_test_data_source); } void teardown() { memfault_data_source_rle_mark_msg_read(); } }; TEST(MemfaultDataSourceRle, Test_DataSourceHasMoreMsgs) { const uint8_t fake_core[] = { 1, 1 }; s_active_data = &fake_core[0]; s_active_data_size = sizeof(fake_core); size_t total_size = 0; bool more_msgs = memfault_data_source_rle_has_more_msgs(&total_size); CHECK(more_msgs); LONGS_EQUAL(2, total_size); // a re-query shouldn't read more data if its already been computed s_active_data = NULL; total_size = 0; more_msgs = memfault_data_source_rle_has_more_msgs(&total_size); CHECK(more_msgs); LONGS_EQUAL(2, total_size); memfault_data_source_rle_mark_msg_read(); more_msgs = memfault_data_source_rle_has_more_msgs(&total_size); CHECK(!more_msgs); } static void prv_get_coredump_data(uint8_t *buf, size_t buf_len, size_t fill_call_size) { for (size_t i = 0; i < buf_len; i += fill_call_size) { const size_t bytes_to_read = MEMFAULT_MIN(fill_call_size, buf_len - i); bool success = memfault_data_source_rle_read_msg(i, &buf[i], bytes_to_read); CHECK(success); } } static void prv_check_pattern(const uint8_t *in, size_t in_len, const uint8_t *expected_out, size_t expected_out_len) { // regardless of the size of the buffer we call read_msg_cb() with // we should get the same result for (size_t fill_size = 1; fill_size < expected_out_len; fill_size++) { s_active_data = in; s_active_data_size = in_len; size_t total_size = 0; bool more_msg = memfault_data_source_rle_has_more_msgs(&total_size); CHECK(more_msg); LONGS_EQUAL(expected_out_len, total_size); uint8_t chunk[total_size]; memset(chunk, 0x0, sizeof(chunk)); prv_get_coredump_data(chunk, sizeof(chunk), fill_size); MEMCMP_EQUAL(expected_out, chunk, expected_out_len); memfault_data_source_rle_mark_msg_read(); } } TEST(MemfaultDataSourceRle, Test_DataSourceEndsWithRepeat) { const uint8_t fake_core[] = { 1, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 9, 9, 9, 9 }; const uint8_t expected_core_rle[] = { 4, 1, 5, 2, 3, 4, 10, 5, 1, 6, 8, 9 }; prv_check_pattern(fake_core, sizeof(fake_core), expected_core_rle, sizeof(expected_core_rle)); } TEST(MemfaultDataSourceRle, Test_DataSourceMultiByteVarintAndEndsWithNonRepeat) { uint8_t fake_core[9000] = { 1, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, }; fake_core[sizeof(fake_core) - 1] = 8; const uint8_t expected_core_rle[] = { 4, 1, 5, 2, 3, 4, 10, 5, 3, 6, 7, 182, 140, 1, 0, 1, 8 }; prv_check_pattern(fake_core, sizeof(fake_core), expected_core_rle, sizeof(expected_core_rle)); } ================================================ FILE: tests/unit/src/test_memfault_demo_shell.cpp ================================================ #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/config.h" #include "memfault/core/math.h" #include "memfault/demo/shell.h" #include "memfault/demo/shell_commands.h" static size_t s_num_chars_sent = 0; static char s_chars_sent_buffer[1024] = { 0 }; static int prv_send_char(char c) { CHECK(s_num_chars_sent < sizeof(s_chars_sent_buffer)); s_chars_sent_buffer[s_num_chars_sent++] = c; return 1; } static int prv_test_handler(int argc, char **argv) { MockActualCall &m = mock().actualCall(__func__); for (int i = 0; i < argc; i++) { char buffer[11] = { 0 }; snprintf(buffer, MEMFAULT_ARRAY_SIZE(buffer), "%d", i); m.withStringParameter(buffer, argv[i]); } return 0; } static const sMemfaultShellCommand s_memfault_shell_commands[] = { { "test", prv_test_handler, "test command" }, { "help", memfault_shell_help_handler, "Lists all commands" }, }; const sMemfaultShellCommand *const g_memfault_shell_commands = s_memfault_shell_commands; const size_t g_memfault_num_shell_commands = MEMFAULT_ARRAY_SIZE(s_memfault_shell_commands); static void prv_receive_str(const char *str) { for (size_t i = 0; i < strlen(str); ++i) { memfault_demo_shell_receive_char(str[i]); } } static void prv_reset_sent_buffer(void) { s_num_chars_sent = 0; memset(s_chars_sent_buffer, 0, sizeof(s_chars_sent_buffer)); } TEST_GROUP(MfltDemoShell) { void setup() { const sMemfaultShellImpl impl = { .send_char = prv_send_char, }; memfault_demo_shell_boot(&impl); STRCMP_EQUAL("\r\nmflt> ", s_chars_sent_buffer); prv_reset_sent_buffer(); } void teardown() { mock().checkExpectations(); mock().clear(); prv_reset_sent_buffer(); memfault_shell_command_set_extensions(NULL, 0); } }; TEST(MfltDemoShell, Test_MfltDemoShellEcho) { memfault_demo_shell_receive_char('h'); memfault_demo_shell_receive_char('i'); CHECK_EQUAL(2, s_num_chars_sent); STRCMP_EQUAL("hi", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellEchoBackspace) { mock().expectOneCall("prv_test_handler").withParameter("0", "test"); prv_receive_str("x\x08test\n"); STRCMP_EQUAL("x\x08\x20\x08test\r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellEnter) { memfault_demo_shell_receive_char('\n'); STRCMP_EQUAL("\r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellEnterCR) { memfault_demo_shell_receive_char('\r'); memfault_demo_shell_receive_char('\r'); STRCMP_EQUAL("\r\nmflt> \r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellEnterCRLF) { memfault_demo_shell_receive_char('\r'); memfault_demo_shell_receive_char('\n'); memfault_demo_shell_receive_char('\r'); memfault_demo_shell_receive_char('\n'); STRCMP_EQUAL("\r\nmflt> \r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellEnterLF) { memfault_demo_shell_receive_char('\n'); memfault_demo_shell_receive_char('\n'); STRCMP_EQUAL("\r\nmflt> \r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellEnterLFCR) { memfault_demo_shell_receive_char('\n'); memfault_demo_shell_receive_char('\r'); memfault_demo_shell_receive_char('\n'); memfault_demo_shell_receive_char('\r'); STRCMP_EQUAL("\r\nmflt> \r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellUnknownCmd) { prv_receive_str("foo\n"); STRCMP_EQUAL("foo\r\nUnknown command: foo\r\nType 'help' to list all commands\r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellTestCmd) { mock() .expectOneCall("prv_test_handler") .withParameter("0", "test") .withParameter("1", "123") .withParameter("2", "abc") .withParameter("3", "def") .withParameter("4", "g"); prv_receive_str("test 123 abc def g\n"); STRCMP_EQUAL("test 123 abc def g\r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellStripLeadingSpaces) { mock().expectOneCall("prv_test_handler").withParameter("0", "test"); prv_receive_str(" test\n"); STRCMP_EQUAL(" test\r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellHelpCmd) { prv_receive_str("help\n"); STRCMP_EQUAL("help\r\ntest: test command\r\nhelp: Lists all commands\r\nmflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellRxBufferFull) { for (size_t i = 0; i < MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE; ++i) { memfault_demo_shell_receive_char('X'); } STRCMP_EQUAL( "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r\n" // MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE // + 1 X's "Unknown command: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r\n" // MEMFAULT_DEMO_SHELL_RX_BUFFER_SIZE // X's "Type 'help' to list all commands\r\n" "mflt> ", s_chars_sent_buffer); } TEST(MfltDemoShell, Test_MfltDemoShellBackspaces) { mock().expectOneCall("prv_test_handler").withParameter("0", "test").withParameter("1", "1"); prv_receive_str("\b\bnop\b\b\btest 1\n"); mock().checkExpectations(); // use a memcmp so we can "see" the backspaces MEMCMP_EQUAL("nop\b \b\b \b\b \btest 1\r\nmflt> ", s_chars_sent_buffer, s_num_chars_sent); } TEST(MfltDemoShell, Test_MfltDemoShellNotBooted) { const sMemfaultShellImpl impl = { .send_char = NULL, }; memfault_demo_shell_boot(&impl); prv_receive_str("test 1\n"); LONGS_EQUAL(0, s_num_chars_sent); } TEST(MfltDemoShell, Test_Extension_Commands) { // baseline, test help output prv_receive_str("help\n"); STRCMP_EQUAL("help\r\ntest: test command\r\nhelp: Lists all commands\r\nmflt> ", s_chars_sent_buffer); prv_reset_sent_buffer(); // attach an extension command table static const sMemfaultShellCommand s_extension_commands[] = { { "test2", prv_test_handler, "test2 command" }, }; memfault_shell_command_set_extensions(s_extension_commands, MEMFAULT_ARRAY_SIZE(s_extension_commands)); prv_receive_str("help\n"); STRCMP_EQUAL( "help\r\ntest: test command\r\nhelp: Lists all commands\r\ntest2: test2 command\r\nmflt> ", s_chars_sent_buffer); } ================================================ FILE: tests/unit/src/test_memfault_event_storage.cpp ================================================ //! @file //! //! @brief #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/core/batched_events.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/event_storage.h" #include "memfault/core/event_storage_implementation.h" #include "memfault/core/math.h" #include "memfault/core/platform/nonvolatile_event_storage.h" extern "C" { // Declaration for test function used to reset event storage extern void memfault_event_storage_reset(void); } static uint8_t s_ram_store[11]; static const size_t s_ram_store_size = sizeof(s_ram_store); static const sMemfaultEventStorageImpl *s_storage_impl; #define MEMFAULT_STORAGE_OVERHEAD 2 static bool prv_fake_event_impl_has_event(size_t *total_size) { return g_memfault_event_data_source.has_more_msgs_cb(total_size); } static bool prv_fake_event_impl_read(uint32_t offset, void *buf, size_t buf_len) { return g_memfault_event_data_source.read_msg_cb(offset, buf, buf_len); } static void prv_fake_event_impl_mark_event_read(void) { g_memfault_event_data_source.mark_msg_read_cb(); } static void prv_assert_read(void *expected_data, size_t data_len) { size_t total_size; bool success = prv_fake_event_impl_has_event(&total_size); CHECK(success); LONGS_EQUAL(data_len, total_size); uint8_t actual_data[data_len]; success = prv_fake_event_impl_read(0, actual_data, sizeof(actual_data)); CHECK(success); MEMCMP_EQUAL(expected_data, actual_data, data_len); prv_fake_event_impl_mark_event_read(); } static void prv_assert_no_more_events(void) { size_t total_size = 0xab; const bool has_event = prv_fake_event_impl_has_event(&total_size); CHECK(!has_event); LONGS_EQUAL(0, total_size); } TEST_GROUP(MemfaultEventStorage) { void setup() { fake_memfault_metrics_platform_locking_reboot(); s_storage_impl = memfault_events_storage_boot(s_ram_store, s_ram_store_size); LONGS_EQUAL(s_ram_store_size, s_storage_impl->get_storage_size_cb()); } void teardown() { CHECK(fake_memfault_platform_metrics_lock_calls_balanced()); mock().checkExpectations(); mock().clear(); } }; static void prv_write_payload(const void *data, size_t data_len, bool rollback) { size_t space_available = s_storage_impl->begin_write_cb(); CHECK(space_available != 0); const uint8_t *byte = (const uint8_t *)data; for (size_t i = 0; i < data_len; i++) { s_storage_impl->append_data_cb(&byte[i], sizeof(byte[i])); } s_storage_impl->finish_write_cb(rollback); } #if MEMFAULT_TEST_PERSISTENT_EVENT_STORAGE_DISABLE TEST(MemfaultEventStorage, Test_MemfaultMetricStoreSingleEvent) { size_t space_available = s_storage_impl->begin_write_cb(); LONGS_EQUAL(s_ram_store_size - MEMFAULT_STORAGE_OVERHEAD, space_available); // if we begin another transaction while one is in progress, no space // should be available space_available = s_storage_impl->begin_write_cb(); LONGS_EQUAL(0, space_available); const uint8_t payload[] = { 0x1, 0x2, 0x3, 0x4 }; s_storage_impl->append_data_cb(&payload, sizeof(payload)); // complete the transaction const bool rollback = false; s_storage_impl->finish_write_cb(rollback); // should be a no-op s_storage_impl->finish_write_cb(rollback); // test memfault_event_storage_bytes_used() + memfault_event_storage_bytes_free() size_t bytes_used = memfault_event_storage_bytes_used(); size_t bytes_free = memfault_event_storage_bytes_free(); LONGS_EQUAL(sizeof(payload) + MEMFAULT_STORAGE_OVERHEAD, bytes_used); LONGS_EQUAL(s_ram_store_size - bytes_used, bytes_free); prv_assert_read((void *)&payload, sizeof(payload)); // should be a no-op prv_fake_event_impl_mark_event_read(); prv_assert_no_more_events(); } TEST(MemfaultEventStorage, Test_ApiMisuse) { prv_assert_no_more_events(); // if there are no events, we shouldn't be trying to read or clear one prv_fake_event_impl_mark_event_read(); uint8_t c; const bool success = prv_fake_event_impl_read(0, &c, sizeof(c)); CHECK(!success); } #if MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED == 0 TEST(MemfaultEventStorage, Test_MemfaultMultiEvent) { // queue up 3 one byte events which due to 2-byte overhead should take up 9 bytes bool rollback = false; size_t space_available; for (uint8_t byte = 0; byte < 3; byte++) { prv_write_payload(&byte, sizeof(byte), rollback); } // start a new event, only header should fit so space available should be 0 space_available = s_storage_impl->begin_write_cb(); LONGS_EQUAL(0, space_available); // drain events bool has_event; size_t event_size; for (uint8_t byte = 0; byte < 3; byte++) { has_event = prv_fake_event_impl_has_event(&event_size); CHECK(has_event); LONGS_EQUAL(1, event_size); // try an illegal read (past valid offset) uint8_t data; bool success = prv_fake_event_impl_read(1, &data, sizeof(data)); CHECK(!success); success = prv_fake_event_impl_read(0, &data, sizeof(data)); CHECK(success); LONGS_EQUAL(byte, data); prv_fake_event_impl_mark_event_read(); } has_event = prv_fake_event_impl_has_event(&event_size); CHECK(!has_event); // abort the write we had in progress and restart it rollback = true; s_storage_impl->finish_write_cb(true); // now write a larger message 1 byte at a time, all 11 bytes of storage should be free // abort the first attempt and then actually do the write on the second attempt const uint8_t payload[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 }; for (int i = 0; i < 2; i++) { rollback = (i == 0); prv_write_payload(&payload, sizeof(payload), rollback); } // new event but header can't fit, so space available should be zero space_available = s_storage_impl->begin_write_cb(); LONGS_EQUAL(0, space_available); has_event = prv_fake_event_impl_has_event(&event_size); CHECK(has_event); LONGS_EQUAL(sizeof(payload), event_size); uint8_t result[event_size]; memset(result, 0x0, sizeof(result)); for (uint32_t i = 0; i < sizeof(payload); i++) { prv_fake_event_impl_read(i, &result[i], sizeof(result[i])); } MEMCMP_EQUAL(payload, result, sizeof(result)); prv_fake_event_impl_mark_event_read(); } #else /* MEMFAULT_EVENT_STORAGE_READ_BATCHING_ENABLED */ void memfault_batched_events_build_header(size_t num_events, sMemfaultBatchedEventsHeader *header_out) { if (num_events <= 1) { return; } // a fake multi-byte header header_out->length = 2; header_out->data[0] = 0xAB; header_out->data[1] = num_events & 0xff; } TEST(MemfaultEventStorage, Test_MemfaultMultiEvent) { // queue up 2, 3 byte events which due to 2-byte overhead should take 10 bytes bool rollback = false; const uint8_t evt1[] = { 0x1, 0x2 }; const uint8_t evt2[] = { 0x3, 0x4 }; prv_write_payload(&evt1, sizeof(evt1), rollback); prv_write_payload(&evt2, sizeof(evt2), rollback); // drain events (all at once) bool has_event; size_t event_size; const uint8_t fake_header[] = { 0xAB, 0x02 }; has_event = prv_fake_event_impl_has_event(&event_size); CHECK(has_event); LONGS_EQUAL(sizeof(fake_header) + sizeof(evt1) + sizeof(evt2), event_size); // try an illegal read (past valid offset) uint8_t data[event_size]; memset(data, 0x0, sizeof(data)); bool success = prv_fake_event_impl_read(8, data, sizeof(data)); CHECK(!success); for (size_t i = 0; i < sizeof(data); i++) { success = prv_fake_event_impl_read(i, &data[i], 1); CHECK(success); } MEMCMP_EQUAL(fake_header, &data[0], sizeof(fake_header)); MEMCMP_EQUAL(evt1, &data[sizeof(fake_header)], sizeof(evt1)); MEMCMP_EQUAL(evt2, &data[sizeof(fake_header) + sizeof(evt1)], sizeof(evt2)); prv_fake_event_impl_mark_event_read(); has_event = prv_fake_event_impl_has_event(&event_size); CHECK(!has_event); } // More bytes in event storage than MEMFAULT_EVENT_STORAGE_READ_BATCHING_MAX_BYTES (4) TEST(MemfaultEventStorage, Test_MemfaultMultiEventLimited) { // queue up two events const uint8_t event1[] = { 0x1, 0x2 }; const uint8_t event2[] = { 0x3 }; const uint8_t event3[] = { 0x4, 0x5 }; bool rollback = false; prv_write_payload(&event1, sizeof(event1), rollback); prv_write_payload(&event2, sizeof(event2), rollback); prv_write_payload(&event3, sizeof(event3), rollback); bool has_event; size_t event_size; has_event = prv_fake_event_impl_has_event(&event_size); CHECK(has_event); LONGS_EQUAL(5, event_size); const uint8_t expected_data[] = { 0xAB, 0x02, 0x1, 0x2, 0x3 }; uint8_t actual_data[sizeof(expected_data)]; bool success = prv_fake_event_impl_read(0, actual_data, sizeof(actual_data)); CHECK(success); MEMCMP_EQUAL(expected_data, actual_data, sizeof(actual_data)); memset(actual_data, 0x0, sizeof(actual_data)); // read 1 byte of header success = prv_fake_event_impl_read(0, &actual_data[0], 1); CHECK(success); // 1 byte header + 1 byte event success = prv_fake_event_impl_read(1, &actual_data[1], 2); CHECK(success); // rest of data - 1 byte event1 + 1 byte event2 success = prv_fake_event_impl_read(3, &actual_data[3], 2); CHECK(success); MEMCMP_EQUAL(expected_data, actual_data, sizeof(actual_data)); prv_fake_event_impl_mark_event_read(); // final event has_event = prv_fake_event_impl_has_event(&event_size); CHECK(has_event); LONGS_EQUAL(2, event_size); success = prv_fake_event_impl_read(0, actual_data, event_size); CHECK(success); MEMCMP_EQUAL(event3, actual_data, sizeof(event3)); prv_fake_event_impl_mark_event_read(); has_event = prv_fake_event_impl_has_event(&event_size); CHECK(!has_event); LONGS_EQUAL(0, event_size); } #endif /* MEMFAULT_EVENT_STORAGE_MAX_READ_BATCH_LEN */ // // We use a compilation flag and run the test suite twice so we can test the default stub // persistent source implementation as well as a real one // #else /* !MEMFAULT_TEST_PERSISTENT_EVENT_STORAGE_DISABLE */ typedef struct { const uint8_t *data; size_t len; } sFakePersistedEvent; const uint8_t test_event0[] = { 0xa7, 0x02, 0x01, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', }; const uint8_t test_event1[] = { 0xa6, 0x02, 0x01, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', }; typedef struct { size_t num_events; size_t events_read; } sFakePersistentEventStorageState; static sFakePersistentEventStorageState s_persistent_event_storage_state = { 0 }; static const sFakePersistedEvent s_fake_persisted_events[] = { { .data = &test_event0[0], .len = sizeof(test_event0) }, { .data = &test_event1[0], .len = sizeof(test_event1) }, }; static bool prv_platform_nv_event_storage_read_has_event(size_t *event_size) { CHECK(s_persistent_event_storage_state.num_events <= MEMFAULT_ARRAY_SIZE(s_fake_persisted_events)); // all events have been read! if (s_persistent_event_storage_state.num_events == s_persistent_event_storage_state.events_read) { *event_size = 0; return false; } *event_size = s_fake_persisted_events[s_persistent_event_storage_state.events_read].len; return true; } static bool prv_platform_nv_event_storage_read(uint32_t offset, void *buf, size_t buf_len) { CHECK(s_persistent_event_storage_state.num_events != 0); CHECK(s_persistent_event_storage_state.num_events <= MEMFAULT_ARRAY_SIZE(s_fake_persisted_events)); const sFakePersistedEvent *event = &s_fake_persisted_events[s_persistent_event_storage_state.events_read]; CHECK(offset + buf_len <= event->len); memcpy(buf, &event->data[offset], buf_len); return true; } static void prv_platform_nv_event_storage_consume(void) { s_persistent_event_storage_state.events_read++; } static int s_expected_write_payload_len = 1; static bool prv_platform_nv_event_storage_write(MemfaultEventReadCallback event_read_cb, size_t total_size) { const bool success = mock().actualCall(__func__).returnBoolValueOrDefault(true); if (!success) { return success; } LONGS_EQUAL(s_expected_write_payload_len, total_size); uint8_t expected_data[total_size]; for (size_t i = 0; i < total_size; i++) { expected_data[i] = (uint8_t)i; } uint8_t actual_data[total_size]; for (size_t i = 0; i < total_size; i++) { event_read_cb(i, &actual_data[i], 1); } MEMCMP_EQUAL(expected_data, actual_data, sizeof(actual_data)); s_expected_write_payload_len++; return true; } void memfault_event_storage_request_persist_callback( const sMemfaultEventStoragePersistCbStatus *status) { const bool async_persist = mock() .actualCall(__func__) .withParameter("volatile_storage_bytes_used", status->volatile_storage.bytes_used) .withParameter("volatile_storage_bytes_free", status->volatile_storage.bytes_free) .returnBoolValueOrDefault(true); if (async_persist) { return; } const int events_persisted = memfault_event_storage_persist(); LONGS_EQUAL(1, events_persisted); } static void prv_write_and_expect_persist_callback(bool async_persist, const void *data, size_t data_len, bool rollback) { mock() .expectOneCall("memfault_event_storage_request_persist_callback") .ignoreOtherParameters() .andReturnValue(async_persist); if (!async_persist) { s_expected_write_payload_len = data_len; mock().expectOneCall("prv_platform_nv_event_storage_write"); } prv_write_payload(data, data_len, rollback); mock().checkExpectations(); } static bool s_nv_storage_enabled = true; static bool prv_platform_nv_event_storage_enabled(void) { return s_nv_storage_enabled; } const sMemfaultNonVolatileEventStorageImpl g_memfault_platform_nv_event_storage_impl = { .enabled = prv_platform_nv_event_storage_enabled, .has_event = prv_platform_nv_event_storage_read_has_event, .read = prv_platform_nv_event_storage_read, .consume = prv_platform_nv_event_storage_consume, .write = prv_platform_nv_event_storage_write, }; TEST(MemfaultEventStorage, Test_ReadPersistedEvents) { const size_t num_persisted_events = MEMFAULT_ARRAY_SIZE(s_fake_persisted_events); s_persistent_event_storage_state = (sFakePersistentEventStorageState){ .num_events = num_persisted_events, .events_read = 0, }; bool rollback = false; bool async = true; uint8_t byte = 0; for (size_t i = 0; i < num_persisted_events; i++) { if (i == 1) { prv_write_and_expect_persist_callback(async, &byte, sizeof(byte), rollback); } if (i >= 1) { const size_t bytes_used = sizeof(byte) + MEMFAULT_STORAGE_OVERHEAD; mock() .expectOneCall("memfault_event_storage_request_persist_callback") .withParameter("volatile_storage_bytes_used", bytes_used) .withParameter("volatile_storage_bytes_free", s_ram_store_size - bytes_used); } const sFakePersistedEvent *event = &s_fake_persisted_events[i]; prv_assert_read((void *)event->data, event->len); mock().checkExpectations(); } // since the ram events have not been flushed yet prv_assert_no_more_events(); } TEST(MemfaultEventStorage, Test_PersistStorageFatalError) { const size_t num_persisted_events = MEMFAULT_ARRAY_SIZE(s_fake_persisted_events); s_persistent_event_storage_state = (sFakePersistentEventStorageState){ .num_events = num_persisted_events, .events_read = 0, }; // queue up one event in ram const bool rollback = false; const bool async = true; uint8_t two_bytes[] = { 0x0, 0x1 }; prv_write_and_expect_persist_callback(async, &two_bytes, sizeof(two_bytes), rollback); const size_t bytes_used = sizeof(two_bytes) + MEMFAULT_STORAGE_OVERHEAD; mock() .expectOneCall("memfault_event_storage_request_persist_callback") .withParameter("volatile_storage_bytes_used", bytes_used) .withParameter("volatile_storage_bytes_free", s_ram_store_size - bytes_used); const sFakePersistedEvent *event = &s_fake_persisted_events[0]; prv_assert_read((void *)event->data, event->len); mock().checkExpectations(); s_nv_storage_enabled = false; // should be a no-op if storage is disabled const int events_persisted = memfault_event_storage_persist(); LONGS_EQUAL(0, events_persisted); // read should fail since nv storage went from enabled -> disabled uint8_t c; const bool success = prv_fake_event_impl_read(0, &c, sizeof(c)); CHECK(!success); prv_assert_read((void *)two_bytes, sizeof(two_bytes)); s_nv_storage_enabled = true; } TEST(MemfaultEventStorage, Test_PersistEvents) { int events_persisted = memfault_event_storage_persist(); LONGS_EQUAL(0, events_persisted); uint8_t byte = 0; uint8_t two_bytes[] = { 0x0, 0x1 }; bool rollback = false; bool async = true; prv_write_and_expect_persist_callback(!async, &byte, sizeof(byte), rollback); s_expected_write_payload_len = 1; prv_write_and_expect_persist_callback(async, &byte, sizeof(byte), rollback); prv_write_and_expect_persist_callback(async, &two_bytes, sizeof(two_bytes), rollback); mock().expectNCalls(2, "prv_platform_nv_event_storage_write"); events_persisted = memfault_event_storage_persist(); LONGS_EQUAL(2, events_persisted); mock().checkExpectations(); s_expected_write_payload_len = 1; prv_write_and_expect_persist_callback(async, &byte, sizeof(byte), rollback); prv_write_and_expect_persist_callback(async, &two_bytes, sizeof(two_bytes), rollback); mock().expectOneCall("prv_platform_nv_event_storage_write"); mock().expectOneCall("prv_platform_nv_event_storage_write").andReturnValue(false); events_persisted = memfault_event_storage_persist(); LONGS_EQUAL(1, events_persisted); mock().checkExpectations(); // should see one event get flushed mock().expectOneCall("prv_platform_nv_event_storage_write"); events_persisted = memfault_event_storage_persist(); LONGS_EQUAL(1, events_persisted); mock().checkExpectations(); } TEST(MemfaultEventStorage, Test_UsedFreeSizes) { const size_t per_event_overhead = 2; const size_t first_half = s_ram_store_size / 2; // Integer truncation is fine const size_t second_half = s_ram_store_size - first_half; const bool async = true; const bool no_rollback = false; // We'll write the same buffer data twice. uint8_t my_buffer[sizeof s_ram_store / 2 + 1] = { 0 }; // 1. Initially empty size_t bytes_used = memfault_event_storage_bytes_used(); size_t bytes_free = memfault_event_storage_bytes_free(); LONGS_EQUAL(bytes_used, 0); LONGS_EQUAL(bytes_free, s_ram_store_size); // NOTE: we write with knowledge of the event overhead but check // bytes used based on total bytes written to event storage. // 2. Partially full prv_write_and_expect_persist_callback(async, my_buffer, first_half - per_event_overhead, no_rollback); bytes_used = memfault_event_storage_bytes_used(); bytes_free = memfault_event_storage_bytes_free(); LONGS_EQUAL(bytes_used, first_half); LONGS_EQUAL(bytes_free, second_half); // 3. Full prv_write_and_expect_persist_callback(async, my_buffer, second_half - per_event_overhead, no_rollback); bytes_used = memfault_event_storage_bytes_used(); bytes_free = memfault_event_storage_bytes_free(); LONGS_EQUAL(bytes_used, s_ram_store_size); LONGS_EQUAL(bytes_free, 0); } #endif /* !MEMFAULT_TEST_PERSISTENT_EVENT_STORAGE_DISABLE */ TEST(MemfaultEventStorage, Test_EventStorageBoot) { uint8_t storage[11] = { 0 }; const size_t storage_size = sizeof(storage); const sMemfaultEventStorageImpl *storage_impl = NULL; memfault_event_storage_reset(); CHECK_FALSE(memfault_event_storage_booted()); storage_impl = memfault_events_storage_boot(storage, storage_size); CHECK(storage_impl != NULL); CHECK(memfault_event_storage_booted()); } #if MEMFAULT_EVENT_STORAGE_RESTORE_STATE static sMfltEventStorageSaveState s_memfault_event_restore_state = { 0 }; static bool s_memfault_event_restore_state_retval = false; bool memfault_event_storage_restore_state(sMfltEventStorageSaveState *state) { *state = s_memfault_event_restore_state; return s_memfault_event_restore_state_retval; } TEST(MemfaultEventStorage, Test_SaveAndRestore) { size_t space_available = s_storage_impl->begin_write_cb(); LONGS_EQUAL(s_ram_store_size - MEMFAULT_STORAGE_OVERHEAD, space_available); const uint8_t payload[] = { 0x1, 0x2, 0x3, 0x4 }; s_storage_impl->append_data_cb(&payload, sizeof(payload)); // complete the transaction const bool rollback = false; s_storage_impl->finish_write_cb(rollback); // should be a no-op s_storage_impl->finish_write_cb(rollback); // Save the current state sMfltEventStorageSaveState state = memfault_event_storage_get_state(); void *context_storage = malloc(state.context_len); void *storage_storage = malloc(state.storage_len); memcpy(context_storage, state.context, state.context_len); memcpy(storage_storage, state.storage, state.storage_len); state.context = context_storage; state.storage = storage_storage; // Reset the event storage memset(s_ram_store, 0, s_ram_store_size); // this also wipes out the internal state memfault_event_storage_reset(); // First test booting without restoring the state s_memfault_event_restore_state_retval = false; memfault_events_storage_boot(s_ram_store, s_ram_store_size); // Should be empty space_available = s_storage_impl->begin_write_cb(); LONGS_EQUAL(s_ram_store_size - MEMFAULT_STORAGE_OVERHEAD, space_available); // Read should fail since there are no events uint8_t c; bool success = prv_fake_event_impl_read(0, &c, sizeof(c)); CHECK(!success); // Reset again memset(s_ram_store, 0, s_ram_store_size); memfault_event_storage_reset(); // Now test with a bad size. Should result in a blank state s_memfault_event_restore_state = state; s_memfault_event_restore_state.context_len = 0; s_memfault_event_restore_state_retval = true; memfault_events_storage_boot(s_ram_store, s_ram_store_size); s_memfault_event_restore_state_retval = false; // Should be empty space_available = s_storage_impl->begin_write_cb(); LONGS_EQUAL(s_ram_store_size - MEMFAULT_STORAGE_OVERHEAD, space_available); // Read should fail since there are no events success = prv_fake_event_impl_read(0, &c, sizeof(c)); CHECK(!success); // This time we should restore the state s_memfault_event_restore_state = state; s_memfault_event_restore_state_retval = true; memfault_events_storage_boot(s_ram_store, s_ram_store_size); s_memfault_event_restore_state_retval = false; free(context_storage); free(storage_storage); // Check that the event storage is available LONGS_EQUAL(s_ram_store_size, s_storage_impl->get_storage_size_cb()); // Read the event back // We should have the same payload as before prv_assert_read((void *)&payload, sizeof(payload)); // should be a no-op prv_fake_event_impl_mark_event_read(); prv_assert_no_more_events(); } #endif // MEMFAULT_EVENT_STORAGE_RESTORE_STATE ================================================ FILE: tests/unit/src/test_memfault_heap_stats.cpp ================================================ //! @file //! //! @brief #include #include #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/core/heap_stats.h" #include "memfault/core/heap_stats_impl.h" #include "memfault/core/math.h" TEST_GROUP(MemfaultHeapStats) { void setup() { fake_memfault_metrics_platform_locking_reboot(); mock().disable(); } void teardown() { CHECK(fake_memfault_platform_metrics_lock_calls_balanced()); memfault_heap_stats_reset(); mock().checkExpectations(); mock().clear(); } }; static bool prv_heap_stat_equality(const sMfltHeapStatEntry *expected, const sMfltHeapStatEntry *actual) { bool match = (expected->lr == actual->lr) && // (expected->ptr == actual->ptr) && // (expected->info.size == actual->info.size) && // (expected->info.in_use == actual->info.in_use); if (!match) { fprintf(stderr, "sMfltHeapStatEntry:\n" " lr: %p : %p\n" " ptr: %p : %p\n" " size: %u : %u\n" " in_use: %u : %u\n", expected->lr, actual->lr, expected->ptr, actual->ptr, (unsigned int)expected->info.size, (unsigned int)actual->info.size, (unsigned int)expected->info.in_use, (unsigned int)actual->info.in_use); } return match; } TEST(MemfaultHeapStats, Test_Basic) { void *lr; MEMFAULT_GET_LR(lr); const sMfltHeapStatEntry expected_heap_stats[] = { { .lr = lr, .ptr = (void *)0x12345679, .info = { .size = 1234, .in_use = 1, }, }, }; bool empty = memfault_heap_stats_empty(); CHECK(empty); // Check initial list head points to empty LONGS_EQUAL(MEMFAULT_HEAP_STATS_LIST_END, g_memfault_heap_stats.stats_pool_head); MEMFAULT_HEAP_STATS_MALLOC(expected_heap_stats[0].ptr, expected_heap_stats[0].info.size); empty = memfault_heap_stats_empty(); CHECK(!empty); bool match = prv_heap_stat_equality(&expected_heap_stats[0], &g_memfault_heap_stats_pool[0]); CHECK(match); } TEST(MemfaultHeapStats, Test_Free) { void *lr; MEMFAULT_GET_LR(lr); const sMfltHeapStatEntry expected_heap_stats[MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool)] = { { .lr = lr, .ptr = (void *)0x12345679, .info = { .size = 1234, .in_use = 1, }, }, { .lr = lr, .ptr = (void *)0x1234567A, .info = { .size = 12345, .in_use = 0, }, }, }; MEMFAULT_HEAP_STATS_MALLOC(expected_heap_stats[0].ptr, expected_heap_stats[0].info.size); LONGS_EQUAL(1, g_memfault_heap_stats.in_use_block_count); LONGS_EQUAL(1, g_memfault_heap_stats.max_in_use_block_count); MEMFAULT_HEAP_STATS_MALLOC(expected_heap_stats[1].ptr, expected_heap_stats[1].info.size); LONGS_EQUAL(2, g_memfault_heap_stats.in_use_block_count); LONGS_EQUAL(2, g_memfault_heap_stats.max_in_use_block_count); MEMFAULT_HEAP_STATS_FREE(expected_heap_stats[1].ptr); LONGS_EQUAL(1, g_memfault_heap_stats.in_use_block_count); LONGS_EQUAL(2, g_memfault_heap_stats.max_in_use_block_count); // test freeing a null pointer MEMFAULT_HEAP_STATS_FREE(NULL); LONGS_EQUAL(1, g_memfault_heap_stats.in_use_block_count); LONGS_EQUAL(2, g_memfault_heap_stats.max_in_use_block_count); // test freeing something that doesn't exist in the tracked list MEMFAULT_HEAP_STATS_FREE((void *)0xabc); // since we don't know that the untracked but non-null pointer being freed // above is invalid, we still decrement the in use count LONGS_EQUAL(0, g_memfault_heap_stats.in_use_block_count); LONGS_EQUAL(2, g_memfault_heap_stats.max_in_use_block_count); // test malloc stats with failed malloc (NULL pointer) MEMFAULT_HEAP_STATS_MALLOC(NULL, 123456); LONGS_EQUAL(0, g_memfault_heap_stats.in_use_block_count); LONGS_EQUAL(2, g_memfault_heap_stats.max_in_use_block_count); // work over the list, confirming that everything matches expected size_t list_count = 0; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { sMfltHeapStatEntry *pthis = &g_memfault_heap_stats_pool[i]; if (pthis->info.size != 0) { list_count++; bool match = prv_heap_stat_equality(&expected_heap_stats[i], pthis); CHECK(match); } } LONGS_EQUAL(2, list_count); // Test correct state after freeing last entry MEMFAULT_HEAP_STATS_FREE(expected_heap_stats[0].ptr); LONGS_EQUAL(MEMFAULT_HEAP_STATS_LIST_END, g_memfault_heap_stats.stats_pool_head); } TEST(MemfaultHeapStats, Test_MaxEntriesRollover) { void *lr; MEMFAULT_GET_LR(lr); const sMfltHeapStatEntry expected_heap_stats[MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool)] = { { .lr = lr, .ptr = (void *)0x12345679, .info = { .size = 1234, .in_use = 1, }, }, { .lr = lr, // this entry should not appear when checking at the end of this test, // so set the data to something exceptional .ptr = (void *)0xabcdef, .info = { .size = 123456, .in_use = 0, }, }, }; // allocate one entry, and then free it MEMFAULT_HEAP_STATS_MALLOC(expected_heap_stats[1].ptr, expected_heap_stats[1].info.size); MEMFAULT_HEAP_STATS_FREE(expected_heap_stats[1].ptr); LONGS_EQUAL(0, g_memfault_heap_stats.in_use_block_count); LONGS_EQUAL(1, g_memfault_heap_stats.max_in_use_block_count); // now allocate enough entries to fill the stats completely for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { MEMFAULT_HEAP_STATS_MALLOC((void *)((uintptr_t)expected_heap_stats[0].ptr + i), expected_heap_stats[0].info.size + i); } LONGS_EQUAL(MEMFAULT_HEAP_STATS_MAX_COUNT, g_memfault_heap_stats.in_use_block_count); LONGS_EQUAL(MEMFAULT_HEAP_STATS_MAX_COUNT, g_memfault_heap_stats.max_in_use_block_count); // work over the list, FIFO, confirming that everything matches expected size_t list_count = 0; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { sMfltHeapStatEntry *pthis = &g_memfault_heap_stats_pool[i]; size_t offset; if (i == 0) { // the first entry is the most recently added, since we wrapped around // ex: // [ 9, 0, 1, 2, 3, 4, 5, 6, 7, 8 ] offset = MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool) - 1; } else { // the start of the allocations is at the second position in the array offset = i - 1; } sMfltHeapStatEntry expected = { .lr = lr, .ptr = (void *)(0x12345679 + offset), .info = { .size = 1234 + (uint32_t)offset, .in_use = 1, }, }; if (pthis->info.size != 0) { list_count++; bool match = prv_heap_stat_equality(&expected, pthis); CHECK(match); } } LONGS_EQUAL(MEMFAULT_HEAP_STATS_MAX_COUNT, list_count); } //! Verify that an allocation that reuses a previously freed address is properly //! cleared from the stats list TEST(MemfaultHeapStats, Test_AddressReuse) { void *lr; MEMFAULT_GET_LR(lr); const sMfltHeapStatEntry expected_heap_stats[] = { { .lr = lr, .ptr = (void *)0x12345679, .info = { .size = 1234, .in_use = 0, }, }, { .lr = lr, .ptr = (void *)0x12345679, .info = { .size = 1234, .in_use = 0, }, }, }; bool empty = memfault_heap_stats_empty(); CHECK(empty); MEMFAULT_HEAP_STATS_MALLOC(expected_heap_stats[0].ptr, expected_heap_stats[0].info.size); MEMFAULT_HEAP_STATS_FREE(expected_heap_stats[0].ptr); MEMFAULT_HEAP_STATS_MALLOC(expected_heap_stats[1].ptr, expected_heap_stats[1].info.size); MEMFAULT_HEAP_STATS_FREE(expected_heap_stats[1].ptr); empty = memfault_heap_stats_empty(); CHECK(!empty); size_t list_count = 0; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { sMfltHeapStatEntry *pthis = &g_memfault_heap_stats_pool[i]; if (pthis->info.size != 0) { list_count++; bool match = prv_heap_stat_equality(&expected_heap_stats[i], pthis); CHECK(match); } } // 2 entries will be populated (though in_use==0 for each), since "never-used" // entries are always used before "unused" (freed) entries LONGS_EQUAL(2, list_count); } //! Verifies logic for reusing heap stat entries. //! //! Heap stats should first look for free entries, then overwrite the oldest entry //! if none are free TEST(MemfaultHeapStats, Test_Reuse) { void *lr; MEMFAULT_GET_LR(lr); const sMfltHeapStatEntry expected_heap_stats = { .lr = lr, .ptr = (void *)0x12345679, .info = { .size = 1234, .in_use = 1, }, }; const sMfltHeapStatEntry middle_entry = { .lr = lr, .ptr = (void *)0x87654321, .info = { .size = 4321, .in_use = 1, }, }; const sMfltHeapStatEntry final_entry = { .lr = lr, .ptr = (void *)0x111111111, .info = { .size = 1111, .in_use = 1, }, }; // Offset to unique entry in middle of heap stats size_t middle_offset = MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool) >> 1; // Fill up the heap stats pool for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { MEMFAULT_HEAP_STATS_MALLOC((void *)((uintptr_t)expected_heap_stats.ptr + i), expected_heap_stats.info.size + i); } // Free entry in middle of array, then malloc again MEMFAULT_HEAP_STATS_FREE((void *)((uintptr_t)expected_heap_stats.ptr + middle_offset)); MEMFAULT_HEAP_STATS_MALLOC(middle_entry.ptr, middle_entry.info.size); // Check latest entry is list head LONGS_EQUAL(middle_offset, g_memfault_heap_stats.stats_pool_head); prv_heap_stat_equality(&middle_entry, &g_memfault_heap_stats_pool[g_memfault_heap_stats.stats_pool_head]); // Check next recent is end of heap stats pool sMfltHeapStatEntry *entry = &g_memfault_heap_stats_pool[g_memfault_heap_stats.stats_pool_head]; prv_heap_stat_equality(&g_memfault_heap_stats_pool[MEMFAULT_HEAP_STATS_MAX_COUNT - 1], &g_memfault_heap_stats_pool[entry->info.next_entry_index]); // Allocate another, should overwrite first entry (current oldest) MEMFAULT_HEAP_STATS_MALLOC(final_entry.ptr, final_entry.info.size); LONGS_EQUAL(0, g_memfault_heap_stats.stats_pool_head); prv_heap_stat_equality(g_memfault_heap_stats_pool, &g_memfault_heap_stats_pool[g_memfault_heap_stats.stats_pool_head]); // walk the other entries and confirm correct values size_t list_count = 0; for (size_t i = 1; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { if (i == middle_offset) { continue; } sMfltHeapStatEntry *pthis = &g_memfault_heap_stats_pool[i]; sMfltHeapStatEntry expected = { .lr = lr, .ptr = (void *)(0x12345679 + i), .info = { .size = 1234 + (uint32_t)i, .in_use = 1, }, }; if (pthis->info.size != 0) { list_count++; bool match = prv_heap_stat_equality(&expected, pthis); if (!match) { fprintf(stderr, "Mismatch at index %zu\n", i); } CHECK(match); } } // We skipped two entries from the pool LONGS_EQUAL(MEMFAULT_HEAP_STATS_MAX_COUNT - 2, list_count); } //! Tests handling freeing the most recent allocation (list head) TEST(MemfaultHeapStats, Test_FreeMostRecent) { void *lr; MEMFAULT_GET_LR(lr); const sMfltHeapStatEntry expected_heap_stats = { .lr = lr, .ptr = (void *)0x12345679, .info = { .size = 1234, .in_use = 1, }, }; size_t end_offset = MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool) - 1; // Fill up the heap stats pool for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { MEMFAULT_HEAP_STATS_MALLOC((void *)((uintptr_t)expected_heap_stats.ptr + i), expected_heap_stats.info.size + i); } // Remove most recent entry (last in pool) MEMFAULT_HEAP_STATS_FREE((void *)((uintptr_t)expected_heap_stats.ptr + end_offset)); // Check that most recent entry is second to last, check entry contents LONGS_EQUAL(end_offset - 1, g_memfault_heap_stats.stats_pool_head); prv_heap_stat_equality(&expected_heap_stats, &g_memfault_heap_stats_pool[g_memfault_heap_stats.stats_pool_head]); } //! Tests that "never-used" entries are used before "unused" (freed) entries TEST(MemfaultHeapStats, Test_NeverUsedVsUnused) { void *lr; MEMFAULT_GET_LR(lr); const sMfltHeapStatEntry expected_heap_stats = { .lr = lr, .ptr = (void *)0x12345678, .info = { .size = 1234, .in_use = 1, }, }; // reference pool. fill this up with matching entries as the heap stats pool // during the allocation calls below. this is breaking the abstraction a bit, // but we need to check what the heap stats pool should look like at the end. sMfltHeapStatEntry reference_pool[MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool)]; // Fill up the heap stats pool, but leave the last entry unused for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool) - 1; i++) { MEMFAULT_HEAP_STATS_MALLOC((void *)((uintptr_t)expected_heap_stats.ptr + i), expected_heap_stats.info.size + i); reference_pool[i] = g_memfault_heap_stats_pool[i]; } // Remove the first allocation MEMFAULT_HEAP_STATS_FREE(expected_heap_stats.ptr); // Unfortunately this peeks into the implementation. The data in // 'g_memfault_heap_stats_pool' is actually part of its public contract, since // we parse it in the backend, so we need to keep it consistent. reference_pool[0].info.in_use = 0; // Allocate again, should use the never-used entry size_t end_offset = MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool) - 1; MEMFAULT_HEAP_STATS_MALLOC((void *)((uintptr_t)expected_heap_stats.ptr + end_offset), expected_heap_stats.info.size + end_offset); reference_pool[end_offset] = g_memfault_heap_stats_pool[end_offset]; // walk the allocated entries and confirm correct values for (size_t i = 1; i < MEMFAULT_ARRAY_SIZE(g_memfault_heap_stats_pool); i++) { bool match = prv_heap_stat_equality(&reference_pool[i], &g_memfault_heap_stats_pool[i]); if (!match) { fprintf(stderr, "Mismatch at index %zu\n", i); } CHECK(match); } // Now allocate again, and confirm the "unused" entry is used now. MEMFAULT_HEAP_STATS_MALLOC((void *)((uintptr_t)expected_heap_stats.ptr + end_offset + 1), expected_heap_stats.info.size + end_offset + 1); const sMfltHeapStatEntry expected = { .lr = lr, .ptr = (void *)(0x12345678 + end_offset + 1), .info = { .size = 1234 + (uint32_t)end_offset + 1, .in_use = 1, }, }; CHECK(prv_heap_stat_equality(&expected, &g_memfault_heap_stats_pool[0])); } static void prv_check_list_end(void) { bool list_end_found = false; if (g_memfault_heap_stats.stats_pool_head == MEMFAULT_HEAP_STATS_LIST_END) { printf("Found end at head\n"); list_end_found = true; } else { uint16_t current_index = g_memfault_heap_stats.stats_pool_head; for (size_t i = 0; i < MEMFAULT_HEAP_STATS_MAX_COUNT; i++) { uint16_t next_index = g_memfault_heap_stats_pool[current_index].info.next_entry_index; if (next_index == MEMFAULT_HEAP_STATS_LIST_END) { printf("Found end at %u\n", current_index); list_end_found = true; break; } current_index = next_index; } } CHECK(list_end_found); } static void prv_print_list(void) { uint16_t current_index = g_memfault_heap_stats.stats_pool_head; printf("List: "); for (size_t i = 0; i < MEMFAULT_HEAP_STATS_MAX_COUNT; i++) { printf("%hu", current_index); if (current_index == MEMFAULT_HEAP_STATS_LIST_END) { break; } printf("[%s] ", g_memfault_heap_stats_pool[current_index].info.in_use ? "used" : "free"); current_index = g_memfault_heap_stats_pool[current_index].info.next_entry_index; } printf("\n"); } static void prv_print_heap_stats(void) { printf("Heap Stats: "); for (size_t i = 0; i < MEMFAULT_HEAP_STATS_MAX_COUNT; i++) { sMfltHeapStatEntry *entry = &g_memfault_heap_stats_pool[i]; printf("Entry[%lu]{lr[%p], ptr[%p], size[%d], used:[%d], next:[%hu]} ", i, entry->lr, entry->ptr, entry->info.size, entry->info.in_use, entry->info.next_entry_index); } printf("\n"); } static void prv_check_for_end(void) { bool result = false; uint16_t current_index = g_memfault_heap_stats.stats_pool_head; for (size_t i = 0; i < MEMFAULT_HEAP_STATS_MAX_COUNT; i++) { if (current_index == MEMFAULT_HEAP_STATS_LIST_END) { result = true; break; } current_index = g_memfault_heap_stats_pool[current_index].info.next_entry_index; } if (current_index == MEMFAULT_HEAP_STATS_LIST_END) { result = true; } CHECK(result); } static void prv_run_list_checks(void) { prv_print_list(); prv_print_heap_stats(); prv_check_list_end(); prv_check_for_end(); } TEST(MemfaultHeapStats, Test_NoCycle) { // Setup the random generator std::random_device rd; std::mt19937 gen(rd()); // Create a distribution of addresses between 1 (0 would be NULL which is invalid) and the max // number of entries + 1. This gives a range of MEMFAULT_HEAP_STATS_MAX_COUNT std::uniform_int_distribution heap_stats_entry_address_generator( 1, MEMFAULT_HEAP_STATS_MAX_COUNT + 1); std::uniform_int_distribution<> malloc_operation_generator(0, 1); // Test pathological case where entries are freed in reverse order (newest first) for (uintptr_t i = 0; i < MEMFAULT_HEAP_STATS_MAX_COUNT; i++) { MEMFAULT_HEAP_STATS_MALLOC((void *)(i + 1), i + 2); } prv_run_list_checks(); // Free every entry at the head of the list until empty for (uintptr_t i = 0; i < MEMFAULT_HEAP_STATS_MAX_COUNT; i++) { MEMFAULT_HEAP_STATS_FREE((void *)(MEMFAULT_HEAP_STATS_MAX_COUNT - i)); } prv_run_list_checks(); CHECK(g_memfault_heap_stats.stats_pool_head == MEMFAULT_HEAP_STATS_LIST_END); // Fill entries again, and then randomly malloc and free many times for (uintptr_t i = 0; i < MEMFAULT_HEAP_STATS_MAX_COUNT * 10; i++) { MEMFAULT_HEAP_STATS_MALLOC((void *)(i + 1 + MEMFAULT_HEAP_STATS_MAX_COUNT), i + 2); } prv_run_list_checks(); for (size_t i = 0; i < 10 * MEMFAULT_HEAP_STATS_MAX_COUNT; i++) { unsigned long entry_addr = heap_stats_entry_address_generator(gen); if (malloc_operation_generator(gen)) { MEMFAULT_HEAP_STATS_MALLOC((void *)entry_addr, entry_addr + 2); } else { MEMFAULT_HEAP_STATS_FREE((void *)entry_addr); } prv_run_list_checks(); } } ================================================ FILE: tests/unit/src/test_memfault_heartbeat_metrics.cpp ================================================ //! @file //! //! @brief #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/core/compiler.h" #include "memfault/core/event_storage.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_tracking.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/overrides.h" #include "memfault/metrics/platform/timer.h" #include "memfault/metrics/reliability.h" #include "memfault/metrics/serializer.h" #include "memfault/metrics/utils.h" extern "C" { static void (*s_serializer_check_cb)(void) = NULL; static uint64_t s_fake_time_ms = 0; uint64_t memfault_platform_get_time_since_boot_ms(void) { return s_fake_time_ms; } static void prv_fake_time_set(uint64_t new_fake_time_ms) { s_fake_time_ms = new_fake_time_ms; } static void prv_fake_time_incr(uint64_t fake_time_delta_ms) { s_fake_time_ms += fake_time_delta_ms; } static const sMemfaultEventStorageImpl *s_fake_event_storage_impl; static void *s_metrics_restore_state; bool memfault_metrics_restore_state(void *state) { if (s_metrics_restore_state) { memcpy(state, s_metrics_restore_state, MEMFAULT_METRICS_CONTEXT_SIZE_BYTES); } return s_metrics_restore_state != NULL; } } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MEMFAULT_UNUSED MemfaultPlatformTimerCallback callback) { return mock() .actualCall(__func__) .withParameter("period_sec", period_sec) .returnBoolValueOrDefault(true); } #define FAKE_STORAGE_SIZE 100 bool memfault_metrics_heartbeat_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl) { mock().actualCall(__func__); if (s_serializer_check_cb != NULL) { s_serializer_check_cb(); } return true; } bool memfault_metrics_session_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl, MEMFAULT_UNUSED eMfltMetricsSessionIndex session_index) { return mock().actualCall(__func__).returnBoolValueOrDefault(true); } size_t memfault_metrics_heartbeat_compute_worst_case_storage_size(void) { return (size_t)mock().actualCall(__func__).returnIntValueOrDefault(FAKE_STORAGE_SIZE); } // clang-format off TEST_GROUP(MemfaultHeartbeatMetrics){ void setup(){ s_fake_time_ms = 0; s_serializer_check_cb = NULL; fake_memfault_metrics_platform_locking_reboot(); static uint8_t s_storage[FAKE_STORAGE_SIZE]; mock().strictOrder(); mock().expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size"); mock().expectOneCall("memfault_metrics_reliability_boot").withParameter("ctx", (sMemfaultMetricsReliabilityCtx*)NULL); // Check that by default the heartbeat interval is once / hour mock().expectOneCall("memfault_platform_metrics_timer_boot").withParameter("period_sec", 3600); s_fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage)); s_metrics_restore_state = NULL; sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = 7 }; int rv = memfault_metrics_boot(s_fake_event_storage_impl, &boot_info); LONGS_EQUAL(0, rv); mock().checkExpectations(); // crash count should have beem copied into heartbeat uint32_t logged_crash_count = 0; memfault_metrics_heartbeat_read_unsigned( MEMFAULT_METRICS_KEY(MemfaultSdkMetric_UnexpectedRebootCount), &logged_crash_count); LONGS_EQUAL(boot_info.unexpected_reboot_count, logged_crash_count); // IntervalMs & RebootCount & operational_hours * operational_crashfree_hours const size_t num_memfault_sdk_metrics = 4; // One heartbeat metric defined using _WITH_SCALE_VALUE const size_t num_scale_value_metrics = 1; // 2 log metrics const size_t num_log_metrics = 2; // 1 uptime metric const size_t num_uptime_metrics = 1; // We should test all the types of available metrics so if this // fails it means there's a new type we aren't yet covering LONGS_EQUAL(kMemfaultMetricType_NumTypes + num_memfault_sdk_metrics + num_scale_value_metrics + num_log_metrics + num_uptime_metrics, memfault_metrics_heartbeat_get_num_metrics()); } void teardown() { // dump the final result & also sanity test that this routine works memfault_metrics_heartbeat_debug_print(); CHECK(fake_memfault_platform_metrics_lock_calls_balanced()); // we are simulating a reboot, so the timer should be running. // Let's stop it and confirm that works and reset our state so the next // boot call will succeed. MemfaultMetricId id = MEMFAULT_METRICS_KEY(MemfaultSdkMetric_IntervalMs); int rv = memfault_metrics_heartbeat_timer_stop(id); LONGS_EQUAL(0, rv); mock().checkExpectations(); mock().clear(); } }; // clang-format on TEST(MemfaultHeartbeatMetrics, Test_BootStorageTooSmall) { // reboot metrics with storage that is too small to actually hold an event // this should result in a warning being emitted mock() .expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size") .andReturnValue(FAKE_STORAGE_SIZE + 1); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = 1 }; int rv = memfault_metrics_boot(s_fake_event_storage_impl, &boot_info); LONGS_EQUAL(-5, rv); mock().checkExpectations(); } TEST(MemfaultHeartbeatMetrics, Test_TimerInitFailed) { // Nothing else should happen if timer initialization failed for some reason mock().expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size"); mock() .expectOneCall("memfault_metrics_reliability_boot") .withParameter("ctx", (sMemfaultMetricsReliabilityCtx *)NULL); mock() .expectOneCall("memfault_platform_metrics_timer_boot") .withParameter("period_sec", 3600) .andReturnValue(false); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = 1 }; int rv = memfault_metrics_boot(s_fake_event_storage_impl, &boot_info); LONGS_EQUAL(-6, rv); mock().checkExpectations(); } TEST(MemfaultHeartbeatMetrics, Test_UnsignedHeartbeatValue) { MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_unsigned); int rv = memfault_metrics_heartbeat_set_unsigned(key, 100); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_set_signed(key, 100); CHECK(rv != 0); rv = memfault_metrics_heartbeat_add(key, 1); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_add(key, 1); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_add(key, 2); LONGS_EQUAL(0, rv); uint32_t val = 0; rv = memfault_metrics_heartbeat_read_unsigned(key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(104, val); // test clipping memfault_metrics_heartbeat_add(key, INT32_MAX); memfault_metrics_heartbeat_add(key, INT32_MAX); rv = memfault_metrics_heartbeat_read_unsigned(key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(UINT32_MAX, val); // confirm metrics count makes sense size_t num_metrics = memfault_metrics_session_get_num_metrics(MEMFAULT_METRICS_SESSION_KEY(heartbeat)); LONGS_EQUAL(12, num_metrics); } TEST(MemfaultHeartbeatMetrics, Test_SignedHeartbeatValue) { MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_signed); int rv = memfault_metrics_heartbeat_set_signed(key, -100); LONGS_EQUAL(0, rv); // try wrong types rv = memfault_metrics_heartbeat_set_unsigned(key, 100); CHECK(rv != 0); rv = memfault_metrics_heartbeat_timer_stop(key); CHECK(rv != 0); rv = memfault_metrics_heartbeat_add(key, 1); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_add(key, 1); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_add(key, 2); LONGS_EQUAL(0, rv); int32_t val = 0; rv = memfault_metrics_heartbeat_read_signed(key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(-96, val); memfault_metrics_heartbeat_add(key, INT32_MAX); memfault_metrics_heartbeat_add(key, INT32_MAX); rv = memfault_metrics_heartbeat_read_signed(key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(INT32_MAX, val); memfault_metrics_heartbeat_set_signed(key, -100); memfault_metrics_heartbeat_add(key, INT32_MIN); rv = memfault_metrics_heartbeat_read_signed(key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(INT32_MIN, val); } TEST(MemfaultHeartbeatMetrics, Test_TimerHeartBeatValueSimple) { MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_timer); // no-op int rv = memfault_metrics_heartbeat_timer_stop(key); CHECK(rv != 0); // start the timer rv = memfault_metrics_heartbeat_timer_start(key); LONGS_EQUAL(0, rv); prv_fake_time_incr(10); // no-op rv = memfault_metrics_heartbeat_timer_start(key); CHECK(rv != 0); rv = memfault_metrics_heartbeat_add(key, 20); CHECK(rv != 0); rv = memfault_metrics_heartbeat_timer_stop(key); LONGS_EQUAL(0, rv); uint32_t val; memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(10, val); } TEST(MemfaultHeartbeatMetrics, Test_TimerHeartBeatReadWhileRunning) { MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_timer); // start the timer int rv = memfault_metrics_heartbeat_timer_start(key); LONGS_EQUAL(0, rv); // read while running uint32_t val; prv_fake_time_incr(10); memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(10, val); // stop the timer prv_fake_time_incr(9); rv = memfault_metrics_heartbeat_timer_stop(key); LONGS_EQUAL(0, rv); memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(19, val); } TEST(MemfaultHeartbeatMetrics, Test_TimerHeartBeatValueRollover) { MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_timer); prv_fake_time_set(0x80000000 - 9); int rv = memfault_metrics_heartbeat_timer_start(key); LONGS_EQUAL(0, rv); prv_fake_time_set(0x80000008); rv = memfault_metrics_heartbeat_timer_stop(key); LONGS_EQUAL(0, rv); uint32_t val; memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(17, val); } TEST(MemfaultHeartbeatMetrics, Test_TimerHeartBeatNoChange) { MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_timer); int rv = memfault_metrics_heartbeat_timer_start(key); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_timer_stop(key); LONGS_EQUAL(0, rv); uint32_t val; memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(0, val); } #define EXPECTED_HEARTBEAT_TIMER_VAL_MS 13 static void prv_serialize_check_cb(void) { MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_timer); uint32_t val; memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(EXPECTED_HEARTBEAT_TIMER_VAL_MS, val); } TEST(MemfaultHeartbeatMetrics, Test_TimerActiveWhenHeartbeatCollected) { MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_timer); int rv = memfault_metrics_heartbeat_timer_start(key); LONGS_EQUAL(0, rv); prv_fake_time_incr(EXPECTED_HEARTBEAT_TIMER_VAL_MS); s_serializer_check_cb = &prv_serialize_check_cb; mock().expectOneCall("memfault_metrics_reliability_collect"); mock().expectOneCall("memfault_metrics_heartbeat_collect_data"); mock().expectOneCall("memfault_metrics_heartbeat_serialize"); memfault_metrics_heartbeat_debug_trigger(); uint32_t val; memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(0, val); // timer should still be running prv_fake_time_incr(EXPECTED_HEARTBEAT_TIMER_VAL_MS); rv = memfault_metrics_heartbeat_timer_stop(key); LONGS_EQUAL(0, rv); memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(EXPECTED_HEARTBEAT_TIMER_VAL_MS, val); // the time is no longer running so an increment shouldn't be counted prv_fake_time_incr(EXPECTED_HEARTBEAT_TIMER_VAL_MS); s_serializer_check_cb = &prv_serialize_check_cb; mock().expectOneCall("memfault_metrics_reliability_collect"); mock().expectOneCall("memfault_metrics_heartbeat_collect_data"); mock().expectOneCall("memfault_metrics_heartbeat_serialize"); memfault_metrics_heartbeat_debug_trigger(); memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(0, val); } TEST(MemfaultHeartbeatMetrics, Test_String) { #define SAMPLE_STRING "0123456789abcdef" static_assert( __builtin_strlen(SAMPLE_STRING) == 16, "be sure to modify tests/unit/stub_includes/memfault_metrics_heartbeat_config.def to " "match exactly, so we can check for buffer overflows!"); MemfaultMetricId key = MEMFAULT_METRICS_KEY(test_key_string); // just the correct size { int rv = memfault_metrics_heartbeat_set_string(key, SAMPLE_STRING); LONGS_EQUAL(0, rv); char sample_string[sizeof(SAMPLE_STRING) + 1]; memset(sample_string, 0, sizeof(sample_string)); rv = memfault_metrics_heartbeat_read_string(key, sample_string, sizeof(sample_string)); LONGS_EQUAL(0, rv); STRCMP_EQUAL(SAMPLE_STRING, (const char *)sample_string); } // set too long a string { int rv = memfault_metrics_heartbeat_set_string(key, SAMPLE_STRING "1"); LONGS_EQUAL(0, rv); char sample_string[sizeof(SAMPLE_STRING) + 1]; memset(sample_string, 0, sizeof(sample_string)); rv = memfault_metrics_heartbeat_read_string(key, sample_string, sizeof(sample_string)); LONGS_EQUAL(0, rv); STRCMP_EQUAL(SAMPLE_STRING, (const char *)sample_string); } // read with bad destination buffer { int rv = memfault_metrics_heartbeat_read_string(key, NULL, 0); CHECK(rv != 0); } // write with longer then shorter string and confirm readback is ok { int rv = memfault_metrics_heartbeat_set_string(key, SAMPLE_STRING); LONGS_EQUAL(0, rv); #define SHORT_TEST_STRING "12" rv = memfault_metrics_heartbeat_set_string(key, SHORT_TEST_STRING); LONGS_EQUAL(0, rv); char sample_string[sizeof(SHORT_TEST_STRING)]; memset(sample_string, 0, sizeof(sample_string)); rv = memfault_metrics_heartbeat_read_string(key, sample_string, sizeof(sample_string)); LONGS_EQUAL(0, rv); STRCMP_EQUAL(SHORT_TEST_STRING, (const char *)sample_string); } // read with a buffer that's too small, and confirm it's a valid string { int rv = memfault_metrics_heartbeat_set_string(key, SAMPLE_STRING); LONGS_EQUAL(0, rv); char sample_string[sizeof(SAMPLE_STRING) - 1]; memset(sample_string, 'a', sizeof(sample_string)); rv = memfault_metrics_heartbeat_read_string(key, sample_string, sizeof(sample_string)); LONGS_EQUAL(0, rv); STRCMP_EQUAL("0123456789abcde", (const char *)sample_string); } } TEST(MemfaultHeartbeatMetrics, Test_BadBoot) { sMemfaultMetricBootInfo info = { .unexpected_reboot_count = 1 }; int rv = memfault_metrics_boot(NULL, &info); LONGS_EQUAL(-3, rv); rv = memfault_metrics_boot(s_fake_event_storage_impl, NULL); LONGS_EQUAL(-3, rv); rv = memfault_metrics_boot(NULL, NULL); LONGS_EQUAL(-3, rv); // calling boot with valid args twice in a row should succeed mock().expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size"); mock() .expectOneCall("memfault_metrics_reliability_boot") .withParameter("ctx", (sMemfaultMetricsReliabilityCtx *)NULL); mock().expectOneCall("memfault_platform_metrics_timer_boot").withParameter("period_sec", 3600); rv = memfault_metrics_boot(s_fake_event_storage_impl, &info); LONGS_EQUAL(0, rv); } TEST(MemfaultHeartbeatMetrics, Test_KeyDONE) { // NOTE: Using the macro MEMFAULT_METRICS_KEY, it's impossible for a non-existent key to trigger a // compilation error so we just create an invalid key. MemfaultMetricId key = (MemfaultMetricId){ INT32_MAX }; int rv = memfault_metrics_heartbeat_set_signed(key, 0); CHECK(rv != 0); rv = memfault_metrics_heartbeat_set_unsigned(key, 0); CHECK(rv != 0); rv = memfault_metrics_heartbeat_add(key, INT32_MIN); CHECK(rv != 0); rv = memfault_metrics_heartbeat_set_string(key, NULL); CHECK(rv != 0); rv = memfault_metrics_heartbeat_timer_start(key); CHECK(rv != 0); rv = memfault_metrics_heartbeat_timer_stop(key); CHECK(rv != 0); int32_t vali32; rv = memfault_metrics_heartbeat_read_signed(key, NULL); CHECK(rv != 0); rv = memfault_metrics_heartbeat_read_signed(key, &vali32); CHECK(rv != 0); uint32_t valu32; rv = memfault_metrics_heartbeat_read_unsigned(key, NULL); CHECK(rv != 0); rv = memfault_metrics_heartbeat_read_unsigned(key, &valu32); CHECK(rv != 0); rv = memfault_metrics_heartbeat_timer_read(key, NULL); CHECK(rv != 0); rv = memfault_metrics_heartbeat_timer_read(key, &valu32); CHECK(rv != 0); rv = memfault_metrics_heartbeat_read_string(key, NULL, 0); CHECK(rv != 0); } void memfault_metrics_heartbeat_collect_data(void) { mock().actualCall(__func__); } TEST(MemfaultHeartbeatMetrics, Test_HeartbeatCollection) { MemfaultMetricId keyi32 = MEMFAULT_METRICS_KEY(test_key_signed); MemfaultMetricId keyu32 = MEMFAULT_METRICS_KEY(test_key_unsigned); memfault_metrics_heartbeat_set_signed(keyi32, 200); memfault_metrics_heartbeat_set_unsigned(keyu32, 199); int32_t vali32; uint32_t valu32; // should fail if we read the wrong type int rv = memfault_metrics_heartbeat_read_signed(keyu32, &vali32); CHECK(rv != 0); rv = memfault_metrics_heartbeat_read_signed(keyi32, &vali32); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_read_unsigned(keyu32, &valu32); LONGS_EQUAL(0, rv); LONGS_EQUAL(vali32, 200); LONGS_EQUAL(valu32, 199); mock().expectOneCall("memfault_metrics_reliability_collect"); mock().expectOneCall("memfault_metrics_heartbeat_collect_data"); mock().expectOneCall("memfault_metrics_heartbeat_serialize"); memfault_metrics_heartbeat_debug_trigger(); mock().checkExpectations(); // values should all be reset rv = memfault_metrics_heartbeat_read_signed(keyi32, &vali32); CHECK(rv != 0); rv = memfault_metrics_heartbeat_read_unsigned(keyu32, &valu32); CHECK(rv != 0); } // Sanity test the alternate setter API TEST(MemfaultHeartbeatMetrics, Test_HeartbeatCollectionAltSetter) { MemfaultMetricId keyi32 = MEMFAULT_METRICS_KEY(test_key_signed); MemfaultMetricId keyu32 = MEMFAULT_METRICS_KEY(test_key_unsigned); MemfaultMetricId keytimer = MEMFAULT_METRICS_KEY(test_key_timer); MemfaultMetricId keystring = MEMFAULT_METRICS_KEY(test_key_string); // integers MEMFAULT_METRIC_SET_SIGNED(test_key_signed, -200); MEMFAULT_METRIC_SET_UNSIGNED(test_key_unsigned, 199); MEMFAULT_METRIC_ADD(test_key_signed, 1); MEMFAULT_METRIC_ADD(test_key_unsigned, 1); // string MEMFAULT_METRIC_SET_STRING(test_key_string, "test"); // timer MEMFAULT_METRIC_TIMER_START(test_key_timer); prv_fake_time_incr(10); MEMFAULT_METRIC_TIMER_STOP(test_key_timer); int32_t vali32; uint32_t valu32; uint32_t timer_valu32; int rv; rv = memfault_metrics_heartbeat_read_signed(keyi32, &vali32); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_read_unsigned(keyu32, &valu32); LONGS_EQUAL(0, rv); char buf[512]; rv = memfault_metrics_heartbeat_read_string(keystring, buf, sizeof(buf)); LONGS_EQUAL(0, rv); rv = memfault_metrics_heartbeat_timer_read(keytimer, &timer_valu32); LONGS_EQUAL(0, rv); LONGS_EQUAL(vali32, -199); LONGS_EQUAL(valu32, 200); STRCMP_EQUAL(buf, "test"); LONGS_EQUAL(timer_valu32, 10); } TEST(MemfaultHeartbeatMetrics, Test_SaveAndRestoreState) { mock().expectOneCall("memfault_metrics_reliability_get_ctx"); const void *metrics_state = memfault_metrics_get_state(); s_metrics_restore_state = malloc(MEMFAULT_METRICS_CONTEXT_SIZE_BYTES); memcpy(s_metrics_restore_state, metrics_state, MEMFAULT_METRICS_CONTEXT_SIZE_BYTES); // reboot the metrics system with the saved state mock().expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size"); mock().expectOneCall("memfault_metrics_reliability_boot").ignoreOtherParameters(); mock().expectOneCall("memfault_platform_metrics_timer_boot").withParameter("period_sec", 3600); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = 7 }; int rv = memfault_metrics_boot(s_fake_event_storage_impl, &boot_info); free(s_metrics_restore_state); s_metrics_restore_state = NULL; LONGS_EQUAL(0, rv); // crash count should have beem copied into heartbeat uint32_t logged_crash_count = 0; memfault_metrics_heartbeat_read_unsigned( MEMFAULT_METRICS_KEY(MemfaultSdkMetric_UnexpectedRebootCount), &logged_crash_count); LONGS_EQUAL(boot_info.unexpected_reboot_count, logged_crash_count); // IntervalMs & RebootCount & operational_hours * operational_crashfree_hours const size_t num_memfault_sdk_metrics = 4; // One heartbeat metric defined using _WITH_SCALE_VALUE const size_t num_scale_value_metrics = 1; // 2 log metrics const size_t num_log_metrics = 2; // 1 uptime metric const size_t num_uptime_metrics = 1; // We should test all the types of available metrics so if this // fails it means there's a new type we aren't yet covering LONGS_EQUAL(kMemfaultMetricType_NumTypes + num_memfault_sdk_metrics + num_scale_value_metrics + num_log_metrics + num_uptime_metrics, memfault_metrics_heartbeat_get_num_metrics()); } ================================================ FILE: tests/unit/src/test_memfault_heartbeat_metrics_debug.cpp ================================================ //! @file //! //! @brief #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/components.h" #include "memfault/metrics/serializer.h" #include "mocks/mock_memfault_platform_debug_log.h" extern "C" { static uint64_t s_boot_time_ms = 0; uint64_t memfault_platform_get_time_since_boot_ms(void) { return s_boot_time_ms; } } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MEMFAULT_UNUSED MemfaultPlatformTimerCallback callback) { return mock() .actualCall(__func__) .withParameter("period_sec", period_sec) .returnBoolValueOrDefault(true); } bool memfault_metrics_heartbeat_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl) { return mock().actualCall(__func__).returnBoolValueOrDefault(true); } bool memfault_metrics_session_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl, MEMFAULT_UNUSED eMfltMetricsSessionIndex session_index) { return mock().actualCall(__func__).returnBoolValueOrDefault(true); } size_t memfault_metrics_heartbeat_compute_worst_case_storage_size(void) { return (size_t)mock().actualCall(__func__).returnIntValueOrDefault(0); } // clang-format off TEST_GROUP(MemfaultHeartbeatMetricsDebug){ void setup(){ s_boot_time_ms = 0; mock().strictOrder(); } void teardown() { mock().checkExpectations(); mock().clear(); } }; // clang-format on void memfault_metrics_heartbeat_collect_data(void) { MEMFAULT_METRIC_SET_UNSIGNED(test_key_unsigned, 1234); MEMFAULT_METRIC_SET_SIGNED(test_key_signed, -100); MEMFAULT_METRIC_SET_STRING(test_key_string, "heyo!"); // add a call here to ensure it doesn't recurse endlessly. customers may be // using this pattern memfault_metrics_heartbeat_debug_print(); } //! check the debug print outputs the correct values depending on metrics state TEST(MemfaultHeartbeatMetricsDebug, Test_DebugPrints) { static uint8_t s_storage[1000]; const sMemfaultEventStorageImpl *s_fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage)); mock().expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size"); mock() .expectOneCall("memfault_metrics_reliability_boot") .withParameter("ctx", (sMemfaultMetricsReliabilityCtx *)NULL); mock().expectOneCall("memfault_platform_metrics_timer_boot").withParameter("period_sec", 3600); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = 1 }; int rv = memfault_metrics_boot(s_fake_event_storage_impl, &boot_info); LONGS_EQUAL(0, rv); mock().checkExpectations(); // this should output the system reset values const char *heartbeat_debug_print_on_boot[] = { "Metrics keys/values:", " MemfaultSdkMetric_IntervalMs: 0", " MemfaultSdkMetric_UnexpectedRebootCount: 1", " operational_hours: null", " operational_crashfree_hours: null", " MemfaultSDKMetric_log_dropped_lines: null", " MemfaultSDKMetric_log_recorded_lines: null", " uptime_s: null", " test_key_unsigned: null", " test_key_signed: null", " test_key_timer: 0", " test_key_string: \"\"", " test_unsigned_scale_value: null", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, heartbeat_debug_print_on_boot, MEMFAULT_ARRAY_SIZE(heartbeat_debug_print_on_boot)); memfault_metrics_heartbeat_debug_print(); mock().checkExpectations(); s_boot_time_ms = 678; MEMFAULT_METRIC_TIMER_START(test_key_timer); s_boot_time_ms = 5678; // debug trigger will update, save, and zero the values const char *heartbeat_debug_print_after_collected[] = { "Metrics keys/values:", " MemfaultSdkMetric_IntervalMs: 5678", " MemfaultSdkMetric_UnexpectedRebootCount: 1", " operational_hours: null", " operational_crashfree_hours: null", " MemfaultSDKMetric_log_dropped_lines: 1", " MemfaultSDKMetric_log_recorded_lines: 1", " uptime_s: 5", " test_key_unsigned: 1234", " test_key_signed: -100", " test_key_timer: 5000", " test_key_string: \"heyo!\"", " test_unsigned_scale_value: null", }; mock().expectOneCall("memfault_metrics_reliability_collect"); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, heartbeat_debug_print_after_collected, MEMFAULT_ARRAY_SIZE(heartbeat_debug_print_after_collected)); mock().expectOneCall("memfault_metrics_heartbeat_serialize"); memfault_metrics_heartbeat_debug_trigger(); mock().checkExpectations(); // after trigger, metrics should be zeroed now const char *heartbeat_debug_print_reset[] = { "Metrics keys/values:", " MemfaultSdkMetric_IntervalMs: 0", " MemfaultSdkMetric_UnexpectedRebootCount: null", " operational_hours: null", " operational_crashfree_hours: null", " MemfaultSDKMetric_log_dropped_lines: null", " MemfaultSDKMetric_log_recorded_lines: null", " uptime_s: null", " test_key_unsigned: null", " test_key_signed: null", " test_key_timer: 0", " test_key_string: \"\"", " test_unsigned_scale_value: null", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, heartbeat_debug_print_reset, MEMFAULT_ARRAY_SIZE(heartbeat_debug_print_reset)); memfault_metrics_heartbeat_debug_print(); mock().checkExpectations(); // Finally test that memfault_metrics_heartbeat_add by itself sets the metric // to is_set const char *heartbeat_add_non_null[] = { "Metrics keys/values:", " MemfaultSdkMetric_IntervalMs: 0", " MemfaultSdkMetric_UnexpectedRebootCount: null", " operational_hours: null", " operational_crashfree_hours: null", " MemfaultSDKMetric_log_dropped_lines: null", " MemfaultSDKMetric_log_recorded_lines: null", " uptime_s: null", " test_key_unsigned: 123", " test_key_signed: null", " test_key_timer: 0", " test_key_string: \"\"", " test_unsigned_scale_value: null", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, heartbeat_add_non_null, MEMFAULT_ARRAY_SIZE(heartbeat_add_non_null)); // call add on this metric alone and confirm it is set MEMFAULT_METRIC_ADD(test_key_unsigned, 123); memfault_metrics_heartbeat_debug_print(); mock().checkExpectations(); } ================================================ FILE: tests/unit/src/test_memfault_heartbeat_metrics_nocustom.cpp ================================================ //! @file //! //! @brief #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/components.h" #include "memfault/metrics/serializer.h" extern "C" { uint64_t memfault_platform_get_time_since_boot_ms(void) { return 0; } } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MEMFAULT_UNUSED MemfaultPlatformTimerCallback callback) { return mock() .actualCall(__func__) .withParameter("period_sec", period_sec) .returnBoolValueOrDefault(true); } size_t memfault_metrics_heartbeat_compute_worst_case_storage_size(void) { return (size_t)mock().actualCall(__func__).returnIntValueOrDefault(0); } bool memfault_metrics_heartbeat_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl) { return mock().actualCall(__func__).returnBoolValueOrDefault(true); } bool memfault_metrics_session_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl, MEMFAULT_UNUSED eMfltMetricsSessionIndex session_index) { return mock().actualCall(__func__).returnBoolValueOrDefault(true); } // clang-format off TEST_GROUP(MemfaultHeartbeatMetricsNoCustom){ void setup() { mock().strictOrder(); } void teardown() { mock().checkExpectations(); mock().clear(); } }; // clang-format on //! Confirm compilation and metric count is correct TEST(MemfaultHeartbeatMetricsNoCustom, Test_CompileAndMetricCount) { size_t num_metrics = memfault_metrics_heartbeat_get_num_metrics(); LONGS_EQUAL(7, num_metrics); } //! Confirm we can boot without any issues (eg writing to unpopulated key //! position) TEST(MemfaultHeartbeatMetricsNoCustom, Test_Boot) { mock() .expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size") .andReturnValue(0); mock() .expectOneCall("memfault_metrics_reliability_boot") .withParameter("ctx", (sMemfaultMetricsReliabilityCtx *)NULL); // Check that by default the heartbeat interval is once / hour mock().expectOneCall("memfault_platform_metrics_timer_boot").withParameter("period_sec", 3600); static uint8_t s_storage[1000]; static const sMemfaultEventStorageImpl *fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage)); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = 1 }; int rv = memfault_metrics_boot(fake_event_storage_impl, &boot_info); LONGS_EQUAL(0, rv); // call this to get coverage on the internal weak function memfault_metrics_heartbeat_collect_data(); } ================================================ FILE: tests/unit/src/test_memfault_http_utils.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include #include #include #include #include "memfault/core/math.h" #include "memfault/core/platform/device_info.h" #include "memfault/http/http_client.h" #include "memfault/http/utils.h" #include "memfault/version.h" const struct MemfaultDeviceInfo g_device_info_default = { .device_serial = "DEMOSERIAL", .software_type = "main", .software_version = "1.0.0", .hardware_version = "main-proto", }; static struct MemfaultDeviceInfo g_device_info; void memfault_platform_get_device_info(struct MemfaultDeviceInfo *info) { *info = g_device_info; } sMfltHttpClientConfig g_mflt_http_client_config; } TEST_GROUP(MfltHttpClientUtils) { void setup() { g_mflt_http_client_config = (sMfltHttpClientConfig){ .api_key = "00112233445566778899aabbccddeeff", // gitleaks:allow }; g_device_info = g_device_info_default; mock().strictOrder(); } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MfltHttpClientUtils, Test_MfltHttpClientOverrides) { STRCMP_EQUAL("chunks.memfault.com", MEMFAULT_HTTP_GET_CHUNKS_API_HOST()); STRCMP_EQUAL("device.memfault.com", MEMFAULT_HTTP_GET_DEVICE_API_HOST()); LONGS_EQUAL(443, MEMFAULT_HTTP_GET_CHUNKS_API_PORT()); LONGS_EQUAL(443, MEMFAULT_HTTP_GET_DEVICE_API_PORT()); g_mflt_http_client_config = (sMfltHttpClientConfig){ .api_key = "00112233445566778899aabbccddeeff", // gitleaks:allow .disable_tls = false, .chunks_api = { .host = "override-chunks.memfault.com", .port = 1, }, .device_api = { .host = "override-api.memfault.com", .port = 2, }, }; STRCMP_EQUAL("override-chunks.memfault.com", MEMFAULT_HTTP_GET_CHUNKS_API_HOST()); STRCMP_EQUAL("override-api.memfault.com", MEMFAULT_HTTP_GET_DEVICE_API_HOST()); LONGS_EQUAL(1, MEMFAULT_HTTP_GET_CHUNKS_API_PORT()); LONGS_EQUAL(2, MEMFAULT_HTTP_GET_DEVICE_API_PORT()); } typedef struct { char buf[256]; size_t bytes_written; } sHttpWriteCtx; static bool prv_http_write_cb(const void *data, size_t data_len, void *ctx) { bool success = mock().actualCall(__func__).returnBoolValueOrDefault(true); if (!success) { return false; } sHttpWriteCtx *write_ctx = (sHttpWriteCtx *)ctx; memcpy(&write_ctx->buf[write_ctx->bytes_written], data, data_len); write_ctx->bytes_written += data_len; return true; } TEST(MfltHttpClientUtils, Test_MfltHttpClientPost) { mock().expectNCalls(11, "prv_http_write_cb"); sHttpWriteCtx ctx = { 0 }; bool success = memfault_http_start_chunk_post(prv_http_write_cb, &ctx, 123); CHECK(success); const char *expected_string = "POST /api/v0/chunks/DEMOSERIAL HTTP/1.1\r\n" "Host:chunks.memfault.com\r\n" "User-Agent:MemfaultSDK/" MEMFAULT_SDK_VERSION_STR "\r\n" "Memfault-Project-Key:00112233445566778899aabbccddeeff\r\n" // gitleaks:allow "Content-Type:application/octet-stream\r\n" "Content-Length:123\r\n\r\n"; STRCMP_EQUAL(expected_string, ctx.buf); } TEST(MfltHttpClientUtils, Test_MfltHttpClientPostSendWriteFailure) { const size_t num_write_calls = 11; for (size_t i = 0; i < num_write_calls; i++) { if (i > 0) { mock().expectNCalls(i, "prv_http_write_cb"); } mock().expectOneCall("prv_http_write_cb").andReturnValue(false); sHttpWriteCtx ctx = { 0 }; bool success = memfault_http_start_chunk_post(prv_http_write_cb, &ctx, 10); CHECK(!success); mock().checkExpectations(); } } TEST(MfltHttpClientUtils, Test_MfltHttpClientGetOtaPayloadUrl) { mock().expectNCalls(26, "prv_http_write_cb"); sHttpWriteCtx ctx = { 0 }; bool success = memfault_http_get_latest_ota_payload_url(prv_http_write_cb, &ctx); CHECK(success); const char *expected_string = "GET " "/api/v0/releases/latest/" "url?&device_serial=DEMOSERIAL&hardware_version=main-proto&" "software_type=main¤t_version=1.0.0 HTTP/1.1\r\n" "Host:device.memfault.com\r\n" "User-Agent:MemfaultSDK/" MEMFAULT_SDK_VERSION_STR "\r\n" "Memfault-Project-Key:00112233445566778899aabbccddeeff\r\n" // gitleaks:allow "\r\n"; STRCMP_EQUAL(expected_string, ctx.buf); } TEST(MfltHttpClientUtils, Test_MfltHttpClientGetOtaPayloadUrlWriteFailure) { const size_t num_write_calls = 26; for (size_t i = 0; i < num_write_calls; i++) { if (i > 0) { mock().expectNCalls(i, "prv_http_write_cb"); } mock().expectOneCall("prv_http_write_cb").andReturnValue(false); sHttpWriteCtx ctx = { 0 }; bool success = memfault_http_get_latest_ota_payload_url(prv_http_write_cb, &ctx); CHECK(!success); mock().checkExpectations(); } } TEST(MfltHttpClientUtils, Test_MfltHttpClientGetOtaPayloadUrlEncodeFailure) { mock().expectNCalls(1, "prv_http_write_cb"); sHttpWriteCtx ctx = { 0 }; g_device_info.device_serial = "++++++++++++++++"; bool success = memfault_http_get_latest_ota_payload_url(prv_http_write_cb, &ctx); CHECK(!success); mock().checkExpectations(); } TEST(MfltHttpClientUtils, Test_MfltHttpClientGetOtaPayload) { mock().expectNCalls(8, "prv_http_write_cb"); sHttpWriteCtx ctx = { 0 }; const char *url = "https://example.ota.payload.com/path/to/ota/payload/yay"; bool success = memfault_http_get_ota_payload(prv_http_write_cb, &ctx, url, strlen(url)); CHECK(success); const char *expected_string = "GET /path/to/ota/payload/yay HTTP/1.1\r\n" "Host:example.ota.payload.com\r\n" "User-Agent:MemfaultSDK/" MEMFAULT_SDK_VERSION_STR "\r\n" "\r\n"; STRCMP_EQUAL(expected_string, ctx.buf); } TEST(MfltHttpClientUtils, Test_MfltHttpClientGetOtaPayloadNoPath) { mock().expectNCalls(8, "prv_http_write_cb"); sHttpWriteCtx ctx = { 0 }; const char *url = "https://example.ota.payload.com"; bool success = memfault_http_get_ota_payload(prv_http_write_cb, &ctx, url, strlen(url)); CHECK(success); const char *expected_string = "GET / HTTP/1.1\r\n" "Host:example.ota.payload.com\r\n" "User-Agent:MemfaultSDK/" MEMFAULT_SDK_VERSION_STR "\r\n" "\r\n"; STRCMP_EQUAL(expected_string, ctx.buf); } TEST(MfltHttpClientUtils, Test_MfltHttpClientGetOtaPayloadBadUrl) { sHttpWriteCtx ctx = { 0 }; const char *url = "ftp://"; bool success = memfault_http_get_ota_payload(prv_http_write_cb, &ctx, url, strlen(url)); CHECK(!success); } TEST(MfltHttpClientUtils, Test_MfltHttpClientGetOtaPayloadFailure) { const size_t num_write_calls = 8; for (size_t i = 0; i < num_write_calls; i++) { if (i > 0) { mock().expectNCalls(i, "prv_http_write_cb"); } mock().expectOneCall("prv_http_write_cb").andReturnValue(false); sHttpWriteCtx ctx = { 0 }; const char *url = "https://example.ota.payload.com/path/to/ota/payload/yay"; bool success = memfault_http_get_ota_payload(prv_http_write_cb, &ctx, url, strlen(url)); CHECK(!success); mock().checkExpectations(); } } static void prv_expect_parse_success(const char *rsp, size_t rsp_len, int expected_http_status) { // first we feed the message as an entire blob and confirm it parses sMemfaultHttpResponseContext ctx = {}; bool done = memfault_http_parse_response(&ctx, rsp, rsp_len); CHECK(done); CHECK(!ctx.parse_error); LONGS_EQUAL(expected_http_status, ctx.http_status_code); LONGS_EQUAL(rsp_len, ctx.data_bytes_processed); // second we feed the message one at a time and confirm it parses memset(&ctx, 0x0, sizeof(ctx)); for (size_t i = 0; i < rsp_len; i++) { done = memfault_http_parse_response(&ctx, &rsp[i], 1); LONGS_EQUAL(1, ctx.data_bytes_processed); LONGS_EQUAL(i == rsp_len - 1, done); CHECK(!ctx.parse_error); if (done) { LONGS_EQUAL(expected_http_status, ctx.http_status_code); } } // third we only parse the header. memset(&ctx, 0x0, sizeof(ctx)); done = memfault_http_parse_response_header(&ctx, rsp, rsp_len); CHECK(done); CHECK(!ctx.parse_error); LONGS_EQUAL(expected_http_status, ctx.http_status_code); // the message-body should not be included in the data_bytes_processed // so we add the parsed content_length to recover the total size const size_t total_rsp_len = (size_t)(ctx.data_bytes_processed + ctx.content_length); LONGS_EQUAL(rsp_len, total_rsp_len); } TEST(MfltHttpClientUtils, Test_MfltResponseParser202) { const char *rsp = "HTTP/1.1 202 Accepted\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "Content-Length: 8\r\n" "Date: Wed, 27 Nov 2019 22:52:57 GMT\r\n" "Connection: keep-alive\r\n" "\r\n" "Accepted"; const size_t rsp_len = strlen(rsp); sMemfaultHttpResponseContext ctx = {}; bool done = memfault_http_parse_response(&ctx, rsp, rsp_len); CHECK(done); CHECK(!ctx.parse_error); LONGS_EQUAL(202, ctx.http_status_code); LONGS_EQUAL(rsp_len, ctx.data_bytes_processed); STRCMP_EQUAL("Accepted", ctx.http_body); } TEST(MfltHttpClientUtils, Test_MfltResponseParserNoContentLength) { const char *rsp = "HTTP/1.1 202 Accepted\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "Content\rLength: 8\r\n" "Date: Wed, 27 Nov 2019 22:52:57 GMT\r\n" "Connection: keep-alive\r\n" "\r\n"; sMemfaultHttpResponseContext ctx = {}; bool done = memfault_http_parse_response(&ctx, rsp, strlen(rsp)); CHECK(done); CHECK(!ctx.parse_error); LONGS_EQUAL(202, ctx.http_status_code); LONGS_EQUAL(0, ctx.content_length); } TEST(MfltHttpClientUtils, Test_MfltResponseParser411) { const char *good_response = "HTTP/1.1 411 Length Required\r\n" "Content-Type: application/json; charset=utf-8\r\n" "content-lenGTH: 67 \r\n" "Date: Wed, 27 Nov 2019 22:47:47 GMT\r\n" "Connection: keep-alive\r\n" "\r\n" "{\"error\":{\"message\":\"Content-Length must be set.\",\"http_code\":411}}"; prv_expect_parse_success(good_response, strlen(good_response), 411); } TEST(MfltHttpClientUtils, Test_VeryLongBody) { const char *rsp_hdr = "HTTP/1.1 202 Accepted\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "Content-Length: 1024\r\n" "Date: Wed, 27 Nov 2019 22:52:57 GMT\r\n" "Connection: keep-alive\r\n" "\r\n"; const size_t rsp_hdr_len = strlen(rsp_hdr); char msg[rsp_hdr_len + 1024]; memcpy(msg, rsp_hdr, rsp_hdr_len); prv_expect_parse_success(msg, sizeof(msg), 202); } static void prv_expect_parse_failure(const void *response, size_t response_len, eMfltHttpParseStatus expected_parse_error) { sMemfaultHttpResponseContext ctx = {}; bool done = memfault_http_parse_response(&ctx, response, response_len); CHECK(done); LONGS_EQUAL(expected_parse_error, ctx.parse_error); // should also fail if we only parse the header memset(&ctx, 0x0, sizeof(ctx)); done = memfault_http_parse_response_header(&ctx, response, response_len); CHECK(done); LONGS_EQUAL(expected_parse_error, ctx.parse_error); } TEST(MfltHttpClientUtils, Test_HttpResponseUnexpectedlyLong) { // One really long header we don't care about but must tolerate. const char *rsp = "HTTP/1.1 202 Accepted\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "ReallyLongHeader:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789;" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\r\n" "Content-Length: 8\r\n" "Date: Wed, 27 Nov 2019 22:52:57 GMT\r\n" "Connection: keep-alive\r\n" "\r\n" "Accepted"; // Body, 8 bytes prv_expect_parse_success(rsp, strlen(rsp), 202); } TEST(MfltHttpClientUtils, Test_HttpResponseUnexpectedlyLongFirstLine) { // One really long header as the first line should fail. const char *rsp = "ReallyLongHeader:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789;" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\r\n" "HTTP/1.1 202 Accepted\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "Content-Length: 8\r\n" "Date: Wed, 27 Nov 2019 22:52:57 GMT\r\n" "Connection: keep-alive\r\n" "\r\n" "Accepted"; // Body, 8 bytes prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_HeaderTooLongError); } TEST(MfltHttpClientUtils, Test_HeaderBeforeStatus) { const char *rsp = "Content-Type: text/plain; charset=utf-8\r\n" "HTTP/1.1 202 Accepted\r\n" "Content-Length: 1024\r\n" "Date: Wed, 27 Nov 2019 22:52:57 GMT\r\n" "Connection: keep-alive\r\n" "\r\n"; prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_ParseStatusLineError); } TEST(MfltHttpClientUtils, Test_MfltResponseBadVersion) { const static char *rsp = "HTTZ/1.1 202 Accepted\r\n"; prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_ParseStatusLineError); } TEST(MfltHttpClientUtils, Test_MfltResponseBadStatusCode) { const static char *rsp = "HTTP/1.1 2a2 Accepted\r\n"; prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_ParseStatusLineError); } TEST(MfltHttpClientUtils, Test_MfltResponseShortStatusCode) { const static char *rsp = "HTTP/1.1 22 Accepted\r\n"; prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_ParseStatusLineError); } TEST(MfltHttpClientUtils, Test_MfltResponseOverLongStatusCode) { LONGS_EQUAL(2147483647, INT_MAX); // sanity check const static char *rsp = "HTTP/1.1 2147483648\r\n"; prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_ParseStatusLineError); } TEST(MfltHttpClientUtils, Test_MfltResponseNoSpace) { const static char *rsp = "HTTP/1.1202 Accepted\r\n"; prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_ParseStatusLineError); } TEST(MfltHttpClientUtils, Test_MfltResponseMalformedMinorVersion) { const static char *rsp = "HTTP/1.a 202 Accepted\r\n"; prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_ParseStatusLineError); } TEST(MfltHttpClientUtils, Test_MfltResponseShort) { const static char *rsp_short = "HTTP/1.1 20\r\n"; prv_expect_parse_failure(rsp_short, strlen(rsp_short), MfltHttpParseStatus_ParseStatusLineError); const static char *rsp_short1 = "HTTP/1.1\r\n"; prv_expect_parse_failure(rsp_short1, strlen(rsp_short1), MfltHttpParseStatus_ParseStatusLineError); const static char *rsp_short2 = "HTTP/1.\r\n"; prv_expect_parse_failure(rsp_short2, strlen(rsp_short2), MfltHttpParseStatus_ParseStatusLineError); const static char *rsp_short3 = "HTTP/1\r\n"; prv_expect_parse_failure(rsp_short3, strlen(rsp_short3), MfltHttpParseStatus_ParseStatusLineError); } TEST(MfltHttpClientUtils, Test_MfltResponseBadContentLength) { const static char *rsp_non_base_10_digit = "HTTP/1.1 202 Accepted\r\n" "Content-Length:1a\r\n\r\n"; prv_expect_parse_failure(rsp_non_base_10_digit, strlen(rsp_non_base_10_digit), MfltHttpParseStatus_ParseHeaderError); const static char *rsp_value_too_large = "HTTP/1.1 202 Accepted\r\n" "Content-Length:2147483648\r\n\r\n"; prv_expect_parse_failure(rsp_value_too_large, strlen(rsp_value_too_large), MfltHttpParseStatus_ParseHeaderError); } TEST(MfltHttpClientUtils, Test_MfltResponseNoColonSeparator) { const static char *rsp = "HTTP/1.1 202 Accepted\r\n" "Content-Length&10\r\n\r\n"; prv_expect_parse_failure(rsp, strlen(rsp), MfltHttpParseStatus_ParseHeaderError); } static void prv_check_result(const char *uri, const char *host, const char *path, bool expect_success, int expected_port) { sMemfaultUriInfo uri_info; bool success = memfault_http_parse_uri(uri, strlen(uri), &uri_info); CHECK(success == expect_success); if (!success) { return; } LONGS_EQUAL(uri_info.host_len, strlen(host)); MEMCMP_EQUAL(uri_info.host, host, uri_info.host_len); LONGS_EQUAL(uri_info.path_len, strlen(path)); if (uri_info.path_len == 0) { CHECK(uri_info.path == NULL); } else { MEMCMP_EQUAL(uri_info.path, path, uri_info.path_len); } const char *http_scheme = "http://"; const bool is_http_scheme = (strncmp(uri, http_scheme, strlen(http_scheme)) == 0); if (expected_port == -1) { expected_port = is_http_scheme ? 80 : 443; } LONGS_EQUAL(is_http_scheme ? kMemfaultUriScheme_Http : kMemfaultUriScheme_Https, uri_info.scheme); LONGS_EQUAL(expected_port, uri_info.port); } static void prv_uri_parse_check(const char *scheme, const char *host, const char *path, bool expect_success) { char uri[256]; int rv = snprintf(uri, sizeof(uri), "%s%s%s", scheme, host, path); CHECK(rv > 0 && (size_t)rv < sizeof(uri)); prv_check_result(uri, host, path, expect_success, -1); } static void prv_uri_with_port_parse_check(const char *scheme, const char *host, int port, const char *path, bool expect_success) { char uri[256]; int rv = snprintf(uri, sizeof(uri), "%s%s:%d%s", scheme, host, port, path); CHECK(rv > 0 && (size_t)rv < sizeof(uri)); prv_check_result(uri, host, path, expect_success, port); } TEST(MfltHttpClientUtils, Test_MfltParseHttpAndHttpsUris) { const char *valid_prefixes[] = { "https://", "http://", "http://username:password@" }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(valid_prefixes); i++) { const bool expect_success = true; const char *p = valid_prefixes[i]; const char *paths[] = { "/", "/a/b/c/d?param1=1¶m2=2", "" }; for (size_t j = 0; j < MEMFAULT_ARRAY_SIZE(paths); j++) { const char *path = paths[j]; //! NB: We'll test with the three valid types of hostnames //! IP-literal: "[::1]" //! IPv4address: "127.0.0.1" //! registered-name: "www.mywebsite.com" prv_uri_parse_check(p, "www.mysite.com", path, expect_success); prv_uri_with_port_parse_check(p, "www.mysite.com", 80, path, expect_success); prv_uri_parse_check(p, "127.0.0.1", path, expect_success); prv_uri_with_port_parse_check(p, "127.0.0.1", 80, path, expect_success); prv_uri_parse_check(p, "[::1]", "/", expect_success); prv_uri_with_port_parse_check(p, "[::1]", 443, path, expect_success); prv_uri_parse_check(p, "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", path, expect_success); prv_uri_with_port_parse_check(p, "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", 832, path, expect_success); } } } TEST(MfltHttpClientUtils, Test_MfltParseUriWithUnsupportedScheme) { const char *unsupported_schemes[] = { "https:/", "http//", "http:/", "ftp://" }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(unsupported_schemes); i++) { const bool expect_success = false; const char *scheme = unsupported_schemes[i]; prv_uri_parse_check(scheme, "www.mysite.com", "/api/v0/a/b/c", expect_success); } } TEST(MfltHttpClientUtils, Test_MfltParseUriWithMalformedHost) { const char *malformed_hosts[] = { "[::1", "username:password@", }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(malformed_hosts); i++) { const bool expect_success = false; const char *h = malformed_hosts[i]; prv_uri_parse_check("https://", h, "/api/v0/a/b/c", expect_success); prv_uri_with_port_parse_check("http://", h, 8000, "/api/v0/a/b/c", expect_success); } } TEST(MfltHttpClientUtils, Test_MfltParseUriWithBogusPort) { const char *hosts_with_bogus_ports[] = { "[::1]:8abc", "www.example.com:80z", }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(hosts_with_bogus_ports); i++) { const bool expect_success = false; const char *h = hosts_with_bogus_ports[i]; prv_uri_parse_check("https://", h, "/api/v0/a/b/c", expect_success); } } TEST(MfltHttpClientUtils, Test_MfltParseUriWithOnlyScheme) { const bool expect_success = false; prv_check_result("http", NULL, NULL, expect_success, 0); prv_check_result("http:/", NULL, NULL, expect_success, 0); prv_check_result("http://", NULL, NULL, expect_success, 0); prv_check_result("http:///", NULL, NULL, expect_success, 0); } TEST(MfltHttpClientUtils, Test_UrlEncode) { const struct inputs { const char *instring; const char *outstring; } input_vectors[] = { { "a", "a" }, { "a b", "a%20b" }, { "a+b%", "a%2Bb%25" }, // reserved characters { "!#$&'()*+,/:;=?@[]", "%21%23%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D" }, // unreserved characters { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~" }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(input_vectors); i++) { const char *instring = input_vectors[i].instring; const char *outstring = input_vectors[i].outstring; char result[strlen(outstring) + 1]; memset(result, '\0', sizeof(result)); int rv = memfault_http_urlencode(instring, strlen(instring), result, sizeof(result)); LONGS_EQUAL(0, rv); STRCMP_EQUAL(outstring, result); } // test with a buffer that is too small { char result[2]; memset(result, 0xA5, sizeof(result)); int rv = memfault_http_urlencode("!", 1, result, sizeof(result)); LONGS_EQUAL(-1, rv); } // perfectly sized buffer { char result[5]; memset(result, 0xA5, sizeof(result)); int rv = memfault_http_urlencode("a!", 2, result, sizeof(result)); LONGS_EQUAL(0, rv); STRCMP_EQUAL("a%21", result); } // buffer 1 char too small { char result[4]; memset(result, 0xA5, sizeof(result)); int rv = memfault_http_urlencode("a!", 2, result, sizeof(result)); LONGS_EQUAL(-1, rv); } { char result[4]; memset(result, 0xA5, sizeof(result)); int rv = memfault_http_urlencode("!a", 2, result, sizeof(result)); LONGS_EQUAL(-1, rv); } // few more edge cases { int rv = memfault_http_urlencode("1", 1, NULL, 0); LONGS_EQUAL(-1, rv); rv = memfault_http_urlencode(NULL, 0, NULL, 1); LONGS_EQUAL(-1, rv); } } TEST(MfltHttpClientUtils, Test_UrlNeedsEscape) { const struct inputs { const char *instring; bool needs_escape; } input_vectors[] = { { "", false }, { "a", false }, { "a b", true }, { "a+b%", true }, // reserved characters { "!#$&'()*+,/:;=?@[]", true }, // unreserved characters { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~", false }, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(input_vectors); i++) { const char *instring = input_vectors[i].instring; const bool needs_escape = input_vectors[i].needs_escape; const bool result = memfault_http_needs_escape(instring, strlen(instring)); CHECK_TEXT(needs_escape == result, instring); } } TEST(MfltHttpClientUtils, Test_BuildLatestOtaUrl) { char url_buf[256]; bool success = memfault_http_build_latest_ota_url(url_buf, sizeof(url_buf)); CHECK(success); const char *expected_url = "https://device.memfault.com/api/v0/releases/latest/url" "?device_serial=DEMOSERIAL" "&hardware_version=main-proto" "&software_type=main" "¤t_version=1.0.0"; STRCMP_EQUAL(expected_url, url_buf); } TEST(MfltHttpClientUtils, Test_BuildLatestOtaUrlWithUrlEncoding) { g_device_info.device_serial = "DEMO SERIAL"; g_device_info.hardware_version = "main+proto"; g_device_info.software_type = "main&test"; g_device_info.software_version = "1.0.0/beta"; char url_buf[256]; bool success = memfault_http_build_latest_ota_url(url_buf, sizeof(url_buf)); CHECK(success); const char *expected_url = "https://device.memfault.com/api/v0/releases/latest/url" "?device_serial=DEMO%20SERIAL" "&hardware_version=main%2Bproto" "&software_type=main%26test" "¤t_version=1.0.0%2Fbeta"; STRCMP_EQUAL(expected_url, url_buf); } TEST(MfltHttpClientUtils, Test_BuildLatestOtaUrlBufferTooSmall) { char url_buf[10]; bool success = memfault_http_build_latest_ota_url(url_buf, sizeof(url_buf)); CHECK(!success); } TEST(MfltHttpClientUtils, Test_BuildLatestOtaUrlNullBuffer) { bool success = memfault_http_build_latest_ota_url(NULL, 256); CHECK(!success); char url_buf[256]; success = memfault_http_build_latest_ota_url(url_buf, 0); CHECK(!success); } TEST(MfltHttpClientUtils, Test_BuildLatestOtaUrlEncodingBufferOverflow) { // Use a device serial that is too long to URL encode into the buffer g_device_info.device_serial = "+++++++++++++++++++++++++++++++++++++++++++"; char url_buf[256]; bool success = memfault_http_build_latest_ota_url(url_buf, sizeof(url_buf)); CHECK(!success); } TEST(MfltHttpClientUtils, Test_BuildChunkPostUrl) { char url_buf[128]; bool success = memfault_http_build_chunk_post_url(url_buf, sizeof(url_buf)); CHECK(success); const char *expected_url = "https://chunks.memfault.com/api/v0/chunks/DEMOSERIAL"; STRCMP_EQUAL(expected_url, url_buf); } TEST(MfltHttpClientUtils, Test_BuildChunkPostUrlBufferTooSmall) { char url_buf[10]; bool success = memfault_http_build_chunk_post_url(url_buf, sizeof(url_buf)); CHECK(!success); } TEST(MfltHttpClientUtils, Test_BuildChunkPostUrlNullBuffer) { bool success = memfault_http_build_chunk_post_url(NULL, 128); CHECK(!success); char url_buf[128]; success = memfault_http_build_chunk_post_url(url_buf, 0); CHECK(!success); } ================================================ FILE: tests/unit/src/test_memfault_log.cpp ================================================ //! @file //! //! @brief #include #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/core/compact_log_serializer.h" #include "memfault/core/log.h" #include "memfault/core/log_impl.h" #include "memfault/core/sdk_assert.h" #include "memfault_log_data_source_private.h" #include "memfault_log_private.h" static bool s_fake_data_source_has_been_triggered; bool memfault_log_data_source_has_been_triggered(void) { return s_fake_data_source_has_been_triggered; } static bool s_memfault_log_restore_state_retval; static sMfltLogSaveState s_memfault_log_restore_state; bool memfault_log_restore_state(sMfltLogSaveState *state) { *state = s_memfault_log_restore_state; return s_memfault_log_restore_state_retval; } TEST_GROUP(MemfaultLog) { void setup() { fake_memfault_metrics_platform_locking_reboot(); s_fake_data_source_has_been_triggered = false; s_memfault_log_restore_state_retval = false; // we selectively enable memfault_log_handle_saved_callback() for certain tests mock().disable(); } void teardown() { CHECK(fake_memfault_platform_metrics_lock_calls_balanced()); memfault_log_reset(); mock().checkExpectations(); mock().clear(); } }; static void prv_run_header_check(uint8_t *log_entry, eMemfaultPlatformLogLevel expected_level, const char *expected_log, size_t expected_log_len) { // NOTE: Since sMfltRamLogEntry is serialized out, we manually check the header here instead of // sharing the struct definition to catch unexpected changes in the layout. LONGS_EQUAL(expected_level & 0x7, log_entry[0]); LONGS_EQUAL(expected_log_len & 0xff, log_entry[1]); MEMCMP_EQUAL(expected_log, &log_entry[2], expected_log_len); } static void prv_read_log_and_check(eMemfaultPlatformLogLevel expected_level, eMemfaultLogRecordType expected_type, const void *expected_log, size_t expected_log_len) { sMemfaultLog log; // scribble a bad pattern to make sure memfault_log_read inits things memset(&log, 0xa5, sizeof(log)); const bool found_log = memfault_log_read(&log); CHECK(found_log); LONGS_EQUAL(expected_log_len, log.msg_len); if (expected_type == kMemfaultLogRecordType_Preformatted) { STRCMP_EQUAL((const char *)expected_log, log.msg); } else { MEMCMP_EQUAL(expected_log, log.msg, expected_log_len); } LONGS_EQUAL(expected_level, log.level); LONGS_EQUAL(expected_type, log.type); } #if !MEMFAULT_COMPACT_LOG_ENABLE TEST(MemfaultLog, Test_BadInit) { // should be no-ops memfault_log_boot(NULL, 10); CHECK_FALSE(memfault_log_booted()); memfault_log_boot((void *)0xbadcafe, 0); CHECK_FALSE(memfault_log_booted()); // calling any API while not enabled should have no effect memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Error, "1", 1); MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Error, "%d", 11223344); sMemfaultLog log; const bool log_found = memfault_log_read(&log); CHECK(!log_found); } #endif // !MEMFAULT_COMPACT_LOG_ENABLE TEST(MemfaultLog, Test_MemfaultLogBasic) { uint8_t s_ram_log_store[20]; CHECK_FALSE(memfault_log_booted()); memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); CHECK(memfault_log_booted()); const char *my_log = "12345678"; const size_t my_log_len = strlen(my_log); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; eMemfaultLogRecordType type = kMemfaultLogRecordType_Preformatted; memfault_log_save_preformatted(level, my_log, my_log_len); // Write a second log, to exercise the s_memfault_ram_logger.log_read_offset // book-keeping: memfault_log_save_preformatted(level, my_log, my_log_len); prv_run_header_check(s_ram_log_store, level, my_log, my_log_len); prv_run_header_check(&s_ram_log_store[10], level, my_log, my_log_len); // Read the two logs: prv_read_log_and_check(level, type, my_log, my_log_len); prv_read_log_and_check(level, type, my_log, my_log_len); // should be no more logs sMemfaultLog log; const bool log_found = memfault_log_read(&log); CHECK(!log_found); } TEST(MemfaultLog, Test_MemfaultLogOversize) { uint8_t s_ram_log_store[MEMFAULT_LOG_MAX_LINE_SAVE_LEN + sizeof(sMfltRamLogEntry)]; memset(s_ram_log_store, 0, sizeof(s_ram_log_store)); memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); char my_log[MEMFAULT_LOG_MAX_LINE_SAVE_LEN + 2]; memset(my_log, 'A', sizeof(my_log)); my_log[sizeof(my_log) - 1] = '\0'; const eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; eMemfaultLogRecordType type = kMemfaultLogRecordType_Preformatted; memfault_log_save_preformatted(level, my_log, strlen(my_log)); my_log[sizeof(my_log) - 2] = '\0'; prv_read_log_and_check(level, type, my_log, MEMFAULT_LOG_MAX_LINE_SAVE_LEN); } void memfault_log_handle_saved_callback(void) { mock().actualCall(__func__); } TEST(MemfaultLog, Test_MemfaultLog_GetRegions) { // try to get regions before init has been called sMemfaultLogRegions regions; bool found = memfault_log_get_regions(®ions); CHECK(!found); // now init and confirm we get the expected regions uint8_t s_ram_log_store[10]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); found = memfault_log_get_regions(®ions); CHECK(found); LONGS_EQUAL(s_ram_log_store, regions.region[1].region_start); LONGS_EQUAL(sizeof(s_ram_log_store), regions.region[1].region_size); // sanity check - first region should be sMfltRamLogger const uint8_t *mflt_ram_logger = (const uint8_t *)regions.region[0].region_start; LONGS_EQUAL(1, mflt_ram_logger[0]); // version == 1 LONGS_EQUAL(1, mflt_ram_logger[1]); // enabled == 1 } #if !MEMFAULT_COMPACT_LOG_ENABLE TEST(MemfaultLog, Test_MemfaultHandleSaveCallback) { uint8_t s_ram_log_store[10]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); mock().enable(); const char *log0 = "log0"; memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Debug, log0, strlen(log0)); // should have been filtered so nothing should be called mock().checkExpectations(); mock().expectOneCall("memfault_log_handle_saved_callback"); MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, "log2"); mock().checkExpectations(); mock().expectOneCall("memfault_log_handle_saved_callback"); memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Warning, log0, strlen(log0)); mock().checkExpectations(); } TEST(MemfaultLog, Test_MemfaultLogTruncation) { const size_t max_log_size = MEMFAULT_LOG_MAX_LINE_SAVE_LEN; char long_log[max_log_size + 1 + 1 /* \0 */] = { 0 }; memset(long_log, 'a', sizeof(long_log) - 1); LONGS_EQUAL(max_log_size + 1, strlen(long_log)); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; uint8_t s_ram_log_store[MEMFAULT_LOG_MAX_LINE_SAVE_LEN * 2]; // both preformatted and formatted variants should wind up being max size memset(s_ram_log_store, 0x0, sizeof(s_ram_log_store)); memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); memfault_log_save_preformatted(level, long_log, strlen(long_log)); prv_run_header_check(s_ram_log_store, level, long_log, MEMFAULT_LOG_MAX_LINE_SAVE_LEN); memset(s_ram_log_store, 0x0, sizeof(s_ram_log_store)); memfault_log_reset(); memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); MEMFAULT_LOG_SAVE(level, "%s", long_log); prv_run_header_check(s_ram_log_store, level, long_log, MEMFAULT_LOG_MAX_LINE_SAVE_LEN); } #endif // !MEMFAULT_COMPACT_LOG_ENABLE TEST(MemfaultLog, Test_MemfaultLogExpireOldest) { uint8_t s_ram_log_store[10]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); const char *log0 = "0"; // 3 bytes const char *log1 = "12"; // 4 bytes with header const char *log2 = "45678"; // 7 bytes with header const char *log3 = "a"; // 6 bytes with header, should span off 4-9 eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; memfault_log_save_preformatted(level, log0, strlen(log0)); memfault_log_save_preformatted(level, log1, strlen(log1)); memfault_log_save_preformatted(level, log2, strlen(log2)); memfault_log_save_preformatted(level, log3, strlen(log3)); prv_run_header_check(&s_ram_log_store[4], level, log3, strlen(log3)); uint32_t dropped_count = memfault_log_get_dropped_count(); uint32_t recorded_count = memfault_log_get_recorded_count(); LONGS_EQUAL(2, dropped_count); LONGS_EQUAL(4, recorded_count); } TEST(MemfaultLog, Test_MemfaultLogFrozenBuffer) { // Test that when the log buffer is frozen (because the logging data source // is using it), logs will not be pruned when a log gets written that does // not fit in the remaining space: mock().enable(); mock().expectOneCall("memfault_log_handle_saved_callback"); uint8_t s_ram_log_store[10]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); const char *log0 = "45678"; // 7 bytes with header eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; memfault_log_save_preformatted(level, log0, strlen(log0)); s_fake_data_source_has_been_triggered = true; const char *log1 = "abcde"; // 7 bytes with header memfault_log_save_preformatted(level, log1, strlen(log1)); prv_run_header_check(&s_ram_log_store[0], level, log0, strlen(log0)); } #if !MEMFAULT_COMPACT_LOG_ENABLE TEST(MemfaultLog, Test_MemfaultLogBufTooLongForStorage) { uint8_t s_ram_log_store[5]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); const uint8_t magic_pattern = 0xa5; memset(s_ram_log_store, magic_pattern, sizeof(s_ram_log_store)); // a log that is simply too big for storage should not be a no-op MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Info, "%s", "more than 5 bytes"); for (size_t i = 0; i < sizeof(s_ram_log_store); i++) { LONGS_EQUAL(magic_pattern, s_ram_log_store[i]); } } TEST(MemfaultLog, Test_LevelFiltering) { uint8_t s_ram_log_store[10] = { 0 }; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); const char *filtered_log = "1234"; const size_t filtered_log_len = strlen(filtered_log); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Debug; memfault_log_save_preformatted(level, filtered_log, filtered_log_len); MEMFAULT_LOG_SAVE(level, "%s", filtered_log); // Enable persisting debug logs memfault_log_set_min_save_level(kMemfaultPlatformLogLevel_Debug); const char *unfiltered_log = "woo"; const size_t unfiltered_log_len = strlen(unfiltered_log); memfault_log_save_preformatted(level, unfiltered_log, unfiltered_log_len); MEMFAULT_LOG_SAVE(level, "%s", unfiltered_log); prv_run_header_check(s_ram_log_store, level, unfiltered_log, unfiltered_log_len); prv_run_header_check(&s_ram_log_store[5], level, unfiltered_log, unfiltered_log_len); } TEST(MemfaultLog, Test_DroppedLogs) { uint8_t s_ram_log_store[13] = { 0 }; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; eMemfaultLogRecordType type = kMemfaultLogRecordType_Preformatted; const char *initial_log = "hi world!"; const size_t initial_log_len = strlen(initial_log); memfault_log_save_preformatted(level, initial_log, initial_log_len); prv_read_log_and_check(level, type, initial_log, initial_log_len); for (int i = 0; i < 6; i++) { MEMFAULT_LOG_SAVE(level, "MSG %d", i); } const char *expected_string = "... 5 messages dropped ..."; prv_read_log_and_check(kMemfaultPlatformLogLevel_Warning, type, expected_string, strlen(expected_string)); const char *expected_msg5 = "MSG 5"; prv_read_log_and_check(level, type, expected_msg5, strlen(expected_msg5)); } #endif // !MEMFAULT_COMPACT_LOG_ENABLE #if defined(__clang__) __attribute__((no_sanitize("undefined"))) #else __attribute__((no_sanitize_undefined)) #endif static bool prv_log_entry_copy_callback(sMfltLogIterator *iter, size_t offset, const char *buf, size_t buf_len) { return mock() .actualCall(__func__) .withPointerParameter("iter", iter) .withUnsignedLongIntParameter("offset", offset) .withConstPointerParameter("buf", buf) .withUnsignedLongIntParameter("buf_len", buf_len) .returnBoolValueOrDefault(true); } static bool prv_iterate_callback(sMfltLogIterator *iter) { mock() .actualCall("prv_iterate_callback") .withUnsignedLongIntParameter("read_offset", iter->read_offset); memfault_log_iter_copy_msg(iter, prv_log_entry_copy_callback); return true; } TEST(MemfaultLog, Test_Iterate) { uint8_t s_ram_log_store[10] = { 0 }; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; const char *log0 = "0"; const size_t log0_len = strlen(log0); memfault_log_save_preformatted(level, log0, log0_len); const char *log1 = "1"; const size_t log1_len = strlen(log1); memfault_log_save_preformatted(level, log1, log1_len); sMfltLogIterator iterator = (sMfltLogIterator){ 0 }; mock().enable(); mock().expectOneCall("prv_iterate_callback").withUnsignedLongIntParameter("read_offset", 0); mock() .expectOneCall("prv_log_entry_copy_callback") .withPointerParameter("iter", &iterator) .withUnsignedLongIntParameter("offset", 0) .withConstPointerParameter("buf", &s_ram_log_store[2]) .withUnsignedLongIntParameter("buf_len", 1); mock().expectOneCall("prv_iterate_callback").withUnsignedLongIntParameter("read_offset", 3); mock() .expectOneCall("prv_log_entry_copy_callback") .withPointerParameter("iter", &iterator) .withUnsignedLongIntParameter("offset", 0) .withConstPointerParameter("buf", &s_ram_log_store[5]) .withUnsignedLongIntParameter("buf_len", 1); memfault_log_iterate(prv_iterate_callback, &iterator); } TEST(MemfaultLog, Test_LogsExport) { uint8_t s_ram_log_store[128] = { 0 }; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; const char *log0 = "Normal Log"; const size_t log0_len = strlen(log0); memfault_log_save_preformatted(level, log0, log0_len); mock().enable(); mock().expectOneCall("memfault_platform_log_raw").withStringParameter("output", log0); memfault_log_export_logs(); mock().checkExpectations(); } static jmp_buf s_assert_jmp_buf; void memfault_sdk_assert_func(void) { // we make use of longjmp because this is a noreturn function longjmp(s_assert_jmp_buf, -1); } TEST(MemfaultLog, Test_LogExportNullLog) { uint8_t s_ram_log_store[128] = { 0 }; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); if (setjmp(s_assert_jmp_buf) == 0) { memfault_log_export_log(NULL); } } TEST(MemfaultLog, Test_SaveAndRestoreState) { uint8_t s_ram_log_store[20]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; const char *log0 = "Normal Log"; const size_t log0_len = strlen(log0); memfault_log_save_preformatted(level, log0, log0_len); // Save the current state sMfltLogSaveState state = memfault_log_get_state(); void *context_storage = malloc(state.context_len); void *storage_storage = malloc(state.storage_len); memcpy(context_storage, state.context, state.context_len); memcpy(storage_storage, state.storage, state.storage_len); state.context = context_storage; state.storage = storage_storage; // Reset the logger- this will wipe out the internal state memfault_log_reset(); // Zero out the log buffer memset(s_ram_log_store, 0, sizeof(s_ram_log_store)); // Restore the state by rebooting the log system s_memfault_log_restore_state = state; s_memfault_log_restore_state_retval = true; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); s_memfault_log_restore_state_retval = false; free(context_storage); free(storage_storage); // Check that the log is still there prv_run_header_check(s_ram_log_store, level, log0, log0_len); prv_read_log_and_check(level, kMemfaultLogRecordType_Preformatted, log0, log0_len); } #if MEMFAULT_COMPACT_LOG_ENABLE bool memfault_vlog_compact_serialize(sMemfaultCborEncoder *encoder, MEMFAULT_UNUSED uint32_t log_id, MEMFAULT_UNUSED uint32_t compressed_fmt, MEMFAULT_UNUSED va_list args) { size_t mock_compact_log_overlong = mock().getData("mock_compact_log_overlong").getUnsignedIntValue(); if (mock_compact_log_overlong) { // Simulate an overlong log by returning false encoder->encoded_size = mock_compact_log_overlong; encoder->status = MEMFAULT_CBOR_ENCODER_STATUS_ENOMEM; return false; } uint8_t *mock_compact_log = (uint8_t *)mock().getData("mock_compact_log").getPointerValue(); size_t mock_compact_log_len = mock().getData("mock_compact_log_len").getUnsignedIntValue(); return memfault_cbor_join(encoder, mock_compact_log, mock_compact_log_len); } bool memfault_vlog_compact_serialize_fallback_entry(MEMFAULT_UNUSED sMemfaultCborEncoder *encoder, MEMFAULT_UNUSED uint32_t log_id, MEMFAULT_UNUSED uint32_t serialized_len) { return false; } TEST(MemfaultLog, Test_CompactLog) { uint8_t s_ram_log_store[20]; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; eMemfaultLogRecordType type = kMemfaultLogRecordType_Compact; uint8_t mock_compact_log[] = { 0x01, 0x02, 0x03, 0x04 }; mock().enable(); // Set mock data for log serialization mock().setData("mock_compact_log", mock_compact_log); mock().setData("mock_compact_log_len", (int)sizeof(mock_compact_log)); mock().ignoreOtherCalls(); memfault_compact_log_save(level, 0, 0); // Read the log: prv_read_log_and_check(level, type, mock_compact_log, sizeof(mock_compact_log)); } TEST(MemfaultLog, Test_CompactLogsExport) { uint8_t s_ram_log_store[40] = { 0 }; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; uint8_t mock_compact_log[] = { 0x01, 0x02, 0x03, 0x04 }; mock().enable(); // Set mock data for log serialization mock().setData("mock_compact_log", mock_compact_log); mock().setData("mock_compact_log_len", (int)sizeof(mock_compact_log)); // Verify that compact log chunk is output mock().expectOneCall("memfault_platform_log_raw").withStringParameter("output", "ML:AQIDBA==:"); mock().ignoreOtherCalls(); memfault_compact_log_save(level, 0, 0, 0); memfault_log_export_logs(); mock().checkExpectations(); } TEST(MemfaultLog, Test_CompactLogsExportMultiChunk) { uint8_t s_ram_log_store[40] = { 0 }; memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); eMemfaultPlatformLogLevel level = kMemfaultPlatformLogLevel_Info; // Initialize mock log data twice the size of max chunk to ensure two log chunks uint8_t multi_chunk_compact_log[MEMFAULT_LOG_EXPORT_CHUNK_MAX_LEN * 2]; for (uint8_t i = 0; i < sizeof(multi_chunk_compact_log); i++) { multi_chunk_compact_log[i] = i; } mock().enable(); // Set mock data for log serialization mock().setData("mock_compact_log", multi_chunk_compact_log); mock().setData("mock_compact_log_len", (int)sizeof(multi_chunk_compact_log)); // Verify two compact log chunks are output with correct payload mock() .expectOneCall("memfault_platform_log_raw") .withStringParameter("output", "ML:AAECAwQFBgcICQ==:"); mock() .expectOneCall("memfault_platform_log_raw") .withStringParameter("output", "ML:CgsMDQ4PEBESEw==:"); mock().ignoreOtherCalls(); memfault_compact_log_save(level, 0, 0, 0); memfault_log_export_logs(); mock().checkExpectations(); } #endif ================================================ FILE: tests/unit/src/test_memfault_log_data_source.cpp ================================================ //! @file //! //! @brief #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include #include #include #include #include "fakes/fake_memfault_platform_metrics_locking.h" #include "fakes/fake_memfault_platform_time.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/log.h" #include "memfault/core/log_impl.h" #include "memfault_log_data_source_private.h" } // The RAM log buffer is exactly sized to fit 4 logs and metadata static uint8_t s_ram_log_store[MEMFAULT_LOG_TIMESTAMPS_ENABLE ? 90 : 58]; static sMemfaultCurrentTime s_current_time; static void prv_time_inc(int secs) { s_current_time.info.unix_timestamp_secs += (uint64_t)secs; fake_memfault_platform_time_set(&s_current_time); } TEST_GROUP(MemfaultLogDataSource) { void setup() { fake_memfault_platform_time_enable(true); s_current_time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = MEMFAULT_LOG_TIMESTAMPS_ENABLE ? 0x12345678 : 0, }, }; fake_memfault_platform_time_set(&s_current_time); memset(s_ram_log_store, 0, sizeof(s_ram_log_store)); memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); memfault_log_set_min_save_level(kMemfaultPlatformLogLevel_Debug); } void teardown() { memfault_log_data_source_reset(); memfault_log_reset(); mock().checkExpectations(); mock().clear(); } }; static size_t prv_add_logs(void) { memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Debug, "debug", strlen("debug")); memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Info, "info", strlen("info")); memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Warning, "warning", strlen("warning")); memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Error, "error", strlen("error")); return 4; } #if MEMFAULT_LOG_TIMESTAMPS_ENABLE static const size_t expected_encoded_size = 83; static const uint8_t expected_encoded_buffer[expected_encoded_size] = { 0xA7, // map of 7 key-value pairs 0x02, 0x06, // key 2 (event type), kMemfaultEventType_LogsTimestamped 0x03, 0x01, // key 3 (cbor schema version), 1 0x0A, 0x64, 'm', 'a', 'i', 'n', // key 10 (sw type), "main" 0x09, 0x65, '1', '.', '2', '.', '3', // key 9 (sw version), "1.2.3" 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', // key 6 (hw version), "evt_24" 0x01, 0x1A, 0x12, 0x34, 0x56, 0x78, // key 1 (captured date), 0x12345678 0x04, 0x8C, // key 4, 8-element array 0x1A, 0x12, 0x34, 0x56, 0x78, // timestamp 0x12345678 0x00, // level 0 (debug) 0x65, 'd', 'e', 'b', 'u', 'g', // "debug" 0x1A, 0x12, 0x34, 0x56, 0x78, // timestamp 0x12345678 0x01, // level 1 (info) 0x64, 'i', 'n', 'f', 'o', // "info" 0x1A, 0x12, 0x34, 0x56, 0x78, // timestamp 0x12345678 0x02, // level 2 (warning) 0x67, 'w', 'a', 'r', 'n', 'i', 'n', 'g', // "warning" 0x1A, 0x12, 0x34, 0x56, 0x78, // timestamp 0x12345678 0x03, // level 3 (error) 0x65, 'e', 'r', 'r', 'o', 'r', // "error" }; #else static const size_t expected_encoded_size = 59; static const uint8_t expected_encoded_buffer[expected_encoded_size] = { 0xA7, // map of 7 key-value pairs 0x02, 0x04, // key 2 (event type), kMemfaultEventType_Logs 0x03, 0x01, // key 3 (cbor schema version), 1 0x0A, 0x64, 'm', 'a', 'i', 'n', // key 10 (sw type), "main" 0x09, 0x65, '1', '.', '2', '.', '3', // key 9 (sw version), "1.2.3" 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', // key 6 (hw version), "evt_24" 0x01, 0x00, // key 1 (captured date), 0 0x04, 0x88, // key 4, 8-element array 0x00, // level 0 (debug) 0x65, 'd', 'e', 'b', 'u', 'g', // "debug" 0x01, // level 1 (info) 0x64, 'i', 'n', 'f', 'o', // "info" 0x02, // level 2 (warning) 0x67, 'w', 'a', 'r', 'n', 'i', 'n', 'g', // "warning" 0x03, // level 3 (error) 0x65, 'e', 'r', 'r', 'o', 'r', // "error" }; #endif TEST(MemfaultLogDataSource, Test_TriggerOnce) { prv_add_logs(); memfault_log_trigger_collection(); CHECK_TRUE(memfault_log_data_source_has_been_triggered()); // Idempotent: memfault_log_trigger_collection(); } TEST(MemfaultLogDataSource, Test_TriggerNoopWhenNoLogs) { memfault_log_trigger_collection(); CHECK_FALSE(memfault_log_data_source_has_been_triggered()); } TEST(MemfaultLogDataSource, Test_TriggerNoopWhenNoUnsentLogs) { prv_add_logs(); memfault_log_trigger_collection(); g_memfault_log_data_source.mark_msg_read_cb(); // All logs in the buffer have been marked as sent. Triggering again should be a no-op: memfault_log_trigger_collection(); CHECK_FALSE(memfault_log_data_source_has_been_triggered()); } TEST(MemfaultLogDataSource, Test_HasMoreMsgsCbNotTriggered) { // Buffer contains a log, but memfault_log_trigger_collection() has not been called: prv_add_logs(); size_t size = 0; CHECK_FALSE(g_memfault_log_data_source.has_more_msgs_cb(&size)); LONGS_EQUAL(0, size); } TEST(MemfaultLogDataSource, Test_HasMoreMsgs) { prv_add_logs(); memfault_log_trigger_collection(); size_t size = 0; CHECK_TRUE(g_memfault_log_data_source.has_more_msgs_cb(&size)); LONGS_EQUAL(expected_encoded_size, size); } TEST(MemfaultLogDataSource, Test_ReadMsg) { prv_add_logs(); memfault_log_trigger_collection(); // These logs will not get included, because they happened after the // memfault_log_trigger_collection() call: prv_add_logs(); uint8_t cbor_buffer[expected_encoded_size] = { 0 }; for (size_t offset = 0; offset < expected_encoded_size; ++offset) { const uint8_t canary = 0x55; uint8_t byte[3] = { canary, 0x00, canary }; CHECK_TRUE(g_memfault_log_data_source.read_msg_cb(offset, &byte[1], 1)); // Check canary values to detect buffer overruns: BYTES_EQUAL(byte[0], canary); BYTES_EQUAL(byte[2], canary); cbor_buffer[offset] = byte[1]; // The time encoded in the message should be the time when memfault_log_trigger_collection() // was called. Time passing while draining the data source should not affect the message: prv_time_inc(131); } MEMCMP_EQUAL(expected_encoded_buffer, cbor_buffer, expected_encoded_size); } TEST(MemfaultLogDataSource, Test_MarkMsgRead) { const size_t num_batch_logs_1 = prv_add_logs(); const size_t size_batch_logs_1 = MEMFAULT_LOG_TIMESTAMPS_ENABLE ? 41 : 25; LONGS_EQUAL(num_batch_logs_1, memfault_log_data_source_count_unsent_logs()); sMfltLogUnsentCount unsent_count = memfault_log_get_unsent_count(); LONGS_EQUAL(num_batch_logs_1, unsent_count.num_logs); LONGS_EQUAL(size_batch_logs_1, unsent_count.bytes); memfault_log_trigger_collection(); // These logs will not get included, because they happened after the // memfault_log_trigger_collection() call: const size_t num_batch_logs_2 = prv_add_logs(); const size_t size_batch_logs_2 = MEMFAULT_LOG_TIMESTAMPS_ENABLE ? 41 : 25; LONGS_EQUAL(num_batch_logs_1 + num_batch_logs_2, memfault_log_data_source_count_unsent_logs()); unsent_count = memfault_log_get_unsent_count(); LONGS_EQUAL(num_batch_logs_1 + num_batch_logs_2, unsent_count.num_logs); LONGS_EQUAL(size_batch_logs_1 + size_batch_logs_2, unsent_count.bytes); g_memfault_log_data_source.mark_msg_read_cb(); LONGS_EQUAL(num_batch_logs_2, memfault_log_data_source_count_unsent_logs()); unsent_count = memfault_log_get_unsent_count(); LONGS_EQUAL(num_batch_logs_2, unsent_count.num_logs); LONGS_EQUAL(size_batch_logs_2, unsent_count.bytes); } ================================================ FILE: tests/unit/src/test_memfault_log_with_timestamps.cpp ================================================ //! @file //! //! @brief // #include // #include // #include // #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/core/log.h" #include "memfault/core/log_impl.h" #include "memfault/core/platform/system_time.h" #include "memfault_log_data_source_private.h" #include "memfault_log_private.h" static bool s_fake_data_source_has_been_triggered; extern "C" { bool memfault_log_data_source_has_been_triggered(void) { return s_fake_data_source_has_been_triggered; } } TEST_GROUP(MemfaultLogWithTimestamp) { void setup() { fake_memfault_metrics_platform_locking_reboot(); s_fake_data_source_has_been_triggered = false; } void teardown() { CHECK(fake_memfault_platform_metrics_lock_calls_balanced()); memfault_log_reset(); mock().checkExpectations(); mock().clear(); } }; static void prv_run_header_check(uint8_t *log_entry, eMemfaultPlatformLogLevel expected_level, const char *expected_log, size_t expected_log_len, bool has_timestamp, uint32_t expected_timestamp) { // NOTE: Since sMfltRamLogEntry is serialized out, we manually check the header here instead of // sharing the struct definition to catch unexpected changes in the layout. LONGS_EQUAL(expected_level & 0x03, log_entry[0] & 0x03); LONGS_EQUAL(expected_timestamp ? 0x10 : 0, log_entry[0] & 0x10); LONGS_EQUAL(expected_log_len & 0xff, log_entry[1]); if (has_timestamp) { uint32_t timestamp_val; memcpy(×tamp_val, &log_entry[2], sizeof(timestamp_val)); LONGS_EQUAL(expected_timestamp, timestamp_val); MEMCMP_EQUAL(expected_log, &log_entry[2 + 4], expected_log_len - 4); } else { MEMCMP_EQUAL(expected_log, &log_entry[2], expected_log_len); } } static void prv_read_log_and_check(eMemfaultPlatformLogLevel expected_level, eMemfaultLogRecordType expected_type, const void *expected_log, size_t expected_log_len, uint32_t expected_timestamp) { sMemfaultLog log; // scribble a bad pattern to make sure memfault_log_read inits things memset(&log, 0xa5, sizeof(log)); const bool found_log = memfault_log_read(&log); CHECK(found_log); LONGS_EQUAL(expected_log_len, log.msg_len); if (expected_type == kMemfaultLogRecordType_Preformatted) { STRCMP_EQUAL((const char *)expected_log, log.msg); } else { MEMCMP_EQUAL(expected_log, log.msg, expected_log_len); } LONGS_EQUAL(expected_level, log.level); LONGS_EQUAL(expected_type, log.type); LONGS_EQUAL(expected_timestamp, log.timestamp); } TEST(MemfaultLogWithTimestamp, Test_MemfaultLogBasic) { uint8_t s_ram_log_store[24]; CHECK_FALSE(memfault_log_booted()); memfault_log_boot(s_ram_log_store, sizeof(s_ram_log_store)); CHECK(memfault_log_booted()); const char *my_log = "12345678"; const char *my_log2 = "abcdefgh"; const size_t my_log_len = strlen(my_log); eMemfaultLogRecordType type = kMemfaultLogRecordType_Preformatted; sMemfaultCurrentTime time = { .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = 0x12345678, }, }; mock() .expectOneCall("memfault_platform_time_get_current") .withOutputParameterReturning("time", (void *)&time, sizeof(time)) .andReturnValue(false); memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Info, my_log, my_log_len); // Write a second log, to exercise the s_memfault_ram_logger.log_read_offset // book-keeping: mock() .expectOneCall("memfault_platform_time_get_current") .withOutputParameterReturning("time", (void *)&time, sizeof(time)) .andReturnValue(true); memfault_log_save_preformatted(kMemfaultPlatformLogLevel_Error, my_log2, my_log_len); // First log has no timestamp prv_run_header_check(s_ram_log_store, kMemfaultPlatformLogLevel_Info, my_log, my_log_len, false, 0); // Second log has a timestamp prv_run_header_check(&s_ram_log_store[10], kMemfaultPlatformLogLevel_Error, my_log2, my_log_len + 4, true, 0x12345678); // Read the two logs: prv_read_log_and_check(kMemfaultPlatformLogLevel_Info, type, my_log, my_log_len, 0); prv_read_log_and_check(kMemfaultPlatformLogLevel_Error, type, my_log2, my_log_len, 0x12345678); // should be no more logs sMemfaultLog log; const bool log_found = memfault_log_read(&log); CHECK(!log_found); } ================================================ FILE: tests/unit/src/test_memfault_metrics_battery.cpp ================================================ #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "comparators/comparator_memfault_metric_ids.hpp" #include "memfault/core/platform/core.h" #include "memfault/metrics/battery.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/battery.h" static const MemfaultMetricId s_metricskey_battery_discharge_duration_ms = MEMFAULT_METRICS_KEY(battery_discharge_duration_ms); static const MemfaultMetricId s_metricskey_battery_soc_pct_drop = MEMFAULT_METRICS_KEY(battery_soc_pct_drop); static const MemfaultMetricId s_metricskey_battery_soc_pct = MEMFAULT_METRICS_KEY(battery_soc_pct); extern "C" { int memfault_platform_get_stateofcharge(sMfltPlatformBatterySoc *soc) { return mock().actualCall(__func__).withOutputParameter("soc", soc).returnIntValueOrDefault(0); } // fake instead of mock for simplicity sake static uint64_t s_fake_time_ms = 0; uint64_t memfault_platform_get_time_since_boot_ms(void) { return s_fake_time_ms; } static void prv_fake_time_set(uint64_t new_fake_time_ms) { s_fake_time_ms = new_fake_time_ms; } static void prv_fake_time_incr(uint64_t fake_time_delta_ms) { s_fake_time_ms += fake_time_delta_ms; } } // helper for setting up memfault_platform_get_stateofcharge mock invocations. // allocates a structure with the specified soc and discharge state, and adds it // to a vector for later cleanup, then sets the mock. // vector of allocated sMfltPlatformBatterySoc structs, for later cleanup static std::vector s_soc_structs; static void prv_mock_memfault_platform_get_stateofcharge(uint32_t soc, bool discharging, int ret = 0) { sMfltPlatformBatterySoc *soc_struct = (sMfltPlatformBatterySoc *)malloc(sizeof(sMfltPlatformBatterySoc)); s_soc_structs.push_back(soc_struct); soc_struct->soc = soc; soc_struct->discharging = discharging; mock() .expectOneCall("memfault_platform_get_stateofcharge") .withOutputParameterReturning("soc", soc_struct, sizeof(sMfltPlatformBatterySoc)) .andReturnValue(ret); } // clang-format off TEST_GROUP(BatteryMetrics) { void setup(){ s_fake_time_ms = 0; mock().strictOrder(); static MemfaultMetricIdsComparator s_metric_id_comparator; mock().installComparator("MemfaultMetricId", s_metric_id_comparator); } void teardown(){ mock().checkExpectations(); mock().removeAllComparatorsAndCopiers(); mock().clear(); // clean up s_soc_structs by freeing all elements. print for each one! std::for_each(s_soc_structs.begin(), s_soc_structs.end(), [](void* n) { free(n); }); s_soc_structs.clear(); s_soc_structs.shrink_to_fit(); } }; // clang-format on TEST(BatteryMetrics, Basic) { // start with battery discharging prv_mock_memfault_platform_get_stateofcharge(100, true); prv_fake_time_set(UINT64_MAX - 99); memfault_metrics_battery_boot(); // 1% drop prv_mock_memfault_platform_get_stateofcharge(99, true); // 100ms elapsed (wraps) prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct_drop) .withParameter("unsigned_value", 1); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_discharge_duration_ms) .withParameter("unsigned_value", 100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 99); memfault_metrics_battery_collect_data(); }; TEST(BatteryMetrics, ChargeConnectEvent) { // start with battery discharging prv_mock_memfault_platform_get_stateofcharge(100, true); memfault_metrics_battery_boot(); // stopped-discharging event memfault_metrics_battery_stopped_discharging(); // 1% drop prv_mock_memfault_platform_get_stateofcharge(99, false); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 99); memfault_metrics_battery_collect_data(); } TEST(BatteryMetrics, NegativeDrop) { // start with battery discharging prv_mock_memfault_platform_get_stateofcharge(99, true); memfault_metrics_battery_boot(); // -1% drop prv_mock_memfault_platform_get_stateofcharge(100, true); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 100); memfault_metrics_battery_collect_data(); } TEST(BatteryMetrics, StateMismatchAtSessionEnd) { // start with battery discharging prv_mock_memfault_platform_get_stateofcharge(100, true); memfault_metrics_battery_boot(); // return not discharging, without first calling memfault_metrics_battery_stopped_discharging() // 1% drop prv_mock_memfault_platform_get_stateofcharge(99, false); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 99); memfault_metrics_battery_collect_data(); } TEST(BatteryMetrics, StateMismatchAtSessionEnd2) { // start with battery discharging prv_mock_memfault_platform_get_stateofcharge(100, true); memfault_metrics_battery_boot(); // stopped-discharging event (charger connected) memfault_metrics_battery_stopped_discharging(); // return discharging, so charger was disconnected // 1% drop prv_mock_memfault_platform_get_stateofcharge(99, true); // 100ms elapsed prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 99); memfault_metrics_battery_collect_data(); // advance another interval, but charger is still disconnected. the second // interval should now be valid. // 0% drop! prv_mock_memfault_platform_get_stateofcharge(99, true); // 100ms elapsed prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct_drop) .withParameter("unsigned_value", 0); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_discharge_duration_ms) .withParameter("unsigned_value", 100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 99); memfault_metrics_battery_collect_data(); } // invalidate first and second interval when charger connects in first interval // and disconnects in the second interval TEST(BatteryMetrics, TwoIntervals2) { // start with battery discharging prv_mock_memfault_platform_get_stateofcharge(100, true); memfault_metrics_battery_boot(); // stopped-discharging event (charger connected) memfault_metrics_battery_stopped_discharging(); // return not-discharging (charger connected) // 1% drop prv_mock_memfault_platform_get_stateofcharge(99, false); // 100ms elapsed prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 99); memfault_metrics_battery_collect_data(); // advance another hour, but charger is still connected. the second hour // should also be invalid // -1% drop prv_mock_memfault_platform_get_stateofcharge(100, false); // 100ms elapsed prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 100); memfault_metrics_battery_collect_data(); // and advance one more hour, this time with the charger disconnected at the // end of the hour // 1% drop prv_mock_memfault_platform_get_stateofcharge(99, true); // 100ms elapsed prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 99); memfault_metrics_battery_collect_data(); // and FINALLY, the 4th hour will be a valid discharging session // 1% drop prv_mock_memfault_platform_get_stateofcharge(98, true); // 100ms elapsed prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct_drop) .withParameter("unsigned_value", 1); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_discharge_duration_ms) .withParameter("unsigned_value", 100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 98); memfault_metrics_battery_collect_data(); } TEST(BatteryMetrics, PerMilleSOC) { // just for fun, show that per-mille works correctly too // start with battery discharging prv_mock_memfault_platform_get_stateofcharge(1000, true); memfault_metrics_battery_boot(); // .1% drop prv_mock_memfault_platform_get_stateofcharge(999, true); // 100ms elapsed prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct_drop) .withParameter("unsigned_value", 1); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_discharge_duration_ms) .withParameter("unsigned_value", 100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 999); memfault_metrics_battery_collect_data(); } TEST(BatteryMetrics, InvalidSOC) { // start with battery at invalid soc prv_mock_memfault_platform_get_stateofcharge(1234, true, -1); memfault_metrics_battery_boot(); // invalid at collection time too prv_mock_memfault_platform_get_stateofcharge(1234, true, -1); memfault_metrics_battery_collect_data(); // now valid; but interval is invalid, so should not be recorded prv_mock_memfault_platform_get_stateofcharge(0, true); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 0); memfault_metrics_battery_collect_data(); // Now set current soc to a weird (but permitted) value prv_mock_memfault_platform_get_stateofcharge(101, true); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 101); memfault_metrics_battery_collect_data(); // now finally, a valid interval prv_mock_memfault_platform_get_stateofcharge(100, true); prv_fake_time_incr(100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct_drop) .withParameter("unsigned_value", 1); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_discharge_duration_ms) .withParameter("unsigned_value", 100); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_metricskey_battery_soc_pct) .withParameter("unsigned_value", 100); memfault_metrics_battery_collect_data(); } ================================================ FILE: tests/unit/src/test_memfault_metrics_connectivity.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "comparators/comparator_memfault_metric_ids.hpp" #include "memfault/metrics/connectivity.h" #include "memfault/metrics/metrics.h" static MemfaultMetricIdsComparator s_metric_id_comparator; // These have to have global scope, so the test teardown can access them static MemfaultMetricId sync_successful_key = MEMFAULT_METRICS_KEY(sync_successful); static MemfaultMetricId sync_failure_key = MEMFAULT_METRICS_KEY(sync_failure); static MemfaultMetricId memfault_sync_successful_key = MEMFAULT_METRICS_KEY(sync_memfault_successful); static MemfaultMetricId memfault_sync_failure_key = MEMFAULT_METRICS_KEY(sync_memfault_failure); static MemfaultMetricId connectivity_connected_time_ms_key = MEMFAULT_METRICS_KEY(connectivity_connected_time_ms); static MemfaultMetricId connectivity_expected_time_ms_key = MEMFAULT_METRICS_KEY(connectivity_expected_time_ms); // clang-format off TEST_GROUP(MemfaultMetricsConnectivity){ void setup() { mock().strictOrder(); mock().installComparator("MemfaultMetricId", s_metric_id_comparator); } void teardown() { mock().checkExpectations(); mock().removeAllComparatorsAndCopiers(); mock().clear(); } }; // clang-format on TEST(MemfaultMetricsConnectivity, Test_SyncMetrics) { // call the basic functions mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &sync_successful_key) .withParameter("amount", 1) .andReturnValue(0); memfault_metrics_connectivity_record_sync_success(); mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &sync_failure_key) .withParameter("amount", 1) .andReturnValue(0); memfault_metrics_connectivity_record_sync_failure(); mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &memfault_sync_successful_key) .withParameter("amount", 1) .andReturnValue(0); memfault_metrics_connectivity_record_memfault_sync_success(); mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &memfault_sync_failure_key) .withParameter("amount", 1) .andReturnValue(0); memfault_metrics_connectivity_record_memfault_sync_failure(); } // Tests for memfault_metrics_connectivity_connected_state_change TEST(MemfaultMetricsConnectivity, Test_ConnectedStateChange) { // walk through the state transitions // start mock() .expectOneCall("memfault_metrics_heartbeat_timer_start") .withParameterOfType("MemfaultMetricId", "key", &connectivity_expected_time_ms_key) .andReturnValue(0); memfault_metrics_connectivity_connected_state_change(kMemfaultMetricsConnectivityState_Started); // connect mock() .expectOneCall("memfault_metrics_heartbeat_timer_start") .withParameterOfType("MemfaultMetricId", "key", &connectivity_expected_time_ms_key) .andReturnValue(0); mock() .expectOneCall("memfault_metrics_heartbeat_timer_start") .withParameterOfType("MemfaultMetricId", "key", &connectivity_connected_time_ms_key) .andReturnValue(0); memfault_metrics_connectivity_connected_state_change(kMemfaultMetricsConnectivityState_Connected); // disconnect mock() .expectOneCall("memfault_metrics_heartbeat_timer_start") .withParameterOfType("MemfaultMetricId", "key", &connectivity_expected_time_ms_key) .andReturnValue(0); mock() .expectOneCall("memfault_metrics_heartbeat_timer_stop") .withParameterOfType("MemfaultMetricId", "key", &connectivity_connected_time_ms_key) .andReturnValue(0); memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_ConnectionLost); // stop mock() .expectOneCall("memfault_metrics_heartbeat_timer_stop") .withParameterOfType("MemfaultMetricId", "key", &connectivity_connected_time_ms_key) .andReturnValue(0); mock() .expectOneCall("memfault_metrics_heartbeat_timer_stop") .withParameterOfType("MemfaultMetricId", "key", &connectivity_expected_time_ms_key) .andReturnValue(0); memfault_metrics_connectivity_connected_state_change(kMemfaultMetricsConnectivityState_Stopped); // junk value, should be ignored memfault_metrics_connectivity_connected_state_change((eMemfaultMetricsConnectivityState)-1); // test connection_lost->started->connected too. expected timer should run the whole time // connection_lost mock() .expectOneCall("memfault_metrics_heartbeat_timer_start") .withParameterOfType("MemfaultMetricId", "key", &connectivity_expected_time_ms_key) .andReturnValue(0); mock() .expectOneCall("memfault_metrics_heartbeat_timer_stop") .withParameterOfType("MemfaultMetricId", "key", &connectivity_connected_time_ms_key) .andReturnValue(0); // started memfault_metrics_connectivity_connected_state_change( kMemfaultMetricsConnectivityState_ConnectionLost); mock() .expectOneCall("memfault_metrics_heartbeat_timer_start") .withParameterOfType("MemfaultMetricId", "key", &connectivity_expected_time_ms_key) .andReturnValue(0); memfault_metrics_connectivity_connected_state_change(kMemfaultMetricsConnectivityState_Started); // connected mock() .expectOneCall("memfault_metrics_heartbeat_timer_start") .withParameterOfType("MemfaultMetricId", "key", &connectivity_expected_time_ms_key) .andReturnValue(0); mock() .expectOneCall("memfault_metrics_heartbeat_timer_start") .withParameterOfType("MemfaultMetricId", "key", &connectivity_connected_time_ms_key) .andReturnValue(0); memfault_metrics_connectivity_connected_state_change(kMemfaultMetricsConnectivityState_Connected); } ================================================ FILE: tests/unit/src/test_memfault_metrics_reliability.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "comparators/comparator_memfault_metric_ids.hpp" #include "memfault/core/platform/core.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/reliability.h" static MemfaultMetricIdsComparator s_metric_id_comparator; // These have to have global scope, so the test teardown can access them static MemfaultMetricId operational_hours_key = MEMFAULT_METRICS_KEY(operational_hours); static MemfaultMetricId operational_crashfree_hours_key = MEMFAULT_METRICS_KEY(operational_crashfree_hours); extern "C" { static uint64_t s_fake_time_ms = 0; uint64_t memfault_platform_get_time_since_boot_ms(void) { return s_fake_time_ms; } static void prv_fake_time_incr(uint64_t fake_time_delta_ms) { s_fake_time_ms += fake_time_delta_ms; } } // clang-format off TEST_GROUP(MemfaultMetricsReliability){ void setup() { s_fake_time_ms = 0; mock().strictOrder(); mock().installComparator("MemfaultMetricId", s_metric_id_comparator); // zero state sMemfaultMetricsReliabilityCtx ctx = { 0 }; memfault_metrics_reliability_boot(&ctx); } void teardown() { mock().checkExpectations(); mock().removeAllComparatorsAndCopiers(); mock().clear(); } }; // 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); // no mocks should be called memfault_metrics_reliability_collect(); // 1 ms more (1 hr total) prv_fake_time_incr(1); bool unexpected_reboot = true; mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &operational_hours_key) .withParameter("amount", 1) .andReturnValue(0); mock() .expectOneCall("memfault_reboot_tracking_get_unexpected_reboot_occurred") .withOutputParameterReturning("unexpected_reboot_occurred", &unexpected_reboot, sizeof(unexpected_reboot)) .andReturnValue(0); mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &operational_crashfree_hours_key) .withParameter("amount", 0) .andReturnValue(0); memfault_metrics_reliability_collect(); // 1 hr more 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(); // 1 ms more (2hr 1ms total) 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(); } //! Test that pre-seeding with memfault_metrics_reliability_boot() yields //! correct operational time TEST(MemfaultMetricsReliability, Test_Boot) { sMemfaultMetricsReliabilityCtx ctx = { .last_heartbeat_ms = 1000u * 60u * 60u * 1000u, // 1k hours .operational_ms = 1000, // accumulated 1 second of operational time .counted_unexpected_reboot = 1, }; memfault_metrics_reliability_boot(&ctx); prv_fake_time_incr(ctx.last_heartbeat_ms); // advance by 1 hr - 999 ms uint32_t operational_hours = 1; prv_fake_time_incr(operational_hours * 60 * 60 * 1000 - 999); mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &operational_hours_key) .withParameter("amount", operational_hours) .andReturnValue(0); mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &operational_crashfree_hours_key) .withParameter("amount", operational_hours) .andReturnValue(0); memfault_metrics_reliability_collect(); } TEST(MemfaultMetricsReliability, Test_BootTimeSinceBootNonZero) { // test cold booting with time-since-boot non-zero (i.e. rtc backed) prv_fake_time_incr(1000u * 60u * 60u * 1000u); // 1k hours memfault_metrics_reliability_boot(NULL); // advance by 1 hr uint32_t operational_hours = 1; prv_fake_time_incr(operational_hours * 60 * 60 * 1000); bool unexpected_reboot = false; mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &operational_hours_key) .withParameter("amount", operational_hours) .andReturnValue(0); mock() .expectOneCall("memfault_reboot_tracking_get_unexpected_reboot_occurred") .withOutputParameterReturning("unexpected_reboot_occurred", &unexpected_reboot, sizeof(unexpected_reboot)) .andReturnValue(0); mock() .expectOneCall("memfault_metrics_heartbeat_add") .withParameterOfType("MemfaultMetricId", "key", &operational_crashfree_hours_key) .withParameter("amount", operational_hours) .andReturnValue(0); memfault_metrics_reliability_collect(); } ================================================ FILE: tests/unit/src/test_memfault_metrics_serializer.cpp ================================================ //! @file //! //! @brief #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_event_storage.h" #include "memfault/core/event_storage.h" #include "memfault/core/serializer_helper.h" #include "memfault/metrics/serializer.h" #include "memfault/metrics/utils.h" static const sMemfaultEventStorageImpl *s_fake_event_storage_impl; #define FAKE_EVENT_STORAGE_SIZE 58 // clang-format off TEST_GROUP(MemfaultMetricsSerializer){ void setup() { static uint8_t s_storage[FAKE_EVENT_STORAGE_SIZE]; s_fake_event_storage_impl = memfault_events_storage_boot( &s_storage, sizeof(s_storage)); } void teardown() { mock().checkExpectations(); mock().clear(); } }; // clang-format on // // For the purposes of our serialization test, we will // just serialize 1 of each supported type, two unset // integers (signed + unsigned), and a non-heartbeat // session metric for a total of 7 metrics. // void memfault_metrics_heartbeat_iterate(MemfaultMetricIteratorCallback cb, void *ctx) { sMemfaultMetricInfo info = { 0 }; // Note: info.key._impl is not needed for serialization so leaving blank info.session_key = MEMFAULT_METRICS_SESSION_KEY(heartbeat); info.type = kMemfaultMetricType_Unsigned; info.val.u32 = 1000; info.is_set = true; cb(ctx, &info); // Test a session metric info.session_key = MEMFAULT_METRICS_SESSION_KEY(test_key_session); cb(ctx, &info); // Test an unset unsigned metric info.session_key = MEMFAULT_METRICS_SESSION_KEY(heartbeat); info.is_set = false; cb(ctx, &info); info.type = kMemfaultMetricType_Signed; info.val.i32 = -1000; info.is_set = true; cb(ctx, &info); // Test an unset signed metric info.is_set = false; cb(ctx, &info); info.type = kMemfaultMetricType_Timer; info.val.u32 = 1234; cb(ctx, &info); info.type = kMemfaultMetricType_String; // chosen to be exactly 16 bytes to match the max storage set in // tests/unit/stub_includes/memfault_metrics_heartbeat_config.def #define SAMPLE_STRING "123456789abcde" uint8_t sample_string[sizeof(SAMPLE_STRING)]; info.val.ptr = sample_string; memcpy(info.val.ptr, SAMPLE_STRING, sizeof(SAMPLE_STRING)); cb(ctx, &info); } size_t memfault_metrics_session_get_num_metrics(eMfltMetricsSessionIndex session_key) { // if this fails, it means we need to add add a report for the new type // to the fake "memfault_metrics_heartbeat_iterate" LONGS_EQUAL(kMemfaultMetricType_NumTypes, 4); size_t num_metrics = 0; if (session_key == MEMFAULT_METRICS_SESSION_KEY(heartbeat)) { // Additionally we test that unset integers (signed + unsigned) are set to null num_metrics = kMemfaultMetricType_NumTypes + 2; } else { // We only have one session metric num_metrics = 1; } return num_metrics; } TEST(MemfaultMetricsSerializer, Test_MemfaultMetricSerialize) { mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", false); memfault_metrics_heartbeat_serialize(s_fake_event_storage_impl); // { // "2": 1, // "3": 1, // "10": "main", // "9": "1.2.3", // "6": "evt_24", // "4": { // "2": 1 // "1": [ 1000, null, -1000, null, 1234, "123456789abcde" ] // } // } const uint8_t expected_serialization[] = { 0xa6, 0x02, 0x01, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xa2, 0x02, MEMFAULT_METRICS_SESSION_KEY(heartbeat), 0x01, 0x86, 0x19, 0x03, 0xe8, 0xf6, 0x39, 0x03, 0xe7, 0xf6, 0x19, 0x04, 0xd2, 0x6e, '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e' }; fake_event_storage_assert_contents_match(expected_serialization, sizeof(expected_serialization)); } TEST(MemfaultMetricsSerializer, Test_MemfaultSessionMetricSerialize) { mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", false); memfault_metrics_session_serialize(s_fake_event_storage_impl, MEMFAULT_METRICS_SESSION_KEY(test_key_session)); // { // "2": 1, // "3": 1, // "10": "main", // "9": "1.2.3", // "6": "evt_24", // "4": { // "2": 0 // "1": [ 1000 ] // } // } const uint8_t expected_serialization[] = { 0xa6, 0x02, 0x01, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xa2, 0x02, 0x00, 0x01, 0x81, 0x19, 0x03, 0xE8 }; fake_event_storage_assert_contents_match(expected_serialization, sizeof(expected_serialization)); } TEST(MemfaultMetricsSerializer, Test_MemfaultHeartbeatMetricSerializeWorstCaseSize) { const size_t worst_case_storage = memfault_metrics_heartbeat_compute_worst_case_storage_size(); LONGS_EQUAL(72, worst_case_storage); } TEST(MemfaultMetricsSerializer, Test_MemfaultSessionMetricSerializeWorstCaseSize) { const size_t worst_case_storage = memfault_metrics_session_compute_worst_case_storage_size( MEMFAULT_METRICS_SESSION_KEY(test_key_session)); LONGS_EQUAL(37, worst_case_storage); } TEST(MemfaultMetricsSerializer, Test_MemfaultMetricSerializeOutOfSpace) { // iterate over all buffer sizes less than the encoding we need // this should exercise all early exit paths for (size_t i = 0; i < FAKE_EVENT_STORAGE_SIZE - 1; i++) { fake_memfault_event_storage_clear(); fake_memfault_event_storage_set_available_space(i); mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", true); memfault_metrics_heartbeat_serialize(s_fake_event_storage_impl); mock().checkExpectations(); } uint32_t drops = memfault_serializer_helper_read_drop_count(); LONGS_EQUAL(FAKE_EVENT_STORAGE_SIZE - 1, drops); drops = memfault_serializer_helper_read_drop_count(); LONGS_EQUAL(0, drops); } TEST(MemfaultMetricsSerializer, Test_MemfaultMetricTypes) { //! These should never change so that the same value can //! always be used to recover the type on the server CHECK_EQUAL(0, kMemfaultMetricType_Unsigned); CHECK_EQUAL(1, kMemfaultMetricType_Signed); CHECK_EQUAL(2, kMemfaultMetricType_Timer); CHECK_EQUAL(3, kMemfaultMetricType_String); //! This can change if new types are appended to the enum //! but we assert here to remind us to add the new type //! to the check here CHECK_EQUAL(4, kMemfaultMetricType_NumTypes); } ================================================ FILE: tests/unit/src/test_memfault_minimal_cbor.cpp ================================================ //! @file //! //! @brief #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include #include #include "memfault/core/math.h" #include "memfault/util/cbor.h" static void prv_write_cb(void *ctx, uint32_t offset, const void *buf, size_t buf_len) { CHECK(ctx != NULL); uint8_t *result_buf = (uint8_t *)ctx; memcpy(&result_buf[offset], buf, buf_len); } } TEST_GROUP(MemfaultMinimalCbor) { void setup() { } void teardown() { } }; static void prv_run_unsigned_integer_encoder_check(uint32_t value, const uint8_t *expected_seq, size_t expected_seq_len) { sMemfaultCborEncoder encoder; uint8_t result[expected_seq_len]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); const bool success = memfault_cbor_encode_unsigned_integer(&encoder, value); CHECK(success); const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(expected_seq_len, encoded_length); MEMCMP_EQUAL(expected_seq, result, expected_seq_len); } TEST(MemfaultMinimalCbor, Test_EncodeUnsignedInt) { // RFC Appendix A. Examples const uint8_t expected_enc_0[] = { 0 }; prv_run_unsigned_integer_encoder_check(0, expected_enc_0, sizeof(expected_enc_0)); const uint8_t expected_enc_1[] = { 1 }; prv_run_unsigned_integer_encoder_check(1, expected_enc_1, sizeof(expected_enc_1)); const uint8_t expected_enc_10[] = { 0x0a }; prv_run_unsigned_integer_encoder_check(10, expected_enc_10, sizeof(expected_enc_10)); const uint8_t expected_enc_23[] = { 0x17 }; prv_run_unsigned_integer_encoder_check(23, expected_enc_23, sizeof(expected_enc_23)); const uint8_t expected_enc_24[] = { 0x18, 0x18 }; prv_run_unsigned_integer_encoder_check(24, expected_enc_24, sizeof(expected_enc_24)); const uint8_t expected_enc_25[] = { 0x18, 0x19 }; prv_run_unsigned_integer_encoder_check(25, expected_enc_25, sizeof(expected_enc_25)); const uint8_t expected_enc_100[] = { 0x18, 0x64 }; prv_run_unsigned_integer_encoder_check(100, expected_enc_100, sizeof(expected_enc_100)); const uint8_t expected_enc_1000[] = { 0x19, 0x03, 0xe8 }; prv_run_unsigned_integer_encoder_check(1000, expected_enc_1000, sizeof(expected_enc_1000)); const uint8_t expected_enc_1000000[] = { 0x1a, 0x00, 0x0f, 0x42, 0x40 }; prv_run_unsigned_integer_encoder_check(1000000, expected_enc_1000000, sizeof(expected_enc_1000000)); // Also try a UINT32_MAX const uint8_t expected_enc_uint32_max[] = { 0x1a, 0xff, 0xff, 0xff, 0xff }; prv_run_unsigned_integer_encoder_check(UINT32_MAX, expected_enc_uint32_max, sizeof(expected_enc_uint32_max)); } static void prv_run_array_encoder_check(const uint32_t *values, size_t num_values, const uint8_t *expected_seq, size_t expected_seq_len) { sMemfaultCborEncoder encoder; uint8_t result[expected_seq_len]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); bool success = memfault_cbor_encode_array_begin(&encoder, num_values); CHECK(success); for (size_t i = 0; i < num_values; i++) { success = memfault_cbor_encode_unsigned_integer(&encoder, values[i]); CHECK(success); } const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(expected_seq_len, encoded_length); MEMCMP_EQUAL(expected_seq, result, expected_seq_len); } TEST(MemfaultMinimalCbor, Test_Join) { // start an encoding for an array char result[4]; memset(result, 0x0, sizeof(result)); sMemfaultCborEncoder encoder; memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); const uint32_t values123[] = { 0x1, 0x2, 0x3 }; bool success = memfault_cbor_encode_array_begin(&encoder, MEMFAULT_ARRAY_SIZE(values123)); CHECK(success); // build list of integers in a separate encoder context sMemfaultCborEncoder encoder2; char cbor_ints[3]; memfault_cbor_encoder_init(&encoder2, prv_write_cb, cbor_ints, sizeof(cbor_ints)); for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(values123); i++) { success = memfault_cbor_encode_unsigned_integer(&encoder2, values123[i]); CHECK(success); } size_t encoded_length = memfault_cbor_encoder_deinit(&encoder2); LONGS_EQUAL(sizeof(cbor_ints), encoded_length); // join results from integer encoding into main encoder success = memfault_cbor_join(&encoder, cbor_ints, encoded_length); const uint8_t three_num_array_expected_encode[] = { 0x83, 0x01, 0x02, 0x03 }; encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(sizeof(three_num_array_expected_encode), encoded_length); MEMCMP_EQUAL(three_num_array_expected_encode, result, sizeof(three_num_array_expected_encode)); } TEST(MemfaultMinimalCbor, Test_EncodeArray) { // RFC Appendix A. Examples // [] const uint8_t empty_array_expected_encode[] = { 0x80 }; prv_run_array_encoder_check(NULL, 0, empty_array_expected_encode, sizeof(empty_array_expected_encode)); // [ 1, 2, 3 ] const uint32_t values123[] = { 0x1, 0x2, 0x3 }; const uint8_t three_num_array_expected_encode[] = { 0x83, 0x01, 0x02, 0x03 }; prv_run_array_encoder_check(values123, MEMFAULT_ARRAY_SIZE(values123), three_num_array_expected_encode, sizeof(three_num_array_expected_encode)); // [ 1, 2, 3 .. 25 ] const uint32_t longer_array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; const uint8_t longer_array_expected_encode[] = { 0x98, 0x19, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19 }; prv_run_array_encoder_check(longer_array, MEMFAULT_ARRAY_SIZE(longer_array), longer_array_expected_encode, sizeof(longer_array_expected_encode)); } static void prv_run_signed_integer_encoder_check(int32_t value, const uint8_t *expected_seq, size_t expected_seq_len) { sMemfaultCborEncoder encoder; uint8_t result[expected_seq_len]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); const bool success = memfault_cbor_encode_signed_integer(&encoder, value); CHECK(success); const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(expected_seq_len, encoded_length); MEMCMP_EQUAL(expected_seq, result, expected_seq_len); } TEST(MemfaultMinimalCbor, Test_EncodeSignedInt) { // RFC Appendix A. Examples const uint8_t expected_enc_neg1[] = { 0x20 }; prv_run_signed_integer_encoder_check(-1, expected_enc_neg1, sizeof(expected_enc_neg1)); const uint8_t expected_enc_neg10[] = { 0x29 }; prv_run_signed_integer_encoder_check(-10, expected_enc_neg10, sizeof(expected_enc_neg10)); const uint8_t expected_enc_neg100[] = { 0x38, 0x63 }; prv_run_signed_integer_encoder_check(-100, expected_enc_neg100, sizeof(expected_enc_neg100)); const uint8_t expected_enc_neg1000[] = { 0x39, 0x03, 0xe7 }; prv_run_signed_integer_encoder_check(-1000, expected_enc_neg1000, sizeof(expected_enc_neg1000)); // positive values should wind up getting encoded as an unsigned integer const uint8_t expected_enc_1000000[] = { 0x1a, 0x00, 0x0f, 0x42, 0x40 }; prv_run_signed_integer_encoder_check(1000000, expected_enc_1000000, sizeof(expected_enc_1000000)); // Also try a INT32_MIN const uint8_t expected_enc_int32_min[] = { 0x3a, 0x7f, 0xff, 0xff, 0xff }; prv_run_signed_integer_encoder_check(INT32_MIN, expected_enc_int32_min, sizeof(expected_enc_int32_min)); } static void prv_run_long_signed_integer_encoder_check(int64_t value, const uint8_t *expected_seq, size_t expected_seq_len) { sMemfaultCborEncoder encoder; uint8_t result[expected_seq_len]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); const bool success = memfault_cbor_encode_long_signed_integer(&encoder, value); CHECK(success); const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(expected_seq_len, encoded_length); MEMCMP_EQUAL(expected_seq, result, expected_seq_len); } TEST(MemfaultMinimalCbor, Test_EncodeLongSignedInt) { // We'll re-run a few of the tests for signed ints because the results should be identical const uint8_t expected_enc_neg1[] = { 0x20 }; prv_run_long_signed_integer_encoder_check(-1, expected_enc_neg1, sizeof(expected_enc_neg1)); // positive values should wind up getting encoded as an unsigned integer const uint8_t expected_enc_1000000[] = { 0x1a, 0x00, 0x0f, 0x42, 0x40 }; prv_run_long_signed_integer_encoder_check(1000000, expected_enc_1000000, sizeof(expected_enc_1000000)); const uint8_t expected_enc_int32_min[] = { 0x3a, 0x7f, 0xff, 0xff, 0xff }; prv_run_long_signed_integer_encoder_check(INT32_MIN, expected_enc_int32_min, sizeof(expected_enc_int32_min)); // NB: Anything > INT32_MIN/MAX should wind up being 9 bytes // RFC Appendix A. Examples const uint8_t expected_enc_1000000000000[] = { 0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00 }; prv_run_long_signed_integer_encoder_check(1000000000000, expected_enc_1000000000000, sizeof(expected_enc_1000000000000)); const uint8_t expected_enc_neg123456789101112[] = { 0x3b, 0x00, 0x00, 0x70, 0x48, 0x86, 0x0f, 0x3a, 0x37 }; prv_run_long_signed_integer_encoder_check(-123456789101112, expected_enc_neg123456789101112, sizeof(expected_enc_neg123456789101112)); } static void prv_run_string_encoder_check(const char *str, const uint8_t *expected_seq, size_t expected_seq_len) { sMemfaultCborEncoder encoder; uint8_t result[expected_seq_len]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); const bool success = memfault_cbor_encode_string(&encoder, str); CHECK(success); const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(expected_seq_len, encoded_length); MEMCMP_EQUAL(expected_seq, result, expected_seq_len); } TEST(MemfaultMinimalCbor, Test_EncodeString) { // RFC Appendix A. Examples const uint8_t expected_enc_empty[] = { 0x60 }; prv_run_string_encoder_check("", expected_enc_empty, sizeof(expected_enc_empty)); const uint8_t expected_enc_a[] = { 0x61, 0x61 }; prv_run_string_encoder_check("a", expected_enc_a, sizeof(expected_enc_a)); const uint8_t expected_enc_IETF[] = { 0x64, 0x49, 0x45, 0x54, 0x46 }; prv_run_string_encoder_check("IETF", expected_enc_IETF, sizeof(expected_enc_IETF)); const uint8_t expected_enc_quote_backslash[] = { 0x62, 0x22, 0x5c }; prv_run_string_encoder_check("\"\\", expected_enc_quote_backslash, sizeof(expected_enc_quote_backslash)); } static void prv_run_incremental_string_encoder_check(const char *str, const uint8_t *expected_seq, size_t expected_seq_len) { sMemfaultCborEncoder encoder; uint8_t result[expected_seq_len]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); const bool success = memfault_cbor_encode_string_begin(&encoder, strlen(str)); CHECK(success); for (size_t i = 0; i < strlen(str); ++i) { CHECK(memfault_cbor_join(&encoder, &str[i], 1)); } const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(expected_seq_len, encoded_length); MEMCMP_EQUAL(expected_seq, result, expected_seq_len); } TEST(MemfaultMinimalCbor, Test_EncodeStringIncremental) { // RFC Appendix A. Examples const uint8_t expected_enc_empty[] = { 0x60 }; prv_run_incremental_string_encoder_check("", expected_enc_empty, sizeof(expected_enc_empty)); const uint8_t expected_enc_a[] = { 0x61, 0x61 }; prv_run_incremental_string_encoder_check("a", expected_enc_a, sizeof(expected_enc_a)); const uint8_t expected_enc_IETF[] = { 0x64, 0x49, 0x45, 0x54, 0x46 }; prv_run_incremental_string_encoder_check("IETF", expected_enc_IETF, sizeof(expected_enc_IETF)); const uint8_t expected_enc_quote_backslash[] = { 0x62, 0x22, 0x5c }; prv_run_incremental_string_encoder_check("\"\\", expected_enc_quote_backslash, sizeof(expected_enc_quote_backslash)); } static void prv_run_binary_encoder_check(const void *buf, size_t buf_len, const uint8_t *expected_seq, size_t expected_seq_len) { sMemfaultCborEncoder encoder; uint8_t result[expected_seq_len]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); const bool success = memfault_cbor_encode_byte_string(&encoder, buf, buf_len); CHECK(success); const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(expected_seq_len, encoded_length); MEMCMP_EQUAL(expected_seq, result, expected_seq_len); } TEST(MemfaultMinimalCbor, Test_EncodeBinaryString) { // RFC Appendix A. Examples const uint8_t expected_enc_empty[] = { 0x40 }; prv_run_binary_encoder_check(NULL, 0, expected_enc_empty, sizeof(expected_enc_empty)); const uint8_t expected_enc_1234[] = { 0x44, 0x01, 0x02, 0x03, 0x04 }; const uint8_t binary_str[] = { 1, 2, 3, 4 }; prv_run_binary_encoder_check(binary_str, sizeof(binary_str), expected_enc_1234, sizeof(expected_enc_1234)); } static void prv_run_uint64_as_double_encoder_check(double g, const uint8_t *expected_seq, size_t expected_seq_len) { uint64_t value = 0; memcpy(&value, &g, sizeof(g)); sMemfaultCborEncoder encoder; uint8_t result[expected_seq_len]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); const bool success = memfault_cbor_encode_uint64_as_double(&encoder, value); CHECK(success); const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(expected_seq_len, encoded_length); MEMCMP_EQUAL(expected_seq, result, expected_seq_len); } TEST(MemfaultMinimalCbor, Test_EncodeFLoatString) { // RFC Appendix A. Examples const uint8_t expected_float_pos[] = { 0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c }; const double g_pos = 1.0e+300; prv_run_uint64_as_double_encoder_check(g_pos, expected_float_pos, sizeof(expected_float_pos)); const uint8_t expected_float_neg[] = { 0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 }; const double g_neg = -4.1; prv_run_uint64_as_double_encoder_check(g_neg, expected_float_neg, sizeof(expected_float_neg)); } static void prv_encode_test_dictionary(sMemfaultCborEncoder *encoder) { // { // "a": "A", // "b": "B", // "c": "C", // "d": "D", // "e": "E" // } bool success = memfault_cbor_encode_dictionary_begin(encoder, 5); CHECK(success); const char *enc[] = { "a", "b", "c", "d", "e" }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(enc); i++) { char value[2] = { 0 }; value[0] = *enc[i] - 32; // get capital representation success = memfault_cbor_encode_string(encoder, enc[i]); CHECK(success); success = memfault_cbor_encode_string(encoder, value); CHECK(success); } } // Real world encoding of a set of data TEST(MemfaultMinimalCbor, Test_EncodeDictionary) { // first let's just compute the size sMemfaultCborEncoder encoder; memfault_cbor_encoder_size_only_init(&encoder); prv_encode_test_dictionary(&encoder); const size_t size_needed = memfault_cbor_encoder_deinit(&encoder); const uint8_t expected_encoding[] = { 0xa5, 0x61, 0x61, 0x61, 0x41, 0x61, 0x62, 0x61, 0x42, 0x61, 0x63, 0x61, 0x43, 0x61, 0x64, 0x61, 0x44, 0x61, 0x65, 0x61, 0x45 }; LONGS_EQUAL(sizeof(expected_encoding), size_needed); uint8_t result[size_needed]; memset(result, 0x0, sizeof(result)); memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); prv_encode_test_dictionary(&encoder); MEMCMP_EQUAL(expected_encoding, result, sizeof(result)); } // nested dictionary TEST(MemfaultMinimalCbor, Test_EncodeNestedDictionary) { // { // "a": { // "b": "c" // } // } const uint8_t expected_encoding[] = { 0xa1, 0x61, 0x61, 0xa1, 0x61, 0x62, 0x61, 0x63 }; uint8_t result[sizeof(expected_encoding)]; memset(result, 0x0, sizeof(result)); sMemfaultCborEncoder encoder; memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); bool success = memfault_cbor_encode_dictionary_begin(&encoder, 1); CHECK(success); success = memfault_cbor_encode_string(&encoder, "a"); CHECK(success); success = memfault_cbor_encode_dictionary_begin(&encoder, 1); CHECK(success); success = memfault_cbor_encode_string(&encoder, "b"); CHECK(success); success = memfault_cbor_encode_string(&encoder, "c"); CHECK(success); const size_t encoded_length = memfault_cbor_encoder_deinit(&encoder); LONGS_EQUAL(sizeof(result), encoded_length); MEMCMP_EQUAL(expected_encoding, result, sizeof(result)); } TEST(MemfaultMinimalCbor, Test_BufTooSmall) { sMemfaultCborEncoder encoder; char result[1]; memfault_cbor_encoder_init(&encoder, prv_write_cb, result, sizeof(result)); // we only have a 1 byte result buffer so a 2 byte encoding should fail const bool success = memfault_cbor_encode_string(&encoder, "a"); CHECK(!success); } TEST(MemfaultMinimalCbor, Test_Null) { sMemfaultCborEncoder encoder; uint8_t result[1]; memfault_cbor_encoder_init(&encoder, prv_write_cb, result, MEMFAULT_ARRAY_SIZE(result)); const bool success = memfault_cbor_encode_null(&encoder); CHECK(success); LONGS_EQUAL(0xF6, result[0]); } ================================================ FILE: tests/unit/src/test_memfault_port_lwip_metrics.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "comparators/comparator_memfault_metric_ids.hpp" #include "lwip/stats.h" #include "memfault/ports/lwip/metrics.h" static MemfaultMetricIdsComparator s_metric_id_comparator; struct mock_stats lwip_stats = { 0 }; static MemfaultMetricId s_tcp_tx_count_id = MEMFAULT_METRICS_KEY(tcp_tx_count); static MemfaultMetricId s_tcp_rx_count_id = MEMFAULT_METRICS_KEY(tcp_rx_count); static MemfaultMetricId s_tcp_drop_count_id = MEMFAULT_METRICS_KEY(tcp_drop_count); static MemfaultMetricId s_udp_tx_count_id = MEMFAULT_METRICS_KEY(udp_tx_count); static MemfaultMetricId s_udp_rx_count_id = MEMFAULT_METRICS_KEY(udp_rx_count); static MemfaultMetricId s_udp_drop_count_id = MEMFAULT_METRICS_KEY(udp_drop_count); static void prv_set_metrics(unsigned int value) { lwip_stats.tcp.xmit = value; lwip_stats.tcp.recv = value; lwip_stats.tcp.drop = value; lwip_stats.udp.xmit = value; lwip_stats.udp.recv = value; lwip_stats.udp.drop = value; } static void prv_clear_metrics(void) { // Clear the stats global memset(&lwip_stats, 0, sizeof lwip_stats); // Clear the static delta values, this requires collecting 2 rounds of metrics memfault_lwip_heartbeat_collect_data(); memfault_lwip_heartbeat_collect_data(); } static void prv_set_expected_calls(unsigned int expected_value) { mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_tcp_tx_count_id) .withParameter("unsigned_value", expected_value); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_tcp_rx_count_id) .withParameter("unsigned_value", expected_value); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_tcp_drop_count_id) .withParameter("unsigned_value", expected_value); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_udp_tx_count_id) .withParameter("unsigned_value", expected_value); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_udp_rx_count_id) .withParameter("unsigned_value", expected_value); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_udp_drop_count_id) .withParameter("unsigned_value", expected_value); } // clang-format off TEST_GROUP(LwipMetrics) { void setup(){ // Disable mock framework while clearing metrics mock().disable(); prv_clear_metrics(); mock().enable(); mock().strictOrder(); mock().installComparator("MemfaultMetricId", s_metric_id_comparator); } void teardown(){ mock().removeAllComparatorsAndCopiers(); mock().clear(); } }; // clang-format on TEST(LwipMetrics, MetricUpdate) { // Test with an initial value update unsigned int new_value = 128; prv_set_metrics(new_value); prv_set_expected_calls(new_value); memfault_lwip_heartbeat_collect_data(); // Check delta is produced (values are 0) prv_set_expected_calls(0); memfault_lwip_heartbeat_collect_data(); // Set values again and check values are set to delta prv_set_metrics(new_value * 2); prv_set_expected_calls(new_value); memfault_lwip_heartbeat_collect_data(); }; TEST(LwipMetrics, MetricRollover) { unsigned int delta = 256; // Set the initial value to close to the uint32_t max // Use half of the delta to easily check for wrap around behavior later unsigned int initial_value = UINT32_MAX - (delta >> 1); // Initialize the stats and check for correct metric values prv_set_metrics(initial_value); prv_set_expected_calls(initial_value); memfault_lwip_heartbeat_collect_data(); // Update the new value to UINT32_MAX + delta to force a wraparound unsigned int new_value = initial_value + delta; prv_set_metrics(new_value); // Metric values should be equal to the delta and handle wraparound correctly prv_set_expected_calls(delta); memfault_lwip_heartbeat_collect_data(); }; ================================================ FILE: tests/unit/src/test_memfault_port_mbedtls_metrics.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "comparators/comparator_memfault_metric_ids.hpp" #include "mbedtls_mem.h" #include "memfault/core/math.h" #include "memfault/ports/mbedtls/metrics.h" #include "memfault/util/align.h" static MemfaultMetricIdsComparator s_metric_id_comparator; static MemfaultMetricId s_mbedtls_mem_used_id = MEMFAULT_METRICS_KEY(mbedtls_mem_used_bytes); static MemfaultMetricId s_mbedtls_mem_max_id = MEMFAULT_METRICS_KEY(mbedtls_mem_max_bytes); static void prv_set_expected_calls(int expected_used, unsigned int expected_max) { mock() .expectOneCall("memfault_metrics_heartbeat_set_signed") .withParameterOfType("MemfaultMetricId", "key", &s_mbedtls_mem_used_id) .withParameter("signed_value", expected_used); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &s_mbedtls_mem_max_id) .withParameter("unsigned_value", expected_max); } static void *prv_test_calloc(size_t num, size_t size) { void *ptr = __wrap_mbedtls_calloc(num, size); // Ensure that memory is max aligned CHECK_EQUAL(0, (uintptr_t)ptr % MEMFAULT_MAX_ALIGN_SIZE); return ptr; } // clang-format off TEST_GROUP(MbedtlsMetrics) { void setup(){ mock().strictOrder(); mock().installComparator("MemfaultMetricId", s_metric_id_comparator); } void teardown(){ memfault_mbedtls_test_clear_values(); mock().removeAllComparatorsAndCopiers(); mock().clear(); } }; // clang-format on TEST(MbedtlsMetrics, MetricUpdate) { // Test metric values after calloc size_t num = 10; size_t size = 5; prv_set_expected_calls(num *size, num *size); void *ptr_a = prv_test_calloc(num, size); memfault_mbedtls_heartbeat_collect_data(); // Test metric values after free __wrap_mbedtls_free(ptr_a); prv_set_expected_calls(0, num *size); memfault_mbedtls_heartbeat_collect_data(); // Test metric values after mix of calloc/free prv_set_expected_calls(2 * num * size, 2 * num * size); prv_set_expected_calls(num *size, 2 * num * size); prv_set_expected_calls(0, 2 * num * size); void *ptr_b = prv_test_calloc(num, size); ptr_a = prv_test_calloc(num, size); memfault_mbedtls_heartbeat_collect_data(); __wrap_mbedtls_free(ptr_a); memfault_mbedtls_heartbeat_collect_data(); __wrap_mbedtls_free(ptr_b); memfault_mbedtls_heartbeat_collect_data(); }; TEST(MbedtlsMetrics, CallocAlignment) { // Iterate through a wide range of sizes to ensure returned memory is aligned void *ptr_array[MEMFAULT_MAX_ALIGN_SIZE]; for (unsigned int i = 0; i < MEMFAULT_ARRAY_SIZE(ptr_array); i++) { ptr_array[i] = prv_test_calloc(1, i); } for (unsigned int i = 0; i < MEMFAULT_ARRAY_SIZE(ptr_array); i++) { __wrap_mbedtls_free(ptr_array[i]); } }; ================================================ FILE: tests/unit/src/test_memfault_port_nrf5_coredump_regions.cpp ================================================ //! @file //! //! @brief #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/components.h" TEST_GROUP(MemfaultNrf5Port) { void setup() { } void teardown() { } }; TEST(MemfaultNrf5Port, Test_RunsOk) { size_t bound = memfault_platform_sanitize_address_range((void *)0x20000000, 0x1000); LONGS_EQUAL(0x1000, bound); bound = memfault_platform_sanitize_address_range((void *)0x20000000, 0x20000000); LONGS_EQUAL(0x40000, bound); bound = memfault_platform_sanitize_address_range((void *)0, 1); LONGS_EQUAL(0, bound); } ================================================ FILE: tests/unit/src/test_memfault_printf_attribute.cpp ================================================ // Clang-format really hates this file, so disable it // clang-format off #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #define printf error_yolo #if defined(printf) #include "memfault/core/compiler.h" #endif } MEMFAULT_PRINTF_LIKE_FUNC(1,2) void memfault_printf(const char *fmt, ...); void memfault_printf(const char *fmt, ...) { // grab va_list and forward to vprintf va_list args; va_start(args, fmt); vprintf(fmt, args); va_end(args); } TEST_GROUP(MfltPrintfAttribute){ void setup() { mock().strictOrder(); } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MfltPrintfAttribute, Test_PrintfImplementation) { // just call it. the real check is the compilation check though. memfault_printf("hello world\n"); } ================================================ FILE: tests/unit/src/test_memfault_ram_backed_coredump_port.cpp ================================================ #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "memfault/config.h" #include "memfault/core/compiler.h" #include "memfault/panics/platform/coredump.h" size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { (void)start_addr; return desired_size; } TEST_GROUP(MfltRamBackedCoredumpPort) { void setup() { } void teardown() { } }; TEST(MfltRamBackedCoredumpPort, Test_GetRegions) { sCoredumpCrashInfo info = { .stack_address = (void *)0x28000000, }; size_t num_regions; const sMfltCoredumpRegion *regions = memfault_platform_coredump_get_regions(&info, &num_regions); LONGS_EQUAL(1, num_regions); const sMfltCoredumpRegion *r = ®ions[0]; LONGS_EQUAL(kMfltCoredumpRegionType_Memory, r->type); LONGS_EQUAL(info.stack_address, r->region_start); LONGS_EQUAL(MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT, r->region_size); } TEST(MfltRamBackedCoredumpPort, Test_GetInfo) { sMfltCoredumpStorageInfo info; memfault_platform_coredump_storage_get_info(&info); // Assert if the size changes so we can catch this and make _sure_ it's what we want to do LONGS_EQUAL(MEMFAULT_PLATFORM_COREDUMP_STORAGE_RAM_SIZE, info.size); } static size_t prv_get_size(void) { sMfltCoredumpStorageInfo info; memfault_platform_coredump_storage_get_info(&info); return info.size; } TEST(MfltRamBackedCoredumpPort, Test_BadOffsets) { size_t size = prv_get_size(); uint8_t byte = 0xAB; bool success = memfault_platform_coredump_storage_read(size, &byte, sizeof(byte)); CHECK(!success); uint32_t word = 0x12345678; success = memfault_platform_coredump_storage_read(size - 1, &word, sizeof(word)); CHECK(!success); success = memfault_platform_coredump_storage_erase(size, size); CHECK(!success); success = memfault_platform_coredump_storage_erase(0, size + 1); CHECK(!success); success = memfault_platform_coredump_storage_write(size, &byte, sizeof(byte)); CHECK(!success); success = memfault_platform_coredump_storage_write(size, &word, sizeof(word)); CHECK(!success); } static void prv_assert_storage_empty(void) { const size_t size = prv_get_size(); uint8_t data[size]; const bool success = memfault_platform_coredump_storage_read(0, &data, sizeof(data)); CHECK(success); uint8_t expected_data[size]; memset(expected_data, 0x0, sizeof(expected_data)); MEMCMP_EQUAL(expected_data, data, sizeof(data)); } TEST(MfltRamBackedCoredumpPort, Test_EraseReadWrite) { const size_t size = prv_get_size(); memfault_platform_coredump_storage_erase(0, size); prv_assert_storage_empty(); // write data byte-by-byte for (size_t i = 0; i < size; i++) { uint8_t d = (i + 1) & 0xff; const bool success = memfault_platform_coredump_storage_write(i, &d, sizeof(d)); CHECK(success); } // read data back byte-by-byte and confirm it matches expectations for (size_t i = 0; i < size; i++) { uint8_t d = 0; const bool success = memfault_platform_coredump_storage_read(i, &d, sizeof(d)); CHECK(success); LONGS_EQUAL((i + 1) & 0xff, d); } // clear the coredump (needs to invalidate at least the first byte) memfault_platform_coredump_storage_clear(); uint8_t clear_byte = 0xab; bool success = memfault_platform_coredump_storage_read(0, &clear_byte, sizeof(clear_byte)); CHECK(success); LONGS_EQUAL(0, clear_byte); // erase everything memfault_platform_coredump_storage_erase(0, size); prv_assert_storage_empty(); } ================================================ FILE: tests/unit/src/test_memfault_ram_reboot_tracking.cpp ================================================ #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include "memfault/core/reboot_tracking.h" #include "memfault_reboot_tracking_private.h" static uint8_t s_mflt_reboot_tracking_region[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; } static void prv_check_crash_count(size_t expected_crash_count) { size_t actual_crash_count = memfault_reboot_tracking_get_crash_count(); LONGS_EQUAL(expected_crash_count, actual_crash_count); } static void prv_check_reboot_info(sMfltResetReasonInfo *expected_reboot_info) { sMfltResetReasonInfo actual_info; bool result = memfault_reboot_tracking_read_reset_info(&actual_info); CHECK(result); LONGS_EQUAL(expected_reboot_info->reason, actual_info.reason); LONGS_EQUAL(expected_reboot_info->pc, actual_info.pc); LONGS_EQUAL(expected_reboot_info->lr, actual_info.lr); LONGS_EQUAL(expected_reboot_info->reset_reason_reg0, actual_info.reset_reason_reg0); LONGS_EQUAL(expected_reboot_info->coredump_saved, actual_info.coredump_saved); } static void prv_check_reboot_reason_read(sMfltRebootReason *expected_reboot_reason) { sMfltRebootReason actual_reboot_reason; int result = memfault_reboot_tracking_get_reboot_reason(&actual_reboot_reason); LONGS_EQUAL(result, 0); LONGS_EQUAL(expected_reboot_reason->prior_stored_reason, actual_reboot_reason.prior_stored_reason); LONGS_EQUAL(expected_reboot_reason->reboot_reg_reason, actual_reboot_reason.reboot_reg_reason); } static void prv_check_unexpected_reboot_occurred(bool expected_value) { bool actual_value; int result = memfault_reboot_tracking_get_unexpected_reboot_occurred(&actual_value); LONGS_EQUAL(result, 0); LONGS_EQUAL(expected_value, actual_value); } TEST_GROUP(MfltRamRebootTracking) { void setup() { // simulate memory initializing with random pattern at boot memset(&s_mflt_reboot_tracking_region[0], 0xBA, sizeof(s_mflt_reboot_tracking_region)); memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, NULL); memfault_reboot_tracking_reset_crash_count(); memfault_reboot_tracking_clear_reboot_reason(); memfault_reboot_tracking_clear_reset_info(); } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MfltRamRebootTracking, Test_ReadResetInfo_BadArgs) { // Check returns false with NULL param CHECK_FALSE(memfault_reboot_tracking_read_reset_info(NULL)); } TEST(MfltRamRebootTracking, Test_ReadResetInfo_AfterReset) { // Reset info already cleared in setup() above // Check returns false when reset info is cleared/reset sMfltResetReasonInfo info; CHECK_FALSE(memfault_reboot_tracking_read_reset_info(&info)); } TEST(MfltRamRebootTracking, Test_NoOpsWithBadRegion) { // Mark coredump, boot with crash memfault_reboot_tracking_mark_coredump_saved(); sResetBootupInfo info = { .reset_reason = kMfltRebootReason_BusFault }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &info); // This point, we could expected coredump_saved = true, crash_count = 1, etc // Instead boot with a NULL region, this will clear the internal pointer and result in failed // operations memfault_reboot_tracking_boot(NULL, NULL); // Check crash count is not reset in buffer sMfltRebootInfo *reboot_info = (sMfltRebootInfo *)s_mflt_reboot_tracking_region; size_t crash_count = memfault_reboot_tracking_get_crash_count(); LONGS_EQUAL(0, crash_count); LONGS_EQUAL(1, reboot_info->crash_count); // Check reset crash does not affect buffer memfault_reboot_tracking_reset_crash_count(); LONGS_EQUAL(1, reboot_info->crash_count); // Check clear reset info does not clear memfault_reboot_tracking_clear_reset_info(); CHECK(reboot_info->coredump_saved); // Check coredump marked returns false reboot_info->coredump_saved = false; memfault_reboot_tracking_mark_coredump_saved(); CHECK_FALSE(reboot_info->coredump_saved); // Check that mark reset imminent does modify the reason memfault_reboot_tracking_mark_reset_imminent(kMfltRebootReason_Assert, NULL); LONGS_EQUAL(reboot_info->last_reboot_reason, kMfltRebootReason_BusFault); } TEST(MfltRamRebootTracking, Test_ReadResetInfo) { sMfltResetReasonInfo read_info; bool crash_found = memfault_reboot_tracking_read_reset_info(&read_info); CHECK(!crash_found); sMfltRebootTrackingRegInfo info = { 0 }; info.pc = 0x1; info.lr = 0x2; const eMemfaultRebootReason reason = kMfltRebootReason_Assert; memfault_reboot_tracking_mark_reset_imminent(reason, &info); memfault_reboot_tracking_mark_coredump_saved(); sResetBootupInfo bootup_info = { .reset_reason_reg = 0x1, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &bootup_info); sMfltResetReasonInfo expected_info = (sMfltResetReasonInfo){ .reason = reason, .pc = info.pc, .lr = info.lr, .reset_reason_reg0 = bootup_info.reset_reason_reg, .coredump_saved = true, }; prv_check_reboot_info(&expected_info); } TEST(MfltRamRebootTracking, Test_NoRamRegionInitialized) { memfault_reboot_tracking_mark_reset_imminent(kMfltRebootReason_Assert, NULL); memfault_reboot_tracking_boot(NULL, NULL); sMfltResetReasonInfo info; bool info_available = memfault_reboot_tracking_read_reset_info(&info); CHECK(!info_available); } TEST(MfltRamRebootTracking, Test_CrashTracking) { // Mark next bootup as created by an error memfault_reboot_tracking_mark_reset_imminent(kMfltRebootReason_UnknownError, NULL); // Crash count should not be incremented yet prv_check_crash_count(0); memfault_reboot_tracking_clear_reset_info(); // Boot with reboot reason, no previously set reason, should bump crash count sResetBootupInfo bootup_info = { .reset_reason_reg = 0x1, .reset_reason = kMfltRebootReason_HardFault, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &bootup_info); prv_check_crash_count(1); // Resetting reboot tracking should not alter crash count memfault_reboot_tracking_clear_reset_info(); prv_check_crash_count(1); memfault_reboot_tracking_reset_crash_count(); prv_check_crash_count(0); } TEST(MfltRamRebootTracking, Test_GetRebootReason_BadArgs) { // Test that reason is invalid if arg is NULL int rc = memfault_reboot_tracking_get_reboot_reason(NULL); LONGS_EQUAL(-1, rc); // Test that reason is invalid before reboot tracking has booted // Set a non-zero value in a field to check that field was not modified sMfltRebootReason reboot_reason = { .reboot_reg_reason = kMfltRebootReason_UnknownError, }; rc = memfault_reboot_tracking_get_reboot_reason(&reboot_reason); LONGS_EQUAL(-1, rc); LONGS_EQUAL((long)kMfltRebootReason_UnknownError, (long)reboot_reason.reboot_reg_reason); } TEST(MfltRamRebootTracking, Test_GetRebootReason_BootNull) { // Test booting with null memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, NULL); sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = kMfltRebootReason_Unknown, .prior_stored_reason = kMfltRebootReason_Unknown, }; prv_check_reboot_reason_read(&expected_reboot_reason); } TEST(MfltRamRebootTracking, Test_GetRebootReason_BootupReason) { // Test booting with a non-error reason sResetBootupInfo info = { .reset_reason = kMfltRebootReason_SoftwareReset, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &info); sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = kMfltRebootReason_SoftwareReset, .prior_stored_reason = kMfltRebootReason_SoftwareReset, }; prv_check_reboot_reason_read(&expected_reboot_reason); // Check that reboot reason is not cleared by // memfault_reboot_tracking_clear_reset_info memfault_reboot_tracking_clear_reset_info(); prv_check_reboot_reason_read(&expected_reboot_reason); } TEST(MfltRamRebootTracking, Test_GetRebootReason_MarkedReason) { // Simulate a crash, mark the reason. This will store the crash reason as prior_stored_reason // and reboot_reg_reason with bootup reason memfault_reboot_tracking_mark_reset_imminent(kMfltRebootReason_Assert, NULL); sResetBootupInfo info = (sResetBootupInfo){ .reset_reason = kMfltRebootReason_PinReset, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &info); sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = kMfltRebootReason_PinReset, .prior_stored_reason = kMfltRebootReason_Assert, }; prv_check_reboot_reason_read(&expected_reboot_reason); } TEST(MfltRamRebootTracking, Test_GetUnexpectedRebootOccurred_BadArgs) { // Test invalid if arg is NULL int rc = memfault_reboot_tracking_get_unexpected_reboot_occurred(NULL); LONGS_EQUAL(-1, rc); bool unexpected_reboot; rc = memfault_reboot_tracking_get_unexpected_reboot_occurred(&unexpected_reboot); LONGS_EQUAL(-1, rc); } TEST(MfltRamRebootTracking, Test_GetUnexpectedRebootOccurred_Expected) { // Test booting with an expected reason sResetBootupInfo info = { .reset_reason = kMfltRebootReason_SoftwareReset }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &info); prv_check_unexpected_reboot_occurred(false); } TEST(MfltRamRebootTracking, Test_GetUnexpectedRebootOccurred_Unexpected) { // Test booting with an unexpected reason sResetBootupInfo info = { .reset_reason = kMfltRebootReason_UnknownError }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &info); prv_check_unexpected_reboot_occurred(true); } TEST(MfltRamRebootTracking, Test_RebootTrackingBooted) { // Reset the struct back to uninitialized and module pointer to null memset(&s_mflt_reboot_tracking_region[0], 0xBA, sizeof(s_mflt_reboot_tracking_region)); memfault_reboot_tracking_boot(NULL, NULL); // Check that reboot tracking not booted CHECK_FALSE(memfault_reboot_tracking_booted()); // Check that reboot tracking booted memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, NULL); CHECK(memfault_reboot_tracking_booted()); } // Reboot Scenario Test Cases // // There are several scenarios we need to handle with reboot tracking // 1. The device only has the boot reason (expected/unexpected), no reason marked before reboot // 2. The device has both a marked reason (expected/unexpected), and a boot reason // (expected/unexpected) // 3. The device has only a marked reason (expected/unexpected) before reboot, no reason at boot // 4. The device has no marked reason and no reason at boot // // Each case below we verify the following functions return values matching the scenario: // - memfault_reboot_tracking_get_crash_count() // - memfault_reboot_tracking_read_reset_info() // - memfault_reboot_tracking_get_reboot_reason() // - memfault_reboot_tracking_get_unexpected_reboot_occurred() // Boot Reason Tests TEST(MfltRamRebootTracking, Test_BootExpectedReason) { sResetBootupInfo bootup_info = { .reset_reason_reg = 0xab, .reset_reason = kMfltRebootReason_DeepSleep, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &bootup_info); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = bootup_info.reset_reason, .reset_reason_reg0 = bootup_info.reset_reason_reg, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = bootup_info.reset_reason, .prior_stored_reason = bootup_info.reset_reason, }; prv_check_crash_count(0); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(false); } TEST(MfltRamRebootTracking, Test_BootUnexpectedReason) { sResetBootupInfo bootup_info = { .reset_reason_reg = 0xab, .reset_reason = kMfltRebootReason_Assert, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &bootup_info); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = bootup_info.reset_reason, .reset_reason_reg0 = bootup_info.reset_reason_reg, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = bootup_info.reset_reason, .prior_stored_reason = bootup_info.reset_reason, }; prv_check_crash_count(1); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(true); } // Marked Reason + Boot Reason Tests TEST(MfltRamRebootTracking, Test_MarkedExpectedBootUnexpectedReason) { eMemfaultRebootReason marked_reason = kMfltRebootReason_FirmwareUpdate; memfault_reboot_tracking_mark_reset_imminent(marked_reason, NULL); sResetBootupInfo bootup_info = { .reset_reason_reg = 0xab, .reset_reason = kMfltRebootReason_Unknown, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &bootup_info); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = marked_reason, .reset_reason_reg0 = bootup_info.reset_reason_reg, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = bootup_info.reset_reason, .prior_stored_reason = marked_reason, }; prv_check_crash_count(0); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(false); } TEST(MfltRamRebootTracking, Test_MarkedExpectedBootExpectedReason) { eMemfaultRebootReason marked_reason = kMfltRebootReason_FirmwareUpdate; memfault_reboot_tracking_mark_reset_imminent(marked_reason, NULL); sResetBootupInfo bootup_info = { .reset_reason_reg = 0xab, .reset_reason = kMfltRebootReason_UserReset, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &bootup_info); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = marked_reason, .reset_reason_reg0 = bootup_info.reset_reason_reg, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = bootup_info.reset_reason, .prior_stored_reason = marked_reason, }; prv_check_crash_count(0); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(false); } TEST(MfltRamRebootTracking, Test_MarkedUnexpectedBootExpectedReason) { eMemfaultRebootReason marked_reason = kMfltRebootReason_HardFault; memfault_reboot_tracking_mark_reset_imminent(marked_reason, NULL); sResetBootupInfo bootup_info = { .reset_reason_reg = 0xab, .reset_reason = kMfltRebootReason_ButtonReset, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &bootup_info); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = marked_reason, .reset_reason_reg0 = bootup_info.reset_reason_reg, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = bootup_info.reset_reason, .prior_stored_reason = marked_reason, }; prv_check_crash_count(1); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(true); } TEST(MfltRamRebootTracking, Test_MarkedUnexpectedBootUnexpectedReason) { eMemfaultRebootReason marked_reason = kMfltRebootReason_HardFault; memfault_reboot_tracking_mark_reset_imminent(marked_reason, NULL); sResetBootupInfo bootup_info = { .reset_reason_reg = 0xab, .reset_reason = kMfltRebootReason_BusFault, }; memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, &bootup_info); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = marked_reason, .reset_reason_reg0 = bootup_info.reset_reason_reg, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = bootup_info.reset_reason, .prior_stored_reason = marked_reason, }; prv_check_crash_count(1); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(true); } // Marked Reason + No Boot Reason Tests TEST(MfltRamRebootTracking, Test_MarkedExpectedReasonBootNull) { eMemfaultRebootReason marked_reason = kMfltRebootReason_FirmwareUpdate; memfault_reboot_tracking_mark_reset_imminent(marked_reason, NULL); memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, NULL); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = marked_reason, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = kMfltRebootReason_Unknown, .prior_stored_reason = marked_reason, }; prv_check_crash_count(0); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(false); } TEST(MfltRamRebootTracking, Test_MarkedUnexpectedReasonBootNull) { eMemfaultRebootReason marked_reason = kMfltRebootReason_Assert; memfault_reboot_tracking_mark_reset_imminent(marked_reason, NULL); memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, NULL); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = marked_reason, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = kMfltRebootReason_Unknown, .prior_stored_reason = marked_reason, }; prv_check_crash_count(1); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(true); } // No Marked Reason + No Boot Reason Test TEST(MfltRamRebootTracking, Test_BootNull) { memfault_reboot_tracking_boot(s_mflt_reboot_tracking_region, NULL); sMfltResetReasonInfo expected_reboot_info = (sMfltResetReasonInfo){ .reason = kMfltRebootReason_Unknown, }; sMfltRebootReason expected_reboot_reason = (sMfltRebootReason){ .reboot_reg_reason = kMfltRebootReason_Unknown, .prior_stored_reason = kMfltRebootReason_Unknown, }; prv_check_crash_count(1); prv_check_reboot_info(&expected_reboot_info); prv_check_reboot_reason_read(&expected_reboot_reason); prv_check_unexpected_reboot_occurred(true); } ================================================ FILE: tests/unit/src/test_memfault_reboot_tracking_serializer.cpp ================================================ #include #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_event_storage.h" #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/event_storage.h" #include "memfault/core/reboot_tracking.h" #include "memfault/core/serializer_helper.h" #include "memfault_reboot_tracking_private.h" static sMfltResetReasonInfo s_fake_reset_reason_info; static const sMemfaultEventStorageImpl *s_fake_event_storage_impl; bool memfault_reboot_tracking_read_reset_info(sMfltResetReasonInfo *info) { *info = s_fake_reset_reason_info; return true; } void memfault_reboot_tracking_clear_reset_info(void) { mock().actualCall(__func__); } TEST_GROUP(MfltRebootTrackingSerializer) { void setup() { static uint8_t s_storage[100]; s_fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage)); s_fake_reset_reason_info = (sMfltResetReasonInfo){ .reason = kMfltRebootReason_Assert, .pc = 0xbadcafe, .lr = 0xdeadbeef, .reset_reason_reg0 = 0x12345678, }; } void teardown() { mock().checkExpectations(); mock().clear(); } }; static void prv_run_reset_reason_serializer_test(const void *expected_data, size_t expected_data_len) { fake_memfault_event_storage_clear(); fake_memfault_event_storage_set_available_space(expected_data_len); mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", false); mock().expectOneCall("memfault_reboot_tracking_clear_reset_info"); const int rv = memfault_reboot_tracking_collect_reset_info(s_fake_event_storage_impl); LONGS_EQUAL(0, rv); fake_event_storage_assert_contents_match(expected_data, expected_data_len); } TEST(MfltRebootTrackingSerializer, Test_Serialize) { const uint8_t expected_data_all[] = { 0xa6, 0x02, 0x02, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xa5, 0x01, 0x19, 0x80, 0x01, 0x02, 0x1a, 0x0b, 0xad, 0xca, 0xfe, 0x03, 0x1a, 0xde, 0xad, 0xbe, 0xef, 0x04, 0x1a, 0x12, 0x34, 0x56, 0x78, 0x05, 0x00 }; // make sure short if there isn't enough storage, the write just fails for (size_t i = 0; i < sizeof(expected_data_all) - 1; i++) { fake_memfault_event_storage_clear(); fake_memfault_event_storage_set_available_space(i); mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", true); const int rv = memfault_reboot_tracking_collect_reset_info(s_fake_event_storage_impl); LONGS_EQUAL(-2, rv); } prv_run_reset_reason_serializer_test(expected_data_all, sizeof(expected_data_all)); s_fake_reset_reason_info.reset_reason_reg0 = 0; const uint8_t expected_data_pc_lr[] = { 0xa6, 0x02, 0x02, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xa4, 0x01, 0x19, 0x80, 0x01, 0x02, 0x1a, 0x0b, 0xad, 0xca, 0xfe, 0x03, 0x1a, 0xde, 0xad, 0xbe, 0xef, 0x05, 0x00 }; prv_run_reset_reason_serializer_test(expected_data_pc_lr, sizeof(expected_data_pc_lr)); s_fake_reset_reason_info.lr = 0; const uint8_t expected_data_pc[] = { 0xa6, 0x02, 0x02, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xa3, 0x01, 0x19, 0x80, 0x01, 0x02, 0x1a, 0x0b, 0xad, 0xca, 0xfe, 0x05, 0x00 }; prv_run_reset_reason_serializer_test(expected_data_pc, sizeof(expected_data_pc)); s_fake_reset_reason_info.pc = 0; // indicate that there is a coredump to go along with it s_fake_reset_reason_info.coredump_saved = true; const uint8_t expected_data_no_optionals[] = { 0xa6, 0x02, 0x02, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xa2, 0x01, 0x19, 0x80, 0x01, 0x05, 0x01 }; prv_run_reset_reason_serializer_test(expected_data_no_optionals, sizeof(expected_data_no_optionals)); } TEST(MfltRebootTrackingSerializer, Test_GetWorstCaseSerializeSize) { const size_t worst_case_size = memfault_reboot_tracking_compute_worst_case_storage_size(); LONGS_EQUAL(52, worst_case_size); } TEST(MfltRebootTrackingSerializer, Test_BadParams) { const int rv = memfault_reboot_tracking_collect_reset_info(NULL); LONGS_EQUAL(-1, rv); } ================================================ FILE: tests/unit/src/test_memfault_rle.cpp ================================================ //! @file //! //! @brief #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/math.h" #include "memfault/util/rle.h" extern "C" { #include #include } TEST_GROUP(MemfaultRle) { void setup() { } void teardown() { } }; typedef struct { const uint8_t *orig_buf; size_t orig_buf_len; uint8_t *write_buf; size_t write_buf_len; size_t write_buf_offset; } sMemfaultRleResultCtx; static void prv_update_result_buf(sMemfaultRleResultCtx *result_ctx, const sMemfaultRleWriteInfo *write_info) { if (!write_info->available) { return; } const size_t idx = result_ctx->write_buf_offset; const size_t write_len = write_info->write_len; const size_t end_offset = idx + write_len + write_info->header_len; CHECK(end_offset <= result_ctx->write_buf_len); uint8_t *write_buf = &result_ctx->write_buf[idx]; const uint8_t *read_buf = &result_ctx->orig_buf[write_info->write_start_offset]; memcpy(&write_buf[0], write_info->header, write_info->header_len); memcpy(&write_buf[write_info->header_len], read_buf, write_len); result_ctx->write_buf_offset += write_info->header_len + write_len; } static void prv_encode_with_fill_interval(sMemfaultRleResultCtx *result_ctx, size_t total_size, size_t fill_call_size) { sMemfaultRleCtx ctx = { 0 }; memfault_rle_encode(&ctx, NULL, 0); // should be a no-op for (size_t i = 0; i < result_ctx->orig_buf_len;) { const size_t read_len = MEMFAULT_MIN(fill_call_size, result_ctx->orig_buf_len - i); const size_t bytes_encoded = memfault_rle_encode(&ctx, &result_ctx->orig_buf[i], read_len); prv_update_result_buf(result_ctx, &ctx.write_info); i += bytes_encoded; } memfault_rle_encode_finalize(&ctx); prv_update_result_buf(result_ctx, &ctx.write_info); LONGS_EQUAL(total_size, ctx.total_rle_size); } static void prv_encode(sMemfaultRleResultCtx *result_ctx, size_t total_size) { prv_encode_with_fill_interval(result_ctx, total_size, total_size); } static void prv_check_pattern(const uint8_t *in, size_t in_len, const uint8_t *expected_out, size_t expected_out_len) { uint8_t encode_buf[expected_out_len]; memset(encode_buf, 0x0, sizeof(encode_buf)); sMemfaultRleResultCtx result_ctx = { 0 }; result_ctx.orig_buf = in; result_ctx.orig_buf_len = in_len; result_ctx.write_buf = encode_buf; result_ctx.write_buf_len = sizeof(encode_buf); prv_encode(&result_ctx, expected_out_len); MEMCMP_EQUAL(expected_out, encode_buf, expected_out_len); } TEST(MemfaultRle, Test_QuickTransitions) { const uint8_t pattern[] = { 0x1, 0x2, 0x2, 0x3, 0x4, 0x4, 0x5, 0x6, 0x6, 0x7, 0x8, 0x8 }; const uint8_t expected[] = { 0x17, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, }; prv_check_pattern(pattern, sizeof(pattern), expected, sizeof(expected)); } TEST(MemfaultRle, Test_2and3RunSequences) { const uint8_t pattern[] = { 0x1, 0x2, 0x2, // 2-byte pattern too short, gets included in current non-repeating sequence 0x3, 0x4, 0x4, 0x4, // 3-byte pattern should start new repeating sequence 0x5, 0x6, 0x6, 0x6, 0x7, 0x7, 0x8, 0x8 }; const uint8_t expected[] = { 0x07, 0x01, 0x02, 0x02, 0x03, 0x06, 0x04, 0x01, 0x05, 0x06, 0x06, 0x04, 0x07, 0x04, 0x08 }; prv_check_pattern(pattern, sizeof(pattern), expected, sizeof(expected)); } TEST(MemfaultRle, Test_SingleByte) { const uint8_t pattern[] = { 0x1, }; const uint8_t expected[] = { 0x01, 0x01 }; prv_check_pattern(pattern, sizeof(pattern), expected, sizeof(expected)); } TEST(MemfaultRle, Test_LongRepeat) { uint8_t pattern[4096]; memset(pattern, 0xEF, sizeof(pattern)); const uint8_t expected[] = { 0x80, 0x40, 0xEF }; prv_check_pattern(pattern, sizeof(pattern), expected, sizeof(expected)); } TEST(MemfaultRle, Test_EndWithRepeatSequence) { const uint8_t pattern[] = { 0x1, 0x2, 0x3, 0x3, 0x3, 0x3 }; const uint8_t expected[] = { 0x03, 0x01, 0x02, 0x08, 0x03 }; prv_check_pattern(pattern, sizeof(pattern), expected, sizeof(expected)); } TEST(MemfaultRle, Test_EndWithNonRepeat) { const uint8_t pattern[] = { 0x1, 0x2, 0x3, 0x3, 0x3, 0x3, 0x5, 0x6, 0x7 }; const uint8_t expected[] = { 0x03, 0x01, 0x02, 0x08, 0x03, 0x05, 0x05, 0x06, 0x07 }; prv_check_pattern(pattern, sizeof(pattern), expected, sizeof(expected)); } TEST(MemfaultRle, Test_VariousEncoderSizes) { uint8_t pattern[400] = { 0x1, 0x2, 0x3, 0x3, 0x3, 0x3, 0x5, 0x6, 0x7, }; const size_t expected_total_size = 12; for (size_t i = 1; i < sizeof(pattern); i++) { uint8_t encode_buf[expected_total_size]; memset(encode_buf, 0x0, sizeof(encode_buf)); sMemfaultRleResultCtx result_ctx = { 0 }; result_ctx.orig_buf = &pattern[0]; result_ctx.orig_buf_len = sizeof(pattern); result_ctx.write_buf = encode_buf; result_ctx.write_buf_len = sizeof(encode_buf); prv_encode_with_fill_interval(&result_ctx, expected_total_size, i); const uint8_t expected[] = { 0x03, 0x01, 0x02, 0x08, 0x03, 0x05, 0x05, 0x06, 0x07, 0x8E, 0x06, 0x00 }; MEMCMP_EQUAL(expected, encode_buf, expected_total_size); } } ================================================ FILE: tests/unit/src/test_memfault_root_cert.cpp ================================================ //! @file //! //! @brief //! A sanity check to ensure the PEM & DER format of each cert exported match #include #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "memfault/core/math.h" #include "memfault/http/root_certs.h" #include "memfault/util/base64.h" TEST_GROUP(MemfaultRootCert) { void setup() { } void teardown() { } }; static char *prv_der_to_pem(const uint8_t *der, size_t der_len) { const char *cert_header = "-----BEGIN CERTIFICATE-----\n"; const char *cert_footer = "-----END CERTIFICATE-----\n"; const size_t header_footer_len = strlen(cert_header) + strlen(cert_footer) + 1; // each 3 byte binary block is encoded as 4 base64 characters const size_t encode_len = MEMFAULT_BASE64_ENCODE_LEN(der_len); // add 1 newline for each 64 characters of pem data const size_t linecount = encode_len / 64 + ((encode_len % 64) ? (1) : (0)); const size_t len_with_newlines = encode_len + linecount; const uint32_t pem_len = len_with_newlines + header_footer_len; char *pem = (char *)calloc(1, pem_len); CHECK(pem != NULL); int result = snprintf(pem, pem_len, "%s", cert_header); CHECK(result > 0); // Convert result to size_t as this will be used to index into pem size_t pem_idx = (size_t)result; // encode the pem char *pem_body = (char *)calloc(1, encode_len); memfault_base64_encode(der, der_len, pem_body); // split into 64 characters + newline for (size_t i = 0; i < linecount; i++) { const size_t line_length = MEMFAULT_MIN(encode_len - i * 64, 64); memcpy(&pem[pem_idx], &pem_body[64 * i], line_length); pem_idx += line_length; pem[pem_idx] = '\n'; pem_idx++; } free(pem_body); snprintf(&pem[pem_idx], pem_len - pem_idx, "%s", cert_footer); return pem; } //! verify the pem string has lines of max 64-characters + newline static bool check_for_newlines(char *pemstring) { char *pch; while ((pch = strtok(pemstring, "\n")) != NULL) { if (strlen(pch) > 64) { return false; } // after first tok set to null for remaining passes pemstring = NULL; } return true; } TEST(MemfaultRootCert, Test_certs) { #define FILL_CERT_ENTRY(certname, refdata) \ { \ .name = "" #certname "", \ .ref_data = refdata, \ .len = certname##_len, \ .data = certname, \ } struct cert_refs { const char *name; const char *ref_data; size_t len; const uint8_t *data; } cert_refs[] = { FILL_CERT_ENTRY(g_memfault_cert_digicert_global_root_g2, MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_G2), FILL_CERT_ENTRY(g_memfault_cert_amazon_root_ca1, MEMFAULT_ROOT_CERTS_AMAZON_ROOT_CA1), FILL_CERT_ENTRY(g_memfault_cert_digicert_global_root_ca, MEMFAULT_ROOT_CERTS_DIGICERT_GLOBAL_ROOT_CA), }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(cert_refs); i++) { char *pem = prv_der_to_pem(cert_refs[i].data, cert_refs[i].len); STRCMP_EQUAL_TEXT(pem, cert_refs[i].ref_data, cert_refs[i].name); free(pem); char *pemstring = (char *)calloc(1, strlen(cert_refs[i].ref_data) + 1); strcpy(pemstring, cert_refs[i].ref_data); const bool newlines_correct = check_for_newlines(pemstring); free(pemstring); CHECK_TRUE_TEXT(newlines_correct, cert_refs[i].name); } } ================================================ FILE: tests/unit/src/test_memfault_sdk_assert.cpp ================================================ #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" static jmp_buf s_assert_jmp_buf; #include "memfault/core/platform/core.h" #include "memfault/core/sdk_assert.h" void memfault_platform_halt_if_debugging(void) { mock().actualCall(__func__); } void memfault_sdk_assert_func_noreturn(void) { // we make use of longjmp because this is a noreturn function longjmp(s_assert_jmp_buf, -1); } TEST_GROUP(MfltSdkAssert) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MfltSdkAssert, Test_MfltCircularBufferInit) { mock().expectOneCall("memfault_platform_halt_if_debugging"); if (setjmp(s_assert_jmp_buf) == 0) { memfault_sdk_assert_func(); } } ================================================ FILE: tests/unit/src/test_memfault_sdk_freertos_metrics.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "comparators/comparator_memfault_metric_ids.hpp" #include "memfault/ports/freertos/metrics.h" #include "memfault/ports/freertos/thread_metrics.h" static MemfaultMetricIdsComparator s_metric_id_comparator; static uint32_t s_idle_task_run_time = 0; static uint32_t s_total_run_time = 0; static MemfaultMetricId idle_runtime_id = MEMFAULT_METRICS_KEY(cpu_usage_pct); static MemfaultMetricId timer_task_stack_free_bytes_id = MEMFAULT_METRICS_KEY(timer_task_stack_free_bytes); // Note: the expected value is the usage value before being scaled // by the backend into a percent static void prv_set_expected_usage_pct(uint32_t expected_value) { mock().expectOneCall("ulTaskGetIdleRunTimeCounter"); mock().expectOneCall("portGET_RUN_TIME_COUNTER_VALUE"); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &idle_runtime_id) .withParameter("unsigned_value", expected_value); mock() .expectOneCall("memfault_metrics_heartbeat_set_unsigned") .withParameterOfType("MemfaultMetricId", "key", &timer_task_stack_free_bytes_id) .withParameter("unsigned_value", 0); } static void prv_set_runtimes(uint32_t idle_time, uint32_t total_time) { s_idle_task_run_time = idle_time; s_total_run_time = total_time; } static void prv_clear_runtimes(void) { prv_set_runtimes(0, 0); // Call to make sure previous values are set memfault_freertos_port_task_runtime_metrics(); } extern "C" { uint32_t ulTaskGetIdleRunTimeCounter(void) { mock().actualCall(__func__); return s_idle_task_run_time; } uint32_t portGET_RUN_TIME_COUNTER_VALUE(void) { mock().actualCall(__func__); return s_total_run_time; } void memfault_freertos_port_thread_metrics(void) { // stub } } // clang-format off TEST_GROUP(FreeRTOSMetrics) { void setup(){ mock().disable(); prv_clear_runtimes(); mock().enable(); mock().strictOrder(); mock().installComparator("MemfaultMetricId", s_metric_id_comparator); } void teardown(){ mock().removeAllComparatorsAndCopiers(); mock().clear(); } }; // clang-format on TEST(FreeRTOSMetrics, MetricNoRollover) { uint32_t total_runtime = 100; uint32_t idle_runtime = 50; prv_set_runtimes(idle_runtime, total_runtime); prv_set_expected_usage_pct(5000); memfault_freertos_port_task_runtime_metrics(); total_runtime += 100; idle_runtime += 50; // Run again to ensure delta is calculated correctly prv_set_runtimes(idle_runtime, total_runtime); prv_set_expected_usage_pct(5000); memfault_freertos_port_task_runtime_metrics(); }; TEST(FreeRTOSMetrics, MetricRollover) { uint32_t total_runtime = UINT32_MAX - 1; uint32_t idle_runtime = UINT32_MAX / 2; prv_set_runtimes(idle_runtime, total_runtime); prv_set_expected_usage_pct(5000); memfault_freertos_port_task_runtime_metrics(); total_runtime += 100; idle_runtime += 25; prv_set_runtimes(idle_runtime, total_runtime); prv_set_expected_usage_pct(7500); memfault_freertos_port_task_runtime_metrics(); }; ================================================ FILE: tests/unit/src/test_memfault_self_test.cpp ================================================ #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/self_test.h" #include "memfault_self_test_private.h" extern "C" { uint32_t memfault_self_test_device_info_test(void) { return mock().actualCall(__func__).returnUnsignedIntValue(); } uint32_t memfault_self_test_component_boot_test(void) { return mock().actualCall(__func__).returnUnsignedIntValue(); } uint32_t memfault_self_test_coredump_regions_test(void) { return mock().actualCall(__func__).returnUnsignedIntValue(); } void memfault_self_test_data_export_test(void) { mock().actualCall(__func__); } void memfault_self_test_reboot_reason_test(void) { mock().actualCall(__func__); } uint32_t memfault_self_test_reboot_reason_test_verify(void) { return mock().actualCall(__func__).returnUnsignedIntValue(); } uint32_t memfault_self_test_time_test(void) { return mock().actualCall(__func__).returnUnsignedIntValue(); } uint32_t memfault_self_test_coredump_storage_test(void) { return mock().actualCall(__func__).returnUnsignedIntValue(); } uint32_t memfault_self_test_coredump_storage_capacity_test(void) { return mock().actualCall(__func__).returnUnsignedIntValue(); } } // Helper function for tests that return a result rather than just running static void prv_run_single_component_test(const char *test_name, eMemfaultSelfTestFlag test_flag) { mock().expectOneCall(test_name).andReturnValue(0); int result = memfault_self_test_run(test_flag); LONGS_EQUAL(0, result); mock().expectOneCall(test_name).andReturnValue(1); result = memfault_self_test_run(test_flag); LONGS_EQUAL(1, result); } TEST_GROUP(MemfaultSelfTest) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTest, Test_NoRunFlags) { int result = memfault_self_test_run(0); LONGS_EQUAL(0, result); } TEST(MemfaultSelfTest, Test_DeviceInfoTest) { prv_run_single_component_test("memfault_self_test_device_info_test", kMemfaultSelfTestFlag_DeviceInfo); } TEST(MemfaultSelfTest, Test_ComponentBootTest) { prv_run_single_component_test("memfault_self_test_component_boot_test", kMemfaultSelfTestFlag_ComponentBoot); } TEST(MemfaultSelfTest, Test_CoredumpRegionsTest) { prv_run_single_component_test("memfault_self_test_coredump_regions_test", kMemfaultSelfTestFlag_CoredumpRegions); } TEST(MemfaultSelfTest, Test_DataExportTest) { mock().expectOneCall("memfault_self_test_data_export_test"); memfault_self_test_run(kMemfaultSelfTestFlag_DataExport); } TEST(MemfaultSelfTest, Test_RebootReasonTest) { mock().expectOneCall("memfault_self_test_reboot_reason_test"); memfault_self_test_run(kMemfaultSelfTestFlag_RebootReason); } TEST(MemfaultSelfTest, Test_RebootReasonTestVerify) { prv_run_single_component_test("memfault_self_test_reboot_reason_test_verify", kMemfaultSelfTestFlag_RebootReasonVerify); } TEST(MemfaultSelfTest, Test_PlatformTimeTest) { prv_run_single_component_test("memfault_self_test_time_test", kMemfaultSelfTestFlag_PlatformTime); } TEST(MemfaultSelfTest, Test_CoredumpStorageTest) { #if MEMFAULT_DEMO_CLI_SELF_TEST_COREDUMP_STORAGE prv_run_single_component_test("memfault_self_test_coredump_storage_test", kMemfaultSelfTestFlag_CoredumpStorage); #else mock().expectNoCall("memfault_self_test_coredump_storage_test"); int result = memfault_self_test_run(kMemfaultSelfTestFlag_CoredumpStorage); LONGS_EQUAL(1, result); #endif } TEST(MemfaultSelfTest, Test_CoredumpStorageCapacityTest) { prv_run_single_component_test("memfault_self_test_coredump_storage_capacity_test", kMemfaultSelfTestFlag_CoredumpStorageCapacity); } TEST(MemfaultSelfTest, Test_SelfTestDefaultHappyPath) { mock().expectOneCall("memfault_self_test_device_info_test").andReturnValue(0); mock().expectOneCall("memfault_self_test_component_boot_test").andReturnValue(0); mock().expectOneCall("memfault_self_test_coredump_regions_test").andReturnValue(0); mock().expectOneCall("memfault_self_test_data_export_test"); mock().expectOneCall("memfault_self_test_time_test").andReturnValue(0); mock().expectOneCall("memfault_self_test_coredump_storage_capacity_test").andReturnValue(0); int result = memfault_self_test_run(kMemfaultSelfTestFlag_Default); LONGS_EQUAL(0, result); } TEST(MemfaultSelfTest, Test_SelfTestDefaultComponentBootFail) { mock().expectOneCall("memfault_self_test_device_info_test").andReturnValue(0); mock().expectOneCall("memfault_self_test_component_boot_test").andReturnValue(1); mock().expectOneCall("memfault_self_test_coredump_regions_test").andReturnValue(0); mock().expectOneCall("memfault_self_test_data_export_test"); mock().expectOneCall("memfault_self_test_time_test").andReturnValue(0); mock().expectOneCall("memfault_self_test_coredump_storage_capacity_test").andReturnValue(0); int result = memfault_self_test_run(kMemfaultSelfTestFlag_Default); LONGS_EQUAL(1, result); } TEST(MemfaultSelfTest, Test_SelfTestRunFailure) { // Test with an unknown flag value, no mock calls expected memfault_self_test_run(0x1000); } ================================================ FILE: tests/unit/src/test_memfault_self_test_component_boot_check.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/event_storage.h" #include "memfault/core/log.h" #include "memfault/core/math.h" #include "memfault/core/reboot_tracking.h" #include "memfault/core/trace_event.h" #include "memfault_self_test_private.h" #include "mocks/mock_memfault_platform_debug_log.h" extern "C" { bool memfault_event_storage_booted(void) { return mock().actualCall(__func__).returnBoolValue(); } bool memfault_log_booted(void) { return mock().actualCall(__func__).returnBoolValue(); } bool memfault_reboot_tracking_booted(void) { return mock().actualCall(__func__).returnBoolValue(); } bool memfault_trace_event_booted(void) { return mock().actualCall(__func__).returnBoolValue(); } } TEST_GROUP(MemfaultSelfTestComponentBooted) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTestComponentBooted, Test_ComponentBootNoneBooted) { // clang-format off const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Component Boot Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Component | Booted?|", "-----------------------------", MEMFAULT_SELF_TEST_END_OUTPUT, }; const char *error_output_lines[] = { "Event Storage | no|", "Logging | no|", "Reboot Tracking | no|", "Trace Event | no|", }; // clang-format on memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_output_lines, MEMFAULT_ARRAY_SIZE(error_output_lines)); mock().expectOneCall("memfault_event_storage_booted").andReturnValue(false); mock().expectOneCall("memfault_log_booted").andReturnValue(false); mock().expectOneCall("memfault_reboot_tracking_booted").andReturnValue(false); mock().expectOneCall("memfault_trace_event_booted").andReturnValue(false); uint32_t result = memfault_self_test_component_boot_test(); UNSIGNED_LONGS_EQUAL(0xf, result); } TEST(MemfaultSelfTestComponentBooted, Test_ComponentBootSingleBoot) { // clang-format off const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Component Boot Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Component | Booted?|", "-----------------------------", MEMFAULT_SELF_TEST_END_OUTPUT, }; const char *error_output_lines[] = { "Event Storage | no|", "Logging | no|", }; const char *info_output_lines[] = { "Reboot Tracking | yes|", }; const char *error_output_lines2[] = { "Trace Event | no|", }; // clang-format on memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_output_lines, MEMFAULT_ARRAY_SIZE(error_output_lines)); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, info_output_lines, MEMFAULT_ARRAY_SIZE(info_output_lines)); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_output_lines2, MEMFAULT_ARRAY_SIZE(error_output_lines2)); mock().expectOneCall("memfault_event_storage_booted").andReturnValue(false); mock().expectOneCall("memfault_log_booted").andReturnValue(false); mock().expectOneCall("memfault_reboot_tracking_booted").andReturnValue(true); mock().expectOneCall("memfault_trace_event_booted").andReturnValue(false); uint32_t result = memfault_self_test_component_boot_test(); UNSIGNED_LONGS_EQUAL(0xb, result); } TEST(MemfaultSelfTestComponentBooted, Test_ComponentBootAllBooted) { // clang-format off const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Component Boot Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Component | Booted?|", "-----------------------------", "All components booted", MEMFAULT_SELF_TEST_END_OUTPUT, }; const char *info_output_lines[] = { "Event Storage | yes|", "Logging | yes|", "Reboot Tracking | yes|", "Trace Event | yes|", }; // clang-format on memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, info_output_lines, MEMFAULT_ARRAY_SIZE(info_output_lines)); mock().expectOneCall("memfault_event_storage_booted").andReturnValue(true); mock().expectOneCall("memfault_log_booted").andReturnValue(true); mock().expectOneCall("memfault_reboot_tracking_booted").andReturnValue(true); mock().expectOneCall("memfault_trace_event_booted").andReturnValue(true); uint32_t result = memfault_self_test_component_boot_test(); UNSIGNED_LONGS_EQUAL(0, result); } ================================================ FILE: tests/unit/src/test_memfault_self_test_coredump_regions.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "components/core/src/memfault_self_test_private.h" #include "memfault/core/math.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" #include "memfault/panics/platform/coredump.h" #include "mocks/mock_memfault_platform_debug_log.h" extern "C" { const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_regions) { return (const sMfltCoredumpRegion *)mock() .actualCall(__func__) .withOutputParameter("num_regions", (void *)num_regions) .returnConstPointerValue(); } const sMfltCoredumpRegion *memfault_coredump_get_sdk_regions(size_t *num_regions) { return (const sMfltCoredumpRegion *)mock() .actualCall(__func__) .withOutputParameter("num_regions", num_regions) .returnConstPointerValue(); } const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( MEMFAULT_UNUSED const sCoredumpCrashInfo *crash_info, size_t *num_regions) { return (const sMfltCoredumpRegion *)mock() .actualCall(__func__) .withOutputParameter("num_regions", (void *)num_regions) .returnConstPointerValue(); } } typedef struct { const sMfltCoredumpRegion *platform_regions; size_t num_platform_regions; const sMfltCoredumpRegion *arch_regions; size_t num_arch_regions; const sMfltCoredumpRegion *sdk_regions; size_t num_sdk_regions; } sTestParameters; // Helper function to setup the coredump region mocks static void prv_set_coredump_region_mocks(sTestParameters *params) { mock() .expectOneCall("memfault_platform_coredump_get_regions") .withOutputParameterReturning("num_regions", (void *)¶ms->num_platform_regions, sizeof(params->num_platform_regions)) .andReturnValue((const void *)params->platform_regions); mock() .expectOneCall("memfault_coredump_get_arch_regions") .withOutputParameterReturning("num_regions", (void *)¶ms->num_arch_regions, sizeof(params->num_arch_regions)) .andReturnValue((const void *)params->arch_regions); mock() .expectOneCall("memfault_coredump_get_sdk_regions") .withOutputParameterReturning("num_regions", (void *)¶ms->num_sdk_regions, sizeof(params->num_sdk_regions)) .andReturnValue((const void *)params->sdk_regions); } TEST_GROUP(MemfaultSelfTestCoredumpRegions) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTestCoredumpRegions, Test_NoRegions) { sTestParameters params = { 0 }; // clang-format off const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump Regions Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump group: Platform Regions", "-----------------------------", " Address| Length| Type|", "-----------------------------", "-----------------------------", "Coredump group: Arch Regions", "-----------------------------", "-----------------------------", " Address| Length| Type|", "-----------------------------", "Coredump group: SDK Regions", "-----------------------------", " Address| Length| Type|", "-----------------------------", "-----------------------------", MEMFAULT_SELF_TEST_END_OUTPUT, }; // clang-format on const char *error_lines[] = { "Number of platform regions = 0", "Platform regions was NULL", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); prv_set_coredump_region_mocks(¶ms); uint32_t result = memfault_self_test_coredump_regions_test(); UNSIGNED_LONGS_EQUAL(1, result); } TEST(MemfaultSelfTestCoredumpRegions, Test_PlatformRegionsOutput) { const sMfltCoredumpRegion test_regions[2] = { { .type = (eMfltCoredumpRegionType)1, .region_start = (void *)0x0, .region_size = 50000, }, { .type = (eMfltCoredumpRegionType)1, .region_start = (void *)0xFFFF, .region_size = 1000, }, }; sTestParameters params = { .platform_regions = (const sMfltCoredumpRegion *)&test_regions, .num_platform_regions = MEMFAULT_ARRAY_SIZE(test_regions), }; prv_set_coredump_region_mocks(¶ms); // clang-format off const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump Regions Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump group: Platform Regions", "-----------------------------", " Address| Length| Type|", "0x00000000| 50000| 1|", "0x0000ffff| 1000| 1|", "-----------------------------", "-----------------------------", "Coredump group: Arch Regions", "-----------------------------", "-----------------------------", " Address| Length| Type|", "-----------------------------", "Coredump group: SDK Regions", "-----------------------------", " Address| Length| Type|", "-----------------------------", "-----------------------------", MEMFAULT_SELF_TEST_END_OUTPUT, }; // clang-format on memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); uint32_t result = memfault_self_test_coredump_regions_test(); UNSIGNED_LONGS_EQUAL(0, result); } TEST(MemfaultSelfTestCoredumpRegions, Test_ArchRegionsOutput) { const sMfltCoredumpRegion test_regions[2] = { { .type = (eMfltCoredumpRegionType)1, .region_start = (void *)0x0, .region_size = 50000, }, { .type = (eMfltCoredumpRegionType)1, .region_start = (void *)0xFFFF, .region_size = 1000, }, }; sTestParameters params = { .platform_regions = (const sMfltCoredumpRegion *)&test_regions, .num_platform_regions = MEMFAULT_ARRAY_SIZE(test_regions), .arch_regions = (const sMfltCoredumpRegion *)&test_regions, .num_arch_regions = MEMFAULT_ARRAY_SIZE(test_regions), }; prv_set_coredump_region_mocks(¶ms); // clang-format off const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump Regions Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump group: Platform Regions", "-----------------------------", " Address| Length| Type|", "0x00000000| 50000| 1|", "0x0000ffff| 1000| 1|", "-----------------------------", "-----------------------------", "Coredump group: Arch Regions", "-----------------------------", "0x00000000| 50000| 1|", "0x0000ffff| 1000| 1|", "-----------------------------", " Address| Length| Type|", "-----------------------------", "Coredump group: SDK Regions", "-----------------------------", " Address| Length| Type|", "-----------------------------", "-----------------------------", MEMFAULT_SELF_TEST_END_OUTPUT, }; // clang-format on memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); uint32_t result = memfault_self_test_coredump_regions_test(); UNSIGNED_LONGS_EQUAL(0, result); } TEST(MemfaultSelfTestCoredumpRegions, Test_SdkRegionsOutput) { const sMfltCoredumpRegion test_regions[2] = { { .type = (eMfltCoredumpRegionType)1, .region_start = (void *)0x0, .region_size = 50000, }, { .type = (eMfltCoredumpRegionType)1, .region_start = (void *)0xFFFF, .region_size = 1000, }, }; sTestParameters params = { .platform_regions = (const sMfltCoredumpRegion *)&test_regions, .num_platform_regions = MEMFAULT_ARRAY_SIZE(test_regions), .sdk_regions = (const sMfltCoredumpRegion *)&test_regions, .num_sdk_regions = MEMFAULT_ARRAY_SIZE(test_regions), }; prv_set_coredump_region_mocks(¶ms); // clang-format off const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump Regions Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump group: Platform Regions", "-----------------------------", " Address| Length| Type|", "0x00000000| 50000| 1|", "0x0000ffff| 1000| 1|", "-----------------------------", "-----------------------------", "Coredump group: Arch Regions", "-----------------------------", "-----------------------------", " Address| Length| Type|", "-----------------------------", "Coredump group: SDK Regions", "-----------------------------", " Address| Length| Type|", "-----------------------------", "0x00000000| 50000| 1|", "0x0000ffff| 1000| 1|", "-----------------------------", MEMFAULT_SELF_TEST_END_OUTPUT, }; // clang-format on memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); uint32_t result = memfault_self_test_coredump_regions_test(); UNSIGNED_LONGS_EQUAL(0, result); } ================================================ FILE: tests/unit/src/test_memfault_self_test_coredump_storage.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "components/core/src/memfault_self_test_private.h" #include "memfault/core/math.h" #include "memfault/core/self_test.h" #include "memfault/panics/coredump.h" #include "mocks/mock_memfault_platform_debug_log.h" bool memfault_coredump_storage_debug_test_begin(void) { return mock().actualCall(__func__).returnBoolValue(); } bool memfault_coredump_storage_debug_test_finish(void) { return mock().actualCall(__func__).returnBoolValue(); } bool memfault_self_test_platform_enable_irqs(void) { return mock().actualCall(__func__).returnBoolValue(); } bool memfault_self_test_platform_disable_irqs(void) { return mock().actualCall(__func__).returnBoolValue(); } bool memfault_coredump_storage_check_size(void) { return mock().actualCall(__func__).returnBoolValue(); } bool memfault_coredump_has_valid_coredump(size_t *total_size_out) { return mock() .actualCall(__func__) .withOutputParameter("total_size_out", total_size_out) .returnBoolValue(); } static void prv_setup_irq_mocks(void) { mock().expectOneCall("memfault_self_test_platform_disable_irqs").andReturnValue(true); mock().expectOneCall("memfault_self_test_platform_enable_irqs").andReturnValue(true); } TEST_GROUP(MemfaultSelfTestCoredumpStorage) { void setup() { const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump Storage Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, MEMFAULT_SELF_TEST_END_OUTPUT, }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTestCoredumpStorage, Test_HasValidCoredump) { mock() .expectOneCall("memfault_coredump_has_valid_coredump") .withUnmodifiedOutputParameter("total_size_out") .andReturnValue(true); const char *error_lines[] = { "Aborting test, valid coredump present", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); uint32_t result = memfault_self_test_coredump_storage_test(); UNSIGNED_LONGS_EQUAL((1 << 0), result); } TEST(MemfaultSelfTestCoredumpStorage, Test_DisableIrqFailure) { mock() .expectOneCall("memfault_coredump_has_valid_coredump") .withUnmodifiedOutputParameter("total_size_out") .andReturnValue(false); mock().expectOneCall("memfault_self_test_platform_disable_irqs").andReturnValue(false); const char *error_lines[] = { "Aborting test, could not disable interrupts", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); uint32_t result = memfault_self_test_coredump_storage_test(); UNSIGNED_LONGS_EQUAL((1 << 1), result); } TEST(MemfaultSelfTestCoredumpStorage, Test_EnableIrqFailure) { mock() .expectOneCall("memfault_coredump_has_valid_coredump") .withUnmodifiedOutputParameter("total_size_out") .andReturnValue(false); mock().expectOneCall("memfault_self_test_platform_disable_irqs").andReturnValue(true); mock().expectOneCall("memfault_coredump_storage_debug_test_begin").andReturnValue(true); mock().expectOneCall("memfault_self_test_platform_enable_irqs").andReturnValue(false); mock().expectOneCall("memfault_coredump_storage_debug_test_finish").andReturnValue(true); const char *warning_lines[] = { "Failed to enable interrupts after test completed", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Warning, warning_lines, MEMFAULT_ARRAY_SIZE(warning_lines)); uint32_t result = memfault_self_test_coredump_storage_test(); UNSIGNED_LONGS_EQUAL(0, result); } TEST(MemfaultSelfTestCoredumpStorage, Test_CoredumpStorage_Failure) { prv_setup_irq_mocks(); mock() .expectOneCall("memfault_coredump_has_valid_coredump") .withUnmodifiedOutputParameter("total_size_out") .andReturnValue(false); mock().expectOneCall("memfault_coredump_storage_debug_test_begin").andReturnValue(false); mock().expectOneCall("memfault_coredump_storage_debug_test_finish").andReturnValue(false); uint32_t result = memfault_self_test_coredump_storage_test(); UNSIGNED_LONGS_EQUAL((1 << 2), result); } TEST(MemfaultSelfTestCoredumpStorage, Test_CoredumpStorage_Success) { prv_setup_irq_mocks(); mock() .expectOneCall("memfault_coredump_has_valid_coredump") .withUnmodifiedOutputParameter("total_size_out") .andReturnValue(false); mock().expectOneCall("memfault_coredump_storage_debug_test_begin").andReturnValue(true); mock().expectOneCall("memfault_coredump_storage_debug_test_finish").andReturnValue(true); uint32_t result = memfault_self_test_coredump_storage_test(); UNSIGNED_LONGS_EQUAL(0, result); } TEST_GROUP(MemfaultSelfTestCoredumpStorageCapacity) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTestCoredumpStorageCapacity, Test_CoredumpStorageCapacity_TooSmall) { const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump Storage Capacity Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, MEMFAULT_SELF_TEST_END_OUTPUT, }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); mock().expectOneCall("memfault_coredump_storage_check_size").andReturnValue(false); uint32_t result = memfault_self_test_coredump_storage_capacity_test(); UNSIGNED_LONGS_EQUAL(1, result); } TEST(MemfaultSelfTestCoredumpStorageCapacity, Test_CoredumpStorageCapacity_Success) { const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Coredump Storage Capacity Test", "Total size required: 0 bytes", "Storage capacity: 0 bytes", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, MEMFAULT_SELF_TEST_END_OUTPUT, }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); mock().expectOneCall("memfault_coredump_storage_check_size").andReturnValue(true); uint32_t result = memfault_self_test_coredump_storage_capacity_test(); UNSIGNED_LONGS_EQUAL(0, result); } ================================================ FILE: tests/unit/src/test_memfault_self_test_data_export.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/math.h" #include "memfault_self_test_private.h" #include "mocks/mock_memfault_platform_debug_log.h" TEST_GROUP(MemfaultSelfTestDeviceInfo) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTestDeviceInfo, Test_DataExportTest) { const char *output_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Data Export Line Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, // Should be equal to MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN - 1 chars "Printing 112 characters, confirm line ends with '1' and is not split", // Parentheses needed for -Wstring-concatenation ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" "++++++++++++++++++1"), MEMFAULT_SELF_TEST_END_OUTPUT, }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, output_lines, MEMFAULT_ARRAY_SIZE(output_lines)); memfault_self_test_data_export_test(); } ================================================ FILE: tests/unit/src/test_memfault_self_test_device_info.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_build_id.h" #include "memfault/core/platform/device_info.h" #include "memfault_self_test_private.h" #define VALID_DEVICE_SERIAL "device_serial-" #define VALID_SOFTWARE_TYPE "1.0_+:software-type" #define VALID_SOFTWARE_VERSION "1.2.3+dev" #define VALID_HARDWARE_VERSION "1.0_+:hw-version" extern "C" { void memfault_platform_get_device_info(sMemfaultDeviceInfo *device_info) { mock().actualCall(__func__).withOutputParameter("device_info", device_info); } } typedef struct { const char *invalid_str; const char *valid_str; uint32_t invalid_result; } sDeviceInfoFieldParam; static const char *invalid_long_field = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; static sMemfaultDeviceInfo s_device_info = { 0 }; static void prv_set_device_info_valid(void) { s_device_info.device_serial = VALID_DEVICE_SERIAL; s_device_info.software_type = VALID_SOFTWARE_TYPE; s_device_info.software_version = VALID_SOFTWARE_VERSION; s_device_info.hardware_version = VALID_HARDWARE_VERSION; } static void prv_test_device_info_helper(long expected_val) { mock() .expectOneCall("memfault_platform_get_device_info") .withOutputParameterReturning("device_info", &s_device_info, sizeof(sMemfaultDeviceInfo)); uint32_t result = memfault_self_test_device_info_test(); LONGS_EQUAL(expected_val, result); } static void prv_test_device_info_field_helper(const char **field, sDeviceInfoFieldParam *params) { // Test NULL *field = NULL; prv_test_device_info_helper(params->invalid_result); // Test short *field = ""; prv_test_device_info_helper(params->invalid_result); // Test long *field = invalid_long_field; prv_test_device_info_helper(params->invalid_result); // Test invalid string *field = params->invalid_str; prv_test_device_info_helper(params->invalid_result); // Test valid string *field = params->valid_str; prv_test_device_info_helper(0); } TEST_GROUP(MemfaultSelfTestDeviceInfo) { void setup() { prv_set_device_info_valid(); g_fake_memfault_build_id_type = kMemfaultBuildIdType_MemfaultBuildIdSha1; } void teardown() { mock().checkExpectations(); mock().clear(); fake_memfault_build_id_reset(); } }; TEST(MemfaultSelfTestDeviceInfo, Test_ValidateDeviceSerial) { sDeviceInfoFieldParam params = { .invalid_str = "invalid+", .valid_str = VALID_DEVICE_SERIAL, .invalid_result = 0x1, }; prv_test_device_info_field_helper(&s_device_info.device_serial, ¶ms); } TEST(MemfaultSelfTestDeviceInfo, Test_ValidateSoftwareType) { sDeviceInfoFieldParam params = { .invalid_str = "invalid{}", .valid_str = VALID_SOFTWARE_TYPE, .invalid_result = 0x2, }; prv_test_device_info_field_helper(&s_device_info.software_type, ¶ms); } TEST(MemfaultSelfTestDeviceInfo, Test_ValidateSoftwareVersion) { sDeviceInfoFieldParam params = { .invalid_str = "invalid\a", .valid_str = VALID_SOFTWARE_VERSION, .invalid_result = 0x4, }; prv_test_device_info_field_helper(&s_device_info.software_version, ¶ms); } TEST(MemfaultSelfTestDeviceInfo, Test_ValidateHardwareVersion) { sDeviceInfoFieldParam params = { .invalid_str = "invalid{}", .valid_str = VALID_HARDWARE_VERSION, .invalid_result = 0x8, }; prv_test_device_info_field_helper(&s_device_info.hardware_version, ¶ms); } TEST(MemfaultSelfTestDeviceInfo, Test_ValidateBuildId) { // Explicitly clear the build ID info, test invalid build ID g_fake_memfault_build_id_type = kMemfaultBuildIdType_None; mock() .expectOneCall("memfault_platform_get_device_info") .withOutputParameterReturning("device_info", &s_device_info, sizeof(sMemfaultDeviceInfo)); uint32_t result = memfault_self_test_device_info_test(); LONGS_EQUAL(0x10, result); // Test a valid build ID g_fake_memfault_build_id_type = kMemfaultBuildIdType_MemfaultBuildIdSha1; mock() .expectOneCall("memfault_platform_get_device_info") .withOutputParameterReturning("device_info", &s_device_info, sizeof(sMemfaultDeviceInfo)); result = memfault_self_test_device_info_test(); LONGS_EQUAL(0, result); } ================================================ FILE: tests/unit/src/test_memfault_self_test_reboot_reason.cpp ================================================ #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "components/core/src/memfault_self_test_private.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/reboot_tracking.h" extern "C" { MEMFAULT_NORETURN void memfault_platform_reboot(void) { mock().actualCall(__func__); } void memfault_reboot_tracking_mark_reset_imminent(eMemfaultRebootReason reboot_reason, const sMfltRebootTrackingRegInfo *reg) { mock() .actualCall(__func__) .withParameter("reboot_reason", reboot_reason) .withParameter("reg", (const void *)reg); } } TEST_GROUP(MemfaultSelfTestRebootReason) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTestRebootReason, Test_RebootTest) { // Call reboot test function and check that mocks are called and set correctly mock() .expectOneCall("memfault_reboot_tracking_mark_reset_imminent") .withParameter("reboot_reason", kMfltRebootReason_SelfTest) .ignoreOtherParameters(); mock().expectOneCall("memfault_platform_reboot"); memfault_self_test_reboot_reason_test(); } TEST(MemfaultSelfTestRebootReason, Test_RebootVerifyTest) { sMfltRebootReason reboot_reason = { .prior_stored_reason = kMfltRebootReason_SelfTest, }; // Verify test fails if reboot reason is not set (reboot tracking not initialized) mock() .expectOneCall("memfault_reboot_tracking_get_reboot_reason") .withOutputParameterReturning("reboot_reason", &reboot_reason, sizeof(reboot_reason)) .andReturnValue(1); unsigned int result = memfault_self_test_reboot_reason_test_verify(); UNSIGNED_LONGS_EQUAL(1, result); // Verify test fails if reboot reason is not set to self test reason reboot_reason.prior_stored_reason = kMfltRebootReason_Unknown; mock() .expectOneCall("memfault_reboot_tracking_get_reboot_reason") .withOutputParameterReturning("reboot_reason", &reboot_reason, sizeof(reboot_reason)) .andReturnValue(0); result = memfault_self_test_reboot_reason_test_verify(); UNSIGNED_LONGS_EQUAL(1, result); // Verify test passes if reboot tracking initialized and correct reason returned reboot_reason.prior_stored_reason = kMfltRebootReason_SelfTest; mock() .expectOneCall("memfault_reboot_tracking_get_reboot_reason") .withOutputParameterReturning("reboot_reason", &reboot_reason, sizeof(reboot_reason)) .andReturnValue(0); result = memfault_self_test_reboot_reason_test_verify(); UNSIGNED_LONGS_EQUAL(0, result); } ================================================ FILE: tests/unit/src/test_memfault_self_test_time.cpp ================================================ #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "components/core/src/memfault_self_test_private.h" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/core/platform/system_time.h" #include "memfault/core/self_test.h" #include "mocks/mock_memfault_platform_debug_log.h" extern "C" { uint64_t memfault_platform_get_time_since_boot_ms(void) { return mock().actualCall(__func__).returnUnsignedLongIntValue(); } void memfault_self_test_platform_delay(MEMFAULT_UNUSED uint32_t delay_ms) { return; } } // Initializes a valid timestamp using the current system time static void prv_initialize_valid_time(sMemfaultCurrentTime *time) { // Get a timepoint relative to epoch const auto since_epoch = std::chrono::system_clock::now().time_since_epoch(); // Convert duration to seconds then resolve to an integer with count const auto unix_timestamp_secs = std::chrono::duration_cast(since_epoch).count(); // Populate time struct *time = (sMemfaultCurrentTime){ .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = (uint64_t)unix_timestamp_secs, }, }; } // Helper function to set up valid memfault_platform_time_get_current results // Parameter time MUST remain in scope until test completes static void prv_set_time_get_current_mock(sMemfaultCurrentTime *time, bool return_value) { mock() .expectOneCall("memfault_platform_time_get_current") .withOutputParameterReturning("time", time, sizeof(sMemfaultCurrentTime)) .andReturnValue(return_value); } // Helper function to set up valid memfault_platform_get_time_since_boot_ms static void prv_set_valid_time_since_boot(void) { mock().expectOneCall("memfault_platform_get_time_since_boot_ms").andReturnValue(1); mock().expectOneCall("memfault_platform_get_time_since_boot_ms").andReturnValue(2); } // Helper function to create a string containing expected output formatted with the current time // Caller of the function must delete the return value // NB: Attempting to return a string object causes a memory leak to occur at the end of each test. // The easiest remedy appears to be to use a heap allocated object instead static std::string *prv_build_timestamp_received_string(sMemfaultCurrentTime *time) { std::stringstream ss; ss << "Verify received timestamp for accuracy. Timestamp received " << (uint64_t)time->info.unix_timestamp_secs; return new std::string(ss.str()); } // Stores the current time struct used by a test mock. Static storage is used to ensure the struct // is around when the mock calls are checked static sMemfaultCurrentTime s_current_time; // Stores a pointer to the current expected output string formed from the current time. Static // storage is used to ensure the struct is around when the mock calls are checked static std::string *s_timestamp_received_str; TEST_GROUP(MemfaultSelfTestTime_GetTimeSinceBootMs) { void setup() { // Initialize time_get_current to valid output prv_initialize_valid_time(&s_current_time); prv_set_time_get_current_mock(&s_current_time, true); // Build up the output string using the current time s_timestamp_received_str = prv_build_timestamp_received_string(&s_current_time); // clang-format off const char * info_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Time Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, s_timestamp_received_str->c_str(), MEMFAULT_SELF_TEST_END_OUTPUT, }; // clang-format on memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, info_lines, MEMFAULT_ARRAY_SIZE(info_lines)); } void teardown() { mock().checkExpectations(); mock().clear(); // Delete the string returns from prv_build_timestamp_received_string delete s_timestamp_received_str; } }; TEST(MemfaultSelfTestTime_GetTimeSinceBootMs, Test_ZeroBootTime) { // Initialize logging mock with expected error lines and number of additional calls const char *error_lines[] = { "Time since boot reported as 0", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); mock().expectOneCall("memfault_platform_get_time_since_boot_ms").andReturnValue(0); // Initialize memfault_platform_time_get_current uint32_t result = memfault_self_test_time_test(); UNSIGNED_LONGS_EQUAL((1 << 0), result); } TEST(MemfaultSelfTestTime_GetTimeSinceBootMs, Test_EndEqualStart) { // Run test with the same start and end timestamps // Set up logging mock with expected error lines const char *error_lines[] = { "Time since boot not monotonically increasing: start[4] vs end[4]", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); // Set up mock to return the same time since boot value for both calls mock().expectOneCall("memfault_platform_get_time_since_boot_ms").andReturnValue(4); mock().expectOneCall("memfault_platform_get_time_since_boot_ms").andReturnValue(4); uint32_t result = memfault_self_test_time_test(); UNSIGNED_LONGS_EQUAL((1 << 1), result); } TEST(MemfaultSelfTestTime_GetTimeSinceBootMs, Test_EndLessThanStart) { // Run test with start timestamp > end timestamp const char *error_lines[] = { "Time since boot not monotonically increasing: start[4] vs " "end[3]", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); // Set up mock to return start > end mock().expectOneCall("memfault_platform_get_time_since_boot_ms").andReturnValue(4); mock().expectOneCall("memfault_platform_get_time_since_boot_ms").andReturnValue(3); uint32_t result = memfault_self_test_time_test(); UNSIGNED_LONGS_EQUAL((1 << 1), result); } TEST_GROUP(MemfaultSelfTestTime_TimeGetCurrent) { void setup() { // Initialize boot time prv_set_valid_time_since_boot(); // clang-format off const char * info_lines[] = { MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Time Test", MEMFAULT_SELF_TEST_BEGIN_OUTPUT, "Time since boot test succeeded", MEMFAULT_SELF_TEST_END_OUTPUT, }; // clang-format on memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, info_lines, MEMFAULT_ARRAY_SIZE(info_lines)); } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTestTime_TimeGetCurrent, Test_Timestamp_Unavailable) { const char *error_lines[] = { "Current timestamp could not be recovered", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); sMemfaultCurrentTime time = {}; mock() .expectOneCall("memfault_platform_time_get_current") .withOutputParameterReturning("time", &time, sizeof(time)) .andReturnValue(false); uint32_t result = memfault_self_test_time_test(); UNSIGNED_LONGS_EQUAL((1 << 2), result); } TEST(MemfaultSelfTestTime_TimeGetCurrent, Test_WrongType) { sMemfaultCurrentTime time = { .type = kMemfaultCurrentTimeType_Unknown, }; const char *error_lines[] = { "Invalid time type returned: 0", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); mock() .expectOneCall("memfault_platform_time_get_current") .withOutputParameterReturning("time", &time, sizeof(time)) .andReturnValue(true); uint32_t result = memfault_self_test_time_test(); UNSIGNED_LONGS_EQUAL((1 << 3), result); } TEST(MemfaultSelfTestTime_TimeGetCurrent, Test_EpochTimeResult) { sMemfaultCurrentTime time = { .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = 0, }, }; const char *error_lines[] = { "Timestamp too far in the past: 0", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Error, error_lines, MEMFAULT_ARRAY_SIZE(error_lines)); mock() .expectOneCall("memfault_platform_time_get_current") .withOutputParameterReturning("time", &time, sizeof(time)) .andReturnValue(true); uint32_t result = memfault_self_test_time_test(); UNSIGNED_LONGS_EQUAL((1 << 4), result); } TEST(MemfaultSelfTestTime_TimeGetCurrent, Test_HappyPath) { sMemfaultCurrentTime time = {}; prv_initialize_valid_time(&time); prv_set_time_get_current_mock(&time, true); std::string *timestamp_received = prv_build_timestamp_received_string(&time); const char *info_lines[] = { timestamp_received->c_str(), }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, info_lines, MEMFAULT_ARRAY_SIZE(info_lines)); uint32_t result = memfault_self_test_time_test(); UNSIGNED_LONGS_EQUAL(0, result); delete timestamp_received; } ================================================ FILE: tests/unit/src/test_memfault_self_test_utils.cpp ================================================ #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/math.h" #include "memfault/core/sdk_assert.h" #include "memfault/core/self_test.h" #include "memfault_self_test_private.h" TEST_GROUP(MemfaultSelfTestUtils) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MemfaultSelfTestUtils, Test_ValidDeviceSerial) { char const *valid_device_serial = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; // Iteratate through all ascii chars // Skip null as this would just terminate the string early for (unsigned int c = 1; c < 0x100; c++) { bool result = memfault_self_test_valid_device_serial((unsigned char)c); if (strchr(valid_device_serial, (char)c) != NULL) { CHECK(result); } else { CHECK_FALSE(result); } } } TEST(MemfaultSelfTestUtils, Test_ValidHwVersionSwType) { char const *valid_hw_version_sw_type = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:.+"; // Iteratate through all ascii chars // Skip null as this would just terminate the string early for (unsigned int c = 1; c < 0x100; c++) { bool result = memfault_self_test_valid_hw_version_sw_type((unsigned char)c); if (strchr(valid_hw_version_sw_type, (char)c) != NULL) { CHECK(result); } else { CHECK_FALSE(result); } } } static void prv_arg_to_test_flag_helper(const char *arg, uint32_t expected_flags) { uint32_t actual_flags = memfault_self_test_arg_to_flag(arg); LONGS_EQUAL(expected_flags, actual_flags); } TEST(MemfaultSelfTestUtils, Test_ArgToTestFlag) { prv_arg_to_test_flag_helper("", kMemfaultSelfTestFlag_Default); prv_arg_to_test_flag_helper("bad_arg", kMemfaultSelfTestFlag_Default); prv_arg_to_test_flag_helper("reboot", kMemfaultSelfTestFlag_RebootReason); prv_arg_to_test_flag_helper("reboot_verify", kMemfaultSelfTestFlag_RebootReasonVerify); } static jmp_buf s_assert_jmp_buf; void memfault_sdk_assert_func(void) { mock().actualCall(__func__); longjmp(s_assert_jmp_buf, -1); } TEST(MemfaultSelfTestUtils, Test_NullArg) { // We expect a call to the SDK assert mock().expectOneCall("memfault_sdk_assert_func"); if (setjmp(s_assert_jmp_buf) == 0) { memfault_self_test_arg_to_flag(NULL); } } TEST(MemfaultSelfTestUtils, Test_Strnlen) { const char *test_str = "test_stri\0ng"; const char test_no_null[5] = { 'a', 'b', 'c', 'd', 'e', }; // Test zero length size_t n = 0; size_t result = memfault_strnlen(test_str, n); LONGS_EQUAL(n, result); // Test length 1 n = 1; result = memfault_strnlen(test_str, n); LONGS_EQUAL(n, result); // Test NULL at index >= n n = 9; result = memfault_strnlen(test_str, n); LONGS_EQUAL(n, result); // Test NULL at index < n n = 12; result = memfault_strnlen(test_str, n); LONGS_EQUAL(9, result); // Test no NULL in string n = MEMFAULT_ARRAY_SIZE(test_no_null); result = memfault_strnlen(test_no_null, n); LONGS_EQUAL(n, result); } ================================================ FILE: tests/unit/src/test_memfault_serializer_helper.cpp ================================================ //! @file //! //! @brief #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_build_id.h" #include "fakes/fake_memfault_platform_time.h" #include "memfault/config.h" #include "memfault/core/math.h" #include "memfault/core/serializer_helper.h" #include "memfault/util/cbor.h" TEST_GROUP(MemfaultMetricsSerializerHelper) { void setup() { fake_memfault_platform_time_enable(false); fake_memfault_build_id_reset(); } void teardown() { mock().checkExpectations(); mock().clear(); fake_memfault_platform_time_enable(false); } }; static void prv_write_cb(void *ctx, uint32_t offset, const void *buf, size_t buf_len) { CHECK(ctx != NULL); uint8_t *result_buf = (uint8_t *)ctx; memcpy(&result_buf[offset], buf, buf_len); } static void prv_encode_metadata_and_check(sMemfaultCborEncoder *e, void *result, const size_t expected_size) { // walk through encodes up to the expected size to make sure they all fail out appropriately for (size_t i = 1; i <= expected_size; i++) { memset(result, 0x0, expected_size); memfault_cbor_encoder_init(e, prv_write_cb, result, i); const bool success = memfault_serializer_helper_encode_metadata(e, kMemfaultEventType_Heartbeat); const bool success_expected = (i == expected_size); LONGS_EQUAL(success_expected, success); } const size_t bytes_encoded = memfault_cbor_encoder_deinit(e); LONGS_EQUAL(expected_size, bytes_encoded); } static void prv_enable_captured_date(void) { fake_memfault_platform_time_enable(true); const sMemfaultCurrentTime time = { .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, .info = { .unix_timestamp_secs = 1586353908, } }; fake_memfault_platform_time_set(&time); } static void prv_enable_build_id(void) { g_fake_memfault_build_id_type = kMemfaultBuildIdType_MemfaultBuildIdSha1; } typedef struct MemfaultSerializerHelperTestVector { const uint8_t *expected_encoding; const size_t expected_encoding_len; bool encode_captured_date; bool build_id_present; } sMemfaultSerializerHelperTestVector; #if MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL // Optional Items: Device Serial const uint8_t test_vector[] = { 0xa7, 0x02, 0x01, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', }; // Optional Items: Device Serial & Unix Epoch Timestamp const uint8_t test_vector_with_timestamp[] = { 0xa8, 0x02, 0x01, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x01, 0x1a, 0x5e, 0x8d, 0xd6, 0xf4, }; // Optional Items: Build Id const uint8_t test_vector_with_build_id[] = { 0xa8, 0x02, 0x01, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x0B, 0x46, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, }; #else // Optional Items: None const uint8_t test_vector[] = { 0xa6, 0x02, 0x01, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', }; // Optional Items: Unix Epoch Timestamp const uint8_t test_vector_with_timestamp[] = { 0xa7, 0x02, 0x01, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x01, 0x1a, 0x5e, 0x8d, 0xd6, 0xf4, }; #if MEMFAULT_EVENT_INCLUDE_BUILD_ID // Optional Items: Build Id const uint8_t test_vector_with_build_id[] = { 0xa7, 0x02, 0x01, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x0B, 0x46, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, }; #else // Optional Items: Build Id present, but MEMFAULT_EVENT_INCLUDE_BUILD_ID=0 const uint8_t test_vector_with_build_id[] = { 0xa6, 0x02, 0x01, 0x03, 0x01, 0x0a, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', }; #endif /* MEMFAULT_EVENT_INCLUDE_BUILD_ID */ #endif /* MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL */ // NB: Device Serial encoding is enabled/disabled at compile time based on the // MEMFAULT_EVENT_INCLUDE_DEVICE_SERIAL. Encoding timestamp is optional based on whether or not // memfault_platform_time_get_current is implemented. Let's run through all possible encoding // combos. const static sMemfaultSerializerHelperTestVector s_test_vectors[] = { { .expected_encoding = test_vector, .expected_encoding_len = sizeof(test_vector), .encode_captured_date = false, .build_id_present = false, }, { .expected_encoding = test_vector_with_timestamp, .expected_encoding_len = sizeof(test_vector_with_timestamp), .encode_captured_date = true, .build_id_present = false, }, { .expected_encoding = test_vector_with_build_id, .expected_encoding_len = sizeof(test_vector_with_build_id), .encode_captured_date = false, .build_id_present = true, }, }; TEST(MemfaultMetricsSerializerHelper, Test_MemfaultMetricSerializeEventMetadata) { for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_test_vectors); i++) { setup(); const sMemfaultSerializerHelperTestVector *vec = &s_test_vectors[i]; uint8_t result[vec->expected_encoding_len]; memset(result, 0xa5, sizeof(result)); if (vec->encode_captured_date) { prv_enable_captured_date(); } if (vec->build_id_present) { prv_enable_build_id(); } sMemfaultCborEncoder encoder; prv_encode_metadata_and_check(&encoder, result, sizeof(result)); MEMCMP_EQUAL(vec->expected_encoding, result, sizeof(result)); } } ================================================ FILE: tests/unit/src/test_memfault_session_metrics.cpp ================================================ //! @file //! //! @brief #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/platform/core.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/timer.h" #include "memfault/metrics/reliability.h" #include "memfault/metrics/serializer.h" #define FAKE_STORAGE_SIZE 1024 extern "C" { static uint64_t s_fake_time_ms = 0; static void prv_fake_time_set(uint64_t new_fake_time_ms) { s_fake_time_ms = new_fake_time_ms; } static const sMemfaultEventStorageImpl *s_fake_event_storage_impl; uint64_t memfault_platform_get_time_since_boot_ms(void) { return s_fake_time_ms; } bool memfault_metrics_heartbeat_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl) { return (size_t)mock().actualCall(__func__).returnBoolValueOrDefault(true); } bool memfault_metrics_session_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl, MEMFAULT_UNUSED eMfltMetricsSessionIndex session_index) { return (size_t)mock().actualCall(__func__).returnBoolValueOrDefault(true); } size_t memfault_metrics_heartbeat_compute_worst_case_storage_size(void) { return (size_t)mock().actualCall(__func__).returnIntValueOrDefault(FAKE_STORAGE_SIZE); } // fakes void memfault_metrics_reliability_boot(sMemfaultMetricsReliabilityCtx *ctx) { (void)ctx; } void memfault_metrics_reliability_collect(void) { } } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MEMFAULT_UNUSED MemfaultPlatformTimerCallback callback) { return mock() .actualCall(__func__) .withParameter("period_sec", period_sec) .returnBoolValueOrDefault(true); } static void session_end_cb(void) { mock().actualCall(__func__); } static void session_start_cb(void) { // Set a metric value to ensure that metrics are reset before session_start_cb MEMFAULT_METRIC_SESSION_SET_UNSIGNED(test_unsigned, test_key_session, 1); mock().actualCall(__func__); } // clang-format off TEST_GROUP(MemfaultSessionMetrics){ void setup() { static uint8_t s_storage[FAKE_STORAGE_SIZE]; s_fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage)); prv_fake_time_set(0); } void teardown() { // Clear session CBs memfault_metrics_session_register_start_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), (MemfaultMetricsSessionStartCb)NULL); memfault_metrics_session_register_end_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), (MemfaultMetricsSessionEndCb)NULL); mock().checkExpectations(); mock().clear(); } }; // clang-format on TEST(MemfaultSessionMetrics, Test_SessionTimer) { MemfaultMetricId key = MEMFAULT_METRICS_KEY_WITH_SESSION(MemfaultSdkMetric_IntervalMs, test_key_session); uint64_t expected = 100; int rv = 0; mock().expectOneCall("memfault_metrics_session_serialize"); rv = MEMFAULT_METRICS_SESSION_START(test_key_session); LONGS_EQUAL(0, rv); prv_fake_time_set(expected); rv = MEMFAULT_METRICS_SESSION_END(test_key_session); LONGS_EQUAL(0, rv); uint32_t val = 0; rv = memfault_metrics_heartbeat_timer_read(key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(expected, val); } TEST(MemfaultSessionMetrics, Test_StringKey) { MemfaultMetricId key = MEMFAULT_METRICS_KEY_WITH_SESSION(test_string, test_key_session); #define TEST_STRING "test_string" int rv = memfault_metrics_heartbeat_set_string(key, TEST_STRING); LONGS_EQUAL(0, rv); char actual[32] = { 0 }; rv = memfault_metrics_heartbeat_read_string(key, (char *)actual, sizeof(actual)); LONGS_EQUAL(0, rv); STRCMP_EQUAL(TEST_STRING, (const char *)actual); } TEST(MemfaultSessionMetrics, Test_SessionTimerStopBeforeStart) { int rv = MEMFAULT_METRICS_SESSION_END(test_key_session); LONGS_EQUAL(-4, rv); } TEST(MemfaultSessionMetrics, Test_UnexpectedRebootResetOnlyOnHeartbeatStart) { MemfaultMetricId reboot_key = MEMFAULT_METRICS_KEY(MemfaultSdkMetric_UnexpectedRebootCount); mock().expectOneCall("memfault_metrics_session_serialize"); memfault_metrics_heartbeat_set_unsigned(reboot_key, 1); uint32_t reboot_val = 0; int rv = memfault_metrics_heartbeat_read_unsigned(reboot_key, &reboot_val); LONGS_EQUAL(0, rv); LONGS_EQUAL(1, reboot_val); rv = MEMFAULT_METRICS_SESSION_START(test_key_session); LONGS_EQUAL(0, rv); LONGS_EQUAL(1, reboot_val); MEMFAULT_METRICS_SESSION_END(test_key_session); } TEST(MemfaultSessionMetrics, Test_RegisterSessionStartCb) { mock().expectOneCall("memfault_metrics_session_serialize"); mock().expectOneCall("session_start_cb"); // Check that a session metric is unset before the session starts uint32_t val = 0; int rv = memfault_metrics_heartbeat_read_unsigned( MEMFAULT_METRICS_KEY_WITH_SESSION(test_unsigned, test_key_session), &val); LONGS_EQUAL(-7, rv); LONGS_EQUAL(0, val); memfault_metrics_session_register_start_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), session_start_cb); MEMFAULT_METRICS_SESSION_START(test_key_session); // Check that the metric is set after the session starts. Metric is set within the start callback rv = memfault_metrics_heartbeat_read_unsigned( MEMFAULT_METRICS_KEY_WITH_SESSION(test_unsigned, test_key_session), &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(1, val); MEMFAULT_METRICS_SESSION_END(test_key_session); // Check that the metric is set after the session ends. Metrics are reset at session start, not // end rv = memfault_metrics_heartbeat_read_unsigned( MEMFAULT_METRICS_KEY_WITH_SESSION(test_unsigned, test_key_session), &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(1, val); } TEST(MemfaultSessionMetrics, Test_RegisterSessionEndCb) { mock().expectOneCall("memfault_metrics_session_serialize"); mock().expectOneCall("session_end_cb"); memfault_metrics_session_register_end_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), session_end_cb); MEMFAULT_METRICS_SESSION_START(test_key_session); MEMFAULT_METRICS_SESSION_END(test_key_session); } TEST(MemfaultSessionMetrics, Test_UnRegisterSessionStartCb) { mock().expectOneCall("memfault_metrics_session_serialize"); memfault_metrics_session_register_start_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), session_start_cb); memfault_metrics_session_register_start_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), (MemfaultMetricsSessionStartCb)NULL); MEMFAULT_METRICS_SESSION_START(test_key_session); MEMFAULT_METRICS_SESSION_END(test_key_session); } TEST(MemfaultSessionMetrics, Test_UnRegisterSessionEndCb) { mock().expectOneCall("memfault_metrics_session_serialize"); memfault_metrics_session_register_end_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), session_end_cb); memfault_metrics_session_register_end_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), (MemfaultMetricsSessionEndCb)NULL); MEMFAULT_METRICS_SESSION_START(test_key_session); MEMFAULT_METRICS_SESSION_END(test_key_session); } TEST(MemfaultSessionMetrics, Test_ResetSession) { int rv = MEMFAULT_METRICS_SESSION_START(test_key_session); LONGS_EQUAL(0, rv); prv_fake_time_set(1234); // Set a session metric to a non-default value MemfaultMetricId key = MEMFAULT_METRICS_KEY_WITH_SESSION(test_unsigned, test_key_session); rv = memfault_metrics_heartbeat_set_unsigned(key, 42); LONGS_EQUAL(0, rv); // Verify the metric is set uint32_t val = 0; rv = memfault_metrics_heartbeat_read_unsigned(key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(42, val); // Reset the session metrics MEMFAULT_METRICS_SESSION_RESET(test_key_session); // After reset, metric should be unset val = 0; rv = memfault_metrics_heartbeat_read_unsigned(key, &val); LONGS_EQUAL(-7, rv); // -7 = MEMFAULT_METRICS_VALUE_NOT_SET LONGS_EQUAL(0, val); MemfaultMetricId session_timer_key = MEMFAULT_METRICS_KEY_WITH_SESSION(MemfaultSdkMetric_IntervalMs, test_key_session); val = 0; rv = memfault_metrics_heartbeat_timer_read(session_timer_key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(0, val); // Now run a new session, and confirm the time matches uint64_t expected = 100; mock().expectOneCall("memfault_metrics_session_serialize"); prv_fake_time_set(0); rv = MEMFAULT_METRICS_SESSION_START(test_key_session); LONGS_EQUAL(0, rv); prv_fake_time_set(expected); rv = MEMFAULT_METRICS_SESSION_END(test_key_session); LONGS_EQUAL(0, rv); val = 0; rv = memfault_metrics_heartbeat_timer_read(session_timer_key, &val); LONGS_EQUAL(0, rv); LONGS_EQUAL(expected, val); } ================================================ FILE: tests/unit/src/test_memfault_session_metrics_debug.cpp ================================================ //! @file test_memfault_session_metrics_debug.cpp //! //! @brief Unit tests for session metrics debug print functions #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_platform_metrics_locking.h" #include "memfault/components.h" #include "memfault/metrics/serializer.h" #include "mocks/mock_memfault_platform_debug_log.h" static uint64_t s_boot_time_ms = 0; uint64_t memfault_platform_get_time_since_boot_ms(void) { return s_boot_time_ms; } #define FAKE_STORAGE_SIZE 1000 static const sMemfaultEventStorageImpl *s_fake_event_storage_impl; static eMfltMetricsSessionIndex s_current_session_key; bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MEMFAULT_UNUSED MemfaultPlatformTimerCallback callback) { return mock() .actualCall(__func__) .withParameter("period_sec", period_sec) .returnBoolValueOrDefault(true); } bool memfault_metrics_heartbeat_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl) { return mock().actualCall(__func__).returnBoolValueOrDefault(true); } bool memfault_metrics_session_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl, MEMFAULT_UNUSED eMfltMetricsSessionIndex session_index) { return mock().actualCall(__func__).returnBoolValueOrDefault(true); } size_t memfault_metrics_heartbeat_compute_worst_case_storage_size(void) { return (size_t)mock().actualCall(__func__).returnIntValueOrDefault(0); } static void test_key_session_end_cb(void) { // This might be called in the callback, so this use case memfault_metrics_session_debug_print(s_current_session_key); } static void test_key_session_dummy_end_cb(void) { // do nothing } TEST_GROUP(MemfaultSessionMetricsDebug) { void setup() { s_boot_time_ms = 0; mock().strictOrder(); static uint8_t s_storage[FAKE_STORAGE_SIZE]; s_fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage)); // memfault_metrics_boot() typically starts the heartbeat timer, but call it manually since we // can't call memfault_metrics_boot() multiple times MEMFAULT_METRIC_TIMER_START(MemfaultSdkMetric_IntervalMs); } void teardown() { // Reset the heartbeat metrics MEMFAULT_METRIC_TIMER_STOP(MemfaultSdkMetric_IntervalMs); mock().expectOneCall("memfault_metrics_reliability_collect"); mock().expectOneCall("memfault_metrics_heartbeat_serialize"); memfault_metrics_heartbeat_debug_trigger(); // Reset the session metrics and the callback for first session memfault_metrics_session_register_end_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), test_key_session_dummy_end_cb); MEMFAULT_METRICS_SESSION_START(test_key_session); mock().expectOneCall("memfault_metrics_session_serialize"); MEMFAULT_METRICS_SESSION_END(test_key_session); MEMFAULT_METRIC_SESSION_TIMER_STOP(test_timer, test_key_session); // Reset session metrics for second session MEMFAULT_METRICS_SESSION_START(test_key_session_two); mock().expectOneCall("memfault_metrics_session_serialize"); MEMFAULT_METRICS_SESSION_END(test_key_session_two); MEMFAULT_METRIC_SESSION_TIMER_STOP(test_timer, test_key_session_two); mock().checkExpectations(); mock().clear(); } }; //! check the debug print outputs the correct values depending on metrics state TEST(MemfaultSessionMetricsDebug, Test_HeartbeatResetState) { // this should output the system reset values const char *expected_debug_print[] = { "Metrics keys/values:", " MemfaultSdkMetric_IntervalMs: 0", " MemfaultSdkMetric_UnexpectedRebootCount: null", " operational_hours: null", " operational_crashfree_hours: null", " MemfaultSDKMetric_log_dropped_lines: null", " MemfaultSDKMetric_log_recorded_lines: null", " uptime_s: null", " test_key_unsigned: null", " test_key_signed: null", " test_key_timer: 0", " test_key_string: \"\"", " test_unsigned_scale_value: null", }; s_current_session_key = MEMFAULT_METRICS_SESSION_KEY(heartbeat); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); memfault_metrics_session_debug_print(s_current_session_key); } //! check the debug print outputs the correct values depending on metrics state TEST(MemfaultSessionMetricsDebug, Test_HeartbeatCollectionUpdateAndReset) { s_boot_time_ms = 678; MEMFAULT_METRIC_TIMER_START(test_key_timer); s_boot_time_ms = 5678; MEMFAULT_METRIC_SET_UNSIGNED(test_key_unsigned, 1234); MEMFAULT_METRIC_SET_SIGNED(test_key_signed, -100); MEMFAULT_METRIC_SET_STRING(test_key_string, "heyo!"); // debug trigger will update, save, and zero the values const char *expected_debug_print[] = { "Metrics keys/values:", " MemfaultSdkMetric_IntervalMs: 5678", " MemfaultSdkMetric_UnexpectedRebootCount: null", " operational_hours: null", " operational_crashfree_hours: null", " MemfaultSDKMetric_log_dropped_lines: null", " MemfaultSDKMetric_log_recorded_lines: null", " uptime_s: null", " test_key_unsigned: 1234", " test_key_signed: -100", " test_key_timer: 5000", " test_key_string: \"heyo!\"", " test_unsigned_scale_value: null", }; s_current_session_key = MEMFAULT_METRICS_SESSION_KEY(heartbeat); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); memfault_metrics_session_debug_print(s_current_session_key); // Clean up. Intentionally do not call timer stop before printing the heartbeat to make sure // we check that we have forced timer updates in our debug print function MEMFAULT_METRIC_TIMER_STOP(test_key_timer); // Force a heartbeat metrics reset mock().expectOneCall("memfault_metrics_reliability_collect"); mock().expectOneCall("memfault_metrics_heartbeat_serialize"); memfault_metrics_heartbeat_debug_trigger(); // after trigger, metrics should be zeroed now const char *expected_debug_print_reset[] = { "Metrics keys/values:", " MemfaultSdkMetric_IntervalMs: 0", " MemfaultSdkMetric_UnexpectedRebootCount: null", " operational_hours: null", " operational_crashfree_hours: null", " MemfaultSDKMetric_log_dropped_lines: null", " MemfaultSDKMetric_log_recorded_lines: null", " uptime_s: null", " test_key_unsigned: null", " test_key_signed: null", " test_key_timer: 0", " test_key_string: \"\"", " test_unsigned_scale_value: null", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print_reset, MEMFAULT_ARRAY_SIZE(expected_debug_print_reset)); memfault_metrics_session_debug_print(s_current_session_key); } //! check for correct result when printing heartbeat metrics for a simple add TEST(MemfaultSessionMetricsDebug, Test_HeartbeatUpdateAdd) { const char *expected_debug_print[] = { "Metrics keys/values:", " MemfaultSdkMetric_IntervalMs: 0", " MemfaultSdkMetric_UnexpectedRebootCount: null", " operational_hours: null", " operational_crashfree_hours: null", " MemfaultSDKMetric_log_dropped_lines: null", " MemfaultSDKMetric_log_recorded_lines: null", " uptime_s: null", " test_key_unsigned: 123", " test_key_signed: null", " test_key_timer: 0", " test_key_string: \"\"", " test_unsigned_scale_value: null", }; s_current_session_key = MEMFAULT_METRICS_SESSION_KEY(heartbeat); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); // call add on this metric alone and confirm it is set MEMFAULT_METRIC_ADD(test_key_unsigned, 123); memfault_metrics_session_debug_print(s_current_session_key); } //! check for correct result when printing session metrics that have been reset TEST(MemfaultSessionMetricsDebug, Test_SessionResetState) { const char *expected_debug_print[] = { "Metrics keys/values:", " test_key_session__MemfaultSdkMetric_IntervalMs: 0", " test_key_session__operational_crashes: null", " test_key_session__test_unsigned: null", " test_key_session__test_string: \"\"", " test_key_session__test_timer: 0", }; s_current_session_key = MEMFAULT_METRICS_SESSION_KEY(test_key_session); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); memfault_metrics_session_debug_print(s_current_session_key); } //! check for correct result when printing session metrics after session has started, but only //! the session timer is updated TEST(MemfaultSessionMetricsDebug, Test_SessionTimerUpdateState) { const char *expected_debug_print[] = { "Metrics keys/values:", " test_key_session__MemfaultSdkMetric_IntervalMs: 5000", " test_key_session__operational_crashes: null", " test_key_session__test_unsigned: null", " test_key_session__test_string: \"\"", " test_key_session__test_timer: 5000", }; s_current_session_key = MEMFAULT_METRICS_SESSION_KEY(test_key_session); memfault_metrics_session_register_end_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), test_key_session_end_cb); s_boot_time_ms = 678; MEMFAULT_METRICS_SESSION_START(test_key_session); MEMFAULT_METRIC_SESSION_TIMER_START(test_timer, test_key_session); s_boot_time_ms = 5678; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); memfault_metrics_session_debug_print(MEMFAULT_METRICS_SESSION_KEY(test_key_session)); } //! check for correct result when printing session metrics before and after a session ends TEST(MemfaultSessionMetricsDebug, Test_SessionUpdateState) { const char *expected_debug_print[] = { "Metrics keys/values:", " test_key_session__MemfaultSdkMetric_IntervalMs: 5000", " test_key_session__operational_crashes: null", " test_key_session__test_unsigned: 35", " test_key_session__test_string: \"sessions!\"", " test_key_session__test_timer: 0", }; s_current_session_key = MEMFAULT_METRICS_SESSION_KEY(test_key_session); memfault_metrics_session_register_end_cb(MEMFAULT_METRICS_SESSION_KEY(test_key_session), test_key_session_end_cb); s_boot_time_ms = 678; MEMFAULT_METRICS_SESSION_START(test_key_session); MEMFAULT_METRIC_SESSION_SET_UNSIGNED(test_unsigned, test_key_session, 35); MEMFAULT_METRIC_SESSION_SET_STRING(test_string, test_key_session, "sessions!"); s_boot_time_ms = 5678; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); memfault_metrics_session_debug_print(s_current_session_key); memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); mock().expectOneCall("memfault_metrics_session_serialize"); MEMFAULT_METRICS_SESSION_END(test_key_session); } //! check for correct result when printing all session metrics that have been reset TEST(MemfaultSessionMetricsDebug, Test_AllSessionsResetState) { const char *expected_debug_print[] = { "Metrics keys/values:", " test_key_session__MemfaultSdkMetric_IntervalMs: 0", " test_key_session__operational_crashes: null", " test_key_session__test_unsigned: null", " test_key_session__test_string: \"\"", " test_key_session__test_timer: 0", " test_key_session_two__MemfaultSdkMetric_IntervalMs: 0", " test_key_session_two__operational_crashes: null", " test_key_session_two__test_unsigned: null", " test_key_session_two__test_string: \"\"", " test_key_session_two__test_timer: 0", " test_key_session_two__test_signed_scale_value: null", }; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); memfault_metrics_all_sessions_debug_print(); } //! check for correct result when printing session metrics after session has started, but only //! the session timer is updated TEST(MemfaultSessionMetricsDebug, Test_AllSessionsTimerUpdateState) { const char *expected_debug_print[] = { "Metrics keys/values:", " test_key_session__MemfaultSdkMetric_IntervalMs: 5000", " test_key_session__operational_crashes: null", " test_key_session__test_unsigned: null", " test_key_session__test_string: \"\"", " test_key_session__test_timer: 5000", " test_key_session_two__MemfaultSdkMetric_IntervalMs: 5000", " test_key_session_two__operational_crashes: null", " test_key_session_two__test_unsigned: null", " test_key_session_two__test_string: \"\"", " test_key_session_two__test_timer: 5000", " test_key_session_two__test_signed_scale_value: null", }; s_boot_time_ms = 678; MEMFAULT_METRICS_SESSION_START(test_key_session); MEMFAULT_METRIC_SESSION_TIMER_START(test_timer, test_key_session); MEMFAULT_METRICS_SESSION_START(test_key_session_two); MEMFAULT_METRIC_SESSION_TIMER_START(test_timer, test_key_session_two); s_boot_time_ms = 5678; memfault_platform_log_set_mock(kMemfaultPlatformLogLevel_Info, expected_debug_print, MEMFAULT_ARRAY_SIZE(expected_debug_print)); memfault_metrics_all_sessions_debug_print(); } ================================================ FILE: tests/unit/src/test_memfault_session_vitals.cpp ================================================ //! @file //! //! @brief #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/platform/core.h" #include "memfault/core/reboot_tracking.h" #include "memfault/metrics/metrics.h" #include "memfault/metrics/platform/timer.h" #include "memfault/metrics/reliability.h" #include "memfault/metrics/serializer.h" #define FAKE_STORAGE_SIZE 1024 extern "C" { uint64_t memfault_platform_get_time_since_boot_ms(void) { return 0; } bool memfault_metrics_heartbeat_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl) { return (size_t)mock().actualCall(__func__).returnBoolValueOrDefault(true); } bool memfault_metrics_session_serialize( MEMFAULT_UNUSED const sMemfaultEventStorageImpl *storage_impl, MEMFAULT_UNUSED eMfltMetricsSessionIndex session_index) { return (size_t)mock().actualCall(__func__).returnBoolValueOrDefault(true); } size_t memfault_metrics_heartbeat_compute_worst_case_storage_size(void) { return (size_t)mock().actualCall(__func__).returnIntValueOrDefault(FAKE_STORAGE_SIZE); } void memfault_reboot_tracking_metrics_session(bool active, uint32_t index) { mock().actualCall(__func__).withParameter("active", active).withParameter("index", index); } void memfault_reboot_tracking_clear_metrics_sessions(void) { mock().actualCall(__func__); } bool memfault_reboot_tracking_metrics_session_was_active(uint32_t index) { return mock().actualCall(__func__).withParameter("index", index).returnBoolValueOrDefault(true); } // fakes void memfault_metrics_reliability_boot(sMemfaultMetricsReliabilityCtx *ctx) { (void)ctx; } void memfault_metrics_reliability_collect(void) { } } bool memfault_platform_metrics_timer_boot(uint32_t period_sec, MEMFAULT_UNUSED MemfaultPlatformTimerCallback callback) { return mock() .actualCall(__func__) .withParameter("period_sec", period_sec) .returnBoolValueOrDefault(true); } TEST_GROUP(SessionVitals) { void setup() { mock().strictOrder(); } void teardown() { MemfaultMetricId id = MEMFAULT_METRICS_KEY(MemfaultSdkMetric_IntervalMs); int rv = memfault_metrics_heartbeat_timer_stop(id); LONGS_EQUAL(0, rv); mock().checkExpectations(); mock().clear(); } }; static void prv_call_metrics_boot(uint32_t unexpected_reboot_count) { static uint8_t s_storage[2048]; static const sMemfaultEventStorageImpl *fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage)); sMemfaultMetricBootInfo boot_info = { .unexpected_reboot_count = unexpected_reboot_count }; int rv = memfault_metrics_boot(fake_event_storage_impl, &boot_info); LONGS_EQUAL(0, rv); } // 1. "Happy" path test sequence is: // 1. start a session to mark it as active in reboot tracking // 2. boot metrics with unexpected_reboot=true // 3. confirm the session is serialized out with operation_crashes = 1 TEST(SessionVitals, Test_SessionOperationalCrashes) { mock() .expectOneCall("memfault_reboot_tracking_metrics_session") .withParameter("active", true) .withParameter("index", 0); MEMFAULT_METRICS_SESSION_START(test_key_session); mock() .expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size") .andReturnValue(FAKE_STORAGE_SIZE); bool unexpected_reboot = true; mock() .expectOneCall("memfault_reboot_tracking_get_unexpected_reboot_occurred") .withOutputParameterReturning("unexpected_reboot_occurred", &unexpected_reboot, sizeof(unexpected_reboot)) .andReturnValue(0); // test_key_session bit mock() .expectOneCall("memfault_reboot_tracking_metrics_session_was_active") .withParameter("index", 0) .andReturnValue(true); mock() .expectOneCall("memfault_reboot_tracking_metrics_session") .withParameter("active", true) .withParameter("index", 0); mock().expectOneCall("memfault_metrics_session_serialize"); mock() .expectOneCall("memfault_reboot_tracking_metrics_session") .withParameter("active", false) .withParameter("index", 0); // test_key_session_two bit mock() .expectOneCall("memfault_reboot_tracking_metrics_session_was_active") .withParameter("index", 1) .andReturnValue(false); mock().expectOneCall("memfault_reboot_tracking_clear_metrics_sessions"); mock() .expectOneCall("memfault_platform_metrics_timer_boot") .withParameter("period_sec", MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) .andReturnValue(true); prv_call_metrics_boot(1); } // 2. Alternate ending: // 1. start a session to mark it as active in reboot tracking // 2. boot metrics with unexpected_reboot=false // 3. confirm no session is serialized, and session_active bit is cleared TEST(SessionVitals, Test_SessionOperationalCrashesExpectedReboot) { mock() .expectOneCall("memfault_reboot_tracking_metrics_session") .withParameter("active", true) .withParameter("index", 0); MEMFAULT_METRICS_SESSION_START(test_key_session); mock() .expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size") .andReturnValue(FAKE_STORAGE_SIZE); bool unexpected_reboot = false; mock() .expectOneCall("memfault_reboot_tracking_get_unexpected_reboot_occurred") .withOutputParameterReturning("unexpected_reboot_occurred", &unexpected_reboot, sizeof(unexpected_reboot)) .andReturnValue(0); mock().expectOneCall("memfault_reboot_tracking_clear_metrics_sessions"); mock() .expectOneCall("memfault_platform_metrics_timer_boot") .withParameter("period_sec", MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) .andReturnValue(true); prv_call_metrics_boot(0); } // 3. And: // 1. report a session as inactive in reboot tracking // 2. boot metrics with unexpected_reboot=true // 3. confirm no session is serialized, and session_active bit is cleared TEST(SessionVitals, Test_SessionOperationalCrashesInactive) { mock() .expectOneCall("memfault_metrics_heartbeat_compute_worst_case_storage_size") .andReturnValue(FAKE_STORAGE_SIZE); bool unexpected_reboot = true; mock() .expectOneCall("memfault_reboot_tracking_get_unexpected_reboot_occurred") .withOutputParameterReturning("unexpected_reboot_occurred", &unexpected_reboot, sizeof(unexpected_reboot)) .andReturnValue(0); // test_key_session bit mock() .expectOneCall("memfault_reboot_tracking_metrics_session_was_active") .withParameter("index", 0) .andReturnValue(false); // test_key_session_two bit mock() .expectOneCall("memfault_reboot_tracking_metrics_session_was_active") .withParameter("index", 1) .andReturnValue(false); mock().expectOneCall("memfault_reboot_tracking_clear_metrics_sessions"); mock() .expectOneCall("memfault_platform_metrics_timer_boot") .withParameter("period_sec", MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) .andReturnValue(true); prv_call_metrics_boot(1); } ================================================ FILE: tests/unit/src/test_memfault_task_watchdog.cpp ================================================ //! @file //! //! @brief #include #include #include #include #include #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "comparators/comparator_memfault_fault_handling.hpp" #include "memfault/core/math.h" #include "memfault/core/platform/core.h" #include "memfault/core/task_watchdog.h" extern "C" { static uint64_t s_fake_time_ms = 0; uint64_t memfault_platform_get_time_since_boot_ms(void) { return s_fake_time_ms; } } void memfault_task_watchdog_platform_refresh_callback(void) { mock().actualCall(__func__); } static Mflt_sMemfaultAssertInfo_Comparator s_assert_info_comparator; TEST_GROUP(MemfaultTaskWatchdog) { void setup() { mock().strictOrder(); mock().installComparator("sMemfaultAssertInfo", s_assert_info_comparator); s_fake_time_ms = 0; memfault_task_watchdog_init(); } void teardown() { mock().checkExpectations(); mock().removeAllComparatorsAndCopiers(); mock().clear(); } }; TEST(MemfaultTaskWatchdog, Test_Basic) { // no channels started mock().expectOneCall("memfault_task_watchdog_platform_refresh_callback"); memfault_task_watchdog_check_all(); // start a channel, confirm we don't expire when time hasn't advanced MEMFAULT_TASK_WATCHDOG_START(task_1); mock().expectOneCall("memfault_task_watchdog_platform_refresh_callback"); memfault_task_watchdog_check_all(); // call for coverage memfault_task_watchdog_bookkeep(); } TEST(MemfaultTaskWatchdog, Test_Expire) { // start a channel and artificially advance time to expire it MEMFAULT_TASK_WATCHDOG_START(task_1); s_fake_time_ms = MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS - 1; mock().expectOneCall("memfault_task_watchdog_platform_refresh_callback"); memfault_task_watchdog_check_all(); s_fake_time_ms = MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS; mock().expectOneCall("memfault_task_watchdog_platform_refresh_callback"); memfault_task_watchdog_check_all(); s_fake_time_ms = MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS + 1; sMemfaultAssertInfo extra_info = { .assert_reason = kMfltRebootReason_TaskWatchdog, }; mock() .expectOneCall("memfault_fault_handling_assert_extra") .withPointerParameter("pc", 0) .withPointerParameter("lr", 0) .withParameterOfType("sMemfaultAssertInfo", "extra_info", &extra_info); memfault_task_watchdog_check_all(); } TEST(MemfaultTaskWatchdog, Test_Feed) { // start a channel, advance time past the timeout, feed it, confirm it doesn't // expire MEMFAULT_TASK_WATCHDOG_START(task_1); s_fake_time_ms = MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS + 1; MEMFAULT_TASK_WATCHDOG_FEED(task_1); mock().expectOneCall("memfault_task_watchdog_platform_refresh_callback"); memfault_task_watchdog_check_all(); } TEST(MemfaultTaskWatchdog, Test_Stop) { // start a channel, advance time past the timeout, feed it, confirm it doesn't // expire MEMFAULT_TASK_WATCHDOG_START(task_1); s_fake_time_ms = MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS + 1; MEMFAULT_TASK_WATCHDOG_STOP(task_1); mock().expectOneCall("memfault_task_watchdog_platform_refresh_callback"); memfault_task_watchdog_check_all(); } TEST(MemfaultTaskWatchdog, Test_ExpireWrapAround) { // start a channel and artificially advance time to expire it. check // the wraparound cases. const uint64_t wrap_start_points[] = { UINT32_MAX, UINT64_MAX, }; for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(wrap_start_points); i++) { s_fake_time_ms = wrap_start_points[i] - MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS; MEMFAULT_TASK_WATCHDOG_START(task_1); s_fake_time_ms += MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS - 1; mock().expectOneCall("memfault_task_watchdog_platform_refresh_callback"); memfault_task_watchdog_check_all(); s_fake_time_ms += 1; mock().expectOneCall("memfault_task_watchdog_platform_refresh_callback"); memfault_task_watchdog_check_all(); s_fake_time_ms += 1; sMemfaultAssertInfo extra_info = { .assert_reason = kMfltRebootReason_TaskWatchdog, }; mock() .expectOneCall("memfault_fault_handling_assert_extra") .withPointerParameter("pc", 0) .withPointerParameter("lr", 0) .withParameterOfType("sMemfaultAssertInfo", "extra_info", &extra_info); memfault_task_watchdog_check_all(); } } ================================================ FILE: tests/unit/src/test_memfault_trace_event.cpp ================================================ //! @file #include #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "fakes/fake_memfault_event_storage.h" #include "memfault/core/arch.h" #include "memfault/core/compact_log_serializer.h" #include "memfault/core/compiler.h" #include "memfault/core/data_packetizer_source.h" #include "memfault/core/event_storage.h" #include "memfault/core/trace_event.h" #include "memfault_trace_event_private.h" bool memfault_arch_is_inside_isr(void) { return mock().actualCall(__func__).returnBoolValueOrDefault(false); } bool memfault_vlog_compact_serialize(sMemfaultCborEncoder *encoder, MEMFAULT_UNUSED uint32_t log_id, MEMFAULT_UNUSED uint32_t compressed_fmt, MEMFAULT_UNUSED va_list args) { const uint8_t cbor[] = { 0x84, 0x08, 0x01, 0x02, 0x03 }; return memfault_cbor_join(encoder, &cbor, sizeof(cbor)); } bool memfault_vlog_compact_serialize_fallback_entry(MEMFAULT_UNUSED sMemfaultCborEncoder *encoder, MEMFAULT_UNUSED uint32_t log_id, MEMFAULT_UNUSED uint32_t serialized_len) { return false; } static const sMemfaultEventStorageImpl *s_fake_event_storage_impl; #define MEMFAULT_TRACE_EVENT_WORST_CASE_SIZE_BYTES (59) TEST_GROUP(MfltTraceEvent) { void setup() { static uint8_t s_storage[100]; fake_memfault_event_storage_clear(); s_fake_event_storage_impl = memfault_events_storage_boot(&s_storage, sizeof(s_storage)); mock().strictOrder(); } void teardown() { mock().checkExpectations(); mock().clear(); memfault_trace_event_reset(); } }; TEST(MfltTraceEvent, Test_BootNullStorage) { const int rv = memfault_trace_event_boot(NULL); CHECK_EQUAL(rv, -4); CHECK_FALSE(memfault_trace_event_booted()); } TEST(MfltTraceEvent, Test_BootStorageTooSmall) { fake_memfault_event_storage_set_available_space(MEMFAULT_TRACE_EVENT_WORST_CASE_SIZE_BYTES - 1); const int rv = memfault_trace_event_boot(s_fake_event_storage_impl); CHECK_EQUAL(rv, -3); CHECK_FALSE(memfault_trace_event_booted()); } TEST(MfltTraceEvent, Test_CaptureButStorageUninitialized) { const int rv = memfault_trace_event_capture(kMfltTraceReasonUser_Unknown, NULL, NULL); CHECK_EQUAL(rv, -1); } static void prv_run_capture_test(void *pc, void *lr, int32_t *status_code, size_t storage_size, size_t expected_encoded_size) { fake_memfault_event_storage_clear(); fake_memfault_event_storage_set_available_space(storage_size); mock().expectOneCall("memfault_arch_is_inside_isr"); mock().expectOneCall("prv_begin_write"); const bool expect_rollback = (storage_size < expected_encoded_size); mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback); const int rv = (status_code == NULL) ? memfault_trace_event_capture(kMfltTraceReasonUser_test, pc, lr) : memfault_trace_event_with_status_capture(kMfltTraceReasonUser_test, pc, lr, *status_code); CHECK_EQUAL(expect_rollback ? -2 : 0, rv); mock().checkExpectations(); } TEST(MfltTraceEvent, Test_CaptureOk_PcAndLr) { fake_memfault_event_storage_clear(); CHECK_FALSE(memfault_trace_event_booted()); const int rv = memfault_trace_event_boot(s_fake_event_storage_impl); CHECK_EQUAL(0, rv); CHECK(memfault_trace_event_booted()); const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 0x44, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x0A, 0x64, 0x6D, 0x61, 0x69, 0x6E, 0x09, 0x65, 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x06, 0x66, 0x65, 0x76, 0x74, 0x5F, 0x32, 0x34, 0x04, 0xA3, 0x06, 0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD, }; // anything less than the expected size should fail to encode for (size_t i = 1; i <= sizeof(expected_data); i++) { void *pc = (void *)0x12345678; void *lr = (void *)0xaabbccdd; prv_run_capture_test(pc, lr, NULL, i, sizeof(expected_data)); } fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data)); } TEST(MfltTraceEvent, Test_CaptureOk_PcAndLrAndStatus) { fake_memfault_event_storage_clear(); const int rv = memfault_trace_event_boot(s_fake_event_storage_impl); CHECK_EQUAL(0, rv); const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 0x0A, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xA4, 0x06, 0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0x07, 0x20 }; // anything less than the expected size should fail to encode for (size_t i = 1; i <= sizeof(expected_data); i++) { void *pc = (void *)0x12345678; void *lr = (void *)0xaabbccdd; int32_t status_code = -1; prv_run_capture_test(pc, lr, &status_code, i, sizeof(expected_data)); } fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data)); } #if !MEMFAULT_COMPACT_LOG_ENABLE TEST(MfltTraceEvent, Test_CaptureOk_PcAndLrAndLog) { fake_memfault_event_storage_clear(); int rv = memfault_trace_event_boot(s_fake_event_storage_impl); CHECK_EQUAL(0, rv); const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 0x0A, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xA4, 0x06, 0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0x08, 0x4E, '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e' }; void *pc = (void *)0x12345678; void *lr = (void *)0xaabbccdd; #if defined(MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED) && \ !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED const size_t expected_check_isr_calls = 2; #else const size_t expected_check_isr_calls = 1; #endif // anything less than the expected size should fail to encode for (size_t i = 1; i <= sizeof(expected_data); i++) { const size_t storage_size = i; fake_memfault_event_storage_clear(); fake_memfault_event_storage_set_available_space(storage_size); mock().expectNCalls(expected_check_isr_calls, "memfault_arch_is_inside_isr"); mock().expectOneCall("prv_begin_write"); const bool expect_rollback = (storage_size < sizeof(expected_data)); mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback); rv = memfault_trace_event_with_log_capture(kMfltTraceReasonUser_test, pc, lr, "%d%d%d%d%d%d789abcdetruncated", 1, 2, 3, 4, 5, 6); CHECK_EQUAL(expect_rollback ? -2 : 0, rv); mock().checkExpectations(); } fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data)); } TEST(MfltTraceEvent, Test_CaptureOk_PcAndLrAndLogFromIsr) { const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 0x0A, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xA4, 0x06, 0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0x08, 0x4E, '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e' }; void *pc = (void *)0x12345678; void *lr = (void *)0xaabbccdd; #if defined(MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED) && \ !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED const size_t expected_check_isr_calls = 2; #else const size_t expected_check_isr_calls = 1; #endif // let's also make sure collection from an isr works as expected fake_memfault_event_storage_clear(); int rv = memfault_trace_event_boot(s_fake_event_storage_impl); CHECK_EQUAL(0, rv); fake_memfault_event_storage_set_available_space(sizeof(expected_data)); mock().expectNCalls(expected_check_isr_calls, "memfault_arch_is_inside_isr").andReturnValue(true); rv = memfault_trace_event_with_log_capture(kMfltTraceReasonUser_test, pc, lr, "%d%d%d%d%d%d789abcdetruncated", 1, 2, 3, 4, 5, 6); CHECK_EQUAL(0, rv); mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", false); rv = memfault_trace_event_try_flush_isr_event(); CHECK_EQUAL(0, rv); mock().checkExpectations(); size_t expected_isr_data_size = sizeof(expected_data); #if defined(MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED) && \ !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED expected_isr_data_size -= 16; uint8_t expected_data_no_log_from_isr[expected_isr_data_size]; memcpy(expected_data_no_log_from_isr, expected_data, expected_isr_data_size); expected_data_no_log_from_isr[38] = 0xA3; // array of 3 items instead of 4 fake_event_storage_assert_contents_match(expected_data_no_log_from_isr, sizeof(expected_data_no_log_from_isr)); #else fake_event_storage_assert_contents_match(expected_data, expected_isr_data_size); #endif } #else TEST(MfltTraceEvent, Test_CaptureOk_PcAndLrAndCompactLog) { fake_memfault_event_storage_clear(); int rv = memfault_trace_event_boot(s_fake_event_storage_impl); CHECK_EQUAL(0, rv); const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 'D', 'A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 0x0A, 0x64, 'm', 'a', 'i', 'n', 0x09, 0x65, '1', '.', '2', '.', '3', 0x06, 0x66, 'e', 'v', 't', '_', '2', '4', 0x04, 0xA3, 0x06, 0x03, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0x09, 0x84, 0x08, 0x01, 0x02, 0x03, }; void *lr = (void *)0xaabbccdd; #if defined(MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED) && \ !MEMFAULT_TRACE_EVENT_WITH_LOG_FROM_ISR_ENABLED const size_t expected_check_isr_calls = 2; #else const size_t expected_check_isr_calls = 1; #endif // anything less than the expected size should fail to encode for (size_t i = 1; i <= sizeof(expected_data); i++) { const size_t storage_size = i; fake_memfault_event_storage_clear(); fake_memfault_event_storage_set_available_space(storage_size); mock().expectNCalls(expected_check_isr_calls, "memfault_arch_is_inside_isr"); mock().expectOneCall("prv_begin_write"); const bool expect_rollback = (storage_size < sizeof(expected_data)); mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback); rv = memfault_trace_event_with_compact_log_capture(kMfltTraceReasonUser_test, lr, 0x40, 8, 1, 2, 3); CHECK_EQUAL(expect_rollback ? -2 : 0, rv); mock().checkExpectations(); } fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data)); } #endif /* MEMFAULT_COMPACT_LOG_ENABLE */ static void prv_setup_isr_test(void) { fake_memfault_event_storage_clear(); int rv = memfault_trace_event_boot(s_fake_event_storage_impl); CHECK_EQUAL(0, rv); mock().expectOneCall("memfault_arch_is_inside_isr").andReturnValue(true); void *pc = (void *)0x12345678; void *lr = (void *)0xaabbccdd; rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, pc, lr); CHECK_EQUAL(0, rv); mock().checkExpectations(); mock().expectOneCall("memfault_arch_is_inside_isr").andReturnValue(true); void *pc2 = (void *)2; void *lr2 = (void *)1; rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, pc2, lr2); CHECK_EQUAL(-2, rv); mock().checkExpectations(); } TEST(MfltTraceEvent, Test_CaptureOk_FromIsrWithForceFlush) { prv_setup_isr_test(); mock().expectOneCall("prv_begin_write"); const bool expect_rollback = false; mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback); const int rv = memfault_trace_event_try_flush_isr_event(); CHECK_EQUAL(0, rv); mock().checkExpectations(); const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 0x44, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x0A, 0x64, 0x6D, 0x61, 0x69, 0x6E, 0x09, 0x65, 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x06, 0x66, 0x65, 0x76, 0x74, 0x5F, 0x32, 0x34, 0x04, 0xA3, 0x06, 0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD, }; fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data)); } TEST(MfltTraceEvent, Test_CaptureOk_FromIsrWithLazyFlush) { prv_setup_isr_test(); bool expect_rollback = true; fake_memfault_event_storage_set_available_space(10); mock().expectOneCall("memfault_arch_is_inside_isr"); mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback); void *pc2 = (void *)NULL; void *lr2 = (void *)1; int rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, pc2, lr2); CHECK_EQUAL(-2, rv); mock().checkExpectations(); const uint8_t expected_data[] = { // event logged from ISR 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 0x44, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x0A, 0x64, 0x6D, 0x61, 0x69, 0x6E, 0x09, 0x65, 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x06, 0x66, 0x65, 0x76, 0x74, 0x5F, 0x32, 0x34, 0x04, 0xA3, 0x06, 0x03, 0x02, 0x1A, 0x12, 0x34, 0x56, 0x78, 0x03, 0x1A, 0xAA, 0xBB, 0xCC, 0xDD, // event not logged from ISR 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 0x44, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x0A, 0x64, 0x6D, 0x61, 0x69, 0x6E, 0x09, 0x65, 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x06, 0x66, 0x65, 0x76, 0x74, 0x5F, 0x32, 0x34, 0x04, 0xA2, 0x06, 0x03, 0x03, 0x01, }; fake_memfault_event_storage_set_available_space(sizeof(expected_data)); expect_rollback = false; // first the ISR event should be flushed now that we aren't in an ISR mock().expectOneCall("memfault_arch_is_inside_isr"); mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback); // then the second event we want to record should be stored mock().expectOneCall("prv_begin_write"); mock().expectOneCall("prv_finish_write").withParameter("rollback", expect_rollback); rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, pc2, lr2); CHECK_EQUAL(0, rv); fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data)); } TEST(MfltTraceEvent, Test_CaptureOk_LrOnly) { fake_memfault_event_storage_clear(); const int rv = memfault_trace_event_boot(s_fake_event_storage_impl); CHECK_EQUAL(0, rv); const uint8_t expected_data[] = { 0xA7, 0x02, 0x02, 0x03, 0x01, 0x07, 0x69, 0x44, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x0A, 0x64, 0x6D, 0x61, 0x69, 0x6E, 0x09, 0x65, 0x31, 0x2E, 0x32, 0x2E, 0x33, 0x06, 0x66, 0x65, 0x76, 0x74, 0x5F, 0x32, 0x34, 0x04, 0xA2, 0x06, 0x03, 0x03, 0x01, }; // anything less than the expected size should fail to encode for (size_t i = 1; i <= sizeof(expected_data); i++) { void *pc = (void *)NULL; void *lr = (void *)1; prv_run_capture_test(pc, lr, NULL, i, sizeof(expected_data)); } fake_event_storage_assert_contents_match(expected_data, sizeof(expected_data)); } TEST(MfltTraceEvent, Test_CaptureStorageFull) { fake_memfault_event_storage_clear(); fake_memfault_event_storage_set_available_space(MEMFAULT_TRACE_EVENT_WORST_CASE_SIZE_BYTES); CHECK_EQUAL(memfault_trace_event_boot(s_fake_event_storage_impl), 0); fake_memfault_event_storage_set_available_space(0); mock().expectOneCall("memfault_arch_is_inside_isr"); mock().expectOneCall("prv_begin_write"); // Expect rollback! mock().expectOneCall("prv_finish_write").withParameter("rollback", true); const int rv = memfault_trace_event_capture(kMfltTraceReasonUser_test, 0, 0); CHECK_EQUAL(-2, rv); } TEST(MfltTraceEvent, Test_GetWorstCaseSerializeSize) { const size_t worst_case_size = memfault_trace_event_compute_worst_case_storage_size(); LONGS_EQUAL(MEMFAULT_TRACE_EVENT_WORST_CASE_SIZE_BYTES, worst_case_size); } ================================================ FILE: tests/unit/src/test_memfault_user_reboot_reasons.cpp ================================================ #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" #include "memfault/core/reboot_reason_types.h" #include "memfault/core/reboot_tracking.h" extern "C" { void memfault_reboot_tracking_mark_reset_imminent(eMemfaultRebootReason reboot_reason, const sMfltRebootTrackingRegInfo *reg) { mock() .actualCall(__func__) .withParameter("reboot_reason", reboot_reason) .withParameter("reg", (const void *)reg); } } TEST_GROUP(MfltUserRebootReasons) { void setup() { } void teardown() { mock().checkExpectations(); mock().clear(); } }; TEST(MfltUserRebootReasons, Test_ExpectedUserRebootReasons) { uint16_t expected_base = MEMFAULT_REBOOT_REASON_EXPECTED_CUSTOM_BASE; LONGS_EQUAL(++expected_base, MEMFAULT_REBOOT_REASON_KEY(ExpectedReboot1)); LONGS_EQUAL(++expected_base, MEMFAULT_REBOOT_REASON_KEY(ExpectedReboot2)); LONGS_EQUAL(++expected_base, MEMFAULT_REBOOT_REASON_KEY(ExpectedReboot3)); } TEST(MfltUserRebootReasons, Test_UnexpectedUserRebootReasons) { uint16_t unexpected_base = MEMFAULT_REBOOT_REASON_UNEXPECTED_CUSTOM_BASE; LONGS_EQUAL(++unexpected_base, MEMFAULT_REBOOT_REASON_KEY(UnexpectedReboot1)); LONGS_EQUAL(++unexpected_base, MEMFAULT_REBOOT_REASON_KEY(UnexpectedReboot2)); LONGS_EQUAL(++unexpected_base, MEMFAULT_REBOOT_REASON_KEY(UnexpectedReboot3)); } TEST(MfltUserRebootReasons, Test_MarkResetWrapper) { mock() .expectOneCall("memfault_reboot_tracking_mark_reset_imminent") .withParameter("reboot_reason", MEMFAULT_REBOOT_REASON_KEY(UnexpectedReboot1)) .ignoreOtherParameters(); MEMFAULT_REBOOT_MARK_RESET_IMMINENT_CUSTOM(UnexpectedReboot1); } ================================================ FILE: tests/unit/src/test_memfault_varint.cpp ================================================ //! @file //! //! @brief #include "CppUTest/MemoryLeakDetectorMallocMacros.h" #include "CppUTest/MemoryLeakDetectorNewMacros.h" #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" extern "C" { #include #include #include "memfault/util/varint.h" static uint8_t s_varint_test_buf[MEMFAULT_UINT32_MAX_VARINT_LENGTH]; } TEST_GROUP(MemfaultVarint) { void setup() { memset(s_varint_test_buf, 0x0, sizeof(s_varint_test_buf)); } void teardown() { } }; TEST(MemfaultVarint, Test_1byteEncoding) { const uint8_t expected_encoding[] = { 127 }; size_t len = memfault_encode_varint_u32(127, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding), len); MEMCMP_EQUAL(expected_encoding, s_varint_test_buf, sizeof(expected_encoding)); } TEST(MemfaultVarint, Test_2byteEncoding) { const uint8_t expected_encoding_min[] = { 128, 1 }; size_t len = memfault_encode_varint_u32(128, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding_min), len); MEMCMP_EQUAL(expected_encoding_min, s_varint_test_buf, sizeof(expected_encoding_min)); const uint8_t expected_encoding_max[] = { 255, 127 }; len = memfault_encode_varint_u32(16383, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding_max), len); MEMCMP_EQUAL(expected_encoding_max, s_varint_test_buf, sizeof(expected_encoding_max)); } TEST(MemfaultVarint, Test_3byteEncoding) { const uint8_t expected_encoding_min[] = { 128, 128, 1 }; size_t len = memfault_encode_varint_u32(16384, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding_min), len); MEMCMP_EQUAL(expected_encoding_min, s_varint_test_buf, sizeof(expected_encoding_min)); const uint8_t expected_encoding_max[] = { 255, 255, 127 }; len = memfault_encode_varint_u32(2097151, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding_max), len); MEMCMP_EQUAL(expected_encoding_max, s_varint_test_buf, sizeof(expected_encoding_max)); } TEST(MemfaultVarint, Test_4byteEncoding) { const uint8_t expected_encoding_min[] = { 128, 128, 128, 1 }; size_t len = memfault_encode_varint_u32(2097152, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding_min), len); MEMCMP_EQUAL(expected_encoding_min, s_varint_test_buf, sizeof(expected_encoding_min)); const uint8_t expected_encoding_max[] = { 255, 255, 255, 127 }; len = memfault_encode_varint_u32(268435455, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding_max), len); MEMCMP_EQUAL(expected_encoding_max, s_varint_test_buf, sizeof(expected_encoding_max)); } TEST(MemfaultVarint, Test_5byteEncoding) { const uint8_t expected_encoding[] = { 239, 253, 182, 245, 13 }; size_t len = memfault_encode_varint_u32(0xdeadbeef, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding), len); MEMCMP_EQUAL(expected_encoding, s_varint_test_buf, sizeof(expected_encoding)); const uint8_t expected_encoding_max[] = { 255, 255, 255, 255, 15 }; len = memfault_encode_varint_u32(0xffffffff, s_varint_test_buf); LONGS_EQUAL(sizeof(expected_encoding_max), len); MEMCMP_EQUAL(expected_encoding_max, s_varint_test_buf, sizeof(expected_encoding_max)); } TEST(MemfaultVarint, Test_Si32_Zero) { const uint8_t zero[] = { 0x0 }; const size_t len = memfault_encode_varint_si32(0x0, s_varint_test_buf); LONGS_EQUAL(sizeof(zero), len); MEMCMP_EQUAL(zero, s_varint_test_buf, sizeof(zero)); } TEST(MemfaultVarint, Test_Si32_MinusOne) { const uint8_t minus_one[] = { 0x1 }; const size_t len = memfault_encode_varint_si32(-1, s_varint_test_buf); LONGS_EQUAL(sizeof(minus_one), len); MEMCMP_EQUAL(minus_one, s_varint_test_buf, sizeof(minus_one)); } TEST(MemfaultVarint, Test_Si32_One) { const uint8_t one[] = { 0x2 }; const size_t len = memfault_encode_varint_si32(1, s_varint_test_buf); LONGS_EQUAL(sizeof(one), len); MEMCMP_EQUAL(one, s_varint_test_buf, sizeof(one)); } TEST(MemfaultVarint, Test_Si32_Si32Max) { const uint8_t si32_max[] = { 254, 255, 255, 255, 15 }; const size_t len = memfault_encode_varint_si32(2147483647, s_varint_test_buf); LONGS_EQUAL(sizeof(si32_max), len); MEMCMP_EQUAL(si32_max, s_varint_test_buf, sizeof(si32_max)); } TEST(MemfaultVarint, Test_Si32_Si32Min) { const uint8_t si32_min[] = { 255, 255, 255, 255, 15 }; const size_t len = memfault_encode_varint_si32(-2147483648, s_varint_test_buf); LONGS_EQUAL(sizeof(si32_min), len); MEMCMP_EQUAL(si32_min, s_varint_test_buf, sizeof(si32_min)); } ================================================ FILE: tests/unit/stub_includes/FreeRTOS.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #ifdef TEST_FREERTOS_METRICS #define configGENERATE_RUN_TIME_STATS 1 #define configNUM_CORES 1 #define configUSE_TRACE_FACILITY 1 #endif ================================================ FILE: tests/unit/stub_includes/lwip/stats.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #define TCP_STATS 1 #define UDP_STATS 1 #ifdef __cplusplus extern "C" { #endif #include struct mock_proto { uint32_t xmit; uint32_t recv; uint32_t drop; }; struct mock_stats { struct mock_proto tcp; struct mock_proto udp; }; extern struct mock_stats lwip_stats; #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/stub_includes/mbedtls_mem.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Declarations for mbedtls memory functions #pragma once #include #ifdef __cplusplus extern "C" { #endif void *__wrap_mbedtls_calloc(size_t n, size_t size); void __wrap_mbedtls_free(void *ptr); void *__real_mbedtls_calloc(size_t n, size_t size); void __real_mbedtls_free(void *ptr); #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/stub_includes/memfault_metrics_heartbeat_config.def ================================================ //! @file //! A fake set of heartbeat metrics we use for unit testing // clang-format off #if !defined(TEST_NO_CUSTOM_METRICS) MEMFAULT_METRICS_KEY_DEFINE(test_key_unsigned, kMemfaultMetricType_Unsigned) MEMFAULT_METRICS_KEY_DEFINE(test_key_signed, kMemfaultMetricType_Signed) MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE(test_key_timer, kMemfaultMetricType_Timer, 0, 3600000) MEMFAULT_METRICS_STRING_KEY_DEFINE(test_key_string, 16) MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE(test_unsigned_scale_value, kMemfaultMetricType_Unsigned, 10) #if MEMFAULT_METRICS_SESSIONS_ENABLED MEMFAULT_METRICS_SESSION_KEY_DEFINE(test_key_session) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(test_unsigned, kMemfaultMetricType_Unsigned, test_key_session) MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(test_string, 16, test_key_session) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(test_timer, kMemfaultMetricType_Timer, test_key_session) MEMFAULT_METRICS_SESSION_KEY_DEFINE(test_key_session_two) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(test_unsigned, kMemfaultMetricType_Unsigned, test_key_session_two) MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION(test_string, 16, test_key_session_two) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(test_timer, kMemfaultMetricType_Timer, test_key_session_two) MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE(test_signed_scale_value, kMemfaultMetricType_Signed, test_key_session_two, 10) #endif // clang-format on #endif #ifdef TEST_LWIP_METRICS #include "memfault_lwip_metrics_heartbeat_config.def" #endif // TEST_LWIP_METRICS #ifdef TEST_FREERTOS_METRICS #include "memfault_metrics_heartbeat_freertos_config.def" #endif // TEST_FREERTOS_METRICS #ifdef TEST_MBEDTLS_METRICS #include "memfault_mbedtls_metrics_heartbeat_config.def" #endif // TEST_MBEDTLS_METRICS ================================================ FILE: tests/unit/stub_includes/memfault_platform_config.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Platform overrides for the default configuration settings in the memfault-firmware-sdk. //! Default configuration settings can be found in "memfault/config.h" #define MEMFAULT_REBOOT_REASON_CUSTOM_ENABLE 1 ================================================ FILE: tests/unit/stub_includes/memfault_reboot_reason_user_config.def ================================================ MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(ExpectedReboot1) MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(ExpectedReboot2) MEMFAULT_EXPECTED_REBOOT_REASON_DEFINE(ExpectedReboot3) MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(UnexpectedReboot1) MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(UnexpectedReboot2) MEMFAULT_UNEXPECTED_REBOOT_REASON_DEFINE(UnexpectedReboot3) ================================================ FILE: tests/unit/stub_includes/memfault_task_watchdog_config.def ================================================ //! @file //! A fake set of task watchdog channels for unit testing MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(task_1) MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(task_2) ================================================ FILE: tests/unit/stub_includes/memfault_trace_reason_user_config.def ================================================ //! @file //! A fake set of user-defined trace reasons we use for unit testing MEMFAULT_TRACE_REASON_DEFINE(bluetooth_cmd_buffer_full) MEMFAULT_TRACE_REASON_DEFINE(sensor_ack_timeout) MEMFAULT_TRACE_REASON_DEFINE(test) ================================================ FILE: tests/unit/stub_includes/sdk_common.h ================================================ #pragma once //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Stub file used to get the NRF5 port to compile in the test environment #include #include "memfault/core/compiler.h" // Based on https://github.com/NordicSemiconductor/nrfx/blob/master/mdk/nrf52.h typedef struct { uint32_t SIZERAMBLOCKS; uint32_t NUMRAMBLOCK; struct { uint32_t RAM; } INFO; } NRF_FICR_Type; static const NRF_FICR_Type s_nrf_ficr = { .SIZERAMBLOCKS = 0x00000000, .NUMRAMBLOCK = 0x00000000, .INFO = { .RAM = 256, }, }; static const NRF_FICR_Type *const NRF_FICR = &s_nrf_ficr; MEMFAULT_USED static uintptr_t s_get_stack_top(void) { uint32_t i; uintptr_t retval = (uintptr_t)&i; return retval; } #define CSTACK s_get_stack_top() #define STACK_TOP (s_get_stack_top() + 1024) ================================================ FILE: tests/unit/stub_includes/task.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #pragma once #include #ifdef __cplusplus extern "C" { #endif uint32_t portGET_RUN_TIME_COUNTER_VALUE(void); uint32_t ulTaskGetIdleRunTimeCounter(void); #define tskKERNEL_VERSION_MAJOR 10 #define tskKERNEL_VERSION_MINOR 4 #define tskKERNEL_VERSION_BUILD 3 typedef uint32_t TaskHandle_t; #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/stub_includes/timers.h ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Stubs for FreeRTOS timers.h. #pragma once #ifdef __cplusplus extern "C" { #endif typedef uint32_t UBaseType_t; typedef uint32_t StackType_t; static TaskHandle_t xTimerGetTimerDaemonTaskHandle(void) { return 0; } static UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask) { (void)xTask; return 0; } #ifdef __cplusplus } #endif ================================================ FILE: tests/unit/stubs/stub_assert.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! Stub function used to generate a non-empty archive to workaround the old //! circa-2005 'ar' utility installed by default on macs void stub_assert(void); void stub_assert(void) { } ================================================ FILE: tests/unit/stubs/stub_component_booted.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "memfault/core/event_storage.h" #include "memfault/core/log.h" #include "memfault/core/reboot_tracking.h" #include "memfault/core/trace_event.h" bool memfault_event_storage_booted(void) { return true; } bool memfault_log_booted(void) { return true; } bool memfault_reboot_tracking_booted(void) { return true; } bool memfault_trace_event_booted(void) { return true; } ================================================ FILE: tests/unit/stubs/stub_mbedtls_mem.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include #include "mbedtls_mem.h" void *__real_mbedtls_calloc(size_t n, size_t size) { return calloc(n, size); } void __real_mbedtls_free(void *ptr) { free(ptr); } ================================================ FILE: tests/unit/stubs/stub_memfault_coredump.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/compiler.h" #include "memfault/panics/coredump.h" bool memfault_coredump_storage_check_size(void) { return false; } bool memfault_coredump_has_valid_coredump(MEMFAULT_UNUSED size_t *total_size_out) { return false; } ================================================ FILE: tests/unit/stubs/stub_memfault_coredump_regions.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/compiler.h" #include "memfault/panics/coredump.h" #include "memfault/panics/coredump_impl.h" const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(MEMFAULT_UNUSED size_t *num_regions) { return NULL; } const sMfltCoredumpRegion *memfault_coredump_get_sdk_regions(MEMFAULT_UNUSED size_t *num_regions) { return NULL; } const sMfltCoredumpRegion *memfault_platform_coredump_get_regions( MEMFAULT_UNUSED const sCoredumpCrashInfo *crash_info, MEMFAULT_UNUSED size_t *num_regions) { return NULL; } ================================================ FILE: tests/unit/stubs/stub_memfault_coredump_storage_debug.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/panics/coredump.h" bool memfault_coredump_storage_debug_test_begin(void) { return true; } bool memfault_coredump_storage_debug_test_finish(void) { return true; } ================================================ FILE: tests/unit/stubs/stub_memfault_log.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/log.h" uint32_t memfault_log_get_dropped_count(void) { return 1; } uint32_t memfault_log_get_recorded_count(void) { return 1; } ================================================ FILE: tests/unit/stubs/stub_memfault_log_save.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details //! //! @brief //! A stub implementation of memfault_log_save() for unit testing #include "memfault/core/compiler.h" #include "memfault/core/log.h" void memfault_log_save(MEMFAULT_UNUSED eMemfaultPlatformLogLevel level, MEMFAULT_UNUSED const char *fmt, ...) { } #if MEMFAULT_COMPACT_LOG_ENABLE void memfault_compact_log_save(MEMFAULT_UNUSED eMemfaultPlatformLogLevel level, MEMFAULT_UNUSED uint32_t log_id, MEMFAULT_UNUSED uint32_t compressed_fmt, ...) { } #endif ================================================ FILE: tests/unit/stubs/stub_platform.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/platform/core.h" // NB: This will require adding CPPUTEST_CPPFLAGS += -DMEMFAULT_NORETURN="" to any Makefile using // this stub to avoid an error void memfault_platform_reboot(void) { return; } ================================================ FILE: tests/unit/stubs/stub_reboot_tracking.c ================================================ //! @file //! //! Copyright (c) Memfault, Inc. //! See LICENSE for details #include "memfault/core/reboot_tracking.h" void memfault_reboot_tracking_mark_reset_imminent( MEMFAULT_UNUSED eMemfaultRebootReason reboot_reason, MEMFAULT_UNUSED const sMfltRebootTrackingRegInfo *reg) { return; } int memfault_reboot_tracking_get_reboot_reason(MEMFAULT_UNUSED sMfltRebootReason *reboot_reason) { return 0; } ================================================ FILE: tests/unit/test.py ================================================ # # Copyright (c) Memfault, Inc. # See LICENSE for details # """ A small wrapper around the Make-based unit test driver, to provide a pytest front-end for running the tests. """ import subprocess from pathlib import Path import pytest TEST_DIR = Path(__file__).parent # Run this special Make target to print out the set of tests that are available. # This will honor the TEST_MAKEFILE_FILTER environment variable, if set, so the # same filtering behavior is supported. output = subprocess.check_output([ "make", "--no-print-directory", "-C", TEST_DIR, "print-makefiles", ]) test_makefiles = output.decode().split() # Pass the test_makefiles list as a parameter to the test function, so that one # test runs for each makefile target. NOTE: this does bypass the Make dependency # graph, especially if pytest is run with the pytest-xdist parallel runner, so # this WILL FAIL in weird ways if we add a shared prerequisite to the test # targets! Right now each test target is independent, so this is OK. @pytest.mark.parametrize( "target_makefile", test_makefiles, ) def test_cpputest(target_makefile: str): subprocess.check_call(["make", "-C", TEST_DIR, target_makefile]) ================================================ FILE: zephyr/module.yml ================================================ name: memfault-firmware-sdk build: cmake: ports/zephyr kconfig: ports/zephyr/Kconfig