forked from memfault/memfault-firmware-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
log.h
188 lines (167 loc) · 7.88 KB
/
log.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#pragma once
//! @file
//!
//! Copyright (c) Memfault, Inc.
//! See License.txt for details
//!
//! @brief
//!
//! A lightweight set of log utilities which can be wrapped around pre-existing logging
//! infrastructure to capture events or errors that transpired leading up to an issue.
//! See https://mflt.io/logging for detailed integration steps.
//!
//! @note These utilities are already integrated into memfault/core/debug_log.h module. If your
//! project does not have a logging subsystem, see the notes in that header about how to leverage
//! the debug_log.h module for that!
//!
//! @note The thread-safety of the module depends on memfault_lock/unlock() API. If calls can be
//! made from multiple tasks, these APIs must be implemented. Locks are _only_ held while copying
//! data into the backing circular buffer so durations will be very quick.
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "memfault/config.h"
#include "memfault/core/compact_log_compile_time_checks.h"
#include "memfault/core/compact_log_helpers.h"
#include "memfault/core/compiler.h"
#include "memfault/core/platform/debug_log.h" // For eMemfaultPlatformLogLevel
#ifdef __cplusplus
extern "C" {
#endif
//! Must be called on boot to initialize the Memfault logging module
//!
//! @param buffer The ram buffer to save logs into
//! @param buf_len The length of the buffer. There's no length restriction but
//! the more space that is available, the longer the trail of breadcrumbs that will
//! be available upon crash
//!
//! @note Until this function is called, all other calls to the module will be no-ops
//! @return true if call was successful and false if the parameters were bad or
//! memfault_log_boot has already been called
bool memfault_log_boot(void *buffer, size_t buf_len);
//! Change the minimum level log saved to the circular buffer
//!
//! By default, any logs >= kMemfaultPlatformLogLevel_Info can be saved
void memfault_log_set_min_save_level(eMemfaultPlatformLogLevel min_log_level);
//! Macro which can be called from a platforms pre-existing logging macro.
//!
//! For example, if your platform already has something like this
//!
//! #define YOUR_PLATFORM_LOG_ERROR(...)
//! my_platform_log_error(__VA_ARGS__)
//!
//! the error data could be automatically recorded by making the
//! following modification
//!
//! #define YOUR_PLATFORM_LOG_ERROR(...)
//! do {
//! MEMFAULT_LOG_SAVE(kMemfaultPlatformLogLevel_Error, __VA_ARGS__);
//! your_platform_log_error(__VA_ARGS__)
//! } while (0)
#define MEMFAULT_LOG_SAVE(_level, ...) memfault_log_save(_level, __VA_ARGS__)
#if MEMFAULT_COMPACT_LOG_ENABLE
//! Same as MEMFAULT_LOG_SAVE except logs use Memfault's "compact" log strategy which offloads
//! formatting to the Memfault cloud to reduce on device codespace and cpu consumption. See
//! https://mflt.io/compact-logs for more details.
#define MEMFAULT_COMPACT_LOG_SAVE(level, format, ...) \
do { \
MEMFAULT_LOGGING_RUN_COMPILE_TIME_CHECKS(format, ## __VA_ARGS__); \
MEMFAULT_LOG_FMT_ELF_SECTION_ENTRY(format, ## __VA_ARGS__); \
memfault_compact_log_save(level, \
MEMFAULT_LOG_FMT_ELF_SECTION_ENTRY_PTR, \
MFLT_GET_COMPRESSED_LOG_FMT(__VA_ARGS__), \
## __VA_ARGS__); \
} while (0)
//! Serializes the provided compact log and saves it to backing storage
//!
//! @note: Should only be called via MEMFAULT_COMPACT_LOG_SAVE macro
void memfault_compact_log_save(eMemfaultPlatformLogLevel level, uint32_t log_id,
uint32_t compressed_fmt, ...);
#endif /* MEMFAULT_COMPACT_LOG_ENABLE */
//! Function which can be called to save a log after it has been formatted
//!
//! Typically a user should be able to use the MEMFAULT_LOG_SAVE macro but if your platform does
//! not have logging macros and you are just using newlib or dlib & printf, you could make
//! the following changes to the _write dependency function:
//!
//! int _write(int fd, char *ptr, int len) {
//! // ... other code such as printing to console ...
//! eMemfaultPlatformLogLevel level =
//! (fd == 2) ? kMemfaultPlatformLogLevel_Error : kMemfaultPlatformLogLevel_Info;
//! memfault_log_save_preformatted(level, ptr, len);
//! // ...
//! }
void memfault_log_save_preformatted(eMemfaultPlatformLogLevel level, const char *log,
size_t log_len);
typedef enum {
kMemfaultLogRecordType_Preformatted = 0,
kMemfaultLogRecordType_Compact = 1,
kMemfaultLogRecordType_NumTypes,
} eMemfaultLogRecordType;
typedef struct {
// the level of the message
eMemfaultPlatformLogLevel level;
// the log returned is a binary "compact log"
// See https://mflt.io/compact-logs for more details
eMemfaultLogRecordType type;
// the length of the msg (not including NUL character)
uint32_t msg_len;
// the message to print which will always be NUL terminated when a preformatted log is returned
// (so it is always safe to call printf without copying the log into another buffer yourself)
char msg[MEMFAULT_LOG_MAX_LINE_SAVE_LEN + 1 /* '\0' */];
} sMemfaultLog;
//! Returns the oldest unread log in memfault log storage
//!
//! @param log[out] When a new log is available, populated with its info
//! @return true if a log was found, false if there were no logs to read.
//!
//! @note For some timing sensitive applications, logs may be written into RAM and later dumped out
//! over UART and/or saved to flash on a lower priority background task. The memfault_log_read()
//! API is designed to be easy to utilize in these situations. For example:
//!
//! Any task:
//! Call MEMFAULT_SAVE_LOG() to quickly write a log into RAM.
//!
//! Optional: Anytime a new log is saved, memfault_log_handle_saved_callback is called by the
//! memfault log module. A platform can choose to implement something like:
//!
//! void memfault_log_handle_saved_callback(void) {
//! my_rtos_schedule_log_read()
//! }
//!
//! Task responsible for flushing logs out to slower mediums (UART, NOR/EMMC Flash, etc):
//! // .. RTOS code to wait for log read event ..
//! sMemfaultLog log = { 0 };
//! const bool log_found = memfault_log_read(&log);
//! if (log_found && (log.type == kMemfaultLogRecordType_Preformatted)) {
//! my_platform_uart_println(log.level, log, log.msg_len);
//! }
bool memfault_log_read(sMemfaultLog *log);
//! Invoked every time a new log has been saved
//!
//! @note By default this is a weak function which behaves as a no-op. Platforms which dispatch
//! console logging to a low priority thread can implement this callback to have a "hook" from
//! where a job to drain new logs can easily be scheduled
extern void memfault_log_handle_saved_callback(void);
//! Formats the provided string and saves it to backing storage
//!
//! @note: Should only be called via MEMFAULT_LOG_SAVE macro
MEMFAULT_PRINTF_LIKE_FUNC(2, 3)
void memfault_log_save(eMemfaultPlatformLogLevel level, const char *fmt, ...);
//! Formats the provided string from a variable argument list
//!
//! @note Prefer saving logs via MEMFAULT_LOG_SAVE() when possible
MEMFAULT_PRINTF_LIKE_FUNC(2, 0)
void memfault_vlog_save(eMemfaultPlatformLogLevel level, const char *fmt, va_list args);
//! Freezes the contents of the log buffer in preparation of uploading the logs to Memfault.
//!
//! Once the log buffer contents have been uploaded, the buffer is unfrozen. While the buffer is
//! frozen, logs can still be added, granted enough space is available in the buffer. If the buffer
//! is full, newly logs will get dropped. Once the buffer is unfrozen again, the oldest logs will be
//! expunged again upon writing new logs that require the space.
//! @note This function must not be called from an ISR context.
void memfault_log_trigger_collection(void);
#ifdef __cplusplus
}
#endif