Skip to content

Commit 989d8ba

Browse files
author
Memfault Inc
committed
Memfault Firmware SDK 1.22.0 (Build 13216)
1 parent 087af4d commit 989d8ba

26 files changed

+354
-70
lines changed

CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,59 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to
77
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88

9+
## [1.22.0] - 2025-03-19
10+
11+
### 📈 Added
12+
13+
- General
14+
15+
- Enable building the SDK on `aarch64`. Note that this only adds support for
16+
building the SDK on that target, full end-to-end support is not yet
17+
available.
18+
19+
- Zephyr:
20+
21+
- For SOC's with a data cache, flush the cache prior to performing a system
22+
reboot, to ensure data integrity of the RAM-backed reboot tracking data.
23+
This is added to the `memfault_platform_reboot()` default implementation,
24+
which can be overridden by the user if needed.
25+
26+
SOC's without a cache will have no effect from this change.
27+
28+
### 🛠️ Changed
29+
30+
- General:
31+
32+
- `MEMFAULT_LOG_TIMESTAMPS_ENABLE` is now enabled by default. When enabled,
33+
and `memfault_platform_time_get_current()` returns a valid timestamp, logs
34+
will include a second-precision timestamp. This applies to both:
35+
36+
- logs captured as part of a coredump
37+
- logs sent to Memfault after invoking `memfault_log_trigger_collection()`
38+
39+
- Remove some 64-bit integer divides used when computing the built-in
40+
`uptime_s` metric, and in the FreeRTOS
41+
`memfault_platform_get_time_since_boot_ms()` implementation.
42+
43+
- ESP-IDF:
44+
45+
- Log lines that are only a single `'\n'` character are no longer recorded in
46+
the Memfault Log Buffer.
47+
48+
### 🚩 Deprecated
49+
50+
### 🔥 Removed
51+
52+
### 🐛 Fixed
53+
54+
- Zephyr:
55+
56+
- The `hwinfo`-based Reset Reason implementation did not clear the reset
57+
reason register after reading the data. Updated to properly call
58+
`hwinfo_clear_reset_cause()` after reading the reset reason.
59+
60+
### 🔒 Security
61+
962
## [1.21.1] - 2025-03-07
1063

1164
### 🐛 Fixed

VERSION

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
BUILD ID: 12975
2-
GIT COMMIT: b107685f75
3-
VERSION: 1.21.1
1+
BUILD ID: 13216
2+
GIT COMMIT: fd0102e4c8
3+
VERSION: 1.22.0

