@@ -38,6 +38,7 @@ MEMFAULT_DISABLE_WARNING("-Wunused-macros")
38
38
#define MEMFAULT_METRICS_TYPE_NO_CHANGE (-4)
39
39
#define MEMFAULT_METRICS_STORAGE_TOO_SMALL (-5)
40
40
#define MEMFAULT_METRICS_TIMER_BOOT_FAILED (-6)
41
+ #define MEMFAULT_METRICS_VALUE_NOT_SET (-7)
41
42
42
43
typedef struct MemfaultMetricKVPair {
43
44
MemfaultMetricId key ;
@@ -90,7 +91,7 @@ MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_memfault_heartbeat_keys) != 0,
90
91
91
92
#define MEMFAULT_METRICS_TIMER_VAL_MAX 0x80000000
92
93
typedef struct MemfaultMetricValueMetadata {
93
- bool is_running : 1 ;
94
+ bool is_running : 1 ;
94
95
// We'll use 32 bits since the rollover time is ~25 days which is much much greater than a
95
96
// reasonable heartbeat interval. This let's us track whether or not the timer is running in the
96
97
// top bit
@@ -100,6 +101,7 @@ typedef struct MemfaultMetricValueMetadata {
100
101
typedef struct MemfaultMetricValueInfo {
101
102
union MemfaultMetricValue * valuep ;
102
103
sMemfaultMetricValueMetadata * meta_datap ;
104
+ bool is_set ;
103
105
} sMemfaultMetricValueInfo ;
104
106
105
107
@@ -180,6 +182,21 @@ static union MemfaultMetricValue s_memfault_heartbeat_values[] = {
180
182
#undef MEMFAULT_METRICS_STRING_KEY_DEFINE
181
183
};
182
184
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
+
183
200
// String value lookup table. Const- the pointers do not change at runtime, so
184
201
// this table can be stored in ROM and save a little RAM.
185
202
#define MEMFAULT_METRICS_KEY_DEFINE (key_name , value_type )
@@ -250,6 +267,12 @@ static const int s_metric_timer_metadata_mapping[] = {
250
267
#undef MEMFAULT_METRICS_STRING_KEY_DEFINE
251
268
};
252
269
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
+
253
276
static struct {
254
277
const sMemfaultEventStorageImpl * storage_impl ;
255
278
} s_memfault_metrics_ctx ;
@@ -280,16 +303,36 @@ static sMemfaultMetricValueMetadata *prv_find_timer_metadatap(eMfltMetricsIndex
280
303
return & s_memfault_heartbeat_timer_values_metadata [timer_index ];
281
304
}
282
305
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 ,
284
327
sMemfaultMetricValueInfo * value_info_out ) {
285
- const size_t idx = ( size_t ) key . _impl ;
328
+ const size_t idx = MEMFAULT_METRICS_ID_TO_KEY ( id ) ;
286
329
if (idx >= MEMFAULT_ARRAY_SIZE (s_memfault_heartbeat_keys )) {
287
330
* value_info_out = (sMemfaultMetricValueInfo ){0 };
288
331
return kMemfaultMetricType_NumTypes ;
289
332
}
290
333
291
334
// 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 ) ;
293
336
// for scalar types, this will be the returned value pointer. non-scalars
294
337
// will be handled in the switch below
295
338
union MemfaultMetricValue * value_ptr = & s_memfault_heartbeat_values [key_index ];
@@ -321,6 +364,10 @@ static eMemfaultMetricType prv_find_value_for_key(MemfaultMetricId key,
321
364
.meta_datap = prv_find_timer_metadatap ((eMfltMetricsIndex )idx ),
322
365
};
323
366
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
+
324
371
return key_type ;
325
372
}
326
373
@@ -374,6 +421,7 @@ static int prv_find_and_set_value_for_key(
374
421
}
375
422
376
423
* value_info .valuep = * new_value ;
424
+ prv_read_write_is_value_set (key , true);
377
425
return 0 ;
378
426
}
379
427
@@ -516,6 +564,7 @@ static bool prv_tally_and_update_timer_cb(MEMFAULT_UNUSED void *ctx,
516
564
static void prv_reset_metrics (void ) {
517
565
// reset all scalar metric values
518
566
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 ));
519
568
520
569
// reset all string metric values. -1 to skip the last, stub entry in the
521
570
// table
@@ -525,6 +574,13 @@ static void prv_reset_metrics(void) {
525
574
((char * )s_memfault_heartbeat_string_values [i ].ptr )[0 ] = 0 ;
526
575
}
527
576
}
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 );
528
584
}
529
585
530
586
static void prv_heartbeat_timer_update (void ) {
@@ -608,6 +664,11 @@ static int prv_find_key_of_type(MemfaultMetricId key, eMemfaultMetricType expect
608
664
if (type != expected_type ) {
609
665
return MEMFAULT_METRICS_TYPE_INCOMPATIBLE ;
610
666
}
667
+ if ((type == kMemfaultMetricType_Signed || type == kMemfaultMetricType_Unsigned ) &&
668
+ !(value_info .is_set )) {
669
+ return MEMFAULT_METRICS_VALUE_NOT_SET ;
670
+ }
671
+
611
672
* value_out = value_info .valuep ;
612
673
return 0 ;
613
674
}
@@ -707,7 +768,8 @@ static bool prv_metrics_heartbeat_iterate_cb(void *ctx,
707
768
sMemfaultMetricInfo info = {
708
769
.key = key_info -> key ,
709
770
.type = key_info -> type ,
710
- .val = * value_info -> valuep
771
+ .val = * value_info -> valuep ,
772
+ .is_set = value_info -> is_set ,
711
773
};
712
774
return ctx_info -> user_cb (ctx_info -> user_ctx , & info );
713
775
}
@@ -748,12 +810,22 @@ static bool prv_heartbeat_debug_print(MEMFAULT_UNUSED void *ctx,
748
810
const char * key_name = s_idx_to_metric_name [key -> _impl ];
749
811
750
812
switch (metric_info -> type ) {
751
- case kMemfaultMetricType_Unsigned :
752
813
case kMemfaultMetricType_Timer :
753
814
MEMFAULT_LOG_DEBUG (" %s: %" PRIu32 , key_name , value -> u32 );
754
815
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 ;
755
823
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
+ }
757
829
break ;
758
830
case kMemfaultMetricType_String :
759
831
MEMFAULT_LOG_DEBUG (" %s: \"%s\"" , key_name , (const char * )value -> ptr );
0 commit comments