Skip to content

Commit 0d7e773

Browse files
author
Memfault Inc
committed
Memfault Firmware SDK 0.42.0 (Build 1778)
1 parent a04d930 commit 0d7e773

35 files changed

+437
-106
lines changed

CHANGES.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
1+
### Changes between Memfault SDK 0.42.0 and 0.41.2 - Mar 22, 2023
2+
3+
#### :chart_with_upwards_trend: Improvements
4+
5+
- Zephyr:
6+
7+
- Add option to capture full thread stacks for classifying stack overflows and
8+
determining stack high watermarks. This feature is enabled by setting
9+
`CONFIG_MEMFAULT_COREDUMP_FULL_THREAD_STACKS=y`
10+
- Remove usage of the `zephyr.h` header in preparation for Zephyr v3.4.0
11+
12+
- `memfault_gdb.py`:
13+
- Add support for exporting data from GCC 12 compiled symbol files
14+
- Add arguments to override device serial ID, software type, software version,
15+
and hardware revision
16+
17+
#### :boom: Breaking Changes
18+
19+
- Metrics:
20+
- Integer type metrics (signed/unsigned) will reset to NULL when not set
21+
during a heartbeat interval. This NULL value will be discarded by Memfault
22+
when received. The previous behavior was to reset to 0 which makes
23+
discarding values difficult as 0 is a valid value for these types. For more
24+
info please see the
25+
[Metrics](https://docs.memfault.com/docs/mcu/metrics-api#setting-metric-values)
26+
docs.
27+
128
### Changes between Memfault SDK 0.41.2 and SDK 0.41.1 - Mar 10, 2023
229

330
#### :chart_with_upwards_trend: Improvements

VERSION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
BUILD ID: 1696
2-
GIT COMMIT: e44b190c9
1+
BUILD ID: 1778
2+
GIT COMMIT: 539b8ab42

components/include/memfault/core/compiler_gcc.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ extern "C" {
2626
#if defined(__clang__)
2727
#define MEMFAULT_NO_OPT __attribute__((optnone))
2828
#else
29-
#define MEMFAULT_NO_OPT __attribute__((optimize("O0")))
29+
// GCC 12 changed to no longer enable the var-tracking debug option when
30+
// compiling without optimizations. Explicitly enable it so we always get a nice
31+
// variable loading experience in GDB, even when the function prologue hasn't
32+
// run yet. This is required when using the memfault_gdb.py hook to upload data.
33+
#define MEMFAULT_NO_OPT __attribute__((optimize("O0", "var-tracking", "var-tracking-assignments")))
3034
#endif
3135

3236
#define MEMFAULT_ALIGNED(x) __attribute__((aligned(x)))

components/include/memfault/core/math.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extern "C" {
1818
#define MEMFAULT_MAX(a , b) (((a) > (b)) ? (a) : (b))
1919
#define MEMFAULT_FLOOR(a, align) (((a) / (align)) * (align))
2020
#define MEMFAULT_ABS(a) (((a) < 0) ? -(a) : (a))
21+
#define MEMFAULT_CEIL_DIV(x, y) (((x) + (y)-1) / (y))
2122

2223
#ifdef __cplusplus
2324
}

components/include/memfault/metrics/metrics.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,10 @@ typedef struct MemfaultMetricsBootInfo {
116116
} sMemfaultMetricBootInfo;
117117

118118
//! Initializes the metric events API.
119-
//! All heartbeat values will be initialized to 0.
119+
//! All heartbeat values will be initialized to their reset values.
120+
//! Integer types will be reset to unset/null.
121+
//! Timer metrics will be reset to 0.
122+
//! String metrics will be reset to empty strings.
120123
//! @param storage_impl The storage location to serialize metrics out to
121124
//! @param boot_info Info added to metrics to facilitate computing aggregate statistics in
122125
//! the Memfault cloud
@@ -126,6 +129,9 @@ int memfault_metrics_boot(const sMemfaultEventStorageImpl *storage_impl,
126129
const sMemfaultMetricBootInfo *boot_info);
127130

128131
//! Set the value of a signed integer metric.
132+
//!
133+
//! Integer metrics that are unset during a heartbeat interval
134+
//! are sent as null and dropped when received.
129135
//! @param key The key of the metric. @see MEMFAULT_METRICS_KEY
130136
//! @param value The new value to set for the metric
131137
//! @return 0 on success, else error code
@@ -162,7 +168,7 @@ int memfault_metrics_heartbeat_timer_stop(MemfaultMetricId key);
162168
//! @param key The key of the metric. @see MEMFAULT_METRICS_KEY
163169
//! @param inc The amount to increment the metric by
164170
//! @return 0 on success, else error code
165-
//! @note The metric must be of type kMemfaultMetricType_Counter
171+
//! @note The metric must be of type kMemfaultMetricType_Unsigned or kMemfaultMetricType_Signed
166172
int memfault_metrics_heartbeat_add(MemfaultMetricId key, int32_t amount);
167173

168174
//! For debugging purposes: prints the current heartbeat values using

components/include/memfault/metrics/utils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
//! @note A user of the Memfault SDK should _never_ call any
1212
//! of these routines directly
1313

14+
#include <stdbool.h>
15+
1416
#include "memfault/metrics/metrics.h"
1517

1618
#ifdef __cplusplus
@@ -26,6 +28,7 @@ union MemfaultMetricValue {
2628
typedef struct {
2729
MemfaultMetricId key;
2830
eMemfaultMetricType type;
31+
bool is_set;
2932
union MemfaultMetricValue val;
3033
} sMemfaultMetricInfo;
3134

components/include/memfault/util/cbor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ bool memfault_cbor_encode_uint64_as_double(sMemfaultCborEncoder *encoder, uint64
155155
//! @return true on success, false otherwise
156156
bool memfault_cbor_encode_long_signed_integer(sMemfaultCborEncoder *encoder, int64_t value);
157157

158+
//! Encode a CBOR null value
159+
//!
160+
//! @param encoder The encoder context to use
161+
//! @return true on success, false otherwise
162+
bool memfault_cbor_encode_null(sMemfaultCborEncoder *encoder);
158163

159164
//! NOTE: For internal use only, included in the header so it's easy for a caller to statically
160165
//! allocate the structure

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 = 41, .patch = 2 }
22+
#define MEMFAULT_SDK_VERSION { .major = 0, .minor = 42, .patch = 0 }
2323

2424
#ifdef __cplusplus
2525
}

components/metrics/src/memfault_metrics.c

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ MEMFAULT_DISABLE_WARNING("-Wunused-macros")
3838
#define MEMFAULT_METRICS_TYPE_NO_CHANGE (-4)
3939
#define MEMFAULT_METRICS_STORAGE_TOO_SMALL (-5)
4040
#define MEMFAULT_METRICS_TIMER_BOOT_FAILED (-6)
41+
#define MEMFAULT_METRICS_VALUE_NOT_SET (-7)
4142

4243
typedef struct MemfaultMetricKVPair {
4344
MemfaultMetricId key;
@@ -90,7 +91,7 @@ MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys) != 0,
9091

9192
#define MEMFAULT_METRICS_TIMER_VAL_MAX 0x80000000
9293
typedef struct MemfaultMetricValueMetadata {
93-
bool is_running:1;
94+
bool is_running : 1;
9495
// We'll use 32 bits since the rollover time is ~25 days which is much much greater than a
9596
// reasonable heartbeat interval. This let's us track whether or not the timer is running in the
9697
// top bit
@@ -100,6 +101,7 @@ typedef struct MemfaultMetricValueMetadata {
100101
typedef struct MemfaultMetricValueInfo {
101102
union MemfaultMetricValue *valuep;
102103
sMemfaultMetricValueMetadata *meta_datap;
104+
bool is_set;
103105
} sMemfaultMetricValueInfo;
104106

105107

@@ -180,6 +182,21 @@ static union MemfaultMetricValue s_memfault_heartbeat_values[] = {
180182
#undef MEMFAULT_METRICS_STRING_KEY_DEFINE
181183
};
182184

185+
// Value Set flag data structures and definitions
186+
// MEMFAULT_IS_SET_FLAGS_PER_BYTE must be a power of 2
187+
// MEMFAULT_IS_SET_FLAGS_DIVIDER must be equal to log2(MEMFAULT_IS_SET_FLAGS_PER_BYTE)
188+
#define MEMFAULT_IS_SET_FLAGS_PER_BYTE 8
189+
#define MEMFAULT_IS_SET_FLAGS_DIVIDER 3
190+
191+
// Create a byte array to contain an is-set flag for each entry in s_memfault_heartbeat_values
192+
static uint8_t s_memfault_heatbeat_value_is_set_flags[MEMFAULT_CEIL_DIV(
193+
MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_values), MEMFAULT_IS_SET_FLAGS_PER_BYTE)];
194+
195+
MEMFAULT_STATIC_ASSERT(
196+
MEMFAULT_ARRAY_SIZE(s_memfault_heatbeat_value_is_set_flags) >=
197+
(MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_values) / MEMFAULT_IS_SET_FLAGS_PER_BYTE),
198+
"Mismatch between s_memfault_heatbeat_value_is_set_flags and s_memfault_heartbeat_values");
199+
183200
// String value lookup table. Const- the pointers do not change at runtime, so
184201
// this table can be stored in ROM and save a little RAM.
185202
#define MEMFAULT_METRICS_KEY_DEFINE(key_name, value_type)
@@ -250,6 +267,12 @@ static const int s_metric_timer_metadata_mapping[] = {
250267
#undef MEMFAULT_METRICS_STRING_KEY_DEFINE
251268
};
252269

270+
// Helper macros to convert between the various metrics indices
271+
#define MEMFAULT_METRICS_ID_TO_KEY(id) ((size_t)(id)._impl)
272+
#define MEMFAULT_METRICS_KEY_TO_KV_INDEX(key) (s_memfault_heartbeat_key_to_valueindex[(key)])
273+
#define MEMFAULT_METRICS_ID_TO_KV_INDEX(id) \
274+
(MEMFAULT_METRICS_KEY_TO_KV_INDEX(MEMFAULT_METRICS_ID_TO_KEY(id)))
275+
253276
static struct {
254277
const sMemfaultEventStorageImpl *storage_impl;
255278
} s_memfault_metrics_ctx;
@@ -280,16 +303,36 @@ static sMemfaultMetricValueMetadata *prv_find_timer_metadatap(eMfltMetricsIndex
280303
return &s_memfault_heartbeat_timer_values_metadata[timer_index];
281304
}
282305

283-
static eMemfaultMetricType prv_find_value_for_key(MemfaultMetricId key,
306+
//! Helper function to read/write is_set bits for the provided metric
307+
//!
308+
//! @param id Metric ID to select corresponding is_set field
309+
//! @param write Boolean to control whether to write 1 to is_set
310+
//! @return Returns the value of metric's is_set field. The updated value is returned if write =
311+
//! true
312+
static bool prv_read_write_is_value_set(MemfaultMetricId id, bool write) {
313+
// Shift the kv index by MEMFAULT_IS_SET_FLAGS_DIVIDER to select byte within
314+
// s_memfault_heartbeat_value_is_set_flags
315+
size_t byte_index = MEMFAULT_METRICS_ID_TO_KV_INDEX(id) >> MEMFAULT_IS_SET_FLAGS_DIVIDER;
316+
// Modulo the kv index by MEMFAULT_IS_SET_FLAGS_PER_BYTE to get bit of the selected byte
317+
size_t bit_index = MEMFAULT_METRICS_ID_TO_KV_INDEX(id) % MEMFAULT_IS_SET_FLAGS_PER_BYTE;
318+
319+
if (write) {
320+
s_memfault_heatbeat_value_is_set_flags[byte_index] |= (1 << bit_index);
321+
}
322+
323+
return (s_memfault_heatbeat_value_is_set_flags[byte_index] >> bit_index) & 0x01;
324+
}
325+
326+
static eMemfaultMetricType prv_find_value_for_key(MemfaultMetricId id,
284327
sMemfaultMetricValueInfo *value_info_out) {
285-
const size_t idx = (size_t)key._impl;
328+
const size_t idx = MEMFAULT_METRICS_ID_TO_KEY(id);
286329
if (idx >= MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys)) {
287330
*value_info_out = (sMemfaultMetricValueInfo){0};
288331
return kMemfaultMetricType_NumTypes;
289332
}
290333

291334
// get the index for the value matching this key.
292-
eMfltMetricKeyToValueIndex key_index = s_memfault_heartbeat_key_to_valueindex[idx];
335+
eMfltMetricKeyToValueIndex key_index = MEMFAULT_METRICS_KEY_TO_KV_INDEX(idx);
293336
// for scalar types, this will be the returned value pointer. non-scalars
294337
// will be handled in the switch below
295338
union MemfaultMetricValue *value_ptr = &s_memfault_heartbeat_values[key_index];
@@ -321,6 +364,10 @@ static eMemfaultMetricType prv_find_value_for_key(MemfaultMetricId key,
321364
.meta_datap = prv_find_timer_metadatap((eMfltMetricsIndex)idx),
322365
};
323366

367+
if (key_type == kMemfaultMetricType_Unsigned || key_type == kMemfaultMetricType_Signed) {
368+
value_info_out->is_set = prv_read_write_is_value_set(id, false);
369+
}
370+
324371
return key_type;
325372
}
326373

@@ -374,6 +421,7 @@ static int prv_find_and_set_value_for_key(
374421
}
375422

376423
*value_info.valuep = *new_value;
424+
prv_read_write_is_value_set(key, true);
377425
return 0;
378426
}
379427

@@ -516,6 +564,7 @@ static bool prv_tally_and_update_timer_cb(MEMFAULT_UNUSED void *ctx,
516564
static void prv_reset_metrics(void) {
517565
// reset all scalar metric values
518566
memset(s_memfault_heartbeat_values, 0, sizeof(s_memfault_heartbeat_values));
567+
memset(s_memfault_heatbeat_value_is_set_flags, 0, sizeof(s_memfault_heatbeat_value_is_set_flags));
519568

520569
// reset all string metric values. -1 to skip the last, stub entry in the
521570
// table
@@ -525,6 +574,13 @@ static void prv_reset_metrics(void) {
525574
((char *)s_memfault_heartbeat_string_values[i].ptr)[0] = 0;
526575
}
527576
}
577+
578+
// Set MemfaultSdkMetric_UnexpectedRebootDidOccur to 0 at the end of each reset.
579+
// This must be explicitly set because we use 0 to track number of operational hours
580+
// Without setting to 0, this defaults to null and will not be counted in the sum
581+
// of total operational hours
582+
memfault_metrics_heartbeat_set_unsigned(
583+
MEMFAULT_METRICS_KEY(MemfaultSdkMetric_UnexpectedRebootDidOccur), 0);
528584
}
529585

530586
static void prv_heartbeat_timer_update(void) {
@@ -608,6 +664,11 @@ static int prv_find_key_of_type(MemfaultMetricId key, eMemfaultMetricType expect
608664
if (type != expected_type) {
609665
return MEMFAULT_METRICS_TYPE_INCOMPATIBLE;
610666
}
667+
if ((type == kMemfaultMetricType_Signed || type == kMemfaultMetricType_Unsigned) &&
668+
!(value_info.is_set)) {
669+
return MEMFAULT_METRICS_VALUE_NOT_SET;
670+
}
671+
611672
*value_out = value_info.valuep;
612673
return 0;
613674
}
@@ -707,7 +768,8 @@ static bool prv_metrics_heartbeat_iterate_cb(void *ctx,
707768
sMemfaultMetricInfo info = {
708769
.key = key_info->key,
709770
.type = key_info->type,
710-
.val = *value_info->valuep
771+
.val = *value_info->valuep,
772+
.is_set = value_info->is_set,
711773
};
712774
return ctx_info->user_cb(ctx_info->user_ctx, &info);
713775
}
@@ -748,12 +810,22 @@ static bool prv_heartbeat_debug_print(MEMFAULT_UNUSED void *ctx,
748810
const char *key_name = s_idx_to_metric_name[key->_impl];
749811

750812
switch (metric_info->type) {
751-
case kMemfaultMetricType_Unsigned:
752813
case kMemfaultMetricType_Timer:
753814
MEMFAULT_LOG_DEBUG(" %s: %" PRIu32, key_name, value->u32);
754815
break;
816+
case kMemfaultMetricType_Unsigned:
817+
if (metric_info->is_set) {
818+
MEMFAULT_LOG_DEBUG(" %s: %" PRIu32, key_name, value->u32);
819+
} else {
820+
MEMFAULT_LOG_DEBUG(" %s: null", key_name);
821+
}
822+
break;
755823
case kMemfaultMetricType_Signed:
756-
MEMFAULT_LOG_DEBUG(" %s: %" PRIi32, key_name, value->i32);
824+
if (metric_info->is_set) {
825+
MEMFAULT_LOG_DEBUG(" %s: %" PRIi32, key_name, value->i32);
826+
} else {
827+
MEMFAULT_LOG_DEBUG(" %s: null", key_name);
828+
}
757829
break;
758830
case kMemfaultMetricType_String:
759831
MEMFAULT_LOG_DEBUG(" %s: \"%s\"", key_name, (const char *)value->ptr);

components/metrics/src/memfault_metrics_serializer.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,41 @@ typedef struct {
3030
bool encode_success;
3131
} sMemfaultSerializerState;
3232

33+
static bool prv_metric_heartbeat_write_integer(sMemfaultSerializerState *state,
34+
sMemfaultCborEncoder *encoder,
35+
const sMemfaultMetricInfo *metric_info) {
36+
if (!metric_info->is_set && !state->compute_worst_case_size) {
37+
return memfault_cbor_encode_null(encoder);
38+
}
39+
40+
if (metric_info->type == kMemfaultMetricType_Unsigned) {
41+
const uint32_t value = state->compute_worst_case_size ? UINT32_MAX : metric_info->val.u32;
42+
return memfault_cbor_encode_unsigned_integer(encoder, value);
43+
}
44+
45+
if (metric_info->type == kMemfaultMetricType_Signed) {
46+
const int32_t value = state->compute_worst_case_size ? INT32_MIN : metric_info->val.i32;
47+
return memfault_cbor_encode_signed_integer(encoder, value);
48+
}
49+
50+
// Should be unreachable
51+
return false;
52+
}
53+
3354
static bool prv_metric_heartbeat_writer(void *ctx, const sMemfaultMetricInfo *metric_info) {
3455
sMemfaultSerializerState *state = (sMemfaultSerializerState *)ctx;
3556
sMemfaultCborEncoder *encoder = &state->encoder;
3657

3758
// encode the value
3859
switch (metric_info->type) {
39-
case kMemfaultMetricType_Timer:
40-
case kMemfaultMetricType_Unsigned: {
60+
case kMemfaultMetricType_Timer: {
4161
const uint32_t value = state->compute_worst_case_size ? UINT32_MAX : metric_info->val.u32;
4262
state->encode_success = memfault_cbor_encode_unsigned_integer(encoder, value);
4363
break;
4464
}
65+
case kMemfaultMetricType_Unsigned:
4566
case kMemfaultMetricType_Signed: {
46-
const int32_t value = state->compute_worst_case_size ? INT32_MIN : metric_info->val.i32;
47-
state->encode_success = memfault_cbor_encode_signed_integer(encoder, value);
67+
state->encode_success = prv_metric_heartbeat_write_integer(state, encoder, metric_info);
4868
break;
4969
}
5070
case kMemfaultMetricType_String: {

0 commit comments

Comments
 (0)