components/core/src/memfault_log_data_source.c

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,31 +100,48 @@ __attribute__((no_sanitize_undefined))
100100
#endif
101101
#endif
102102
static bool
103-
prv_copy_msg_callback(sMfltLogIterator *iter, MEMFAULT_UNUSED size_t offset, const char *buf,
104-
size_t buf_len) {
103+
prv_serialize_msg_callback(sMfltLogIterator *iter, MEMFAULT_UNUSED size_t offset, const char *buf,
104+
size_t buf_len) {
105105
sMfltLogEncodingCtx *const ctx = (sMfltLogEncodingCtx *)iter->user_ctx;
106-
return memfault_cbor_join(&ctx->encoder, buf, buf_len);
107-
}
108106

109-
static bool prv_encode_current_log(sMemfaultCborEncoder *encoder, sMfltLogIterator *iter) {
110-
if (!memfault_cbor_encode_unsigned_integer(encoder,
107+
#if MEMFAULT_LOG_TIMESTAMPS_ENABLE
108+
// Encode the timestamp value, if it's present, otherwise insert a 0
109+
uint32_t timestamp;
110+
if (memfault_log_hdr_is_timestamped(iter->entry.hdr)) {
111+
if (buf_len < sizeof(timestamp)) {
112+
return false;
113+
}
114+
memcpy(&timestamp, buf, sizeof(timestamp));
115+
buf += sizeof(timestamp);
116+
buf_len -= sizeof(timestamp);
117+
} else {
118+
timestamp = 0;
119+
}
120+
121+
if (!memfault_cbor_encode_unsigned_integer(&ctx->encoder, timestamp)) {
122+
return false;
123+
}
124+
#endif
125+
126+
// Encode the log level
127+
if (!memfault_cbor_encode_unsigned_integer(&ctx->encoder,
111128
memfault_log_get_level_from_hdr(iter->entry.hdr))) {
112129
return false;
113130
}
114131

115132
eMemfaultLogRecordType type = memfault_log_get_type_from_hdr(iter->entry.hdr);
116-
bool success;
117133

118134
// Note: We encode "preformatted" logs (i.e logs that have run through printf) as cbor text
119135
// string and "compact" logs as a cbor byte array so we can differentiate between the two while
120136
// decoding
121137
if (type == kMemfaultLogRecordType_Preformatted) {
122-
success = memfault_cbor_encode_string_begin(encoder, iter->entry.len);
138+
// The message is not null-terminated, so we can't use
139+
// memfault_cbor_encode_string() directly.
140+
bool success = memfault_cbor_encode_string_begin(&ctx->encoder, buf_len);
141+
return success && memfault_cbor_join(&ctx->encoder, buf, buf_len);
123142
} else { // kMemfaultLogRecordType_Compact
124-
success = memfault_cbor_encode_byte_string_begin(encoder, iter->entry.len);
143+
return memfault_cbor_encode_byte_string(&ctx->encoder, buf, buf_len);
125144
}
126-
127-
return (success && memfault_log_iter_copy_msg(iter, prv_copy_msg_callback));
128145
}
129146

130147
static bool prv_log_iterate_encode_callback(sMfltLogIterator *iter) {
@@ -133,7 +150,7 @@ static bool prv_log_iterate_encode_callback(sMfltLogIterator *iter) {
133150
return false;
134151
}
135152
if (!prv_log_is_sent(iter->entry.hdr)) {
136-
ctx->has_encoding_error |= !prv_encode_current_log(&ctx->encoder, iter);
153+
ctx->has_encoding_error |= !memfault_log_iter_copy_msg(iter, prv_serialize_msg_callback);
137154
// It's possible more logs have been added to the buffer
138155
// after the memfault_log_data_source_has_been_triggered() call. They cannot be included,
139156
// because the total message size has already been communicated to the packetizer.
@@ -146,16 +163,24 @@ static bool prv_log_iterate_encode_callback(sMfltLogIterator *iter) {
146163

147164
static bool prv_encode(sMemfaultCborEncoder *encoder, void *iter) {
148165
sMfltLogEncodingCtx *ctx = (sMfltLogEncodingCtx *)((sMfltLogIterator *)iter)->user_ctx;
149-
if (!memfault_serializer_helper_encode_metadata_with_time(encoder, kMemfaultEventType_Logs,
166+
#if MEMFAULT_LOG_TIMESTAMPS_ENABLE
167+
eMemfaultEventType event_type = kMemfaultEventType_LogsTimestamped;
168+
// To save space, all logs are encoded into a single array (as opposed to using a map or
169+
// array per log):
170+
const size_t elements_per_log = 3; // timestamp, lvl, msg
171+
#else
172+
eMemfaultEventType event_type = kMemfaultEventType_Logs;
173+
const size_t elements_per_log = 2; // lvl, msg
174+
#endif
175+
176+
if (!memfault_serializer_helper_encode_metadata_with_time(encoder, event_type,
150177
&ctx->trigger_time)) {
151178
return false;
152179
}
153180
if (!memfault_cbor_encode_unsigned_integer(encoder, kMemfaultEventKey_EventInfo)) {
154181
return false;
155182
}
156-
// To save space, all logs are encoded into a single array (as opposed to using a map or
157-
// array per log):
158-
const size_t elements_per_log = 2; // hdr, msg
183+
159184
if (!memfault_cbor_encode_array_begin(encoder, elements_per_log * ctx->num_logs)) {
160185
return false;
161186
}

components/demo/src/memfault_demo_cli_log.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ int memfault_demo_cli_cmd_test_log(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED cha
3333
}
3434

3535
int memfault_demo_cli_cmd_trigger_logs(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) {
36+
MEMFAULT_LOG_RAW("Triggering log collection");
3637
memfault_log_trigger_collection();
3738
return 0;
3839
}

components/demo/src/panics/memfault_demo_panics.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ int memfault_demo_cli_loadaddr(int argc, char *argv[]) {
223223
return -1;
224224
}
225225
uint32_t addr = (uint32_t)strtoul(argv[1], NULL, 0);
226-
uint32_t val = *(uint32_t *)addr;
226+
uint32_t val = *(uint32_t *)(uintptr_t)addr;
227227

228228
MEMFAULT_LOG_INFO("Read 0x%08" PRIx32 " from 0x%08" PRIx32, val, (uint32_t)(uintptr_t)addr);
229229
return 0;

components/include/memfault/core/compiler_gcc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ extern "C" {
5252
#define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0)
5353
#define MEMFAULT_GET_PC(_a) __asm volatile("mov %0, pc" : "=r"(_a))
5454
#define MEMFAULT_BREAKPOINT(val) __asm volatile("bkpt " #val)
55+
#elif defined(__aarch64__)
56+
#define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0)
57+
#define MEMFAULT_GET_PC(_a) __asm volatile("adr %0, ." : "=r"(_a))
58+
#define MEMFAULT_BREAKPOINT(val) __asm volatile("brk %0" : : "I"(val))
5559
#elif defined(__XTENSA__)
5660
#define MEMFAULT_GET_LR(_a) _a = __builtin_return_address(0)
5761
#define MEMFAULT_GET_PC(_a) \

components/include/memfault/core/serializer_key_ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ typedef enum {
3636
// Deprecated: kMemfaultEventType_LogError = 3, use trace_with_log feature instead
3737
kMemfaultEventType_Logs = 4,
3838
kMemfaultEventType_Cdr = 5,
39+
kMemfaultEventType_LogsTimestamped = 6,
3940
} eMemfaultEventType;
4041

4142
//! EventInfo dictionary keys for events with type kMemfaultEventType_Heartbeat.

components/include/memfault/default_config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ extern "C" {
169169
//! available. Currently timestamps are only decoded from logs embedded in
170170
//! coredumps.
171171
#ifndef MEMFAULT_LOG_TIMESTAMPS_ENABLE
172-
#define MEMFAULT_LOG_TIMESTAMPS_ENABLE 0
172+
#define MEMFAULT_LOG_TIMESTAMPS_ENABLE 1
173173
#endif
174174

175175
//! Controls whether or not multiple events will be batched into a single

components/include/memfault/version.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ typedef struct {
2020
} sMfltSdkVersion;
2121

2222
#define MEMFAULT_SDK_VERSION \
23-
{ .major = 1, .minor = 21, .patch = 1 }
24-
#define MEMFAULT_SDK_VERSION_STR "1.21.1"
23+
{ .major = 1, .minor = 22, .patch = 0 }
24+
#define MEMFAULT_SDK_VERSION_STR "1.22.0"
2525

2626
#ifdef __cplusplus
2727
}

components/metrics/src/memfault_metrics.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,26 @@ static void prv_collect_builtin_data(void) {
556556
prv_memfault_collect_log_metrics();
557557
#endif
558558
#if MEMFAULT_METRICS_UPTIME_ENABLE
559-
MEMFAULT_METRIC_SET_UNSIGNED(uptime_s, memfault_platform_get_time_since_boot_ms() / 1000);
559+
// uptime_s is a 32-bit value measuring seconds since boot. Convert the uptime
560+
// in milliseconds to seconds using the following approach, to avoid 64-bit
561+
// division:
562+
//
563+
// precomputed_scale_factor = (1 << 32) / 1000 = 0x418937
564+
// uptime_seconds = (uptime_ms * 0x418937) >> 32
565+
//
566+
// The maximum millisecond value that can be converted to seconds without
567+
// overflowing using this algorithm is:
568+
//
569+
// (0xFFFF_FFFF_FFFF_FFFF / ((1 << 32) / 1000)) = 4294967592000 milliseconds
570+
//
571+
// 4294967592000 / 1000 / 60 / 60 / 24 / 365 ≈ 136 years
572+
//
573+
// 4294967592000 / 1000 = 4294967592 seconds, 0x1_0000_0128 in hex, which
574+
// exceeds the possible value for a 32-bit integer, so we don't lose any range
575+
// with this algorithm.
576+
const uint32_t uptime_seconds =
577+
(uint32_t)((memfault_platform_get_time_since_boot_ms() * 0x418937ull) >> 32);
578+
MEMFAULT_METRIC_SET_UNSIGNED(uptime_s, uptime_seconds);
560579
#endif
561580
}
562581

components/panics/src/memfault_stdlib_assert.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@
2020
void __aeabi_assert(const char *failedExpr, const char *file, int line) {
2121
(void)failedExpr, (void)file, (void)line;
2222
#elif (defined(__GNUC__))
23+
// Disable -Wreserved-identifier for clang
24+
#pragma GCC diagnostic push
25+
#if defined(__clang__)
26+
#pragma GCC diagnostic ignored "-Wreserved-identifier"
27+
#endif
28+
extern void __assert_func(const char *file, int line, const char *func, const char *failedexpr);
2329
void __assert_func(const char *file, int line, const char *func, const char *failedexpr) {
30+
#pragma GCC diagnostic pop
2431
(void)file, (void)line, (void)func, (void)failedexpr;
2532
#endif
2633
MEMFAULT_ASSERT(0);

examples/freertos/src/memfault/memfault_platform_config.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@
2323
// Enable adding custom demo shell commands
2424
#define MEMFAULT_DEMO_SHELL_COMMAND_EXTENSIONS 1
2525

26-
#define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1
26+
#if !defined(MEMFAULT_PLATFORM_HAS_LOG_CONFIG)
27+
#define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1
28+
#endif

examples/freertos/src/memfault/memfault_platform_port.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
#include "memfault/ports/freertos_coredump.h"
1313
#include "memfault/ports/reboot_reason.h"
1414

15+
#if !defined(MEMFAULT_EXAMPLE_USE_REAL_TIME)
16+
#define MEMFAULT_EXAMPLE_USE_REAL_TIME 1
17+
#endif
18+
1519
// Buffer used to store formatted string for output
1620
#define MEMFAULT_DEBUG_LOG_BUFFER_SIZE_BYTES \
1721
(sizeof("2024-11-27T14:19:29Z|123456780 I ") + MEMFAULT_DATA_EXPORT_BASE64_CHUNK_MAX_LEN)
@@ -82,6 +86,7 @@ void memfault_platform_log_raw(const char *fmt, ...) {
8286
}
8387

8488
bool memfault_platform_time_get_current(sMemfaultCurrentTime *time_output) {
89+
#if MEMFAULT_EXAMPLE_USE_REAL_TIME
8590
// Get time from time.h
8691

8792
// Debug: print time fields
@@ -105,6 +110,10 @@ bool memfault_platform_time_get_current(sMemfaultCurrentTime *time_output) {
105110
},
106111
};
107112
return true;
113+
#else
114+
(void)time_output;
115+
return false;
116+
#endif
108117
}
109118

110119
void memfault_platform_reboot_tracking_boot(void) {

examples/freertos/src/metrics.c

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
#define DEBUG_PRINTF(fmt, ...)
2525
#endif
2626

27+
#if !defined(MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE)
28+
#define MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE 1
29+
#endif
30+
2731
static StackType_t metrics_task_stack[EXAMPLE_TASK_STACKS];
2832
static StaticTask_t metrics_task_tcb;
2933

@@ -97,27 +101,39 @@ static void prv_collect_libc_heap_usage_metrics(void) {
97101
MEMFAULT_METRIC_SET_UNSIGNED(memory_pct_max, heap_pct);
98102
}
99103

104+
#if MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE
100105
static void prv_daily_heartbeat(void) {
101-
// This function is called once a day to stop + start a "daily_heartbeat"
102-
// session.
106+
// Generate a daily_heartbeat session report once a day, based on
107+
// MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS. This demonstrates a simple way to
108+
// track metrics over a 24 hour interval.
109+
MEMFAULT_STATIC_ASSERT((24 * 60 * 60) % MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS == 0,
110+
"Heartbeat interval must be an even divisor of a day");
111+
// For testing, set MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS to a low value (1 second), and
112+
// uncomment the below line
113+
// #define DAILY_HEARTBEAT_INTERVAL_COUNT (24)
114+
#define DAILY_HEARTBEAT_INTERVAL_COUNT ((24 * 60 * 60) / MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS)
115+
static uint64_t s_last_daily_heartbeat_interval = 0;
103116

104-
// The first time this is called on boot will be a no-op, since the session
105-
// has not been started yet
106-
uint32_t uptime_s = memfault_platform_get_time_since_boot_ms() / 1000;
117+
if (++s_last_daily_heartbeat_interval % DAILY_HEARTBEAT_INTERVAL_COUNT == 0) {
118+
// The first time this is called on boot will be a no-op, since the session
119+
// has not been started yet
120+
uint32_t uptime_s = memfault_platform_get_time_since_boot_ms() / 1000;
107121

108-
if (uptime_s > MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) {
109-
MEMFAULT_LOG_INFO("📆 Triggering daily heartbeat");
110-
}
122+
if (uptime_s > MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) {
123+
MEMFAULT_LOG_INFO("📆 Triggering daily heartbeat");
124+
}
111125

112-
// Record a sample metric into the daily session
113-
MEMFAULT_METRIC_SESSION_SET_UNSIGNED(uptime_s, daily_heartbeat, uptime_s);
126+
// Record a sample metric into the daily session
127+
MEMFAULT_METRIC_SESSION_SET_UNSIGNED(uptime_s, daily_heartbeat, uptime_s);
114128

115-
// End the session
116-
MEMFAULT_METRICS_SESSION_END(daily_heartbeat);
129+
// End the session
130+
MEMFAULT_METRICS_SESSION_END(daily_heartbeat);
117131

118-
// Start a new session for the next daily interval
119-
MEMFAULT_METRICS_SESSION_START(daily_heartbeat);
132+
// Start a new session for the next daily interval
133+
MEMFAULT_METRICS_SESSION_START(daily_heartbeat);
134+
}
120135
}
136+
#endif // MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE
121137

122138
void memfault_metrics_heartbeat_collect_data(void) {
123139
MEMFAULT_LOG_INFO("💓 Heartbeat callback triggered");
@@ -131,19 +147,9 @@ void memfault_metrics_heartbeat_collect_data(void) {
131147
#endif
132148
prv_collect_libc_heap_usage_metrics();
133149

134-
// Call prv_daily_heartbeat() once a day, based on MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS
135-
// This is a simple way to track daily metrics
136-
MEMFAULT_STATIC_ASSERT((24 * 60 * 60) % MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS == 0,
137-
"Heartbeat interval must be an even divisor of a day");
138-
// For testing, set MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS to a low value (1 second), and
139-
// uncomment the below line
140-
// #define DAILY_HEARTBEAT_INTERVAL_COUNT (24)
141-
#define DAILY_HEARTBEAT_INTERVAL_COUNT ((24 * 60 * 60) / MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS)
142-
static uint64_t s_last_daily_heartbeat_interval = 0;
143-
144-
if (++s_last_daily_heartbeat_interval % DAILY_HEARTBEAT_INTERVAL_COUNT == 0) {
145-
prv_daily_heartbeat();
146-
}
150+
#if MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE
151+
prv_daily_heartbeat();
152+
#endif
147153

148154
// For demonstration purposes, print the current values. This is not
149155
// recommended for production.

0 commit comments

Comments
 (0)