Line data Source code
1 : #include "AP_Logger.h"
2 :
3 : #if HAL_LOGGING_ENABLED
4 :
5 : #include "AP_Logger_Backend.h"
6 :
7 : #include "AP_Logger_File.h"
8 : #include "AP_Logger_Flash_JEDEC.h"
9 : #include "AP_Logger_W25NXX.h"
10 : #include "AP_Logger_MAVLink.h"
11 :
12 : #include <AP_InternalError/AP_InternalError.h>
13 : #include <GCS_MAVLink/GCS.h>
14 : #include <AP_BoardConfig/AP_BoardConfig.h>
15 : #include <AP_Rally/AP_Rally.h>
16 : #include <AP_Vehicle/AP_Vehicle_Type.h>
17 :
18 : #if HAL_LOGGER_FENCE_ENABLED
19 : #include <AC_Fence/AC_Fence.h>
20 : #endif
21 :
22 : AP_Logger *AP_Logger::_singleton;
23 :
24 : extern const AP_HAL::HAL& hal;
25 :
26 : #ifndef HAL_LOGGING_FILE_BUFSIZE
27 : #if HAL_MEM_CLASS >= HAL_MEM_CLASS_1000
28 : // adjust buffer size for extra space allocated on more capable boards
29 : #define HAL_LOGGING_FILE_BUFSIZE (200-((AP_FATFS_MAX_IO_SIZE-AP_FATFS_MIN_IO_SIZE)/1024))
30 : #elif HAL_MEM_CLASS >= HAL_MEM_CLASS_500
31 : #define HAL_LOGGING_FILE_BUFSIZE 80
32 : #elif HAL_MEM_CLASS >= HAL_MEM_CLASS_300
33 : #define HAL_LOGGING_FILE_BUFSIZE 50
34 : #else
35 : #define HAL_LOGGING_FILE_BUFSIZE 16
36 : #endif
37 : #endif
38 :
39 : #ifndef HAL_LOGGING_DATAFLASH_DRIVER
40 : #define HAL_LOGGING_DATAFLASH_DRIVER AP_Logger_Flash_JEDEC
41 : #endif
42 :
43 : #ifndef HAL_LOGGING_STACK_SIZE
44 : #define HAL_LOGGING_STACK_SIZE 1580
45 : #endif
46 :
47 : #ifndef HAL_LOGGING_MAV_BUFSIZE
48 : #define HAL_LOGGING_MAV_BUFSIZE 8
49 : #endif
50 :
51 : #ifndef HAL_LOGGING_FILE_TIMEOUT
52 : #define HAL_LOGGING_FILE_TIMEOUT 5
53 : #endif
54 :
55 : // by default log for 15 seconds after disarming
56 : #ifndef HAL_LOGGER_ARM_PERSIST
57 : #define HAL_LOGGER_ARM_PERSIST 15
58 : #endif
59 :
60 : #ifndef HAL_LOGGER_MIN_MB_FREE
61 : #if AP_FILESYSTEM_LITTLEFS_ENABLED
62 : #if AP_FILESYSTEM_LITTLEFS_FLASH_TYPE == AP_FILESYSTEM_FLASH_W25NXX
63 : #define HAL_LOGGER_MIN_MB_FREE 10
64 : #else
65 : #define HAL_LOGGER_MIN_MB_FREE 2
66 : #endif
67 : #else
68 : #define HAL_LOGGER_MIN_MB_FREE 500
69 : #endif
70 : #endif
71 :
72 : #ifndef HAL_LOGGING_BACKENDS_DEFAULT
73 : # if HAL_LOGGING_FILESYSTEM_ENABLED && (CONFIG_HAL_BOARD == HAL_BOARD_SITL)
74 : # define HAL_LOGGING_BACKENDS_DEFAULT Backend_Type::FILESYSTEM
75 : # elif HAL_LOGGING_DATAFLASH_ENABLED
76 : # define HAL_LOGGING_BACKENDS_DEFAULT Backend_Type::BLOCK
77 : # elif HAL_LOGGING_FILESYSTEM_ENABLED
78 : # define HAL_LOGGING_BACKENDS_DEFAULT Backend_Type::FILESYSTEM
79 : # elif HAL_LOGGING_MAVLINK_ENABLED
80 : # define HAL_LOGGING_BACKENDS_DEFAULT Backend_Type::MAVLINK
81 : # else
82 : # define HAL_LOGGING_BACKENDS_DEFAULT 0
83 : # endif
84 : #endif
85 :
86 : // when adding new msgs we start at a different index in replay
87 : #if APM_BUILD_TYPE(APM_BUILD_Replay)
88 : #define LOGGING_FIRST_DYNAMIC_MSGID REPLAY_LOG_NEW_MSG_MAX
89 : #else
90 : #define LOGGING_FIRST_DYNAMIC_MSGID 254
91 : #endif
92 :
93 : static constexpr uint16_t MAX_LOG_FILES = 500;
94 : static constexpr uint16_t MIN_LOG_FILES = 2;
95 :
96 : const AP_Param::GroupInfo AP_Logger::var_info[] = {
97 : // @Param: _BACKEND_TYPE
98 : // @DisplayName: AP_Logger Backend Storage type
99 : // @Description: Bitmap of what Logger backend types to enable. Block-based logging is available on SITL and boards with dataflash chips. Multiple backends can be selected.
100 : // @Bitmask: 0:File,1:MAVLink,2:Block
101 : // @User: Standard
102 : // @RebootRequired: True
103 : AP_GROUPINFO("_BACKEND_TYPE", 0, AP_Logger, _params.backend_types, uint8_t(HAL_LOGGING_BACKENDS_DEFAULT)),
104 :
105 : // @Param: _FILE_BUFSIZE
106 : // @DisplayName: Logging File and Block Backend buffer size max (in kibibytes)
107 : // @Description: The File and Block backends use a buffer to store data before writing to the block device. Raising this value may reduce "gaps" in your SD card logging but increases memory usage. This buffer size may be reduced to free up available memory
108 : // @Units: KiB
109 : // @Range: 4 200
110 : // @User: Standard
111 : AP_GROUPINFO("_FILE_BUFSIZE", 1, AP_Logger, _params.file_bufsize, HAL_LOGGING_FILE_BUFSIZE),
112 :
113 : // @Param: _DISARMED
114 : // @DisplayName: Enable logging while disarmed
115 : // @Description: If LOG_DISARMED is set to 1 then logging will be enabled at all times including when disarmed. Logging before arming can make for very large logfiles but can help a lot when tracking down startup issues and is necessary if logging of EKF replay data is selected via the LOG_REPLAY parameter. If LOG_DISARMED is set to 2, then logging will be enabled when disarmed, but not if a USB connection is detected. This can be used to prevent unwanted data logs being generated when the vehicle is connected via USB for log downloading or parameter changes. If LOG_DISARMED is set to 3 then logging will happen while disarmed, but if the vehicle never arms then the logs using the filesystem backend will be discarded on the next boot.
116 : // @Values: 0:Disabled,1:Enabled,2:Disabled on USB connection,3:Discard log on reboot if never armed
117 : // @User: Standard
118 : AP_GROUPINFO("_DISARMED", 2, AP_Logger, _params.log_disarmed, 0),
119 :
120 : // @Param: _REPLAY
121 : // @DisplayName: Enable logging of information needed for Replay
122 : // @Description: If LOG_REPLAY is set to 1 then the EKF2 and EKF3 state estimators will log detailed information needed for diagnosing problems with the Kalman filter. LOG_DISARMED must be set to 1 or 2 or else the log will not contain the pre-flight data required for replay testing of the EKF's. It is suggested that you also raise LOG_FILE_BUFSIZE to give more buffer space for logging and use a high quality microSD card to ensure no sensor data is lost.
123 : // @Values: 0:Disabled,1:Enabled
124 : // @User: Standard
125 : AP_GROUPINFO("_REPLAY", 3, AP_Logger, _params.log_replay, 0),
126 :
127 : // @Param: _FILE_DSRMROT
128 : // @DisplayName: Stop logging to current file on disarm
129 : // @Description: When set, the current log file is closed when the vehicle is disarmed. If LOG_DISARMED is set then a fresh log will be opened. Applies to the File and Block logging backends.
130 : // @Values: 0:Disabled,1:Enabled
131 : // @User: Standard
132 : AP_GROUPINFO("_FILE_DSRMROT", 4, AP_Logger, _params.file_disarm_rot, 0),
133 :
134 : #if HAL_LOGGING_MAVLINK_ENABLED
135 : // @Param: _MAV_BUFSIZE
136 : // @DisplayName: Maximum AP_Logger MAVLink Backend buffer size
137 : // @Description: Maximum amount of memory to allocate to AP_Logger-over-mavlink
138 : // @User: Advanced
139 : // @Units: kB
140 : AP_GROUPINFO("_MAV_BUFSIZE", 5, AP_Logger, _params.mav_bufsize, HAL_LOGGING_MAV_BUFSIZE),
141 : #endif
142 :
143 : // @Param: _FILE_TIMEOUT
144 : // @DisplayName: Timeout before giving up on file writes
145 : // @Description: This controls the amount of time before failing writes to a log file cause the file to be closed and logging stopped.
146 : // @User: Standard
147 : // @Units: s
148 : AP_GROUPINFO("_FILE_TIMEOUT", 6, AP_Logger, _params.file_timeout, HAL_LOGGING_FILE_TIMEOUT),
149 :
150 : // @Param: _FILE_MB_FREE
151 : // @DisplayName: Old logs on the SD card will be deleted to maintain this amount of free space
152 : // @Description: Set this such that the free space is larger than your largest typical flight log
153 : // @Units: MB
154 : // @Range: 2 1000
155 : // @User: Standard
156 : AP_GROUPINFO("_FILE_MB_FREE", 7, AP_Logger, _params.min_MB_free, HAL_LOGGER_MIN_MB_FREE),
157 :
158 : // @Param: _FILE_RATEMAX
159 : // @DisplayName: Maximum logging rate for file backend
160 : // @Description: This sets the maximum rate that streaming log messages will be logged to the file backend. A value of zero means that rate limiting is disabled.
161 : // @Units: Hz
162 : // @Range: 0 1000
163 : // @Increment: 0.1
164 : // @User: Standard
165 : AP_GROUPINFO("_FILE_RATEMAX", 8, AP_Logger, _params.file_ratemax, 0),
166 :
167 : #if HAL_LOGGING_MAVLINK_ENABLED
168 : // @Param: _MAV_RATEMAX
169 : // @DisplayName: Maximum logging rate for mavlink backend
170 : // @Description: This sets the maximum rate that streaming log messages will be logged to the mavlink backend. A value of zero means that rate limiting is disabled.
171 : // @Units: Hz
172 : // @Range: 0 1000
173 : // @Increment: 0.1
174 : // @User: Standard
175 : AP_GROUPINFO("_MAV_RATEMAX", 9, AP_Logger, _params.mav_ratemax, 10),
176 : #endif
177 :
178 : #if HAL_LOGGING_BLOCK_ENABLED
179 : // @Param: _BLK_RATEMAX
180 : // @DisplayName: Maximum logging rate for block backend
181 : // @Description: This sets the maximum rate that streaming log messages will be logged to the block backend. A value of zero means that rate limiting is disabled.
182 : // @Units: Hz
183 : // @Range: 0 1000
184 : // @Increment: 0.1
185 : // @User: Standard
186 : AP_GROUPINFO("_BLK_RATEMAX", 10, AP_Logger, _params.blk_ratemax, 0),
187 : #endif
188 :
189 : // @Param: _DARM_RATEMAX
190 : // @DisplayName: Maximum logging rate when disarmed
191 : // @Description: This sets the maximum rate that streaming log messages will be logged to any backend when disarmed. A value of zero means that the normal backend rate limit is applied.
192 : // @Units: Hz
193 : // @Range: 0 1000
194 : // @Increment: 0.1
195 : // @User: Standard
196 : AP_GROUPINFO("_DARM_RATEMAX", 11, AP_Logger, _params.disarm_ratemax, 0),
197 :
198 : // @Param: _MAX_FILES
199 : // @DisplayName: Maximum number of log files
200 : // @Description: This sets the maximum number of log file that will be written on dataflash or sd card before starting to rotate log number. Limit is capped at 500 logs.
201 : // @Range: 2 500
202 : // @Increment: 1
203 : // @User: Advanced
204 : // @RebootRequired: True
205 : AP_GROUPINFO("_MAX_FILES", 12, AP_Logger, _params.max_log_files, MAX_LOG_FILES),
206 :
207 : AP_GROUPEND
208 : };
209 :
210 : #define streq(x, y) (!strcmp(x, y))
211 :
212 2100 : AP_Logger::AP_Logger()
213 : {
214 2100 : AP_Param::setup_object_defaults(this, var_info);
215 2100 : if (_singleton != nullptr) {
216 0 : AP_HAL::panic("AP_Logger must be singleton");
217 : }
218 :
219 2100 : _singleton = this;
220 2100 : }
221 :
222 2094 : void AP_Logger::init(const AP_Int32 &log_bitmask, const struct LogStructure *structures, uint8_t num_types)
223 : {
224 2094 : _log_bitmask = &log_bitmask;
225 :
226 2094 : if (hal.util->was_watchdog_armed()) {
227 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Forcing logging for watchdog reset");
228 0 : _params.log_disarmed.set(LogDisarmed::LOG_WHILE_DISARMED);
229 : }
230 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
231 2094 : validate_structures(structures, num_types);
232 2094 : dump_structures(structures, num_types);
233 : #endif
234 2094 : _num_types = num_types;
235 2094 : _structures = structures;
236 :
237 : // the "main" logging type needs to come before mavlink so that
238 : // index 0 is correct
239 : static const struct {
240 : Backend_Type type;
241 : AP_Logger_Backend* (*probe_fn)(AP_Logger&, LoggerMessageWriter_DFLogStart*);
242 : } backend_configs[] {
243 : #if HAL_LOGGING_FILESYSTEM_ENABLED
244 : { Backend_Type::FILESYSTEM, AP_Logger_File::probe },
245 : #endif
246 : #if HAL_LOGGING_DATAFLASH_ENABLED
247 : { Backend_Type::BLOCK, HAL_LOGGING_DATAFLASH_DRIVER::probe },
248 : #endif
249 : #if HAL_LOGGING_MAVLINK_ENABLED
250 : { Backend_Type::MAVLINK, AP_Logger_MAVLink::probe },
251 : #endif
252 : };
253 :
254 2094 : uint8_t remaining_types = _params.backend_types;
255 8376 : for (const auto &backend_config : backend_configs) {
256 6282 : uint8_t type = uint8_t(backend_config.type);
257 6282 : if ((remaining_types & type) == 0) {
258 4188 : continue; // skip if not enabled
259 : }
260 2094 : remaining_types &= ~type; // remember that we processed this type
261 2094 : if (_next_backend == LOGGER_MAX_BACKENDS) {
262 0 : AP_BoardConfig::config_error("Too many logger backends");
263 : return;
264 : }
265 :
266 : LoggerMessageWriter_DFLogStart *message_writer =
267 2094 : NEW_NOTHROW LoggerMessageWriter_DFLogStart();
268 2094 : if (message_writer == nullptr) {
269 0 : AP_BoardConfig::allocation_error("message writer");
270 : }
271 2094 : backends[_next_backend] = backend_config.probe_fn(*this, message_writer);
272 2094 : if (backends[_next_backend] == nullptr) {
273 0 : AP_BoardConfig::allocation_error("logger backend");
274 : }
275 2094 : _next_backend++;
276 : }
277 :
278 2094 : if (remaining_types) { // there was a type we didn't process
279 0 : AP_BoardConfig::config_error("Unknown logger backend");
280 : return;
281 : }
282 :
283 4188 : for (uint8_t i=0; i<_next_backend; i++) {
284 2094 : backends[i]->Init();
285 : }
286 :
287 2094 : start_io_thread();
288 :
289 2094 : EnableWrites(true);
290 : }
291 :
292 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
293 : #include <stdio.h>
294 :
295 : #define DEBUG_LOG_STRUCTURES 0
296 :
297 : extern const AP_HAL::HAL& hal;
298 : #define Debug(fmt, args ...) do {::fprintf(stderr, "%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); } while(0)
299 :
300 : /// return the number of commas present in string
301 407089 : static uint8_t count_commas(const char *string)
302 : {
303 407089 : uint8_t ret = 0;
304 16903968 : for (uint8_t i=0; i<strlen(string); i++) {
305 16496879 : if (string[i] == ',') {
306 3200052 : ret++;
307 : }
308 : }
309 407089 : return ret;
310 : }
311 :
312 : /// return a unit name given its ID
313 0 : const char* AP_Logger::unit_name(const uint8_t unit_id)
314 : {
315 0 : for (uint8_t i=0; i<_num_units; i++) {
316 0 : if (_units[i].ID == unit_id) {
317 0 : return _units[i].unit;
318 : }
319 : }
320 0 : return nullptr;
321 : }
322 :
323 : /// return a multiplier value given its ID
324 0 : double AP_Logger::multiplier_name(const uint8_t multiplier_id)
325 : {
326 0 : for (uint8_t i=0; i<_num_multipliers; i++) {
327 0 : if (_multipliers[i].ID == multiplier_id) {
328 0 : return _multipliers[i].multiplier;
329 : }
330 : }
331 : // Should we abort here?
332 0 : return 1.0f;
333 : }
334 :
335 : /// pretty-print field information from a log structure
336 0 : void AP_Logger::dump_structure_field(const struct LogStructure *logstructure, const char *label, const uint8_t fieldnum)
337 : {
338 0 : ::fprintf(stderr, " %s (%s)*(%f)\n", label, unit_name(logstructure->units[fieldnum]), multiplier_name(logstructure->multipliers[fieldnum]));
339 0 : }
340 :
341 : /// pretty-print log structures
342 : /// @note structures MUST be well-formed
343 2094 : void AP_Logger::dump_structures(const struct LogStructure *logstructures, const uint8_t num_types)
344 : {
345 : #if DEBUG_LOG_STRUCTURES
346 : for (uint16_t i=0; i<num_types; i++) {
347 : const struct LogStructure *logstructure = &logstructures[i];
348 : ::fprintf(stderr, "%s\n", logstructure->name);
349 : char label[32] = { };
350 : uint8_t labeloffset = 0;
351 : int8_t fieldnum = 0;
352 : for (uint8_t j=0; j<strlen(logstructure->labels); j++) {
353 : char labelchar = logstructure->labels[j];
354 : if (labelchar == '\0') {
355 : break;
356 : }
357 : if (labelchar == ',') {
358 : dump_structure_field(logstructure, label, fieldnum);
359 : fieldnum++;
360 : labeloffset = 0;
361 : memset(label, '\0', 32);
362 : } else {
363 : label[labeloffset++] = labelchar;
364 : }
365 : }
366 : dump_structure_field(logstructure, label, fieldnum);
367 : ::fprintf(stderr, "\n"); // just add a CR to the output
368 : }
369 : #endif
370 2094 : }
371 :
372 407089 : bool AP_Logger::labels_string_is_good(const char *labels) const
373 : {
374 407089 : bool passed = true;
375 407089 : if (strlen(labels) >= LS_LABELS_SIZE) {
376 0 : Debug("Labels string too long (%u >= %u)", unsigned(strlen(labels)), unsigned(LS_LABELS_SIZE));
377 0 : passed = false;
378 : }
379 : // This goes through and slices labels up into substrings by
380 : // changing commas to nulls - keeping references to each string in
381 : // label_offsets.
382 : char *label_offsets[LS_LABELS_SIZE];
383 407089 : uint8_t label_offsets_offset = 0;
384 407089 : char labels_copy[LS_LABELS_SIZE+1] {};
385 407089 : strncpy(labels_copy, labels, LS_LABELS_SIZE);
386 407089 : if (labels_copy[0] == ',') {
387 0 : Debug("Leading comma in (%s)", labels);
388 0 : passed = false;
389 : }
390 407089 : label_offsets[label_offsets_offset++] = labels_copy;
391 407089 : const uint8_t len = strnlen(labels_copy, LS_LABELS_SIZE);
392 16903968 : for (uint8_t i=0; i<len; i++) {
393 16496879 : if (labels_copy[i] == ',') {
394 3200052 : if (labels_copy[i+1] == '\0') {
395 0 : Debug("Trailing comma in (%s)", labels);
396 0 : passed = false;
397 0 : continue;
398 : }
399 3200052 : labels_copy[i] = '\0';
400 3200052 : label_offsets[label_offsets_offset++] = &labels_copy[i+1];
401 : }
402 : }
403 3607141 : for (uint8_t i=0; i<label_offsets_offset-1; i++) {
404 21058476 : for (uint8_t j=i+1; j<label_offsets_offset; j++) {
405 17858424 : if (!strcmp(label_offsets[i], label_offsets[j])) {
406 0 : Debug("Duplicate label (%s) in (%s)", label_offsets[i], labels);
407 0 : passed = false;
408 : }
409 : }
410 : }
411 407089 : return passed;
412 : }
413 :
414 407089 : bool AP_Logger::validate_structure(const struct LogStructure *logstructure, const int16_t offset)
415 : {
416 407089 : bool passed = true;
417 :
418 : #if DEBUG_LOG_STRUCTURES
419 : Debug("offset=%d ID=%d NAME=%s", offset, logstructure->msg_type, logstructure->name);
420 : #endif
421 :
422 : // fields must be null-terminated
423 : #define CHECK_ENTRY(fieldname,fieldname_s,fieldlen) \
424 : do { \
425 : if (strnlen(logstructure->fieldname, fieldlen) > fieldlen-1) { \
426 : Debug(" Message %s." fieldname_s " not NULL-terminated or too long", logstructure->name); \
427 : passed = false; \
428 : } \
429 : } while (false)
430 407089 : CHECK_ENTRY(name, "name", LS_NAME_SIZE);
431 407089 : CHECK_ENTRY(format, "format", LS_FORMAT_SIZE);
432 407089 : CHECK_ENTRY(labels, "labels", LS_LABELS_SIZE);
433 407089 : CHECK_ENTRY(units, "units", LS_UNITS_SIZE);
434 407089 : CHECK_ENTRY(multipliers, "multipliers", LS_MULTIPLIERS_SIZE);
435 : #undef CHECK_ENTRY
436 :
437 : // ensure each message ID is only used once
438 407089 : if (seen_ids[logstructure->msg_type]) {
439 0 : Debug(" ID %d used twice (LogStructure offset=%d)", logstructure->msg_type, offset);
440 0 : passed = false;
441 : }
442 407089 : seen_ids[logstructure->msg_type] = true;
443 :
444 : // ensure we have enough labels to cover columns
445 407089 : uint8_t fieldcount = strlen(logstructure->format);
446 407089 : uint8_t labelcount = count_commas(logstructure->labels)+1;
447 407089 : if (fieldcount != labelcount) {
448 0 : Debug(" %s fieldcount=%u does not match labelcount=%u",
449 : logstructure->name, fieldcount, labelcount);
450 0 : passed = false;
451 : }
452 :
453 407089 : if (!labels_string_is_good(logstructure->labels)) {
454 0 : passed = false;
455 : }
456 :
457 : // check that the structure is of an appropriate length to take fields
458 407089 : const int16_t msg_len = Write_calc_msg_len(logstructure->format);
459 407089 : if (msg_len != logstructure->msg_len) {
460 0 : Debug(" Calculated message length for (%s) based on format field (%s) does not match structure size (%d != %u)", logstructure->name, logstructure->format, msg_len, logstructure->msg_len);
461 0 : passed = false;
462 : }
463 :
464 : // ensure we have units for each field:
465 407089 : if (strlen(logstructure->units) != fieldcount) {
466 0 : Debug(" %s fieldcount=%u does not match unitcount=%u",
467 : logstructure->name, (unsigned)fieldcount, (unsigned)strlen(logstructure->units));
468 0 : passed = false;
469 : }
470 :
471 : // ensure we have multipliers for each field
472 407089 : if (strlen(logstructure->multipliers) != fieldcount) {
473 0 : Debug(" %s fieldcount=%u does not match multipliercount=%u",
474 : logstructure->name, (unsigned)fieldcount, (unsigned)strlen(logstructure->multipliers));
475 0 : passed = false;
476 : }
477 :
478 : // ensure the FMTU messages reference valid units
479 4014230 : for (uint8_t j=0; j<strlen(logstructure->units); j++) {
480 3607141 : char logunit = logstructure->units[j];
481 : uint8_t k;
482 41828375 : for (k=0; k<_num_units; k++) {
483 41828375 : if (logunit == _units[k].ID) {
484 : // found this one
485 3607141 : break;
486 : }
487 : }
488 3607141 : if (k == _num_units) {
489 0 : Debug(" invalid unit=%c", logunit);
490 0 : passed = false;
491 : }
492 : }
493 :
494 : // ensure the FMTU messages reference valid multipliers
495 4014230 : for (uint8_t j=0; j<strlen(logstructure->multipliers); j++) {
496 3607141 : char logmultiplier = logstructure->multipliers[j];
497 : uint8_t k;
498 13895198 : for (k=0; k<_num_multipliers; k++) {
499 13895198 : if (logmultiplier == _multipliers[k].ID) {
500 : // found this one
501 3607141 : break;
502 : }
503 : }
504 3607141 : if (k == _num_multipliers) {
505 0 : Debug(" invalid multiplier=%c", logmultiplier);
506 0 : passed = false;
507 : }
508 : }
509 :
510 : // ensure any float has a multiplier of zero
511 : if (false && passed) {
512 : for (uint8_t j=0; j<strlen(logstructure->multipliers); j++) {
513 : const char fmt = logstructure->format[j];
514 : if (fmt != 'f' && fmt != 'd' && fmt != 'g') {
515 : continue;
516 : }
517 : const char logmultiplier = logstructure->multipliers[j];
518 : if (logmultiplier == '0' ||
519 : logmultiplier == '?' ||
520 : logmultiplier == '-') {
521 : continue;
522 : }
523 : Debug(" %s[%u] float with non-zero multiplier=%c",
524 : logstructure->name,
525 : j,
526 : logmultiplier);
527 : passed = false;
528 : }
529 : }
530 :
531 407089 : return passed;
532 : }
533 :
534 2094 : void AP_Logger::validate_structures(const struct LogStructure *logstructures, const uint8_t num_types)
535 : {
536 2094 : Debug("Validating structures");
537 2094 : bool passed = true;
538 :
539 399697 : for (uint16_t i=0; i<num_types; i++) {
540 397603 : const struct LogStructure *logstructure = &logstructures[i];
541 397603 : passed = validate_structure(logstructure, i) && passed;
542 : }
543 :
544 : // ensure units are unique:
545 81666 : for (uint16_t i=0; i<ARRAY_SIZE(log_Units); i++) {
546 79572 : const struct UnitStructure &a = log_Units[i];
547 1551654 : for (uint16_t j=i+1; j<ARRAY_SIZE(log_Units); j++) {
548 1472082 : const struct UnitStructure &b = log_Units[j];
549 1472082 : if (a.ID == b.ID) {
550 0 : Debug("duplicate unit id=%c (%s/%s)", a.ID, a.unit, b.unit);
551 0 : passed = false;
552 : }
553 1472082 : if (streq(a.unit, b.unit)) {
554 0 : Debug("duplicate unit=%s (%c/%c)", a.unit, a.ID, b.ID);
555 0 : passed = false;
556 : }
557 : }
558 : }
559 :
560 : // ensure multipliers are unique:
561 33504 : for (uint16_t i=0; i<ARRAY_SIZE(log_Multipliers); i++) {
562 31410 : const struct MultiplierStructure &a = log_Multipliers[i];
563 251280 : for (uint16_t j=i+1; j<ARRAY_SIZE(log_Multipliers); j++) {
564 219870 : const struct MultiplierStructure &b = log_Multipliers[j];
565 219870 : if (a.ID == b.ID) {
566 0 : Debug("duplicate multiplier id=%c (%f/%f)",
567 : a.ID, a.multiplier, b.multiplier);
568 0 : passed = false;
569 : }
570 219870 : if (is_equal(a.multiplier, b.multiplier)) {
571 2094 : if (a.ID == '?' && b.ID == '0') {
572 : // special case
573 2094 : continue;
574 : }
575 0 : Debug("duplicate multiplier=%f (%c/%c)",
576 : a.multiplier, a.ID, b.ID);
577 0 : passed = false;
578 : }
579 : }
580 : }
581 :
582 2094 : if (!passed) {
583 0 : AP_BoardConfig::config_error("See console: Log structures invalid");
584 : }
585 2094 : }
586 :
587 : #endif // CONFIG_HAL_BOARD == HAL_BOARD_SITL
588 :
589 6734022023 : const struct LogStructure *AP_Logger::structure(uint16_t num) const
590 : {
591 6734022023 : return &_structures[num];
592 : }
593 :
594 1255233 : bool AP_Logger::logging_present() const
595 : {
596 1255233 : return _next_backend != 0;
597 : }
598 416870097 : bool AP_Logger::logging_enabled() const
599 : {
600 416870097 : if (_next_backend == 0) {
601 0 : return false;
602 : }
603 435084540 : for (uint8_t i=0; i<_next_backend; i++) {
604 416870097 : if (backends[i]->logging_enabled()) {
605 398655653 : return true;
606 : }
607 : }
608 18214443 : return false;
609 : }
610 1254200 : bool AP_Logger::logging_failed() const
611 : {
612 1254200 : if (_next_backend < 1) {
613 : // we should not have been called!
614 0 : return true;
615 : }
616 2507424 : for (uint8_t i=0; i<_next_backend; i++) {
617 1254200 : if (backends[i]->logging_failed()) {
618 976 : return true;
619 : }
620 : }
621 1253224 : return false;
622 : }
623 :
624 2840 : void AP_Logger::Write_MessageF(const char *fmt, ...)
625 : {
626 2840 : char msg[65] {}; // sizeof(log_Message.msg) + null-termination
627 :
628 : va_list ap;
629 2840 : va_start(ap, fmt);
630 2840 : hal.util->vsnprintf(msg, sizeof(msg), fmt, ap);
631 2840 : va_end(ap);
632 :
633 2840 : Write_Message(msg);
634 2840 : }
635 :
636 2079 : void AP_Logger::backend_starting_new_log(const AP_Logger_Backend *backend)
637 : {
638 2079 : _log_start_count++;
639 2079 : }
640 :
641 758853915 : bool AP_Logger::should_log(const uint32_t mask) const
642 : {
643 758853915 : bool armed = vehicle_is_armed();
644 :
645 758853943 : if (!(mask & *_log_bitmask)) {
646 699393635 : return false;
647 : }
648 59460344 : if (!armed && !log_while_disarmed()) {
649 3060400 : return false;
650 : }
651 56399919 : if (in_log_download()) {
652 3048 : return false;
653 : }
654 56396852 : if (_next_backend == 0) {
655 0 : return false;
656 : }
657 56396852 : return true;
658 : }
659 :
660 : /*
661 : return true if in log download which should prevent logging
662 : */
663 323223574 : bool AP_Logger::in_log_download() const
664 : {
665 323223574 : if (uint8_t(_params.backend_types) & uint8_t(Backend_Type::BLOCK)) {
666 : // when we have a BLOCK backend then listing completely prevents logging
667 957253 : return transfer_activity != TransferActivity::IDLE;
668 : }
669 : // for other backends listing does not interfere with logging
670 322266297 : return transfer_activity == TransferActivity::SENDING;
671 : }
672 :
673 78297 : const struct UnitStructure *AP_Logger::unit(uint16_t num) const
674 : {
675 78297 : return &_units[num];
676 : }
677 :
678 30856 : const struct MultiplierStructure *AP_Logger::multiplier(uint16_t num) const
679 : {
680 30856 : return &log_Multipliers[num];
681 : }
682 :
683 : #define FOR_EACH_BACKEND(methodcall) \
684 : do { \
685 : for (uint8_t i=0; i<_next_backend; i++) { \
686 : backends[i]->methodcall; \
687 : } \
688 : } while (0)
689 :
690 1033 : void AP_Logger::PrepForArming()
691 : {
692 2066 : FOR_EACH_BACKEND(PrepForArming());
693 1033 : }
694 :
695 2086 : void AP_Logger::setVehicle_Startup_Writer(vehicle_startup_message_Writer writer)
696 : {
697 2086 : _vehicle_messages = writer;
698 2086 : }
699 :
700 : #if AP_RTC_LOGGING_ENABLED
701 : // Write RTC data
702 7107 : void AP_Logger::Write_RTC()
703 : {
704 14174 : FOR_EACH_BACKEND(Write_RTC());
705 7107 : }
706 : #endif // AP_RTC_LOGGING_ENABLED
707 :
708 8379852 : void AP_Logger::set_vehicle_armed(const bool armed_state)
709 : {
710 8379852 : if (armed_state == _armed) {
711 : // no change in status
712 8377558 : return;
713 : }
714 2294 : _armed = armed_state;
715 :
716 2294 : if (_armed) {
717 : // went from disarmed to armed
718 : #if HAL_LOGGER_FILE_CONTENTS_ENABLED
719 : // get a set of @SYS files logged:
720 1162 : file_content_prepare_for_arming = true;
721 : #endif
722 : } else {
723 : // went from armed to disarmed
724 2264 : FOR_EACH_BACKEND(vehicle_was_disarmed());
725 : }
726 : }
727 :
728 : #if APM_BUILD_TYPE(APM_BUILD_Replay)
729 : /*
730 : remember formats for replay. This allows WriteV() to work within
731 : replay
732 : */
733 : void AP_Logger::save_format_Replay(const void *pBuffer)
734 : {
735 : if (((uint8_t *)pBuffer)[2] == LOG_FORMAT_MSG) {
736 : struct log_Format *fmt = (struct log_Format *)pBuffer;
737 : struct log_write_fmt *f = NEW_NOTHROW log_write_fmt;
738 : f->msg_type = fmt->type;
739 : f->msg_len = fmt->length;
740 : f->name = strndup(fmt->name, sizeof(fmt->name));
741 : f->fmt = strndup(fmt->format, sizeof(fmt->format));
742 : f->labels = strndup(fmt->labels, sizeof(fmt->labels));
743 : f->next = log_write_fmts;
744 : log_write_fmts = f;
745 : }
746 : }
747 : #endif
748 :
749 :
750 : // start functions pass straight through to backend:
751 172617168 : void AP_Logger::WriteBlock(const void *pBuffer, uint16_t size) {
752 : #if APM_BUILD_TYPE(APM_BUILD_Replay)
753 : save_format_Replay(pBuffer);
754 : #endif
755 345234237 : FOR_EACH_BACKEND(WriteBlock(pBuffer, size));
756 172617174 : }
757 :
758 : // only the first backend write need succeed for us to be successful
759 766629 : bool AP_Logger::WriteBlock_first_succeed(const void *pBuffer, uint16_t size)
760 : {
761 766629 : if (_next_backend == 0) {
762 0 : return false;
763 : }
764 :
765 766629 : for (uint8_t i=1; i<_next_backend; i++) {
766 0 : backends[i]->WriteBlock(pBuffer, size);
767 : }
768 :
769 766629 : return backends[0]->WriteBlock(pBuffer, size);
770 : }
771 :
772 : // write a replay block. This differs from other as it returns false if a backend doesn't
773 : // have space for the msg
774 167710066 : bool AP_Logger::WriteReplayBlock(uint8_t msg_id, const void *pBuffer, uint16_t size) {
775 167710066 : bool ret = true;
776 167710066 : if (log_replay()) {
777 772397 : uint8_t buf[3+size];
778 772397 : buf[0] = HEAD_BYTE1;
779 772397 : buf[1] = HEAD_BYTE2;
780 772397 : buf[2] = msg_id;
781 772397 : memcpy(&buf[3], pBuffer, size);
782 1544794 : for (uint8_t i=0; i<_next_backend; i++) {
783 772397 : if (!backends[i]->WritePrioritisedBlock(buf, sizeof(buf), true)) {
784 0 : ret = false;
785 : }
786 : }
787 772397 : }
788 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
789 : // things will almost certainly go sour. However, if we are not
790 : // logging while disarmed then the EKF can be started and trying
791 : // to log things even 'though the backends might be saying "no".
792 167710066 : if (!ret && log_while_disarmed()) {
793 0 : AP_HAL::panic("Failed to log replay block");
794 : }
795 : #endif
796 167710066 : return ret;
797 : }
798 :
799 220561 : void AP_Logger::WriteCriticalBlock(const void *pBuffer, uint16_t size) {
800 441122 : FOR_EACH_BACKEND(WriteCriticalBlock(pBuffer, size));
801 220561 : }
802 :
803 0 : void AP_Logger::WritePrioritisedBlock(const void *pBuffer, uint16_t size, bool is_critical) {
804 0 : FOR_EACH_BACKEND(WritePrioritisedBlock(pBuffer, size, is_critical));
805 0 : }
806 :
807 : // change me to "DoTimeConsumingPreparations"?
808 8 : void AP_Logger::EraseAll() {
809 16 : FOR_EACH_BACKEND(EraseAll());
810 8 : }
811 : // change me to "LoggingAvailable"?
812 132942 : bool AP_Logger::CardInserted(void) {
813 132942 : for (uint8_t i=0; i< _next_backend; i++) {
814 132942 : if (backends[i]->CardInserted()) {
815 132942 : return true;
816 : }
817 : }
818 0 : return false;
819 : }
820 :
821 0 : void AP_Logger::StopLogging()
822 : {
823 0 : FOR_EACH_BACKEND(stop_logging());
824 0 : }
825 :
826 0 : uint16_t AP_Logger::find_last_log() const {
827 0 : if (_next_backend == 0) {
828 0 : return 0;
829 : }
830 0 : return backends[0]->find_last_log();
831 : }
832 18216 : void AP_Logger::get_log_boundaries(uint16_t log_num, uint32_t & start_page, uint32_t & end_page) {
833 18216 : if (_next_backend == 0) {
834 0 : return;
835 : }
836 18216 : backends[0]->get_log_boundaries(log_num, start_page, end_page);
837 : }
838 21682 : void AP_Logger::get_log_info(uint16_t log_num, uint32_t &size, uint32_t &time_utc) {
839 21682 : if (_next_backend == 0) {
840 0 : return;
841 : }
842 21682 : backends[0]->get_log_info(log_num, size, time_utc);
843 : }
844 365472 : int16_t AP_Logger::get_log_data(uint16_t log_num, uint16_t page, uint32_t offset, uint16_t len, uint8_t *data) {
845 365472 : if (_next_backend == 0) {
846 0 : return 0;
847 : }
848 365472 : return backends[0]->get_log_data(log_num, page, offset, len, data);
849 : }
850 18255 : uint16_t AP_Logger::get_num_logs(void) {
851 18255 : if (_next_backend == 0) {
852 0 : return 0;
853 : }
854 18255 : return backends[0]->get_num_logs();
855 : }
856 :
857 1898417 : uint16_t AP_Logger::get_max_num_logs() {
858 1898417 : const auto max_logs = constrain_uint16(_params.max_log_files.get(), MIN_LOG_FILES, MAX_LOG_FILES);
859 1898417 : if (_params.max_log_files.get() != max_logs) {
860 1 : _params.max_log_files.set_and_save_ifchanged(static_cast<int16_t>(max_logs));
861 : }
862 1898417 : return static_cast<uint16_t>(_params.max_log_files.get());
863 : }
864 :
865 : /* we're started if any of the backends are started */
866 44302073 : bool AP_Logger::logging_started(void) const {
867 46253433 : for (uint8_t i=0; i< _next_backend; i++) {
868 44302072 : if (backends[i]->logging_started()) {
869 42350712 : return true;
870 : }
871 : }
872 1951361 : return false;
873 : }
874 :
875 23305 : void AP_Logger::handle_mavlink_msg(GCS_MAVLINK &link, const mavlink_message_t &msg)
876 : {
877 23305 : switch (msg.msgid) {
878 4814 : case MAVLINK_MSG_ID_REMOTE_LOG_BLOCK_STATUS:
879 9628 : FOR_EACH_BACKEND(remote_log_block_status_msg(link, msg));
880 4814 : break;
881 18491 : case MAVLINK_MSG_ID_LOG_REQUEST_LIST:
882 : FALLTHROUGH;
883 : case MAVLINK_MSG_ID_LOG_REQUEST_DATA:
884 : FALLTHROUGH;
885 : case MAVLINK_MSG_ID_LOG_ERASE:
886 : FALLTHROUGH;
887 : case MAVLINK_MSG_ID_LOG_REQUEST_END:
888 18491 : handle_log_message(link, msg);
889 18491 : break;
890 : }
891 23305 : }
892 :
893 41684408 : void AP_Logger::periodic_tasks() {
894 : #ifndef HAL_BUILD_AP_PERIPH
895 41684408 : handle_log_send();
896 : #endif
897 83368816 : FOR_EACH_BACKEND(periodic_tasks());
898 41684408 : }
899 :
900 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL || CONFIG_HAL_BOARD == HAL_BOARD_LINUX
901 : // currently only AP_Logger_File support this:
902 0 : void AP_Logger::flush(void) {
903 0 : FOR_EACH_BACKEND(flush());
904 0 : }
905 : #endif
906 :
907 :
908 1287 : void AP_Logger::Write_EntireMission()
909 : {
910 2574 : FOR_EACH_BACKEND(Write_EntireMission());
911 1287 : }
912 :
913 267613 : void AP_Logger::Write_Message(const char *message)
914 : {
915 535124 : FOR_EACH_BACKEND(Write_Message(message));
916 267613 : }
917 1027468 : void AP_Logger::Write_MessageChunk(uint8_t id, const char *messagechunk, uint8_t chunk_seq)
918 : {
919 2052936 : FOR_EACH_BACKEND(Write_MessageChunk(id, messagechunk, chunk_seq));
920 1027468 : }
921 :
922 9720 : void AP_Logger::Write_Mode(uint8_t mode, const ModeReason reason)
923 : {
924 19440 : FOR_EACH_BACKEND(Write_Mode(mode, reason));
925 9720 : }
926 :
927 97833 : void AP_Logger::Write_Parameter(const char *name, float value)
928 : {
929 195366 : FOR_EACH_BACKEND(Write_Parameter(name, value, quiet_nanf()));
930 97833 : }
931 :
932 2026 : void AP_Logger::Write_Mission_Cmd(const AP_Mission &mission,
933 : const AP_Mission::Mission_Command &cmd,
934 : LogMessages id)
935 : {
936 4052 : FOR_EACH_BACKEND(Write_Mission_Cmd(mission, cmd, id));
937 2026 : }
938 :
939 : #if HAL_RALLY_ENABLED
940 8 : void AP_Logger::Write_RallyPoint(uint8_t total,
941 : uint8_t sequence,
942 : const RallyLocation &rally_point)
943 : {
944 16 : FOR_EACH_BACKEND(Write_RallyPoint(total, sequence, rally_point));
945 8 : }
946 :
947 886 : void AP_Logger::Write_Rally()
948 : {
949 1772 : FOR_EACH_BACKEND(Write_Rally());
950 886 : }
951 : #endif
952 :
953 : #if HAL_LOGGER_FENCE_ENABLED
954 1001 : void AP_Logger::Write_Fence()
955 : {
956 2002 : FOR_EACH_BACKEND(Write_Fence());
957 1001 : }
958 : #endif
959 :
960 0 : void AP_Logger::Write_NamedValueFloat(const char *name, float value)
961 : {
962 0 : Write(
963 : "NVF",
964 : "TimeUS,Name,Value",
965 : "s#-",
966 : "F--",
967 : "QNf",
968 : AP_HAL::micros(),
969 : name,
970 : value
971 : );
972 0 : }
973 :
974 : // output a FMT message for each backend if not already done so
975 50963 : void AP_Logger::Safe_Write_Emit_FMT(log_write_fmt *f)
976 : {
977 101926 : for (uint8_t i=0; i<_next_backend; i++) {
978 50963 : backends[i]->Safe_Write_Emit_FMT(f->msg_type);
979 : }
980 50963 : }
981 :
982 1116947 : uint32_t AP_Logger::num_dropped() const
983 : {
984 1116947 : if (_next_backend == 0) {
985 0 : return 0;
986 : }
987 1116947 : return backends[0]->num_dropped();
988 : }
989 :
990 :
991 : // end functions pass straight through to backend
992 :
993 : /* Write support */
994 126204 : void AP_Logger::Write(const char *name, const char *labels, const char *fmt, ...)
995 : {
996 : va_list arg_list;
997 :
998 126204 : va_start(arg_list, fmt);
999 126204 : WriteV(name, labels, nullptr, nullptr, fmt, arg_list);
1000 126204 : va_end(arg_list);
1001 126204 : }
1002 :
1003 79313 : void AP_Logger::Write(const char *name, const char *labels, const char *units, const char *mults, const char *fmt, ...)
1004 : {
1005 : va_list arg_list;
1006 :
1007 79313 : va_start(arg_list, fmt);
1008 79313 : WriteV(name, labels, units, mults, fmt, arg_list);
1009 79313 : va_end(arg_list);
1010 79313 : }
1011 :
1012 42021092 : void AP_Logger::WriteStreaming(const char *name, const char *labels, const char *fmt, ...)
1013 : {
1014 : va_list arg_list;
1015 :
1016 42021092 : va_start(arg_list, fmt);
1017 42021092 : WriteV(name, labels, nullptr, nullptr, fmt, arg_list, false, true);
1018 42021092 : va_end(arg_list);
1019 42021092 : }
1020 :
1021 43138676 : void AP_Logger::WriteStreaming(const char *name, const char *labels, const char *units, const char *mults, const char *fmt, ...)
1022 : {
1023 : va_list arg_list;
1024 :
1025 43138676 : va_start(arg_list, fmt);
1026 43138676 : WriteV(name, labels, units, mults, fmt, arg_list, false, true);
1027 43138608 : va_end(arg_list);
1028 43138608 : }
1029 :
1030 1 : void AP_Logger::WriteCritical(const char *name, const char *labels, const char *fmt, ...)
1031 : {
1032 : va_list arg_list;
1033 :
1034 1 : va_start(arg_list, fmt);
1035 1 : WriteV(name, labels, nullptr, nullptr, fmt, arg_list, true);
1036 1 : va_end(arg_list);
1037 1 : }
1038 :
1039 0 : void AP_Logger::WriteCritical(const char *name, const char *labels, const char *units, const char *mults, const char *fmt, ...)
1040 : {
1041 : va_list arg_list;
1042 :
1043 0 : va_start(arg_list, fmt);
1044 0 : WriteV(name, labels, units, mults, fmt, arg_list, true);
1045 0 : va_end(arg_list);
1046 0 : }
1047 :
1048 85365233 : void AP_Logger::WriteV(const char *name, const char *labels, const char *units, const char *mults, const char *fmt, va_list arg_list,
1049 : bool is_critical, bool is_streaming)
1050 : {
1051 : // WriteV is not safe in replay as we can re-use IDs
1052 85365233 : const bool direct_comp = APM_BUILD_TYPE(APM_BUILD_Replay);
1053 85365233 : struct log_write_fmt *f = msg_fmt_for_name(name, labels, units, mults, fmt, direct_comp);
1054 85365338 : if (f == nullptr) {
1055 : // unable to map name to a messagetype; could be out of
1056 : // msgtypes, could be out of slots, ...
1057 : #if !APM_BUILD_TYPE(APM_BUILD_Replay)
1058 2 : INTERNAL_ERROR(AP_InternalError::error_t::logger_mapfailure);
1059 : #endif
1060 0 : return;
1061 : }
1062 :
1063 170730259 : for (uint8_t i=0; i<_next_backend; i++) {
1064 : va_list arg_copy;
1065 85365187 : va_copy(arg_copy, arg_list);
1066 85365187 : backends[i]->Write(f->msg_type, arg_copy, is_critical, is_streaming);
1067 85364923 : va_end(arg_copy);
1068 : }
1069 : }
1070 :
1071 : /*
1072 : when we are doing replay logging we want to delay start of the EKF
1073 : until after the headers are out so that on replay all parameter
1074 : values are available
1075 : */
1076 42719218 : bool AP_Logger::allow_start_ekf() const
1077 : {
1078 42719218 : if (!log_replay() || !log_while_disarmed()) {
1079 42542702 : return true;
1080 : }
1081 :
1082 352946 : for (uint8_t i=0; i<_next_backend; i++) {
1083 176516 : if (!backends[i]->allow_start_ekf()) {
1084 86 : return false;
1085 : }
1086 : }
1087 :
1088 176430 : return true;
1089 : }
1090 :
1091 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1092 85406824 : bool AP_Logger::assert_same_fmt_for_name(const AP_Logger::log_write_fmt *f,
1093 : const char *name,
1094 : const char *labels,
1095 : const char *units,
1096 : const char *mults,
1097 : const char *fmt) const
1098 : {
1099 85406824 : bool passed = true;
1100 85406824 : if (!streq(f->name, name)) {
1101 : // why exactly were we called?!
1102 0 : Debug("format names differ (%s) != (%s)", f->name, name);
1103 0 : passed = false;
1104 : }
1105 85406824 : if (!streq(f->labels, labels)) {
1106 0 : Debug("format labels differ (%s) vs (%s)", f->labels, labels);
1107 0 : passed = false;
1108 : }
1109 85406824 : if (!streq(f->fmt, fmt)) {
1110 0 : Debug("format fmt differ (%s) vs (%s)",
1111 : (f->fmt ? f->fmt : "nullptr"),
1112 : (fmt ? fmt : "nullptr"));
1113 0 : passed = false;
1114 : }
1115 : #if !APM_BUILD_TYPE(APM_BUILD_Replay)
1116 85406824 : if ((f->units != nullptr && units == nullptr) ||
1117 85406824 : (f->units == nullptr && units != nullptr) ||
1118 43229032 : (units !=nullptr && !streq(f->units, units))) {
1119 0 : Debug("format units differ (%s) vs (%s)",
1120 : (f->units ? f->units : "nullptr"),
1121 : (units ? units : "nullptr"));
1122 0 : passed = false;
1123 : }
1124 85406824 : if ((f->mults != nullptr && mults == nullptr) ||
1125 85406824 : (f->mults == nullptr && mults != nullptr) ||
1126 43229032 : (mults != nullptr && !streq(f->mults, mults))) {
1127 0 : Debug("format mults differ (%s) vs (%s)",
1128 : (f->mults ? f->mults : "nullptr"),
1129 : (mults ? mults : "nullptr"));
1130 0 : passed = false;
1131 : }
1132 85406824 : if (!passed) {
1133 0 : AP_BoardConfig::config_error("See console: Format definition must be consistent for every call of Write");
1134 : }
1135 : #endif
1136 85406824 : return passed;
1137 : }
1138 : #endif
1139 :
1140 85416182 : AP_Logger::log_write_fmt *AP_Logger::msg_fmt_for_name(const char *name, const char *labels, const char *units, const char *mults, const char *fmt, const bool direct_comp, const bool copy_strings)
1141 : {
1142 85416182 : WITH_SEMAPHORE(log_write_fmts_sem);
1143 : struct log_write_fmt *f;
1144 317000647 : for (f = log_write_fmts; f; f=f->next) {
1145 316991161 : if (!direct_comp) {
1146 316864597 : if (f->name == name) { // ptr comparison
1147 : // already have an ID for this name:
1148 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1149 85355875 : if (!assert_same_fmt_for_name(f, name, labels, units, mults, fmt)) {
1150 0 : return nullptr;
1151 : }
1152 : #endif
1153 85355875 : return f;
1154 : }
1155 : } else {
1156 : // direct comparison used from scripting where pointer is not maintained
1157 126564 : if (strcmp(f->name,name) == 0) {
1158 : // already have an ID for this name:
1159 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1160 50949 : if (!assert_same_fmt_for_name(f, name, labels, units, mults, fmt)) {
1161 0 : return nullptr;
1162 : }
1163 : #endif
1164 50949 : return f;
1165 : }
1166 : }
1167 : }
1168 :
1169 9486 : f = (struct log_write_fmt *)calloc(1, sizeof(*f));
1170 9486 : if (f == nullptr) {
1171 : // out of memory
1172 0 : return nullptr;
1173 : }
1174 : // no message type allocated for this name. Try to allocate one:
1175 9486 : int16_t msg_type = find_free_msg_type();
1176 9486 : if (msg_type == -1) {
1177 0 : free(f);
1178 0 : return nullptr;
1179 : }
1180 9486 : f->msg_type = msg_type;
1181 :
1182 9486 : if (copy_strings) {
1183 : // cannot use pointers to memory that might move, must allocate and copy
1184 14 : struct log_write_fmt_strings *ls_copy = (struct log_write_fmt_strings*)malloc(sizeof(log_write_fmt_strings));
1185 14 : if (ls_copy == nullptr) {
1186 0 : free(f);
1187 0 : return nullptr;
1188 : }
1189 :
1190 14 : strncpy_noterm(ls_copy->name, name, sizeof(ls_copy->name));
1191 14 : strncpy_noterm(ls_copy->format, fmt, sizeof(ls_copy->format));
1192 14 : strncpy_noterm(ls_copy->labels, labels, sizeof(ls_copy->labels));
1193 :
1194 14 : f->name = ls_copy->name;
1195 14 : f->fmt = ls_copy->format;
1196 14 : f->labels = ls_copy->labels;
1197 :
1198 14 : if (units != nullptr) {
1199 6 : strncpy_noterm(ls_copy->units, units, sizeof(ls_copy->units));
1200 6 : f->units = ls_copy->units;
1201 : }
1202 :
1203 14 : if (mults != nullptr) {
1204 6 : strncpy_noterm(ls_copy->multipliers, mults, sizeof(ls_copy->multipliers));
1205 6 : f->mults = ls_copy->multipliers;
1206 : }
1207 :
1208 : } else {
1209 9472 : f->name = name;
1210 9472 : f->fmt = fmt;
1211 9472 : f->labels = labels;
1212 9472 : f->units = units;
1213 9472 : f->mults = mults;
1214 : }
1215 :
1216 9486 : int16_t tmp = Write_calc_msg_len(fmt);
1217 9486 : if (tmp == -1) {
1218 0 : free(f);
1219 0 : return nullptr;
1220 : }
1221 :
1222 9486 : f->msg_len = tmp;
1223 :
1224 : // add direct_comp formats to start of list, otherwise add to the end, this minimises the number of string comparisons when walking the list in future calls
1225 9486 : if (direct_comp || (log_write_fmts == nullptr)) {
1226 2101 : f->next = log_write_fmts;
1227 2101 : log_write_fmts = f;
1228 : } else {
1229 7385 : struct log_write_fmt *list_end = log_write_fmts;
1230 21118 : while (list_end->next) {
1231 13733 : list_end=list_end->next;
1232 : }
1233 7385 : list_end->next = f;
1234 : }
1235 :
1236 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1237 9486 : struct log_write_fmt_strings ls_strings = {};
1238 9486 : struct LogStructure ls = {
1239 9486 : f->msg_type,
1240 9486 : f->msg_len,
1241 : ls_strings.name,
1242 : ls_strings.format,
1243 : ls_strings.labels,
1244 : ls_strings.units,
1245 : ls_strings.multipliers
1246 9486 : };
1247 9486 : memcpy((char*)ls_strings.name, f->name, MIN(sizeof(ls_strings.name), strlen(f->name)));
1248 9486 : memcpy((char*)ls_strings.format, f->fmt, MIN(sizeof(ls_strings.format), strlen(f->fmt)));
1249 9486 : memcpy((char*)ls_strings.labels, f->labels, MIN(sizeof(ls_strings.labels), strlen(f->labels)));
1250 9486 : if (f->units != nullptr) {
1251 7088 : memcpy((char*)ls_strings.units, f->units, MIN(sizeof(ls_strings.units), strlen(f->units)));
1252 : } else {
1253 2398 : memset((char*)ls_strings.units, '?', MIN(sizeof(ls_strings.format), strlen(f->fmt)));
1254 : }
1255 9486 : if (f->mults != nullptr) {
1256 7088 : memcpy((char*)ls_strings.multipliers, f->mults, MIN(sizeof(ls_strings.multipliers), strlen(f->mults)));
1257 : } else {
1258 2398 : memset((char*)ls_strings.multipliers, '?', MIN(sizeof(ls_strings.format), strlen(f->fmt)));
1259 : }
1260 9486 : if (!validate_structure(&ls, (int16_t)-1)) {
1261 0 : AP_BoardConfig::config_error("See console: Log structure invalid");
1262 : }
1263 : #endif
1264 :
1265 9486 : return f;
1266 85416310 : }
1267 :
1268 60062398 : const struct LogStructure *AP_Logger::structure_for_msg_type(const uint8_t msg_type) const
1269 : {
1270 6744191546 : for (uint16_t i=0; i<_num_types;i++) {
1271 6725686730 : const struct LogStructure *s = structure(i);
1272 6725675524 : if (s->msg_type == msg_type) {
1273 : // in use
1274 41546376 : return s;
1275 : }
1276 : }
1277 18504816 : return nullptr;
1278 : }
1279 :
1280 18506902 : const struct AP_Logger::log_write_fmt *AP_Logger::log_write_fmt_for_msg_type(const uint8_t msg_type) const
1281 : {
1282 : struct log_write_fmt *f;
1283 66832575 : for (f = log_write_fmts; f; f=f->next) {
1284 66832593 : if (f->msg_type == msg_type) {
1285 18506920 : return f;
1286 : }
1287 : }
1288 0 : return nullptr;
1289 : }
1290 :
1291 :
1292 : // returns true if the msg_type is already taken
1293 30708 : bool AP_Logger::msg_type_in_use(const uint8_t msg_type) const
1294 : {
1295 : // check static list of messages (e.g. from LOG_COMMON_STRUCTURES)
1296 : // check the write format types to see if we've used this one
1297 5892429 : for (uint16_t i=0; i<_num_types;i++) {
1298 5861721 : if (structure(i)->msg_type == msg_type) {
1299 : // in use
1300 0 : return true;
1301 : }
1302 : }
1303 :
1304 : struct log_write_fmt *f;
1305 86019 : for (f = log_write_fmts; f; f=f->next) {
1306 76533 : if (f->msg_type == msg_type) {
1307 21222 : return true;
1308 : }
1309 : }
1310 9486 : return false;
1311 : }
1312 :
1313 : // find a free message type
1314 9486 : int16_t AP_Logger::find_free_msg_type() const
1315 : {
1316 9486 : const uint8_t start = LOGGING_FIRST_DYNAMIC_MSGID;
1317 : // avoid using 255 here; perhaps we want to use it to extend things later
1318 30708 : for (uint16_t msg_type=start; msg_type>0; msg_type--) { // more likely to be free at end
1319 30708 : if (! msg_type_in_use(msg_type)) {
1320 9486 : return msg_type;
1321 : }
1322 : }
1323 0 : return -1;
1324 : }
1325 :
1326 : /*
1327 : * It is assumed that logstruct's char* variables are valid strings of
1328 : * maximum lengths for those fields (given in LogStructure.h e.g. LS_NAME_SIZE)
1329 : */
1330 21417 : bool AP_Logger::fill_logstructure(struct LogStructure &logstruct, const uint8_t msg_type) const
1331 : {
1332 : // check the static lists first...
1333 21417 : const LogStructure *found = structure_for_msg_type(msg_type);
1334 21417 : if (found != nullptr) {
1335 13267 : logstruct = *found;
1336 13267 : return true;
1337 : }
1338 :
1339 : // find log structure information corresponding to msg_type:
1340 : struct log_write_fmt *f;
1341 27621 : for (f = log_write_fmts; f; f=f->next) {
1342 27621 : if (f->msg_type == msg_type) {
1343 8150 : break;
1344 : }
1345 : }
1346 :
1347 8150 : if (!f) {
1348 0 : return false;
1349 : }
1350 :
1351 8150 : logstruct.msg_type = msg_type;
1352 8150 : strncpy((char*)logstruct.name, f->name, LS_NAME_SIZE);
1353 8150 : strncpy((char*)logstruct.format, f->fmt, LS_FORMAT_SIZE);
1354 8150 : strncpy((char*)logstruct.labels, f->labels, LS_LABELS_SIZE);
1355 8150 : if (f->units != nullptr) {
1356 5833 : strncpy((char*)logstruct.units, f->units, LS_UNITS_SIZE);
1357 : } else {
1358 2317 : memset((char*)logstruct.units, '\0', LS_UNITS_SIZE);
1359 2317 : memset((char*)logstruct.units, '?', MIN(LS_UNITS_SIZE,strlen(logstruct.format)));
1360 : }
1361 8150 : if (f->mults != nullptr) {
1362 5833 : strncpy((char*)logstruct.multipliers, f->mults, LS_MULTIPLIERS_SIZE);
1363 : } else {
1364 2317 : memset((char*)logstruct.multipliers, '\0', LS_MULTIPLIERS_SIZE);
1365 2317 : memset((char*)logstruct.multipliers, '?', MIN(LS_MULTIPLIERS_SIZE, strlen(logstruct.format)));
1366 : // special magic to set units/mults for TimeUS, by far and
1367 : // away the most common first field
1368 2317 : if (!strncmp(logstruct.labels, "TimeUS,", MIN(LS_LABELS_SIZE, strlen("TimeUS,")))) {
1369 2317 : ((char*)(logstruct.units))[0] = 's';
1370 2317 : ((char*)(logstruct.multipliers))[0] = 'F';
1371 : }
1372 : }
1373 8150 : logstruct.msg_len = f->msg_len;
1374 8150 : return true;
1375 : }
1376 :
1377 : /* calculate the length of output of a format string. Note that this
1378 : * returns an int16_t; if it returns -1 then an error has occurred.
1379 : * This was mechanically converted from init_field_types in
1380 : * Tools/Replay/MsgHandler.cpp */
1381 467538 : int16_t AP_Logger::Write_calc_msg_len(const char *fmt) const
1382 : {
1383 467538 : size_t len = LOG_PACKET_HEADER_LEN;
1384 4564629 : for (size_t i=0; i<strlen(fmt); i++) {
1385 4097091 : switch(fmt[i]) {
1386 6282 : case 'a' : len += sizeof(int16_t[32]); break;
1387 42911 : case 'b' : len += sizeof(int8_t); break;
1388 182124 : case 'c' : len += sizeof(int16_t); break;
1389 25196 : case 'd' : len += sizeof(double); break;
1390 30023 : case 'e' : len += sizeof(int32_t); break;
1391 1885520 : case 'f' : len += sizeof(float); break;
1392 0 : case 'g' : len += sizeof(float16_s); break;
1393 75118 : case 'h' : len += sizeof(int16_t); break;
1394 31116 : case 'i' : len += sizeof(int32_t); break;
1395 7118 : case 'n' : len += sizeof(char[4]); break;
1396 693092 : case 'B' : len += sizeof(uint8_t); break;
1397 33504 : case 'C' : len += sizeof(uint16_t); break;
1398 0 : case 'E' : len += sizeof(uint32_t); break;
1399 315995 : case 'H' : len += sizeof(uint16_t); break;
1400 203692 : case 'I' : len += sizeof(uint32_t); break;
1401 106158 : case 'L' : len += sizeof(int32_t); break;
1402 2094 : case 'M' : len += sizeof(uint8_t); break;
1403 16830 : case 'N' : len += sizeof(char[16]); break;
1404 16754 : case 'Z' : len += sizeof(char[64]); break;
1405 0 : case 'q' : len += sizeof(int64_t); break;
1406 423564 : case 'Q' : len += sizeof(uint64_t); break;
1407 0 : default:
1408 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
1409 0 : AP_HAL::panic("Unknown format specifier (%c)", fmt[i]);
1410 : #endif
1411 0 : return -1;
1412 : }
1413 : }
1414 467538 : if (len > LOG_PACKET_MAX_LEN) {
1415 0 : return -1;
1416 : }
1417 467538 : return (int16_t)len;
1418 : }
1419 :
1420 : /*
1421 : see if we need to save a crash dump. Returns true if either no crash
1422 : dump available or we have saved it to sdcard. This is called
1423 : continuously until success to account for late mount of the microSD
1424 : */
1425 1717 : bool AP_Logger::check_crash_dump_save(void)
1426 : {
1427 : #if defined(AP_CRASHDUMP_FATFS_ENABLED) && AP_CRASHDUMP_FATFS_ENABLED
1428 : if (hal.util->last_crash_dump_size() > 0) {
1429 : GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Previous CrashDump: APM/CrashDump.DAT");
1430 : return true;
1431 : }
1432 : // The SD card may not be mounted yet, so keep checking.
1433 : return false;
1434 : #else
1435 1717 : int fd = AP::FS().open("@SYS/crash_dump.bin", O_RDONLY);
1436 1717 : if (fd == -1) {
1437 : // we don't have a crash dump file. The @SYS filesystem
1438 : // returns -1 for open on empty files
1439 1717 : return true;
1440 : }
1441 0 : int fd2 = AP::FS().open("APM/crash_dump.bin", O_WRONLY|O_CREAT|O_TRUNC);
1442 0 : if (fd2 == -1) {
1443 : // sdcard not available yet, try again later
1444 0 : AP::FS().close(fd);
1445 0 : return false;
1446 : }
1447 : uint8_t buf[128];
1448 : int32_t n;
1449 0 : while ((n = AP::FS().read(fd, buf, sizeof(buf))) > 0) {
1450 0 : AP::FS().write(fd2, buf, n);
1451 : }
1452 0 : AP::FS().close(fd2);
1453 0 : AP::FS().close(fd);
1454 0 : GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Saved crash_dump.bin");
1455 0 : return true;
1456 : #endif
1457 : }
1458 :
1459 : // thread for processing IO - in general IO involves a long blocking DMA write to an SPI device
1460 : // and the thread will sleep while this completes preventing other tasks from running, it therefore
1461 : // is necessary to run the IO in it's own thread
1462 2094 : void AP_Logger::io_thread(void)
1463 : {
1464 2094 : uint32_t last_run_us = AP_HAL::micros();
1465 2094 : uint32_t last_stack_us = last_run_us;
1466 2094 : uint32_t last_crash_check_us = last_run_us;
1467 2094 : bool done_crash_dump_save = false;
1468 :
1469 : while (true) {
1470 52999716 : uint32_t now = AP_HAL::micros();
1471 :
1472 52999716 : uint32_t delay = 250U; // always have some delay
1473 52999716 : if (now - last_run_us < 1000) {
1474 52957283 : delay = MAX(1000 - (now - last_run_us), delay);
1475 : }
1476 52999716 : hal.scheduler->delay_microseconds(delay);
1477 :
1478 52997622 : last_run_us = AP_HAL::micros();
1479 :
1480 105995244 : FOR_EACH_BACKEND(io_timer());
1481 :
1482 52997622 : if (now - last_stack_us > 100000U) {
1483 1520757 : last_stack_us = now;
1484 1520757 : hal.util->log_stack_info();
1485 : }
1486 :
1487 : // check for saving a crash dump file every 5s
1488 52997622 : if (!done_crash_dump_save &&
1489 2341507 : now - last_crash_check_us > 5000000U) {
1490 1717 : last_crash_check_us = now;
1491 1717 : done_crash_dump_save = check_crash_dump_save();
1492 : }
1493 : #if HAL_LOGGER_FILE_CONTENTS_ENABLED
1494 52997622 : file_content_update();
1495 : #endif
1496 52997622 : }
1497 : }
1498 :
1499 : // start the update thread
1500 2094 : void AP_Logger::start_io_thread(void)
1501 : {
1502 2094 : WITH_SEMAPHORE(_log_send_sem);
1503 :
1504 2094 : if (_io_thread_started) {
1505 0 : return;
1506 : }
1507 :
1508 2094 : if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_Logger::io_thread, void), "log_io", HAL_LOGGING_STACK_SIZE, AP_HAL::Scheduler::PRIORITY_IO, 1)) {
1509 0 : AP_HAL::panic("Failed to start Logger IO thread");
1510 : }
1511 :
1512 2094 : _io_thread_started = true;
1513 2094 : return;
1514 2094 : }
1515 :
1516 : /* End of Write support */
1517 :
1518 : #undef FOR_EACH_BACKEND
1519 :
1520 : // Wrote an event packet
1521 39494 : void AP_Logger::Write_Event(LogEvent id)
1522 : {
1523 39494 : const struct log_Event pkt{
1524 : LOG_PACKET_HEADER_INIT(LOG_EVENT_MSG),
1525 39494 : time_us : AP_HAL::micros64(),
1526 : id : (uint8_t)id
1527 39494 : };
1528 39494 : WriteCriticalBlock(&pkt, sizeof(pkt));
1529 39494 : }
1530 :
1531 : // Write an error packet
1532 4323 : void AP_Logger::Write_Error(LogErrorSubsystem sub_system,
1533 : LogErrorCode error_code)
1534 : {
1535 4323 : const struct log_Error pkt{
1536 : LOG_PACKET_HEADER_INIT(LOG_ERROR_MSG),
1537 4323 : time_us : AP_HAL::micros64(),
1538 : sub_system : uint8_t(sub_system),
1539 : error_code : uint8_t(error_code),
1540 4323 : };
1541 4323 : WriteCriticalBlock(&pkt, sizeof(pkt));
1542 4323 : }
1543 :
1544 : /*
1545 : return true if we are in a logging persistance state, where we keep
1546 : logging after a disarm or an arming failure
1547 : */
1548 93072729 : bool AP_Logger::in_log_persistance(void) const
1549 : {
1550 93072729 : uint32_t now = AP_HAL::millis();
1551 93072655 : uint32_t persist_ms = HAL_LOGGER_ARM_PERSIST*1000U;
1552 93072655 : if (_force_long_log_persist) {
1553 : // log for 10x longer than default
1554 120385 : persist_ms *= 10U;
1555 : }
1556 :
1557 : // keep logging for HAL_LOGGER_ARM_PERSIST seconds after disarming
1558 93072655 : const uint32_t arm_change_ms = hal.util->get_last_armed_change();
1559 93072619 : if (!hal.util->get_soft_armed() && arm_change_ms != 0 && now - arm_change_ms < persist_ms) {
1560 13478190 : return true;
1561 : }
1562 :
1563 : // keep logging for HAL_LOGGER_ARM_PERSIST seconds after an arming failure
1564 79594398 : if (_last_arming_failure_ms && now - _last_arming_failure_ms < persist_ms) {
1565 494229 : return true;
1566 : }
1567 :
1568 79100169 : return false;
1569 : }
1570 :
1571 :
1572 : /*
1573 : return true if we should log while disarmed
1574 : */
1575 231149809 : bool AP_Logger::log_while_disarmed(void) const
1576 : {
1577 231149809 : if (_force_log_disarmed) {
1578 0 : return true;
1579 : }
1580 261108588 : if (_params.log_disarmed == LogDisarmed::LOG_WHILE_DISARMED ||
1581 261108584 : _params.log_disarmed == LogDisarmed::LOG_WHILE_DISARMED_DISCARD ||
1582 29958787 : (_params.log_disarmed == LogDisarmed::LOG_WHILE_DISARMED_NOT_USB && !hal.gpio->usb_connected())) {
1583 201191021 : return true;
1584 : }
1585 :
1586 29958783 : return in_log_persistance();
1587 : }
1588 :
1589 : #if HAL_LOGGER_FILE_CONTENTS_ENABLED
1590 1276 : void AP_Logger::prepare_at_arming_sys_file_logging()
1591 : {
1592 : // free existing content:
1593 1276 : at_arm_file_content.reset();
1594 :
1595 : /*
1596 : log files useful for diagnostics on arming. We log on arming as
1597 : with LOG_DISARMED we don't want to log the statistics at boot or
1598 : we wouldn't get a realistic idea of key system values
1599 : Note that some of these files may not exist, in that case they
1600 : are ignored
1601 : */
1602 : static const char *log_content_filenames[] = {
1603 : "@SYS/uarts.txt",
1604 : #ifdef HAL_DEBUG_BUILD
1605 : // logging dma.txt has a performance impact
1606 : "@SYS/dma.txt",
1607 : #endif
1608 : "@SYS/memory.txt",
1609 : "@SYS/threads.txt",
1610 : "@SYS/timers.txt",
1611 : "@ROMFS/hwdef.dat",
1612 : "@SYS/storage.bin",
1613 : "@SYS/crash_dump.bin",
1614 : "@ROMFS/defaults.parm",
1615 : "APM/CrashDump.DAT",
1616 : };
1617 14036 : for (const auto *name : log_content_filenames) {
1618 12760 : log_file_content(at_arm_file_content, name);
1619 : }
1620 1276 : }
1621 :
1622 1276 : void AP_Logger::FileContent::reset()
1623 : {
1624 1276 : WITH_SEMAPHORE(sem);
1625 1276 : file_list *next = nullptr;
1626 2139 : for (auto *c = head; c != nullptr; c = next) {
1627 863 : next = c->next;
1628 863 : delete [] c->filename;
1629 863 : delete c;
1630 : }
1631 1276 : head = nullptr;
1632 1276 : tail = nullptr;
1633 1276 : if (fd != -1) {
1634 191 : AP::FS().close(fd);
1635 191 : fd = -1;
1636 : }
1637 1276 : counter = 0;
1638 1276 : fast = false;
1639 1276 : offset = 0;
1640 1276 : }
1641 :
1642 : // removes victim from FileContent ***and delete()s it***
1643 11711 : void AP_Logger::FileContent::remove_and_free(file_list *victim)
1644 : {
1645 11711 : WITH_SEMAPHORE(sem);
1646 :
1647 11711 : file_list *prev = nullptr;
1648 11711 : for (auto *c = head; c != nullptr; prev = c, c = c->next) {
1649 11711 : if (c != victim) {
1650 0 : continue;
1651 : }
1652 :
1653 : // found the item to remove; remove it and return
1654 11711 : if (prev == nullptr) {
1655 11711 : head = victim->next;
1656 : } else {
1657 0 : prev->next = victim->next;
1658 : }
1659 11711 : delete [] victim->filename;
1660 11711 : delete victim;
1661 11711 : return;
1662 : }
1663 11711 : }
1664 :
1665 :
1666 : /*
1667 : log the content of a file in FILE log messages
1668 : */
1669 93 : void AP_Logger::log_file_content(const char *filename)
1670 : {
1671 93 : log_file_content(normal_file_content, filename);
1672 93 : }
1673 :
1674 12853 : void AP_Logger::log_file_content(FileContent &file_content, const char *filename)
1675 : {
1676 12853 : WITH_SEMAPHORE(file_content.sem);
1677 12853 : auto *file = NEW_NOTHROW file_list;
1678 12853 : if (file == nullptr) {
1679 0 : return;
1680 : }
1681 : // make copy to allow original to go out of scope
1682 12853 : const size_t len = strlen(filename)+1;
1683 12853 : char * tmp_filename = NEW_NOTHROW char[len];
1684 12853 : if (tmp_filename == nullptr) {
1685 0 : delete file;
1686 0 : return;
1687 : }
1688 12853 : strncpy(tmp_filename, filename, len);
1689 12853 : file->filename = tmp_filename;
1690 : // Remove directory if whole file name will not fit
1691 12853 : const char * name = strrchr(file->filename, '/');
1692 12853 : if ((len-1 > sizeof(file->log_filename)) && (name != nullptr)) {
1693 3921 : strncpy_noterm(file->log_filename, name+1, sizeof(file->log_filename));
1694 : } else {
1695 8932 : strncpy_noterm(file->log_filename, file->filename, sizeof(file->log_filename));
1696 : }
1697 12853 : if (file_content.head == nullptr) {
1698 1356 : file_content.tail = file_content.head = file;
1699 1356 : file_content.fd = -1;
1700 : } else {
1701 11497 : file_content.tail->next = file;
1702 11497 : file_content.tail = file;
1703 : }
1704 12853 : }
1705 :
1706 : /*
1707 : periodic call to log file content
1708 : */
1709 52997622 : void AP_Logger::file_content_update(void)
1710 : {
1711 52997622 : if (file_content_prepare_for_arming) {
1712 1276 : file_content_prepare_for_arming = false;
1713 1276 : prepare_at_arming_sys_file_logging();
1714 : }
1715 :
1716 52997622 : file_content_update(at_arm_file_content);
1717 52997622 : file_content_update(normal_file_content);
1718 52997622 : }
1719 :
1720 105995244 : void AP_Logger::file_content_update(FileContent &file_content)
1721 : {
1722 105995244 : auto *file = file_content.head;
1723 105995244 : if (file == nullptr) {
1724 105665461 : return;
1725 : }
1726 :
1727 : /* this function is called at around 100Hz on average (tested on
1728 : 400Hz copter). We don't want to saturate the logging with file
1729 : data, so we reduce the frequency of 64 byte file writes by a
1730 : factor of 10. For crash dump files we dump 10x faster so
1731 : we get it in a reasonable time (full dump of 450k in about 1
1732 : minute)
1733 : */
1734 3356980 : file_content.counter++;
1735 3356980 : const uint8_t frequency = file_content.fast?1:10;
1736 3356980 : if (file_content.counter % frequency != 0) {
1737 3015486 : return;
1738 : }
1739 :
1740 341494 : if (file_content.fd == -1) {
1741 : // open a new file
1742 11986 : file_content.fd = AP::FS().open(file->filename, O_RDONLY);
1743 22956 : file_content.fast = strncmp(file->filename, "@SYS/crash_dump", 15) == 0 ||
1744 10970 : strcmp(file->filename, "APM/CrashDump.DAT") == 0;
1745 11986 : if (file_content.fd == -1) {
1746 9361 : file_content.remove_and_free(file);
1747 9361 : return;
1748 : }
1749 2625 : file_content.offset = 0;
1750 2625 : if (file_content.fast) {
1751 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Logging %s", file->filename);
1752 : }
1753 : }
1754 :
1755 332133 : struct log_File pkt {
1756 : LOG_PACKET_HEADER_INIT(LOG_FILE_MSG),
1757 : };
1758 332133 : memcpy(pkt.filename, file->log_filename, sizeof(pkt.filename));
1759 332133 : const auto length = AP::FS().read(file_content.fd, pkt.data, sizeof(pkt.data));
1760 332133 : if (length <= 0) {
1761 2350 : AP::FS().close(file_content.fd);
1762 2350 : file_content.fd = -1;
1763 2350 : file_content.remove_and_free(file);
1764 2350 : return;
1765 : }
1766 329783 : pkt.offset = file_content.offset;
1767 329783 : pkt.length = length;
1768 329783 : if (WriteBlock_first_succeed(&pkt, sizeof(pkt))) {
1769 315470 : file_content.offset += length;
1770 : } else {
1771 : // seek back ready for another try
1772 14313 : AP::FS().lseek(file_content.fd, file_content.offset, SEEK_SET);
1773 : }
1774 : }
1775 : #endif // HAL_LOGGER_FILE_CONTENTS_ENABLED
1776 :
1777 : namespace AP {
1778 :
1779 694656197 : AP_Logger &logger()
1780 : {
1781 694656197 : return *AP_Logger::get_singleton();
1782 : }
1783 :
1784 : };
1785 :
1786 : #endif // HAL_LOGGING_ENABLED
|