Skip to content

Commit b315d88

Browse files
author
Memfault Inc
committed
Memfault Firmware SDK 0.39.1 (Build 1436)
1 parent 5d66be4 commit b315d88

File tree

12 files changed

+536
-36
lines changed

12 files changed

+536
-36
lines changed

CHANGES.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
### Changes between Memfault SDK 0.39.1 and SDK 0.38.0 - Feb 3, 2023
2+
3+
#### :rocket: New Features
4+
5+
- **Experimental**
6+
- CMSIS-Pack support
7+
- Out Of Memory reboot reason added
8+
9+
#### :chart_with_upwards_trend: Improvements
10+
11+
- ESP-IDF:
12+
- The default implementation of `memfault_platform_coredump_get_regions` is
13+
changed to collect the current active stack, .bss, .data, and .heap regions.
14+
Additionally if you are using ESP-IDF >= 4.4.0, the SDK will prioritize
15+
collecting FreeRTOS regions containing task TCB and stack data.
16+
- Assert coredumps are now labeled with the Assert reason
17+
118
### Changes between Memfault SDK 0.39.0 and SDK 0.38.0 - Feb 3, 2023
219

320
#### :boom: Breaking Changes

VERSION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
BUILD ID: 1415
2-
GIT COMMIT: cb7a3096e
1+
BUILD ID: 1436
2+
GIT COMMIT: 38142ae37

components/include/memfault/core/reboot_reason_types.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ typedef enum MfltResetReason {
5050
kMfltRebootReason_WatchdogDeprecated = 0x8002,
5151

5252
kMfltRebootReason_BrownOutReset = 0x8003,
53-
kMfltRebootReason_Nmi = 0x8004, // Non-Maskable Interrupt
53+
kMfltRebootReason_Nmi = 0x8004, // Non-Maskable Interrupt
5454

5555
// More details about nomenclature in https://mflt.io/root-cause-watchdogs
5656
kMfltRebootReason_HardwareWatchdog = 0x8005,
@@ -68,6 +68,9 @@ typedef enum MfltResetReason {
6868
// to a previous version was initiated
6969
kMfltRebootReason_FirmwareUpdateError = 0x8009,
7070

71+
// A software reset triggered due to a dynamic memory (heap) allocation failure.
72+
kMfltRebootReason_OutOfMemory = 0x800A,
73+
7174
// Resets from Arm Faults
7275
kMfltRebootReason_BusFault = 0x9100,
7376
kMfltRebootReason_MemFault = 0x9200,

components/include/memfault/panics/arch/xtensa/xtensa.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <stdint.h>
1212

1313
#include "memfault/core/compiler.h"
14+
#include "memfault/core/reboot_reason_types.h"
1415

1516
#ifdef __cplusplus
1617
extern "C" {
@@ -43,6 +44,12 @@ MEMFAULT_PACKED_STRUCT MfltRegState {
4344
uint32_t excvaddr;
4445
};
4546

47+
//! Called by platform assertion handlers to save info before triggering fault handling
48+
//!
49+
//! @param pc Pointer to address of assertion location
50+
//! @param lr Pointer to return address of assertion location
51+
//! @param reason Reboot reason for the assertion
52+
void memfault_xtensa_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason);
4653

4754
#ifdef __cplusplus
4855
}

components/include/memfault/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ typedef struct {
1919
uint8_t patch;
2020
} sMfltSdkVersion;
2121

22-
#define MEMFAULT_SDK_VERSION { .major = 0, .minor = 39, .patch = 0 }
22+
#define MEMFAULT_SDK_VERSION { .major = 0, .minor = 39, .patch = 1 }
2323

2424
#ifdef __cplusplus
2525
}

