LCOV - code coverage report
Current view: top level - AP_Logger - AP_Logger_File.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 390 560 69.6 %
Date: 2026-09-09 11:02:08 Functions: 33 38 86.8 %

          Line data    Source code
       1             : /* 
       2             :    AP_Logger logging - file oriented variant
       3             : 
       4             :    This uses posix file IO to create log files called logs/NN.bin in the
       5             :    given directory
       6             : 
       7             :    SD Card Rates on PixHawk:
       8             :     - deletion rate seems to be ~50 files/second.
       9             :     - stat seems to be ~150/second
      10             :     - readdir loop of 511 entry directory ~62,000 microseconds
      11             :  */
      12             : 
      13             : #include "AP_Logger_config.h"
      14             : 
      15             : #if HAL_LOGGING_FILESYSTEM_ENABLED
      16             : 
      17             : #include <AP_HAL/AP_HAL.h>
      18             : #include <AP_Filesystem/AP_Filesystem.h>
      19             : #if AP_FILESYSTEM_LITTLEFS_ENABLED
      20             : #include <AP_Filesystem/AP_Filesystem_FlashMemory_LittleFS.h>
      21             : #endif
      22             : 
      23             : #include "AP_Logger.h"
      24             : #include "AP_Logger_File.h"
      25             : 
      26             : #include <AP_Common/AP_Common.h>
      27             : #include <AP_InternalError/AP_InternalError.h>
      28             : #include <AP_RTC/AP_RTC.h>
      29             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      30             : #include <AP_AHRS/AP_AHRS.h>
      31             : 
      32             : #include <AP_Math/AP_Math.h>
      33             : #include <GCS_MAVLink/GCS.h>
      34             : #include <stdio.h>
      35             : 
      36             : 
      37             : extern const AP_HAL::HAL& hal;
      38             : 
      39             : #define LOGGER_PAGE_SIZE 1024UL
      40             : 
      41             : #define MB_to_B 1000000
      42             : #define B_to_MB 0.000001
      43             : 
      44             : // time between tries to open log
      45             : #define LOGGER_FILE_REOPEN_MS 5000
      46             : 
      47             : /*
      48             :   constructor
      49             :  */
      50        2088 : AP_Logger_File::AP_Logger_File(AP_Logger &front,
      51        2088 :                                LoggerMessageWriter_DFLogStart *writer) :
      52             :     AP_Logger_Backend(front, writer),
      53        2088 :     _log_directory(HAL_BOARD_LOG_DIRECTORY)
      54             : {
      55        2088 :     df_stats_clear();
      56        2088 : }
      57             : 
      58             : 
      59        2063 : void AP_Logger_File::ensure_log_directory_exists()
      60             : {
      61             :     int ret;
      62             :     struct stat st;
      63             : 
      64        2063 :     EXPECT_DELAY_MS(3000);
      65        2063 :     ret = AP::FS().stat(_log_directory, &st);
      66        2063 :     if (ret == -1) {
      67           1 :         ret = AP::FS().mkdir(_log_directory);
      68             :     }
      69        2063 :     if (ret == -1 && errno != EEXIST) {
      70           0 :         GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Failed to create log directory %s : %s", _log_directory, strerror(errno));
      71             :     }
      72        2063 : }
      73             : 
      74        2088 : void AP_Logger_File::Init()
      75             : {
      76             :     // determine and limit file backend buffersize
      77        2088 :     uint32_t bufsize = _front._params.file_bufsize;
      78        2088 :     bufsize *= 1024;
      79             : 
      80        2088 :     const uint32_t desired_bufsize = bufsize;
      81             : 
      82             :     // If we can't allocate the full size, try to reduce it until we can allocate it
      83        2088 :     while (!_writebuf.set_size(bufsize) && bufsize >= _writebuf_chunk) {
      84           0 :         bufsize *= 0.9;
      85             :     }
      86        2088 :     if (bufsize >= _writebuf_chunk && bufsize != desired_bufsize) {
      87           0 :         DEV_PRINTF("AP_Logger: reduced buffer %u/%u\n", (unsigned)bufsize, (unsigned)desired_bufsize);
      88             :     }
      89             : 
      90        2088 :     if (!_writebuf.get_size()) {
      91           0 :         DEV_PRINTF("Out of memory for logging\n");
      92           0 :         return;
      93             :     }
      94             : 
      95        2088 :     DEV_PRINTF("AP_Logger_File: buffer size=%u\n", (unsigned)bufsize);
      96             : 
      97        2088 :     _initialised = true;
      98             : 
      99        2088 :     const char* custom_dir = hal.util->get_custom_log_directory();
     100        2088 :     if (custom_dir != nullptr){
     101           0 :         _log_directory = custom_dir;
     102             :     }
     103             : 
     104        2088 :     uint16_t last_log_num = find_last_log();
     105        2088 :     if (last_log_is_marked_discard) {
     106             :         // delete the last log leftover from LOG_DISARMED=3
     107           0 :         char *filename = _log_file_name(last_log_num);
     108           0 :         if (filename != nullptr) {
     109           0 :             AP::FS().unlink(filename);
     110           0 :             free(filename);
     111             :         }
     112             :     }
     113             : 
     114        2088 :     Prep_MinSpace();
     115             : }
     116             : 
     117           0 : bool AP_Logger_File::file_exists(const char *filename) const
     118             : {
     119             :     struct stat st;
     120           0 :     EXPECT_DELAY_MS(3000);
     121           0 :     if (AP::FS().stat(filename, &st) == -1) {
     122             :         // hopefully errno==ENOENT.  If some error occurs it is
     123             :         // probably better to assume this file exists.
     124           0 :         return false;
     125             :     }
     126           0 :     return true;
     127           0 : }
     128             : 
     129           0 : bool AP_Logger_File::log_exists(const uint16_t lognum) const
     130             : {
     131           0 :     char *filename = _log_file_name(lognum);
     132           0 :     if (filename == nullptr) {
     133           0 :         return false; // ?!
     134             :     }
     135           0 :     bool ret = file_exists(filename);
     136           0 :     free(filename);
     137           0 :     return ret;
     138             : }
     139             : 
     140      149912 : void AP_Logger_File::periodic_1Hz()
     141             : {
     142      149912 :     AP_Logger_Backend::periodic_1Hz();
     143             : 
     144      149912 :     if (_initialised &&
     145      149912 :         _write_fd == -1 && _read_fd == -1 &&
     146        8003 :         erase.log_num == 0 &&
     147        8003 :         erase.was_logging) {
     148             :         // restart logging after an erase if needed
     149           0 :         erase.was_logging = false;
     150             :         // setup to open the log in the backend thread
     151           0 :         start_new_log_pending = true;
     152             :     }
     153             :     
     154      449736 :     if (_initialised &&
     155      149912 :         !start_new_log_pending &&
     156      157915 :         _write_fd == -1 && _read_fd == -1 &&
     157      307827 :         logging_enabled() &&
     158        1884 :         !recent_open_error()) {
     159             :         // setup to open the log in the backend thread
     160        1884 :         start_new_log_pending = true;
     161             :     }
     162             : 
     163      149912 :     if (!io_thread_alive()) {
     164           0 :         if (io_thread_warning_decimation_counter == 0 && _initialised) {
     165             :             // we don't print this error unless we did initialise. When _initialised is set to true
     166             :             // we register the IO timer callback
     167           0 :             GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "AP_Logger: stuck thread (%s)", last_io_operation);
     168             :         }
     169           0 :         if (io_thread_warning_decimation_counter++ > 30) {
     170           0 :             io_thread_warning_decimation_counter = 0;
     171             :         }
     172             :     }
     173             : 
     174      168606 :     if (rate_limiter == nullptr &&
     175       18694 :         (_front._params.file_ratemax > 0 ||
     176       16866 :          _front._params.disarm_ratemax > 0 ||
     177       16865 :          _front._log_pause)) {
     178             :         // setup rate limiting if log rate max > 0Hz or log pause of streaming entries is requested
     179        1829 :         rate_limiter = NEW_NOTHROW AP_Logger_RateLimiter(_front, _front._params.file_ratemax, _front._params.disarm_ratemax);
     180             :     }
     181      149912 : }
     182             : 
     183    41565342 : void AP_Logger_File::periodic_fullrate()
     184             : {
     185    41565342 :     AP_Logger_Backend::push_log_blocks();
     186    41565342 : }
     187             : 
     188    85162434 : uint32_t AP_Logger_File::bufferspace_available()
     189             : {
     190    85162434 :     const uint32_t space = _writebuf.space();
     191    85162407 :     const uint32_t crit = critical_message_reserved_space(_writebuf.get_size());
     192             : 
     193    85162370 :     return (space > crit) ? space - crit : 0;
     194             : }
     195             : 
     196   573849533 : bool AP_Logger_File::recent_open_error(void) const
     197             : {
     198   573849533 :     if (_open_error_ms == 0) {
     199   573850724 :         return false;
     200             :     }
     201         794 :     return AP_HAL::millis() - _open_error_ms < LOGGER_FILE_REOPEN_MS;
     202             : }
     203             : 
     204             : // return true for CardInserted() if we successfully initialized
     205      134609 : bool AP_Logger_File::CardInserted(void) const
     206             : {
     207      134609 :     return _initialised && !recent_open_error();
     208             : }
     209             : 
     210             : // returns the amount of disk space available in _log_directory (in bytes)
     211             : // returns -1 on error
     212      135293 : int64_t AP_Logger_File::disk_space_avail()
     213             : {
     214      135293 :     return AP::FS().disk_free(_log_directory);
     215             : }
     216             : 
     217             : // returns the total amount of disk space (in use + available) in
     218             : // _log_directory (in bytes).
     219             : // returns -1 on error
     220           1 : int64_t AP_Logger_File::disk_space()
     221             : {
     222           1 :     return AP::FS().disk_space(_log_directory);
     223             : }
     224             : 
     225             : /*
     226             :   convert a dirent to a log number
     227             :  */
     228     1552205 : bool AP_Logger_File::dirent_to_log_num(const dirent *de, uint16_t &log_num) const
     229             : {
     230     1552205 :     uint8_t length = strlen(de->d_name);
     231     1552205 :     if (length < 5) {
     232       40818 :         return false;
     233             :     }
     234     1511387 :     if (strncmp(&de->d_name[length-4], ".BIN", 4) != 0) {
     235             :         // doesn't end in .BIN
     236       20409 :         return false;
     237             :     }
     238             : 
     239     1490978 :     uint16_t thisnum = strtoul(de->d_name, nullptr, 10);
     240     1490978 :     if (thisnum > _front.get_max_num_logs()) {
     241        1000 :         return false;
     242             :     }
     243     1489978 :     log_num = thisnum;
     244     1489978 :     return true;
     245             : }
     246             : 
     247             : 
     248             : // find_oldest_log - find oldest log in _log_directory
     249             : // returns 0 if no log was found
     250      334021 : uint16_t AP_Logger_File::find_oldest_log()
     251             : {
     252      334021 :     if (_cached_oldest_log != 0) {
     253      331838 :         return _cached_oldest_log;
     254             :     }
     255             : 
     256        2183 :     uint16_t last_log_num = find_last_log();
     257        2183 :     if (last_log_num == 0) {
     258          12 :         return 0;
     259             :     }
     260             : 
     261        2171 :     uint16_t current_oldest_log = 0; // 0 is invalid
     262             : 
     263             :     // We could count up to find_last_log(), but if people start
     264             :     // relying on the min_avail_space_percent feature we could end up
     265             :     // doing a *lot* of asprintf()s and stat()s
     266        2171 :     EXPECT_DELAY_MS(3000);
     267        2171 :     auto *d = AP::FS().opendir(_log_directory);
     268        2171 :     if (d == nullptr) {
     269             :         // SD card may have died?  On linux someone may have rm-rf-d
     270           0 :         return 0;
     271             :     }
     272             : 
     273             :     // we only remove files which look like xxx.BIN
     274        2171 :     EXPECT_DELAY_MS(3000);
     275      525685 :     for (struct dirent *de=AP::FS().readdir(d); de; de=AP::FS().readdir(d)) {
     276      523514 :         EXPECT_DELAY_MS(3000);
     277             :         uint16_t thisnum;
     278      523514 :         if (!dirent_to_log_num(de, thisnum)) {
     279             :             // not a log filename
     280        7013 :             continue;
     281             :         }
     282      516501 :         if (current_oldest_log == 0) {
     283        2495 :             current_oldest_log = thisnum;
     284             :         } else {
     285      514006 :             if (current_oldest_log <= last_log_num) {
     286      314190 :                 if (thisnum > last_log_num) {
     287         224 :                     current_oldest_log = thisnum;
     288      313966 :                 } else if (thisnum < current_oldest_log) {
     289       11038 :                     current_oldest_log = thisnum;
     290             :                 }
     291             :             } else { // current_oldest_log > last_log_num
     292      199816 :                 if (thisnum > last_log_num) {
     293      130017 :                     if (thisnum < current_oldest_log) {
     294        1740 :                         current_oldest_log = thisnum;
     295             :                     }
     296             :                 }
     297             :             }
     298             :         }
     299      523514 :     }
     300        2171 :     AP::FS().closedir(d);
     301        2171 :     _cached_oldest_log = current_oldest_log;
     302             : 
     303        2171 :     return current_oldest_log;
     304        2171 : }
     305             : 
     306        2088 : void AP_Logger_File::Prep_MinSpace()
     307             : {
     308        2088 :     if (hal.util->was_watchdog_reset()) {
     309             :         // don't clear space if watchdog reset, it takes too long
     310           1 :         return;
     311             :     }
     312             : 
     313        2087 :     if (!CardInserted()) {
     314           0 :         return;
     315             :     }
     316             : 
     317        2087 :     const uint16_t first_log_to_remove = find_oldest_log();
     318        2087 :     if (first_log_to_remove == 0) {
     319             :         // no files to remove
     320          12 :         return;
     321             :     }
     322             : 
     323        2075 :     const int64_t target_free = (int64_t)_front._params.min_MB_free * MB_to_B;
     324             : 
     325        2075 :     uint16_t log_to_remove = first_log_to_remove;
     326             : 
     327        2075 :     uint16_t count = 0;
     328             :     do {
     329        2075 :         int64_t avail = disk_space_avail();
     330        2075 :         if (avail == -1) {
     331           0 :             break;
     332             :         }
     333        2075 :         if (avail >= target_free) {
     334        2075 :             break;
     335             :         }
     336           0 :         if (count++ > _front.get_max_num_logs() + 10) {
     337             :             // *way* too many deletions going on here.  Possible internal error.
     338           0 :             INTERNAL_ERROR(AP_InternalError::error_t::logger_too_many_deletions);
     339           0 :             break;
     340             :         }
     341           0 :         char *filename_to_remove = _log_file_name(log_to_remove);
     342           0 :         if (filename_to_remove == nullptr) {
     343           0 :             INTERNAL_ERROR(AP_InternalError::error_t::logger_bad_getfilename);
     344           0 :             break;
     345             :         }
     346           0 :         if (file_exists(filename_to_remove)) {
     347           0 :             DEV_PRINTF("Removing (%s) for minimum-space requirements (%.0fMB < %.0fMB)\n",
     348             :                                 filename_to_remove, (double)avail*B_to_MB, (double)target_free*B_to_MB);
     349           0 :             EXPECT_DELAY_MS(2000);
     350           0 :             if (AP::FS().unlink(filename_to_remove) == -1) {
     351           0 :                 _cached_oldest_log = 0;
     352           0 :                 DEV_PRINTF("Failed to remove %s: %s\n", filename_to_remove, strerror(errno));
     353           0 :                 free(filename_to_remove);
     354           0 :                 if (errno == ENOENT) {
     355             :                     // corruption - should always have a continuous
     356             :                     // sequence of files...  however, there may be still
     357             :                     // files out there, so keep going.
     358             :                 } else {
     359           0 :                     break;
     360             :                 }
     361             :             } else {
     362           0 :                 free(filename_to_remove);
     363             :             }
     364           0 :         }
     365           0 :         log_to_remove++;
     366           0 :         if (log_to_remove > _front.get_max_num_logs()) {
     367           0 :             log_to_remove = 1;
     368             :         }
     369           0 :     } while (log_to_remove != first_log_to_remove);
     370             : }
     371             : 
     372             : /*
     373             :   construct a log file name given a log number.
     374             :   The number in the log filename will be zero-padded.
     375             :   Note: Caller must free.
     376             :  */
     377       83865 : char *AP_Logger_File::_log_file_name(const uint16_t log_num) const
     378             : {
     379       83865 :     char *buf = nullptr;
     380       83865 :     if (asprintf(&buf, "%s/%08u.BIN", _log_directory, (unsigned)log_num) == -1) {
     381           0 :         return nullptr;
     382             :     }
     383       83866 :     return buf;
     384             : }
     385             : 
     386             : /*
     387             :   return path name of the lastlog.txt marker file
     388             :   Note: Caller must free.
     389             :  */
     390       26635 : char *AP_Logger_File::_lastlog_file_name(void) const
     391             : {
     392       26635 :     char *buf = nullptr;
     393       26635 :     if (asprintf(&buf, "%s/LASTLOG.TXT", _log_directory) == -1) {
     394           0 :         return nullptr;
     395             :     }
     396       26635 :     return buf;
     397             : }
     398             : 
     399             : 
     400             : // remove all log files
     401           0 : void AP_Logger_File::EraseAll()
     402             : {
     403           0 :     if (hal.util->get_soft_armed()) {
     404             :         // do not want to do any filesystem operations while we are e.g. flying
     405           0 :         return;
     406             :     }
     407           0 :     if (!_initialised) {
     408           0 :         return;
     409             :     }
     410             : 
     411           0 :     erase.was_logging = (_write_fd != -1);
     412           0 :     stop_logging();
     413             : 
     414           0 :     erase.log_num = 1;
     415             : }
     416             : 
     417   261508208 : bool AP_Logger_File::WritesOK() const
     418             : {
     419   261508208 :     if (_write_fd == -1) {
     420      403367 :         return false;
     421             :     }
     422   261104841 :     if (recent_open_error()) {
     423           1 :         return false;
     424             :     }
     425   261104938 :     return true;
     426             : }
     427             : 
     428             : 
     429   261508426 : bool AP_Logger_File::StartNewLogOK() const
     430             : {
     431   261508426 :     if (recent_open_error()) {
     432        6358 :         return false;
     433             :     }
     434             : #if !APM_BUILD_TYPE(APM_BUILD_Replay) && !APM_BUILD_TYPE(APM_BUILD_UNKNOWN)
     435   261502025 :     if (hal.scheduler->in_main_thread()) {
     436   260515258 :         return false;
     437             :     }
     438             : #endif
     439      986795 :     return AP_Logger_Backend::StartNewLogOK();
     440             : }
     441             : 
     442             : /* Write a block of data at current offset */
     443    98712627 : bool AP_Logger_File::_WritePrioritisedBlock(const void *pBuffer, uint16_t size, bool is_critical)
     444             : {
     445    98712627 :     WITH_SEMAPHORE(semaphore);
     446             : 
     447             : #if APM_BUILD_TYPE(APM_BUILD_Replay)
     448             :     if (AP::FS().write(_write_fd, pBuffer, size) != size) {
     449             :         AP_HAL::panic("Short write");
     450             :     }
     451             :     return true;
     452             : #endif
     453             : 
     454             : 
     455    98712863 :     uint32_t space = _writebuf.space();
     456             : 
     457   102493697 :     if (_writing_startup_messages &&
     458     3780834 :         _startup_messagewriter->fmt_done()) {
     459             :         // the state machine has called us, and it has finished
     460             :         // writing format messages out.  It can always get back to us
     461             :         // with more messages later, so let's leave room for other
     462             :         // things:
     463     3413963 :         const uint32_t now = AP_HAL::millis();
     464     3413963 :         const bool must_dribble = (now - last_messagewrite_message_sent) > 100;
     465     6824439 :         if (!must_dribble &&
     466     3410476 :             space < non_messagewriter_message_reserved_space(_writebuf.get_size())) {
     467             :             // this message isn't dropped, it will be sent again...
     468           6 :             return false;
     469             :         }
     470     3413957 :         last_messagewrite_message_sent = now;
     471             :     } else {
     472             :         // we reserve some amount of space for critical messages:
     473    95298900 :         if (!is_critical && space < critical_message_reserved_space(_writebuf.get_size())) {
     474         151 :             _dropped++;
     475         151 :             return false;
     476             :         }
     477             :     }
     478             : 
     479             :     // if no room for entire message - drop it:
     480    98712706 :     if (space < size) {
     481         378 :         _dropped++;
     482         378 :         return false;
     483             :     }
     484             : 
     485    98712328 :     _writebuf.write((uint8_t*)pBuffer, size);
     486    98712328 :     df_stats_gather(size, _writebuf.space());
     487    98712328 :     return true;
     488    98712863 : }
     489             : 
     490             : /*
     491             :   find the highest log number
     492             :  */
     493       24572 : uint16_t AP_Logger_File::find_last_log()
     494             : {
     495       24572 :     unsigned ret = 0;
     496       24572 :     char *fname = _lastlog_file_name();
     497       24572 :     if (fname == nullptr) {
     498           0 :         return ret;
     499             :     }
     500       24572 :     EXPECT_DELAY_MS(3000);
     501       24572 :     FileData *fd = AP::FS().load_file(fname);
     502       24572 :     free(fname);
     503       24572 :     last_log_is_marked_discard = false;
     504       24572 :     if (fd != nullptr) {
     505       24538 :         char *endptr = nullptr;
     506       24538 :         ret = strtol((const char *)fd->data, &endptr, 10);
     507       24538 :         if (endptr != nullptr) {
     508       24538 :             last_log_is_marked_discard = *endptr == 'D';
     509             :         }
     510       24538 :         delete fd;
     511             :     }
     512       24572 :     return ret;
     513       24572 : }
     514             : 
     515       41933 : uint32_t AP_Logger_File::_get_log_size(const uint16_t log_num)
     516             : {
     517       41933 :     char *fname = _log_file_name(log_num);
     518       41933 :     if (fname == nullptr) {
     519           0 :         return 0;
     520             :     }
     521       41933 :     if (_write_fd != -1 && write_fd_semaphore.take_nonblocking()) {
     522        1886 :         if (_write_filename != nullptr && strcmp(_write_filename, fname) == 0) {
     523             :             // it is the file we are currently writing
     524          20 :             free(fname);
     525          20 :             write_fd_semaphore.give();
     526          20 :             return _write_offset;
     527             :         }
     528        1866 :         write_fd_semaphore.give();
     529             :     }
     530             :     struct stat st;
     531       41913 :     EXPECT_DELAY_MS(3000);
     532       41913 :     if (AP::FS().stat(fname, &st) != 0) {
     533          10 :         free(fname);
     534          10 :         return 0;
     535             :     }
     536       41903 :     free(fname);
     537       41903 :     return st.st_size;
     538       41913 : }
     539             : 
     540       21660 : uint32_t AP_Logger_File::_get_log_time(const uint16_t log_num)
     541             : {
     542       21660 :     char *fname = _log_file_name(log_num);
     543       21660 :     if (fname == nullptr) {
     544           0 :         return 0;
     545             :     }
     546       21660 :     if (_write_fd != -1 && write_fd_semaphore.take_nonblocking()) {
     547        1809 :         if (_write_filename != nullptr && strcmp(_write_filename, fname) == 0) {
     548             :             // it is the file we are currently writing
     549          16 :             free(fname);
     550          16 :             write_fd_semaphore.give();
     551             : #if AP_RTC_ENABLED
     552             :             uint64_t utc_usec;
     553          16 :             if (!AP::rtc().get_utc_usec(utc_usec)) {
     554           1 :                 return 0;
     555             :             }
     556          15 :             return utc_usec / 1000000U;
     557             : #else
     558             :             return 0;
     559             : #endif
     560             :         }
     561        1793 :         write_fd_semaphore.give();
     562             :     }
     563             :     struct stat st;
     564       21644 :     EXPECT_DELAY_MS(3000);
     565       21644 :     if (AP::FS().stat(fname, &st) != 0) {
     566           0 :         free(fname);
     567           0 :         return 0;
     568             :     }
     569       21644 :     free(fname);
     570       21644 :     return st.st_mtime;
     571       21644 : }
     572             : 
     573             : /*
     574             :   find the number of pages in a log
     575             :  */
     576       18210 : void AP_Logger_File::get_log_boundaries(const uint16_t list_entry, uint32_t & start_page, uint32_t & end_page)
     577             : {
     578       18210 :     const uint16_t log_num = log_num_from_list_entry(list_entry);
     579       18210 :     if (log_num == 0) {
     580             :         // that failed - probably no logs
     581           0 :         start_page = 0;
     582           0 :         end_page = 0;
     583           0 :         return;
     584             :     }
     585             : 
     586       18210 :     start_page = 0;
     587       18210 :     end_page = _get_log_size(log_num) / LOGGER_PAGE_SIZE;
     588             : }
     589             : 
     590             : /*
     591             :   retrieve data from a log file
     592             :  */
     593      292064 : int16_t AP_Logger_File::get_log_data(const uint16_t list_entry, const uint16_t page, const uint32_t offset, const uint16_t len, uint8_t *data)
     594             : {
     595      292064 :     if (!_initialised) {
     596           0 :         return -1;
     597             :     }
     598             : 
     599      292064 :     const uint16_t log_num = log_num_from_list_entry(list_entry);
     600      292064 :     if (log_num == 0) {
     601             :         // that failed - probably no logs
     602           0 :         return -1;
     603             :     }
     604             : 
     605      292064 :     if (_read_fd != -1 && log_num != _read_fd_log_num) {
     606           0 :         AP::FS().close(_read_fd);
     607           0 :         _read_fd = -1;
     608             :     }
     609      292064 :     if (_read_fd == -1) {
     610       18210 :         char *fname = _log_file_name(log_num);
     611       18210 :         if (fname == nullptr) {
     612           0 :             return -1;
     613             :         }
     614       18210 :         stop_logging();
     615       18210 :         EXPECT_DELAY_MS(3000);
     616       18210 :         _read_fd = AP::FS().open(fname, O_RDONLY);
     617       18210 :         if (_read_fd == -1) {
     618           0 :             const int saved_errno = errno;
     619           0 :             if (saved_errno != ENOENT) {
     620           0 :                 _open_error_ms = AP_HAL::millis();
     621             :             }
     622           0 :             ::printf("Log read open fail for %s - %s\n",
     623             :                      fname, strerror(saved_errno));
     624           0 :             DEV_PRINTF("Log read open fail for %s - %s\n",
     625             :                                 fname, strerror(saved_errno));
     626           0 :             free(fname);
     627           0 :             return -1;
     628             :         }
     629       18210 :         free(fname);
     630       18210 :         _read_offset = 0;
     631       18210 :         _read_fd_log_num = log_num;
     632       18210 :     }
     633      292064 :     uint32_t ofs = page * (uint32_t)LOGGER_PAGE_SIZE + offset;
     634             : 
     635      292064 :     if (ofs != _read_offset) {
     636       18199 :         if (AP::FS().lseek(_read_fd, ofs, SEEK_SET) == (off_t)-1) {
     637           0 :             AP::FS().close(_read_fd);
     638           0 :             _read_fd = -1;
     639           0 :             return -1;
     640             :         }
     641       18199 :         _read_offset = ofs;
     642             :     }
     643      292064 :     int16_t ret = (int16_t)AP::FS().read(_read_fd, data, len);
     644      292064 :     if (ret > 0) {
     645      292061 :         _read_offset += ret;
     646             :     }
     647      292064 :     return ret;
     648             : }
     649             : 
     650       18217 : void AP_Logger_File::end_log_transfer()
     651             : {
     652       18217 :     if (_read_fd != -1) {
     653       18210 :         AP::FS().close(_read_fd);
     654       18210 :         _read_fd = -1;
     655             :     }
     656       18217 : }
     657             : 
     658             : /*
     659             :   find size and date of a log
     660             :  */
     661       21660 : void AP_Logger_File::get_log_info(const uint16_t list_entry, uint32_t &size, uint32_t &time_utc)
     662             : {
     663       21660 :     uint16_t log_num = log_num_from_list_entry(list_entry);
     664       21660 :     if (log_num == 0) {
     665             :         // that failed - probably no logs
     666           0 :         size = 0;
     667           0 :         time_utc = 0;
     668           0 :         return;
     669             :     }
     670             : 
     671       21660 :     size = _get_log_size(log_num);
     672       21660 :     time_utc = _get_log_time(log_num);
     673             : }
     674             : 
     675             : 
     676             : /*
     677             :   get the number of logs - note that the log numbers must be consecutive
     678             :  */
     679       18238 : uint16_t AP_Logger_File::get_num_logs()
     680             : {
     681       18238 :     auto *d = AP::FS().opendir(_log_directory);
     682       18238 :     if (d == nullptr) {
     683           0 :         return 0;
     684             :     }
     685       18238 :     uint16_t high = find_last_log();
     686       18238 :     uint16_t ret = high;
     687       18238 :     uint16_t smallest_above_last = 0;
     688             : 
     689       18238 :     EXPECT_DELAY_MS(2000);
     690     1046929 :     for (struct dirent *de=AP::FS().readdir(d); de; de=AP::FS().readdir(d)) {
     691     1028691 :         EXPECT_DELAY_MS(100);
     692             :         uint16_t thisnum;
     693     1028691 :         if (!dirent_to_log_num(de, thisnum)) {
     694             :             // not a log filename
     695       55214 :             continue;
     696             :         }
     697             : 
     698      973477 :         if (thisnum > high && (smallest_above_last == 0 || thisnum < smallest_above_last)) {
     699          30 :             smallest_above_last = thisnum;
     700             :         }
     701     1028691 :     }
     702       18238 :     AP::FS().closedir(d);
     703       18238 :     if (smallest_above_last != 0) {
     704             :         // we have wrapped, add in the logs with high numbers
     705           6 :         ret += (_front.get_max_num_logs() - smallest_above_last) + 1;
     706             :     }
     707             : 
     708       18238 :     return ret;
     709       18238 : }
     710             : 
     711             : /*
     712             :   stop logging
     713             :  */
     714       20322 : void AP_Logger_File::stop_logging(void)
     715             : {
     716             :     // best-case effort to avoid annoying the IO thread
     717       20322 :     const bool have_sem = write_fd_semaphore.take(hal.util->get_soft_armed()?1:20);
     718       20322 :     if (_write_fd != -1) {
     719         137 :         int fd = _write_fd;
     720         137 :         _write_fd = -1;
     721         137 :         AP::FS().close(fd);
     722             :     }
     723       20322 :     if (have_sem) {
     724       20322 :         write_fd_semaphore.give();
     725             :     }
     726       20322 : }
     727             : 
     728             : /*
     729             :   does start_new_log in the logger thread
     730             :  */
     731         179 : void AP_Logger_File::PrepForArming_start_logging()
     732             : {
     733         179 :     if (logging_started()) {
     734           0 :         return;
     735             :     }
     736             : 
     737         179 :     uint32_t start_ms = AP_HAL::millis();
     738         179 :     const uint32_t open_limit_ms = 1000;
     739             : 
     740             :     /*
     741             :       log open happens in the io_timer thread. We allow for a maximum
     742             :       of 1s to complete the open
     743             :      */
     744         179 :     start_new_log_pending = true;
     745         179 :     EXPECT_DELAY_MS(1000);
     746         642 :     while (AP_HAL::millis() - start_ms < open_limit_ms) {
     747         642 :         if (logging_started()) {
     748         179 :             break;
     749             :         }
     750             : #if !APM_BUILD_TYPE(APM_BUILD_Replay) && AP_AHRS_ENABLED
     751             :         // keep the EKF ticking over
     752         463 :         AP::ahrs().update();
     753             : #endif
     754         463 :         hal.scheduler->delay(1);
     755             :     }
     756         179 : }
     757             : 
     758             : /*
     759             :   start writing to a new log file
     760             :  */
     761        2063 : void AP_Logger_File::start_new_log(void)
     762             : {
     763        2063 :     if (recent_open_error()) {
     764             :         // we have previously failed to open a file - don't try again
     765             :         // to prevent us trying to open files while in flight
     766           0 :         return;
     767             :     }
     768             : 
     769        2063 :     if (erase.log_num != 0) {
     770             :         // don't start a new log while erasing, but record that we
     771             :         // want to start logging when erase finished
     772           0 :         erase.was_logging = true;
     773           0 :         return;
     774             :     }
     775             : 
     776        2063 :     const bool open_error_ms_was_zero = (_open_error_ms == 0);
     777             : 
     778             :     // set _open_error here to avoid infinite recursion.  Simply
     779             :     // writing a prioritised block may try to open a log - which means
     780             :     // if anything in the start_new_log path does a GCS_SEND_TEXT()
     781             :     // (for example), you will end up recursing if we don't take
     782             :     // precautions.  We will reset _open_error if we actually manage
     783             :     // to open the log...
     784        2063 :     _open_error_ms = AP_HAL::millis();
     785             : 
     786        2063 :     stop_logging();
     787             : 
     788        2063 :     start_new_log_reset_variables();
     789             : 
     790        2063 :     if (_read_fd != -1) {
     791           0 :         AP::FS().close(_read_fd);
     792           0 :         _read_fd = -1;
     793             :     }
     794             : 
     795        2063 :     if (disk_space_avail() < _free_space_min_avail && disk_space() > 0) {
     796           0 :         DEV_PRINTF("Out of space for logging\n");
     797           0 :         return;
     798             :     }
     799             : 
     800        2063 :     uint16_t log_num = find_last_log();
     801             :     // re-use empty logs if possible
     802        2063 :     if (_get_log_size(log_num) > 0 || log_num == 0) {
     803        2001 :         log_num++;
     804             :     }
     805        2063 :     if (log_num > _front.get_max_num_logs()) {
     806           6 :         log_num = 1;
     807             :     }
     808        2063 :     if (!write_fd_semaphore.take(1)) {
     809           0 :         return;
     810             :     }
     811        2063 :     if (_write_filename) {
     812         130 :         free(_write_filename);
     813         130 :         _write_filename = nullptr;        
     814             :     }
     815        2063 :     _write_filename = _log_file_name(log_num);
     816        2063 :     if (_write_filename == nullptr) {
     817           0 :         write_fd_semaphore.give();
     818           0 :         return;
     819             :     }
     820             : 
     821             : #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
     822             :     // remember if we had utc time when we opened the file
     823             : #if AP_RTC_ENABLED
     824             :     uint64_t utc_usec;
     825             :     _need_rtc_update = !AP::rtc().get_utc_usec(utc_usec);
     826             : #endif
     827             : #endif
     828             : 
     829             :     // create the log directory if need be
     830        2063 :     ensure_log_directory_exists();
     831             : 
     832        2063 :     EXPECT_DELAY_MS(3000);
     833        2063 :     _writebuf.clear();
     834        2063 :     _write_fd = AP::FS().open(_write_filename, O_WRONLY|O_CREAT|O_TRUNC);
     835        2063 :     _cached_oldest_log = 0;
     836             : 
     837        2063 :     if (_write_fd == -1) {
     838           0 :         write_fd_semaphore.give();
     839           0 :         int saved_errno = errno;
     840           0 :         if (open_error_ms_was_zero) {
     841           0 :             ::printf("Log open fail for %s - %s\n",
     842             :                      _write_filename, strerror(saved_errno));
     843           0 :             DEV_PRINTF("Log open fail for %s - %s\n",
     844             :                                 _write_filename, strerror(saved_errno));
     845             :         }
     846           0 :         return;
     847             :     }
     848        2063 :     _last_write_ms = AP_HAL::millis();
     849        2063 :     _open_error_ms = 0;
     850        2063 :     _write_offset = 0;
     851        2063 :     write_fd_semaphore.give();
     852             : 
     853             :     // now update lastlog.txt with the new log number
     854        2063 :     last_log_is_marked_discard = _front._params.log_disarmed == AP_Logger::LogDisarmed::LOG_WHILE_DISARMED_DISCARD;
     855        2063 :     if (!write_lastlog_file(log_num)) {
     856           0 :         _open_error_ms = AP_HAL::millis();
     857             :     }
     858        2063 : }
     859             : 
     860             : /*
     861             :   write LASTLOG.TXT, possibly with a discard marker
     862             :  */
     863        2063 : bool AP_Logger_File::write_lastlog_file(uint16_t log_num)
     864             : {
     865             :     // now update lastlog.txt with the new log number
     866        2063 :     char *fname = _lastlog_file_name();
     867             : 
     868        2063 :     EXPECT_DELAY_MS(3000);
     869        2063 :     int fd = AP::FS().open(fname, O_WRONLY|O_CREAT);
     870        2063 :     free(fname);
     871        2063 :     if (fd == -1) {
     872           0 :         return false;
     873             :     }
     874             : 
     875             :     char buf[30];
     876        2063 :     snprintf(buf, sizeof(buf), "%u%s\r\n", (unsigned)log_num, last_log_is_marked_discard?"D":"");
     877        2063 :     const ssize_t to_write = strlen(buf);
     878        2063 :     const ssize_t written = AP::FS().write(fd, buf, to_write);
     879        2063 :     AP::FS().close(fd);
     880        2063 :     return written == to_write;
     881        2063 : }
     882             : 
     883             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL || CONFIG_HAL_BOARD == HAL_BOARD_LINUX
     884           0 : void AP_Logger_File::flush(void)
     885             : #if APM_BUILD_TYPE(APM_BUILD_Replay) || APM_BUILD_TYPE(APM_BUILD_UNKNOWN)
     886             : {
     887           0 :     uint32_t tnow = AP_HAL::millis();
     888           0 :     while (_write_fd != -1 && _initialised && !recent_open_error() && _writebuf.available()) {
     889             :         // convince the IO timer that it really is OK to write out
     890             :         // less than _writebuf_chunk bytes:
     891           0 :         if (tnow > 2001) { // avoid resetting _last_write_time to 0
     892           0 :             _last_write_time = tnow - 2001;
     893             :         }
     894           0 :         io_timer();
     895             :     }
     896           0 :     if (write_fd_semaphore.take(1)) {
     897           0 :         if (_write_fd != -1) {
     898           0 :             ::fsync(_write_fd);
     899             :         }
     900           0 :         write_fd_semaphore.give();
     901             :     } else {
     902           0 :         INTERNAL_ERROR(AP_InternalError::error_t::logger_flushing_without_sem);
     903             :     }
     904           0 : }
     905             : #else
     906             : {
     907             :     // flush is for replay and examples only
     908           0 : }
     909             : #endif // APM_BUILD_TYPE(APM_BUILD_Replay) || APM_BUILD_TYPE(APM_BUILD_UNKNOWN)
     910             : #endif
     911             : 
     912    52836791 : void AP_Logger_File::io_timer(void)
     913             : {
     914    52836791 :     uint32_t tnow = AP_HAL::millis();
     915    52836791 :     _io_timer_heartbeat = tnow;
     916             : 
     917    52836791 :     if (start_new_log_pending) {
     918        2063 :         start_new_log();
     919        2063 :         start_new_log_pending = false;
     920             :     }
     921             : 
     922    52836791 :     if (erase.log_num != 0) {
     923             :         // continue erase
     924           0 :         erase_next();
     925    51810763 :         return;
     926             :     }
     927             : 
     928    52836791 :     if (_write_fd == -1 || !_initialised || recent_open_error()) {
     929     2956876 :         return;
     930             :     }
     931             : 
     932    49879915 :     if (last_log_is_marked_discard && hal.util->get_soft_armed()) {
     933             :         // time to make the log permanent
     934           0 :         const auto log_num = find_last_log();
     935           0 :         last_log_is_marked_discard = false;
     936           0 :         write_lastlog_file(log_num);
     937             :     }
     938             : 
     939    49879915 :     uint32_t nbytes = _writebuf.available();
     940    49879915 :     if (nbytes == 0) {
     941       46020 :         return;
     942             :     }
     943    49833895 :     if (nbytes < _writebuf_chunk && 
     944    48808498 :         tnow - _last_write_time < 2000UL) {
     945             :         // write in _writebuf_chunk-sized chunks, but always write at
     946             :         // least once per 2 seconds if data is available
     947    48807867 :         return;
     948             :     }
     949             : 
     950             : #if !AP_FILESYSTEM_LITTLEFS_ENABLED // too expensive on littlefs, rely on ENOSPC below
     951     1026028 :     if (tnow - _free_space_last_check_time > _free_space_check_interval) {
     952      131155 :         _free_space_last_check_time = tnow;
     953      131155 :         last_io_operation = "disk_space_avail";
     954      131155 :         if (disk_space_avail() < _free_space_min_avail && disk_space() > 0) {
     955           0 :             DEV_PRINTF("Out of space for logging\n");
     956           0 :             stop_logging();
     957           0 :             _open_error_ms = AP_HAL::millis(); // prevent logging starting again for 5s
     958           0 :             last_io_operation = "";
     959           0 :             return;
     960             :         }
     961      131155 :         last_io_operation = "";
     962             :     }
     963             : #endif
     964     1026028 :     _last_write_time = tnow;
     965     1026028 :     if (nbytes > _writebuf_chunk) {
     966             :         // be kind to the filesystem layer
     967     1022740 :         nbytes = _writebuf_chunk;
     968             :     }
     969             : 
     970             :     uint32_t size;
     971     1026028 :     const uint8_t *head = _writebuf.readptr(size);
     972     1026028 :     nbytes = MIN(nbytes, size);
     973             : 
     974             : #if !AP_FILESYSTEM_LITTLEFS_ENABLED
     975             :     // try to align writes on a 512 byte boundary to avoid filesystem reads
     976     1026028 :     if ((nbytes + _write_offset) % 512 != 0) {
     977         789 :         uint32_t ofs = (nbytes + _write_offset) % 512;
     978         789 :         if (ofs < nbytes) {
     979         621 :             nbytes -= ofs;
     980             :         }
     981             :     }
     982             : #endif
     983     1026028 :     last_io_operation = "write";
     984     1026028 :     if (!write_fd_semaphore.take(1)) {
     985           0 :         return;
     986             :     }
     987     1026028 :     if (_write_fd == -1) {
     988           0 :         write_fd_semaphore.give();
     989           0 :         return;
     990             :     }
     991             : 
     992     1026028 :     uint32_t bytes_until_fsync = AP::FS().bytes_until_fsync(_write_fd);
     993     1026028 :     if (bytes_until_fsync > 0 && nbytes > bytes_until_fsync) {
     994           0 :         nbytes = bytes_until_fsync; // write exactly enough to sync
     995             :     }
     996             : 
     997     1026028 :     ssize_t nwritten = AP::FS().write(_write_fd, head, nbytes);
     998     1026028 :     last_io_operation = "";
     999     1026028 :     if (nwritten <= 0) {
    1000           0 :         if (errno == ENOSPC) {
    1001           0 :             DEV_PRINTF("Out of space for logging\n");
    1002           0 :             stop_logging();
    1003           0 :             _open_error_ms = AP_HAL::millis(); // prevent logging starting again for 5s
    1004           0 :             last_io_operation = "";
    1005           0 :         } else if ((tnow - _last_write_ms)/1000U > unsigned(_front._params.file_timeout)) {
    1006             :             // if we can't write for LOG_FILE_TIMEOUT seconds we give up and close
    1007             :             // the file. This allows us to cope with temporary write
    1008             :             // failures caused by directory listing
    1009           0 :             last_io_operation = "close";
    1010           0 :             AP::FS().close(_write_fd);
    1011           0 :             last_io_operation = "";
    1012           0 :             _write_fd = -1;
    1013           0 :             printf("Failed to write to File: %s\n", strerror(errno));
    1014             :         }
    1015           0 :         _last_write_failed = true;
    1016             :     } else {
    1017     1026028 :         _last_write_failed = false;
    1018     1026028 :         _last_write_ms = tnow;
    1019     1026028 :         _write_offset += nwritten;
    1020     1026028 :         _writebuf.advance(nwritten);
    1021             : 
    1022             :         // we know nwritten > 0 so we won't sync if bytes_until_fsync == 0
    1023     1026028 :         if ((uint32_t)nwritten == bytes_until_fsync) {
    1024           0 :             last_io_operation = "fsync";
    1025           0 :             AP::FS().fsync(_write_fd);
    1026           0 :             last_io_operation = "";
    1027             :         }
    1028             : 
    1029             : #if AP_RTC_ENABLED && CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
    1030             :         // ChibiOS does not update mtime on writes, so if we opened
    1031             :         // without knowing the time we should update it later
    1032             :         if (_need_rtc_update) {
    1033             :             uint64_t utc_usec;
    1034             :             if (AP::rtc().get_utc_usec(utc_usec)) {
    1035             :                 AP::FS().set_mtime(_write_filename, utc_usec/(1000U*1000U));
    1036             :                 _need_rtc_update = false;
    1037             :             }
    1038             :         }
    1039             : #endif
    1040             :     }
    1041             : 
    1042     1026028 :     write_fd_semaphore.give();
    1043             : }
    1044             : 
    1045     1398826 : bool AP_Logger_File::io_thread_alive() const
    1046             : {
    1047     1398826 :     if (!hal.scheduler->is_system_initialized()) {
    1048             :         // the system has long pauses during initialisation, assume still OK
    1049         357 :         return true;
    1050             :     }
    1051             :     // if the io thread hasn't had a heartbeat in a while then it is
    1052             : #if CONFIG_HAL_BOARD == HAL_BOARD_ESP32
    1053             :     uint32_t timeout_ms = 10000;
    1054             : #else
    1055     1398469 :     uint32_t timeout_ms = 5000;
    1056             : #endif
    1057             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL && !defined(HAL_BUILD_AP_PERIPH)
    1058             :     // the IO thread is working with hardware - writing to a physical
    1059             :     // disk.  Unfortunately these hardware devices do not obey our
    1060             :     // SITL speedup options, so we allow for it here.
    1061     1398469 :     SITL::SIM *sitl = AP::sitl();
    1062     1398469 :     if (sitl != nullptr && sitl->speedup > 0) {
    1063     1398469 :         timeout_ms *= sitl->speedup;
    1064             :     }
    1065             : #endif
    1066     1398469 :     return (AP_HAL::millis() - _io_timer_heartbeat) < timeout_ms;
    1067             : }
    1068             : 
    1069     1248934 : bool AP_Logger_File::logging_failed() const
    1070             : {
    1071     1248934 :     if (!_initialised) {
    1072           0 :         return true;
    1073             :     }
    1074     1248934 :     if (recent_open_error()) {
    1075          20 :         return true;
    1076             :     }
    1077     1248914 :     if (!io_thread_alive()) {
    1078             :         // No heartbeat in a second.  IO thread is dead?! Very Not
    1079             :         // Good.
    1080           0 :         return true;
    1081             :     }
    1082     1248914 :     if (_last_write_failed) {
    1083           0 :         return true;
    1084             :     }
    1085             : 
    1086     1248914 :     return false;
    1087             : }
    1088             : 
    1089             : /*
    1090             :   erase another file in async erase operation
    1091             :  */
    1092           0 : void AP_Logger_File::erase_next(void)
    1093             : {
    1094           0 :     char *fname = _log_file_name(erase.log_num);
    1095           0 :     if (fname == nullptr) {
    1096           0 :         erase.log_num = 0;
    1097           0 :         return;
    1098             :     }
    1099             : 
    1100           0 :     AP::FS().unlink(fname);
    1101           0 :     free(fname);
    1102             : 
    1103           0 :     erase.log_num++;
    1104           0 :     if (erase.log_num <= _front.get_max_num_logs()) {
    1105           0 :         return;
    1106             :     }
    1107             :     
    1108           0 :     fname = _lastlog_file_name();
    1109           0 :     if (fname != nullptr) {
    1110           0 :         AP::FS().unlink(fname);
    1111           0 :         free(fname);
    1112             :     }
    1113             : 
    1114           0 :     _cached_oldest_log = 0;
    1115             : 
    1116           0 :     erase.log_num = 0;
    1117             : }
    1118             : 
    1119             : #endif // HAL_LOGGING_FILESYSTEM_ENABLED
    1120             : 

Generated by: LCOV version 1.14