@@ -91,7 +91,7 @@ static MemfaultMetricsSessionEndCb s_session_end_cbs[] = {
91
91
max_value , session_key )
92
92
#define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION (key_name , value_type , session_key )
93
93
#define MEMFAULT_METRICS_SESSION_KEY_DEFINE (key_name ) \
94
- MEMFAULT_METRICS_KEY_WITH_SESSION (MemfaultSdkMetric_IntervalMs, key_name),
94
+ _MEMFAULT_METRICS_ID_CREATE (MemfaultSdkMetric_IntervalMs, key_name),
95
95
#define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE (key_name , value_type , scale_value )
96
96
#define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE (key_name , value_type , \
97
97
session_key , scale_value )
@@ -111,6 +111,44 @@ static MemfaultMetricId s_memfault_metrics_session_timer_keys[] = {
111
111
{ 0 } // dummy entry to prevent empty array
112
112
};
113
113
114
+ #if MEMFAULT_METRICS_SESSIONS_ENABLED
115
+ // Generate session key to operational_crashes metric key mapping
116
+ #define MEMFAULT_METRICS_KEY_DEFINE (key_name , value_type )
117
+ #define MEMFAULT_METRICS_STRING_KEY_DEFINE (key_name , max_length )
118
+ #define MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION (key_name , max_length , session_key )
119
+ #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE (key_name , value_type , min_value , max_value )
120
+ #define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION (key_name , value_type , min_value , \
121
+ max_value , session_key )
122
+ #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION (key_name , value_type , session_key )
123
+ #define MEMFAULT_METRICS_SESSION_KEY_DEFINE (session_name ) \
124
+ _MEMFAULT_METRICS_ID_CREATE(operational_crashes, session_name),
125
+ #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE (key_name , value_type , scale_value )
126
+ #define MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE (key_name , value_type , \
127
+ session_key , scale_value )
128
+
129
+ static MemfaultMetricId s_memfault_metrics_operational_crashes_keys [] = {
130
+ #include "memfault/metrics/heartbeat_config.def"
131
+ #include MEMFAULT_METRICS_USER_HEARTBEAT_DEFS_FILE
132
+ #undef MEMFAULT_METRICS_KEY_DEFINE
133
+ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE
134
+ #undef MEMFAULT_METRICS_STRING_KEY_DEFINE
135
+ #undef MEMFAULT_METRICS_STRING_KEY_DEFINE_WITH_SESSION
136
+ #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE
137
+ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION
138
+ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION
139
+ #undef MEMFAULT_METRICS_SESSION_KEY_DEFINE_
140
+ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE
141
+ #undef MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION_AND_SCALE_VALUE
142
+ { 0 } // dummy entry to prevent empty array
143
+ };
144
+
145
+ // Active sessions are tracked in a single 32-bit word in the reboot tracking
146
+ // data. This limits the maximum sessions to 32.
147
+ MEMFAULT_STATIC_ASSERT (MEMFAULT_ARRAY_SIZE (s_memfault_metrics_operational_crashes_keys ) <= 32 ,
148
+ "Too many sessions defined. Max allowed defined sessions is 32." );
149
+
150
+ #endif
151
+
114
152
typedef struct MemfaultMetricKVPair {
115
153
MemfaultMetricId key ;
116
154
eMemfaultMetricType type ;
@@ -166,9 +204,11 @@ typedef struct MemfaultMetricKVPair {
166
204
MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION(key_name, kMemfaultMetricType_String, 0, \
167
205
max_length, session_key)
168
206
169
- #define MEMFAULT_METRICS_SESSION_KEY_DEFINE (session_name ) \
170
- MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(MemfaultSdkMetric_IntervalMs, \
171
- kMemfaultMetricType_Timer, session_name)
207
+ #define MEMFAULT_METRICS_SESSION_KEY_DEFINE (session_name ) \
208
+ MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(MemfaultSdkMetric_IntervalMs, \
209
+ kMemfaultMetricType_Timer, session_name) \
210
+ MEMFAULT_METRICS_KEY_DEFINE_WITH_SESSION(operational_crashes, kMemfaultMetricType_Unsigned, \
211
+ session_name)
172
212
173
213
#define MEMFAULT_METRICS_KEY_DEFINE_WITH_SCALE_VALUE (key_name , value_type , scale_value ) \
174
214
MEMFAULT_KV_PAIR_ENTRY(key_name, value_type, 0, 0, \
@@ -209,7 +249,8 @@ static const sMemfaultMetricKVPair s_memfault_heartbeat_keys[] = {
209
249
210
250
#define MEMFAULT_METRICS_SESSION_KEY_DEFINE (session_name ) \
211
251
MEMFAULT_METRICS_KEY_DEFINE(session_name##__##MemfaultSdkMetric_IntervalMs, \
212
- kMemfaultMetricType_Unsigned)
252
+ kMemfaultMetricType_Timer) \
253
+ MEMFAULT_METRICS_KEY_DEFINE(session_name##__##operational_crashes, kMemfaultMetricType_Unsigned)
213
254
214
255
#define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION (key_name , value_type , min_value , \
215
256
max_value , session_key ) \
@@ -262,8 +303,11 @@ static const sMemfaultMetricKVPair s_memfault_heartbeat_keys[] = {
262
303
263
304
//! Sessions have the following built-in keys:
264
305
//! - "<session name>__MemfaultSdkMetric_IntervalMs"
265
- #define MEMFAULT_METRICS_SESSION_KEY_DEFINE (key_name ) \
266
- MEMFAULT_METRICS_KEY_DEFINE(key_name##__##MemfaultSdkMetric_IntervalMs, kMemfaultMetricType_Timer)
306
+ //! - "<session name>__operational_crashes"
307
+ #define MEMFAULT_METRICS_SESSION_KEY_DEFINE (key_name ) \
308
+ MEMFAULT_METRICS_KEY_DEFINE(key_name##__##MemfaultSdkMetric_IntervalMs, \
309
+ kMemfaultMetricType_Timer) \
310
+ MEMFAULT_METRICS_KEY_DEFINE(key_name##__##operational_crashes, kMemfaultMetricType_Unsigned)
267
311
268
312
#define MEMFAULT_METRICS_KEY_DEFINE_WITH_RANGE_AND_SESSION (key_name , value_type , min_value , \
269
313
max_value , session_key ) \
@@ -1006,16 +1050,21 @@ int memfault_metrics_heartbeat_read_string(MemfaultMetricId key, char *read_val,
1006
1050
return rv ;
1007
1051
}
1008
1052
1009
- int memfault_metrics_session_start (eMfltMetricsSessionIndex session_key ) {
1010
- int rv ;
1053
+ static int prv_metrics_session_start (eMfltMetricsSessionIndex session_key , bool start_timer ) {
1054
+ int rv = 0 ;
1011
1055
memfault_lock ();
1012
1056
{
1013
1057
// Reset all metrics for the session. Any changes that happened before the
1014
1058
// session was started don't matter and can be discarded.
1015
1059
prv_reset_metrics (false, session_key );
1016
1060
1017
- MemfaultMetricId key = s_memfault_metrics_session_timer_keys [session_key ];
1018
- rv = prv_find_timer_metric_and_update (key , kMemfaultTimerOp_Start );
1061
+ if (start_timer ) {
1062
+ MemfaultMetricId key = s_memfault_metrics_session_timer_keys [session_key ];
1063
+ rv = prv_find_timer_metric_and_update (key , kMemfaultTimerOp_Start );
1064
+ }
1065
+
1066
+ // Mark the session as active for tracking operational_crashes
1067
+ memfault_reboot_tracking_metrics_session (true, session_key );
1019
1068
}
1020
1069
memfault_unlock ();
1021
1070
@@ -1027,17 +1076,23 @@ int memfault_metrics_session_start(eMfltMetricsSessionIndex session_key) {
1027
1076
return rv ;
1028
1077
}
1029
1078
1030
- int memfault_metrics_session_end (eMfltMetricsSessionIndex session_key ) {
1079
+ int memfault_metrics_session_start (eMfltMetricsSessionIndex session_key ) {
1080
+ return prv_metrics_session_start (session_key , true);
1081
+ }
1082
+
1083
+ static int prv_metrics_session_end (eMfltMetricsSessionIndex session_key , bool stop_timer ) {
1031
1084
MemfaultMetricsSessionEndCb session_end_cb = s_session_end_cbs [session_key ];
1032
1085
if (session_end_cb != NULL ) {
1033
1086
session_end_cb ();
1034
1087
}
1035
1088
1036
- int rv ;
1089
+ int rv = 0 ;
1037
1090
memfault_lock ();
1038
1091
{
1039
- MemfaultMetricId key = s_memfault_metrics_session_timer_keys [session_key ];
1040
- rv = prv_find_timer_metric_and_update (key , kMemfaultTimerOp_Stop );
1092
+ if (stop_timer ) {
1093
+ MemfaultMetricId key = s_memfault_metrics_session_timer_keys [session_key ];
1094
+ rv = prv_find_timer_metric_and_update (key , kMemfaultTimerOp_Stop );
1095
+ }
1041
1096
1042
1097
if (rv == 0 ) {
1043
1098
bool serialize_result =
@@ -1046,12 +1101,18 @@ int memfault_metrics_session_end(eMfltMetricsSessionIndex session_key) {
1046
1101
rv = MEMFAULT_METRICS_STORAGE_TOO_SMALL ;
1047
1102
}
1048
1103
}
1104
+ // Mark the session as inactive for tracking operational_crashes
1105
+ memfault_reboot_tracking_metrics_session (false, session_key );
1049
1106
}
1050
1107
memfault_unlock ();
1051
1108
1052
1109
return rv ;
1053
1110
}
1054
1111
1112
+ int memfault_metrics_session_end (eMfltMetricsSessionIndex session_key ) {
1113
+ return prv_metrics_session_end (session_key , true);
1114
+ }
1115
+
1055
1116
void memfault_metrics_session_register_start_cb (eMfltMetricsSessionIndex session_key ,
1056
1117
MemfaultMetricsSessionStartCb session_start_cb ) {
1057
1118
memfault_lock ();
@@ -1231,6 +1292,55 @@ void memfault_metrics_heartbeat_debug_trigger(void) {
1231
1292
prv_heartbeat_timer ();
1232
1293
}
1233
1294
1295
+ #if MEMFAULT_METRICS_SESSIONS_ENABLED
1296
+ //! Called on boot, this function checks if the reboot was unexpected. If so,
1297
+ //! any session that was active at time of reboot is triggered to record an
1298
+ //! `operational_crash=1` metric, and serialized to storage.
1299
+ static void prv_session_check_for_unexpected_reboot (void ) {
1300
+ int rv = -1 ;
1301
+ bool unexpected_reboot ;
1302
+
1303
+ for (eMfltMetricsSessionIndex session_key = (eMfltMetricsSessionIndex )0 ;
1304
+ session_key < (MEMFAULT_ARRAY_SIZE (s_memfault_metrics_operational_crashes_keys ));
1305
+ session_key ++ ) {
1306
+ // This table always ends with a blank entry, so we can stop one iteration
1307
+ // early (or if no sessions were defined). Some compilers warn on empty
1308
+ // arrays.
1309
+ if (session_key == MEMFAULT_ARRAY_SIZE (s_memfault_metrics_operational_crashes_keys ) - 1 ) {
1310
+ break ;
1311
+ }
1312
+
1313
+ // Only run the check for unexpected reboot once, if it hasn't been already
1314
+ // run in this function. Ideally this check would be outside the for loop,
1315
+ // but since we can't conditionally compile this whole block based on if
1316
+ // any sessions are defined, we have to do it this way.
1317
+ if (rv != 0 ) {
1318
+ rv = memfault_reboot_tracking_get_unexpected_reboot_occurred (& unexpected_reboot );
1319
+ if ((rv != 0 ) || !unexpected_reboot ) {
1320
+ break ;
1321
+ }
1322
+ }
1323
+
1324
+ // If the session was active at time of reboot, serialize a session with the
1325
+ // duration_ms=0 and operational_crashes=1.
1326
+ if (memfault_reboot_tracking_metrics_session_was_active (session_key )) {
1327
+ // Do not start the session timer, to keep the session duration = 0ms
1328
+ prv_metrics_session_start (session_key , false);
1329
+
1330
+ memfault_metrics_heartbeat_add (s_memfault_metrics_operational_crashes_keys [session_key ], 1 );
1331
+ // Note: the below function clears the reboot tracking bit for this session,
1332
+ // since the session is now inactive. A second crash after this won't be
1333
+ // recorded as an operational_crash for the session (until the session is
1334
+ // activated).
1335
+ prv_metrics_session_end (session_key , false);
1336
+ }
1337
+ }
1338
+
1339
+ // Unconditionally clear the reboot tracking data for "active sessions".
1340
+ memfault_reboot_tracking_clear_metrics_sessions ();
1341
+ }
1342
+ #endif
1343
+
1234
1344
int memfault_metrics_boot (const sMemfaultEventStorageImpl * storage_impl ,
1235
1345
const sMemfaultMetricBootInfo * info ) {
1236
1346
if (storage_impl == NULL || info == NULL ) {
@@ -1262,6 +1372,10 @@ int memfault_metrics_boot(const sMemfaultEventStorageImpl *storage_impl,
1262
1372
return rv ;
1263
1373
}
1264
1374
1375
+ #if MEMFAULT_METRICS_SESSIONS_ENABLED
1376
+ prv_session_check_for_unexpected_reboot ();
1377
+ #endif
1378
+
1265
1379
#if MEMFAULT_PLATFORM_METRICS_CONNECTIVITY_BOOT
1266
1380
memfault_platform_metrics_connectivity_boot ();
1267
1381
#endif
0 commit comments