components/panics/src/memfault_fault_handling_xtensa.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ const sMfltCoredumpRegion *memfault_coredump_get_arch_regions(size_t *num_region
2222

2323
static eMemfaultRebootReason s_crash_reason = kMfltRebootReason_Unknown;
2424

25+
void memfault_xtensa_fault_handling_assert(void *pc, void *lr, eMemfaultRebootReason reason) {
26+
sMfltRebootTrackingRegInfo info = {
27+
.pc = (uint32_t)pc,
28+
.lr = (uint32_t)lr,
29+
};
30+
s_crash_reason = reason;
31+
memfault_reboot_tracking_mark_reset_imminent(s_crash_reason, &info);
32+
}
33+
2534
void memfault_fault_handler(const sMfltRegState *regs, eMemfaultRebootReason reason) {
2635
if (s_crash_reason == kMfltRebootReason_Unknown) {
2736
sMfltRebootTrackingRegInfo info = {

ports/esp_idf/memfault/CMakeLists.txt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,28 @@ list(APPEND MEMFAULT_COMPONENTS_INC_FOLDERS
5252
$ENV{MEMFAULT_PLATFORM_EXTRA_INCLUDES}
5353
)
5454

55+
# For version >= 4.4.0, we can collect smaller coredumps by default
56+
# by prioritizing active stack and FreeRTOS regions first. ESP-IDF <= 4.4.0
57+
# uses a simpler scheme collecting all of DRAM. See
58+
# common/memfault_platform_coredump.c for more info.
59+
# Note: CMake does not short-circuit logic statements, nested ifs required
60+
# Note: ENV{IDF_VERSION} added in esp-idf 4.4.0
61+
if (DEFINED ENV{IDF_VERSION})
62+
if ($ENV{IDF_VERSION} VERSION_GREATER_EQUAL "4.4.0")
63+
list(APPEND MEMFAULT_COMPONENTS_SRCS
64+
${MEMFAULT_SDK_ROOT}/ports/freertos/src/memfault_freertos_ram_regions.c
65+
)
66+
67+
list(APPEND MEMFAULT_COMPONENTS_INC_FOLDERS
68+
${MEMFAULT_SDK_ROOT}/ports/include/
69+
)
70+
71+
# Add a linker fragment to place FreeRTOS timers and task objects in the same area of dram0.bss
72+
set(COMPONENT_ADD_LDFRAGMENTS "${MEMFAULT_ESP_IDF_PORT_COMMON}/memfault_esp_freertos.lf")
73+
endif()
74+
endif()
75+
76+
5577
# Register Memfault SDK Component
5678
set(COMPONENT_SRCS ${MEMFAULT_COMPONENTS_SRCS})
5779
set(COMPONENT_ADD_INCLUDEDIRS ${MEMFAULT_COMPONENTS_INC_FOLDERS})
@@ -94,3 +116,30 @@ target_link_libraries(
94116
INTERFACE
95117
-T ${compact_log_linker_script}
96118
)
119+
120+
# Link required libraries and add compiler flags to enable FreeRTOS region collection
121+
# in >= 4.4.0. Note: CMake does not short-circuit logic statements, nested ifs required
122+
# Note: ENV{IDF_VERSION} added in esp-idf 4.4.0
123+
if(DEFINED ENV{IDF_VERSION})
124+
if ($ENV{IDF_VERSION} VERSION_GREATER_EQUAL "4.4.0")
125+
# Policy change requires CMake v3.13+
126+
cmake_minimum_required(VERSION 3.13)
127+
128+
# First set new policy for target_link_libraries, this resolves a warning when using on
129+
# targets not created in this directory
130+
cmake_policy(SET CMP0079 NEW)
131+
132+
# Get the name of the ESP FreeRTOS target/library
133+
idf_component_get_property(freertos_lib freertos COMPONENT_LIB)
134+
135+
# Link this component to FreeRTOS, use INTERFACE because we're only sharing headers
136+
target_link_libraries(${freertos_lib} INTERFACE ${this_component})
137+
138+
# Lastly ensure that our FreeRTOS trace hooks are defined first by adding this
139+
# compile option to the FreeRTOS target to include with all source
140+
# This method is an alternative to #include within FreeRTOSConfig.h which esp-idf
141+
# makes very difficult to do.
142+
get_filename_component(freertos_trace_header ${MEMFAULT_SDK_ROOT}/ports/include/memfault/ports/freertos_trace.h ABSOLUTE)
143+
target_compile_options(${freertos_lib} INTERFACE -include ${freertos_trace_header})
144+
endif()
145+
endif()
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# This linker fragment should yield a similar snippet in
2+
# <build_dir>/esp-idf/esp_system/ld/sections.ld
3+
# The important lines are EXCLUDE_FILE and the symbols surrounding the
4+
# task and timer rules
5+
#/* Shared RAM */
6+
# .dram0.bss (NOLOAD) :
7+
# {
8+
# . = ALIGN (8);
9+
# _bss_start = ABSOLUTE(.);
10+
#
11+
# ...
12+
# *(EXCLUDE_FILE(*libfreertos.a:tasks.* *libfreertos.a:timers.*) .bss EXCLUDE_FILE(*libfreertos.a:tasks.* *libfreertos.a:timers.*) .bss.*)
13+
# *(EXCLUDE_FILE(*libfreertos.a:tasks.* *libfreertos.a:timers.*) COMMON)
14+
# ...
15+
#
16+
# ...
17+
# _memfault_capture_tasks_start = ABSOLUTE(.);
18+
# *libfreertos.a:tasks.*(.bss .bss.* COMMON)
19+
# _memfault_capture_tasks_end = ABSOLUTE(.);
20+
# _memfault_capture_timers_start = ABSOLUTE(.);
21+
# *libfreertos.a:timers.*(.bss .bss.* COMMON)
22+
# _memfault_capture_timers_end = ABSOLUTE(.);
23+
# ...
24+
# } > dram0_0_seg
25+
26+
# Create a scheme to describe input sections to an output section
27+
[scheme:memfault]
28+
entries:
29+
common -> dram0_bss
30+
bss -> dram0_bss
31+
32+
# Create a mapping using the defined scheme. Add linker symbols around
33+
# to capture start and end of task and timer location.
34+
# Note: ldgen does not properly generate rules for section fragments that include multiple
35+
# input sections. To work around this, we duplicate a rule for both bss and common section
36+
# fragments
37+
[mapping:memfault_freertos]
38+
archive: libfreertos.a
39+
entries:
40+
timers (memfault);
41+
bss-> dram0_bss SURROUND(memfault_timers_bss),
42+
common -> dram0_bss SURROUND(memfault_timers_common)
43+
tasks (memfault);
44+
bss -> dram0_bss SURROUND(memfault_tasks_bss),
45+
common -> dram0_bss SURROUND(memfault_tasks_common)

ports/esp_idf/memfault/common/memfault_fault_handler.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@
2121

2222
// Note: The esp-idf implements abort which will invoke the esp-idf coredump handler as well as a
2323
// chip reboot so we just piggback off of that
24-
void memfault_fault_handling_assert(void *pc, void *lr) { abort(); }
24+
void memfault_fault_handling_assert(void *pc, void *lr) {
25+
memfault_xtensa_fault_handling_assert(pc, lr, kMfltRebootReason_Assert);
26+
abort();
27+
}
2528

2629
void memfault_fault_handling_assert_extra(void *pc, void *lr, sMemfaultAssertInfo *extra_info) {
30+
memfault_xtensa_fault_handling_assert(pc, lr, extra_info->assert_reason);
2731
abort();
2832
}
2933

ports/esp_idf/memfault/common/memfault_platform_coredump.c

Lines changed: 124 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
#include "memfault/esp_port/uart.h"
3131
#include "memfault/util/crc16_ccitt.h"
3232

33+
// Needed for >= v4.4.0 default coredump collection
34+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
35+
#include "memfault/ports/freertos_coredump.h"
36+
#endif
37+
3338
// Factor out issues with Espressif's ESP32 to ESP conversion in sdkconfig
3439
#define COREDUMPS_ENABLED \
3540
(CONFIG_ESP32_ENABLE_COREDUMP || CONFIG_ESP_COREDUMP_ENABLE)
@@ -53,13 +58,26 @@
5358
esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL)
5459
#endif
5560

61+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
62+
// Memory regions used for esp-idf >= 4.4.0
63+
// Active stack (1) + task/timer and bss/common regions (4) +
64+
// freertos tasks (MEMFAULT_PLATFORM_MAX_TASK_REGIONS) + bss(1) + data(1) + heap(1)
65+
#define MEMFAULT_ESP_PORT_NUM_REGIONS (1 + 4 + MEMFAULT_PLATFORM_MAX_TASK_REGIONS + 1 + 1 + 1)
66+
#else // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
67+
// Memory regions for esp-idf < 4.4.0
68+
// Active stack (1) + bss(1) + data(1) + heap(1)
69+
#define MEMFAULT_ESP_PORT_NUM_REGIONS (1 + 1 + 1 + 1)
70+
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
71+
5672
typedef struct {
5773
uint32_t magic;
5874
esp_partition_t partition;
5975
uint32_t crc;
6076
} sEspIdfCoredumpPartitionInfo;
6177

6278
static sEspIdfCoredumpPartitionInfo s_esp32_coredump_partition_info;
79+
static const uintptr_t esp32_dram_start_addr = SOC_DRAM_LOW;
80+
static const uintptr_t esp32_dram_end_addr = SOC_DRAM_HIGH;
6381

6482
static uint32_t prv_get_partition_info_crc(void) {
6583
return memfault_crc16_ccitt_compute(MEMFAULT_CRC16_CCITT_INITIAL_VALUE,
@@ -75,42 +93,121 @@ static const esp_partition_t *prv_get_core_partition(void) {
7593
return &s_esp32_coredump_partition_info.partition;
7694
}
7795

78-
//! By default, we attempt to collect all of internal RAM as part of a Coredump
96+
// We use two different default coredump collection methods
97+
// due to differences in esp-idf versions. The following helper
98+
// is only used for esp-idf >= 4.4.0
99+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
100+
//! Helper function to get bss and common sections of task and timer objects
101+
size_t prv_get_freertos_bss_common(sMfltCoredumpRegion *regions, size_t num_regions) {
102+
if (regions == NULL || num_regions == 0) {
103+
return 0;
104+
}
105+
106+
size_t region_index = 0;
107+
extern uint32_t _memfault_timers_bss_start;
108+
extern uint32_t _memfault_timers_bss_end;
109+
extern uint32_t _memfault_timers_common_start;
110+
extern uint32_t _memfault_timers_common_end;
111+
extern uint32_t _memfault_tasks_bss_start;
112+
extern uint32_t _memfault_tasks_bss_end;
113+
extern uint32_t _memfault_tasks_common_start;
114+
extern uint32_t _memfault_tasks_common_end;
115+
116+
// ldgen has a bug that does not exclude rules matching multiple input sections at the
117+
// same time. To work around this, we instead emit a symbol for each section we're attempting
118+
// to collect. This means 8 symbols (tasks/timers + bss/common). If this is ever fixed we
119+
// can remove the need to collect 4 separate regions.
120+
size_t timers_bss_length =
121+
(uintptr_t)&_memfault_timers_bss_end - (uintptr_t)&_memfault_timers_bss_start;
122+
size_t timers_common_length =
123+
(uintptr_t)&_memfault_timers_common_end - (uintptr_t)&_memfault_timers_common_start;
124+
size_t tasks_bss_length =
125+
(uintptr_t)&_memfault_tasks_bss_end - (uintptr_t)&_memfault_tasks_bss_start;
126+
size_t tasks_common_length =
127+
(uintptr_t)&_memfault_tasks_common_end - (uintptr_t)&_memfault_tasks_common_start;
128+
129+
regions[region_index++] =
130+
MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&_memfault_timers_bss_start, timers_bss_length);
131+
regions[region_index++] =
132+
MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&_memfault_timers_common_start, timers_common_length);
133+
regions[region_index++] =
134+
MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&_memfault_tasks_bss_start, tasks_bss_length);
135+
regions[region_index++] =
136+
MEMFAULT_COREDUMP_MEMORY_REGION_INIT(&_memfault_tasks_common_start, tasks_common_length);
137+
138+
return region_index;
139+
}
140+
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
141+
142+
//! Simple implementation to ensure address is in SRAM range.
143+
//!
144+
//! @note The function is intentionally defined as weak so someone can
145+
//! easily override the port defaults by re-defining a non-weak version of
146+
//! the function in another file
147+
MEMFAULT_WEAK size_t memfault_platform_sanitize_address_range(void *start_addr,
148+
size_t desired_size) {
149+
const uintptr_t start_addr_int = (uintptr_t)start_addr;
150+
const uintptr_t end_addr_int = start_addr_int + desired_size;
151+
152+
if ((start_addr_int < esp32_dram_start_addr) || (start_addr_int > esp32_dram_end_addr)) {
153+
return 0;
154+
}
155+
156+
if (end_addr_int > esp32_dram_end_addr) {
157+
return esp32_dram_end_addr - start_addr_int;
158+
}
159+
160+
return desired_size;
161+
}
162+
163+
//! By default we prioritize collecting active stack, bss, data, and heap.
164+
//!
165+
//! In esp-idf >= 4.4.0, we additionally collect bss and stack regions for
166+
//! FreeRTOS tasks.
79167
//!
80168
//! @note The function is intentionally defined as weak so someone can
81169
//! easily override the port defaults by re-defining a non-weak version of
82170
//! the function in another file
83171
MEMFAULT_WEAK
84172
const sMfltCoredumpRegion *memfault_platform_coredump_get_regions(
85-
const sCoredumpCrashInfo *crash_info, size_t *num_regions) {
86-
static sMfltCoredumpRegion s_coredump_regions[1];
87-
88-
// The ESP32S2 + S3 have a different memory map than the ESP32; IRAM and DRAM
89-
// share the same pysical SRAM, but are mapped at different addresses. We need
90-
// to account for the placement of IRAM data, which offsets the start of
91-
// placed DRAM.
92-
//
93-
// https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf#subsubsection.3.3.2
94-
// https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf#subsubsection.4.3.2
95-
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2)
96-
// use this symbol defined by the linker to find the start of DRAM
173+
const sCoredumpCrashInfo *crash_info, size_t *num_regions) {
174+
static sMfltCoredumpRegion s_coredump_regions[MEMFAULT_ESP_PORT_NUM_REGIONS];
175+
int region_idx = 0;
176+
177+
const size_t stack_size = memfault_platform_sanitize_address_range(
178+
crash_info->stack_address, MEMFAULT_PLATFORM_ACTIVE_STACK_SIZE_TO_COLLECT);
179+
180+
// First, capture the active stack
181+
s_coredump_regions[region_idx++] =
182+
MEMFAULT_COREDUMP_MEMORY_REGION_INIT(crash_info->stack_address, stack_size);
183+
184+
// Second, capture the task regions, if esp-idf >= 4.4.0
185+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
186+
region_idx += memfault_freertos_get_task_regions(
187+
&s_coredump_regions[region_idx], MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx);
188+
189+
// Third, capture the FreeRTOS-specific bss regions
190+
region_idx += prv_get_freertos_bss_common(&s_coredump_regions[region_idx],
191+
MEMFAULT_ARRAY_SIZE(s_coredump_regions) - region_idx);
192+
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
193+
194+
// Next, capture all of .bss + .data that we can fit.
97195
extern uint32_t _data_start;
98-
const uint32_t esp32_dram_start_addr = (uint32_t)&_data_start;
99-
#else
100-
const uint32_t esp32_dram_start_addr = SOC_DMA_LOW;
101-
#endif
196+
extern uint32_t _data_end;
197+
extern uint32_t _bss_start;
198+
extern uint32_t _bss_end;
199+
extern uint32_t _heap_start;
102200

103-
size_t dram_collection_len = SOC_DMA_HIGH - esp32_dram_start_addr;
104-
const esp_partition_t *core_part = prv_get_core_partition();
105-
if (core_part != NULL) {
106-
// NB: Leave some space in storage for other regions collected by the SDK
107-
dram_collection_len = MEMFAULT_MIN((core_part->size * 7) / 8,
108-
dram_collection_len);
109-
}
201+
s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(
202+
&_bss_start, (uint32_t)((uintptr_t)&_bss_end - (uintptr_t)&_bss_start));
203+
s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(
204+
&_data_start, (uint32_t)((uintptr_t)&_data_end - (uintptr_t)&_data_start));
205+
// Finally, capture as much of the heap as we can fit
206+
s_coredump_regions[region_idx++] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(
207+
&_heap_start, (uint32_t)(esp32_dram_end_addr - (uintptr_t)&_heap_start));
208+
209+
*num_regions = region_idx;
110210

111-
s_coredump_regions[0] = MEMFAULT_COREDUMP_MEMORY_REGION_INIT(
112-
(void *)esp32_dram_start_addr, dram_collection_len);
113-
*num_regions = MEMFAULT_ARRAY_SIZE(s_coredump_regions);
114211
return &s_coredump_regions[0];
115212
}
116213

0 commit comments

Comments
 (0)