123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131 |
- /*
- AP_Logger logging - file oriented variant
- This uses posix file IO to create log files called logs/NN.bin in the
- given directory
- SD Card Rates on PixHawk:
- - deletion rate seems to be ~50 files/second.
- - stat seems to be ~150/second
- - readdir loop of 511 entry directory ~62,000 microseconds
- */
- #include <AP_HAL/AP_HAL.h>
- #include <AP_Filesystem/AP_Filesystem.h>
- #if HAVE_FILESYSTEM_SUPPORT
- #include "AP_Logger_File.h"
- #include <AP_Common/AP_Common.h>
- #include <AP_InternalError/AP_InternalError.h>
- #include <AP_RTC/AP_RTC.h>
- #include <AP_Math/AP_Math.h>
- #include <GCS_MAVLink/GCS.h>
- #include <stdio.h>
- extern const AP_HAL::HAL& hal;
- #define MAX_LOG_FILES 500U
- #define LOGGER_PAGE_SIZE 1024UL
- #ifndef HAL_LOGGER_WRITE_CHUNK_SIZE
- #define HAL_LOGGER_WRITE_CHUNK_SIZE 4096
- #endif
- /*
- constructor
- */
- AP_Logger_File::AP_Logger_File(AP_Logger &front,
- LoggerMessageWriter_DFLogStart *writer,
- const char *log_directory) :
- AP_Logger_Backend(front, writer),
- _write_fd(-1),
- _read_fd(-1),
- _log_directory(log_directory),
- _writebuf(0),
- _writebuf_chunk(HAL_LOGGER_WRITE_CHUNK_SIZE),
- _perf_write(hal.util->perf_alloc(AP_HAL::Util::PC_ELAPSED, "DF_write")),
- _perf_fsync(hal.util->perf_alloc(AP_HAL::Util::PC_ELAPSED, "DF_fsync")),
- _perf_errors(hal.util->perf_alloc(AP_HAL::Util::PC_COUNT, "DF_errors")),
- _perf_overruns(hal.util->perf_alloc(AP_HAL::Util::PC_COUNT, "DF_overruns"))
- {
- df_stats_clear();
- }
- void AP_Logger_File::Init()
- {
- AP_Logger_Backend::Init();
- // create the log directory if need be
- int ret;
- struct stat st;
- const char* custom_dir = hal.util->get_custom_log_directory();
- if (custom_dir != nullptr){
- _log_directory = custom_dir;
- }
- EXPECT_DELAY_MS(3000);
- ret = AP::FS().stat(_log_directory, &st);
- if (ret == -1) {
- ret = AP::FS().mkdir(_log_directory);
- }
- if (ret == -1 && errno != EEXIST) {
- printf("Failed to create log directory %s : %s\n", _log_directory, strerror(errno));
- }
- // determine and limit file backend buffersize
- uint32_t bufsize = _front._params.file_bufsize;
- if (bufsize > 64) {
- bufsize = 64; // PixHawk has DMA limitations.
- }
- bufsize *= 1024;
- const uint32_t desired_bufsize = bufsize;
- // If we can't allocate the full size, try to reduce it until we can allocate it
- while (!_writebuf.set_size(bufsize) && bufsize >= _writebuf_chunk) {
- bufsize *= 0.9;
- }
- if (bufsize >= _writebuf_chunk && bufsize != desired_bufsize) {
- hal.console->printf("AP_Logger: reduced buffer %u/%u\n", (unsigned)bufsize, (unsigned)desired_bufsize);
- }
- if (!_writebuf.get_size()) {
- hal.console->printf("Out of memory for logging\n");
- return;
- }
- hal.console->printf("AP_Logger_File: buffer size=%u\n", (unsigned)bufsize);
- _initialised = true;
- hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&AP_Logger_File::_io_timer, void));
- }
- bool AP_Logger_File::file_exists(const char *filename) const
- {
- struct stat st;
- EXPECT_DELAY_MS(3000);
- if (AP::FS().stat(filename, &st) == -1) {
- // hopefully errno==ENOENT. If some error occurs it is
- // probably better to assume this file exists.
- return false;
- }
- return true;
- }
- bool AP_Logger_File::log_exists(const uint16_t lognum) const
- {
- char *filename = _log_file_name(lognum);
- if (filename == nullptr) {
- return false; // ?!
- }
- bool ret = file_exists(filename);
- free(filename);
- return ret;
- }
- void AP_Logger_File::periodic_1Hz()
- {
- if (_rotate_pending && !logging_enabled()) {
- _rotate_pending = false;
- // handle log rotation once we stop logging
- stop_logging();
- }
- if (!io_thread_alive()) {
- if (io_thread_warning_decimation_counter == 0 && _initialised) {
- // we don't print this error unless we did initialise. When _initialised is set to true
- // we register the IO timer callback
- gcs().send_text(MAV_SEVERITY_CRITICAL, "AP_Logger: stuck thread (%s)", last_io_operation);
- }
- if (io_thread_warning_decimation_counter++ > 57) {
- io_thread_warning_decimation_counter = 0;
- }
- // If you try to close the file here then it will almost
- // certainly block. Since this is the main thread, this is
- // likely to cause a crash.
- // semaphore_write_fd not taken here as if the io thread is
- // dead it may not release lock...
- _write_fd = -1;
- _initialised = false;
- }
- df_stats_log();
- }
- void AP_Logger_File::periodic_fullrate()
- {
- AP_Logger_Backend::push_log_blocks();
- }
- uint32_t AP_Logger_File::bufferspace_available()
- {
- const uint32_t space = _writebuf.space();
- const uint32_t crit = critical_message_reserved_space();
- return (space > crit) ? space - crit : 0;
- }
- // return true for CardInserted() if we successfully initialized
- bool AP_Logger_File::CardInserted(void) const
- {
- return _initialised && !_open_error;
- }
- // returns the amount of disk space available in _log_directory (in bytes)
- // returns -1 on error
- int64_t AP_Logger_File::disk_space_avail()
- {
- return AP::FS().disk_free(_log_directory);
- }
- // returns the total amount of disk space (in use + available) in
- // _log_directory (in bytes).
- // returns -1 on error
- int64_t AP_Logger_File::disk_space()
- {
- return AP::FS().disk_space(_log_directory);
- }
- // returns the available space in _log_directory as a percentage
- // returns -1.0f on error
- float AP_Logger_File::avail_space_percent()
- {
- int64_t avail = disk_space_avail();
- if (avail == -1) {
- return -1.0f;
- }
- int64_t space = disk_space();
- if (space == -1) {
- return -1.0f;
- }
- return (avail/(float)space) * 100;
- }
- // find_oldest_log - find oldest log in _log_directory
- // returns 0 if no log was found
- uint16_t AP_Logger_File::find_oldest_log()
- {
- if (_cached_oldest_log != 0) {
- return _cached_oldest_log;
- }
- uint16_t last_log_num = find_last_log();
- if (last_log_num == 0) {
- return 0;
- }
- uint16_t current_oldest_log = 0; // 0 is invalid
- // We could count up to find_last_log(), but if people start
- // relying on the min_avail_space_percent feature we could end up
- // doing a *lot* of asprintf()s and stat()s
- EXPECT_DELAY_MS(3000);
- DIR *d = AP::FS().opendir(_log_directory);
- if (d == nullptr) {
- // SD card may have died? On linux someone may have rm-rf-d
- return 0;
- }
- // we only remove files which look like xxx.BIN
- EXPECT_DELAY_MS(3000);
- for (struct dirent *de=AP::FS().readdir(d); de; de=AP::FS().readdir(d)) {
- EXPECT_DELAY_MS(3000);
- uint8_t length = strlen(de->d_name);
- if (length < 5) {
- // not long enough for \d+[.]BIN
- continue;
- }
- if (strncmp(&de->d_name[length-4], ".BIN", 4)) {
- // doesn't end in .BIN
- continue;
- }
- uint16_t thisnum = strtoul(de->d_name, nullptr, 10);
- if (thisnum > MAX_LOG_FILES) {
- // ignore files above our official maximum...
- continue;
- }
- if (current_oldest_log == 0) {
- current_oldest_log = thisnum;
- } else {
- if (current_oldest_log <= last_log_num) {
- if (thisnum > last_log_num) {
- current_oldest_log = thisnum;
- } else if (thisnum < current_oldest_log) {
- current_oldest_log = thisnum;
- }
- } else { // current_oldest_log > last_log_num
- if (thisnum > last_log_num) {
- if (thisnum < current_oldest_log) {
- current_oldest_log = thisnum;
- }
- }
- }
- }
- }
- AP::FS().closedir(d);
- _cached_oldest_log = current_oldest_log;
- return current_oldest_log;
- }
- void AP_Logger_File::Prep_MinSpace()
- {
- if (hal.util->was_watchdog_reset()) {
- // don't clear space if watchdog reset, it takes too long
- return;
- }
- const uint16_t first_log_to_remove = find_oldest_log();
- if (first_log_to_remove == 0) {
- // no files to remove
- return;
- }
- _cached_oldest_log = 0;
- uint16_t log_to_remove = first_log_to_remove;
- uint16_t count = 0;
- do {
- float avail = avail_space_percent();
- if (is_equal(avail, -1.0f)) {
- break;
- }
- if (avail >= min_avail_space_percent) {
- break;
- }
- if (count++ > MAX_LOG_FILES+10) {
- // *way* too many deletions going on here. Possible internal error.
- AP::internalerror().error(AP_InternalError::error_t::logger_too_many_deletions);
- break;
- }
- char *filename_to_remove = _log_file_name(log_to_remove);
- if (filename_to_remove == nullptr) {
- AP::internalerror().error(AP_InternalError::error_t::logger_bad_getfilename);
- break;
- }
- if (file_exists(filename_to_remove)) {
- hal.console->printf("Removing (%s) for minimum-space requirements (%.2f%% < %.0f%%)\n",
- filename_to_remove, (double)avail, (double)min_avail_space_percent);
- EXPECT_DELAY_MS(2000);
- if (AP::FS().unlink(filename_to_remove) == -1) {
- hal.console->printf("Failed to remove %s: %s\n", filename_to_remove, strerror(errno));
- free(filename_to_remove);
- if (errno == ENOENT) {
- // corruption - should always have a continuous
- // sequence of files... however, there may be still
- // files out there, so keep going.
- } else {
- break;
- }
- } else {
- free(filename_to_remove);
- }
- }
- log_to_remove++;
- if (log_to_remove > MAX_LOG_FILES) {
- log_to_remove = 1;
- }
- } while (log_to_remove != first_log_to_remove);
- }
- void AP_Logger_File::Prep() {
- if (!NeedPrep()) {
- return;
- }
- if (hal.util->get_soft_armed()) {
- // do not want to do any filesystem operations while we are e.g. flying
- return;
- }
- Prep_MinSpace();
- }
- bool AP_Logger_File::NeedPrep()
- {
- if (!CardInserted()) {
- // should not have been called?!
- return false;
- }
- if (avail_space_percent() < min_avail_space_percent) {
- return true;
- }
- return false;
- }
- /*
- construct a log file name given a log number.
- The number in the log filename will *not* be zero-padded.
- Note: Caller must free.
- */
- char *AP_Logger_File::_log_file_name_short(const uint16_t log_num) const
- {
- char *buf = nullptr;
- if (asprintf(&buf, "%s/%u.BIN", _log_directory, (unsigned)log_num) == -1) {
- return nullptr;
- }
- return buf;
- }
- /*
- construct a log file name given a log number.
- The number in the log filename will be zero-padded.
- Note: Caller must free.
- */
- char *AP_Logger_File::_log_file_name_long(const uint16_t log_num) const
- {
- char *buf = nullptr;
- if (asprintf(&buf, "%s/%08u.BIN", _log_directory, (unsigned)log_num) == -1) {
- return nullptr;
- }
- return buf;
- }
- /*
- return a log filename appropriate for the supplied log_num if a
- filename exists with the short (not-zero-padded name) then it is the
- appropirate name, otherwise the long (zero-padded) version is.
- Note: Caller must free.
- */
- char *AP_Logger_File::_log_file_name(const uint16_t log_num) const
- {
- char *filename = _log_file_name_short(log_num);
- if (filename == nullptr) {
- return nullptr;
- }
- if (file_exists(filename)) {
- return filename;
- }
- free(filename);
- return _log_file_name_long(log_num);
- }
- /*
- return path name of the lastlog.txt marker file
- Note: Caller must free.
- */
- char *AP_Logger_File::_lastlog_file_name(void) const
- {
- char *buf = nullptr;
- if (asprintf(&buf, "%s/LASTLOG.TXT", _log_directory) == -1) {
- return nullptr;
- }
- return buf;
- }
- // remove all log files
- void AP_Logger_File::EraseAll()
- {
- if (hal.util->get_soft_armed()) {
- // do not want to do any filesystem operations while we are e.g. flying
- return;
- }
- if (!_initialised) {
- return;
- }
- const bool was_logging = (_write_fd != -1);
- stop_logging();
- for (uint16_t log_num=1; log_num<=MAX_LOG_FILES; log_num++) {
- char *fname = _log_file_name(log_num);
- if (fname == nullptr) {
- break;
- }
- EXPECT_DELAY_MS(3000);
- AP::FS().unlink(fname);
- free(fname);
- }
- char *fname = _lastlog_file_name();
- if (fname != nullptr) {
- AP::FS().unlink(fname);
- free(fname);
- }
- _cached_oldest_log = 0;
- if (was_logging) {
- start_new_log();
- }
- }
- bool AP_Logger_File::WritesOK() const
- {
- if (_write_fd == -1) {
- return false;
- }
- if (_open_error) {
- return false;
- }
- return true;
- }
- bool AP_Logger_File::StartNewLogOK() const
- {
- if (_open_error) {
- return false;
- }
- return AP_Logger_Backend::StartNewLogOK();
- }
- /* Write a block of data at current offset */
- bool AP_Logger_File::_WritePrioritisedBlock(const void *pBuffer, uint16_t size, bool is_critical)
- {
- if (! WriteBlockCheckStartupMessages()) {
- _dropped++;
- return false;
- }
- if (!semaphore.take(1)) {
- return false;
- }
-
- uint32_t space = _writebuf.space();
- if (_writing_startup_messages &&
- _startup_messagewriter->fmt_done()) {
- // the state machine has called us, and it has finished
- // writing format messages out. It can always get back to us
- // with more messages later, so let's leave room for other
- // things:
- const uint32_t now = AP_HAL::millis();
- const bool must_dribble = (now - last_messagewrite_message_sent) > 100;
- if (!must_dribble &&
- space < non_messagewriter_message_reserved_space()) {
- // this message isn't dropped, it will be sent again...
- semaphore.give();
- return false;
- }
- last_messagewrite_message_sent = now;
- } else {
- // we reserve some amount of space for critical messages:
- if (!is_critical && space < critical_message_reserved_space()) {
- _dropped++;
- semaphore.give();
- return false;
- }
- }
- // if no room for entire message - drop it:
- if (space < size) {
- hal.util->perf_count(_perf_overruns);
- _dropped++;
- semaphore.give();
- return false;
- }
- _writebuf.write((uint8_t*)pBuffer, size);
- df_stats_gather(size);
- semaphore.give();
- return true;
- }
- /*
- find the highest log number
- */
- uint16_t AP_Logger_File::find_last_log()
- {
- unsigned ret = 0;
- char *fname = _lastlog_file_name();
- if (fname == nullptr) {
- return ret;
- }
- EXPECT_DELAY_MS(3000);
- int fd = AP::FS().open(fname, O_RDONLY);
- free(fname);
- if (fd != -1) {
- char buf[10];
- memset(buf, 0, sizeof(buf));
- if (AP::FS().read(fd, buf, sizeof(buf)-1) > 0) {
- ret = strtol(buf, NULL, 10);
- }
- AP::FS().close(fd);
- }
- return ret;
- }
- uint32_t AP_Logger_File::_get_log_size(const uint16_t log_num)
- {
- char *fname = _log_file_name(log_num);
- if (fname == nullptr) {
- return 0;
- }
- if (_write_fd != -1 && write_fd_semaphore.take_nonblocking()) {
- if (_write_filename != nullptr && strcmp(_write_filename, fname) == 0) {
- // it is the file we are currently writing
- free(fname);
- write_fd_semaphore.give();
- return _write_offset;
- }
- write_fd_semaphore.give();
- }
- struct stat st;
- EXPECT_DELAY_MS(3000);
- if (AP::FS().stat(fname, &st) != 0) {
- printf("Unable to fetch Log File Size: %s\n", strerror(errno));
- free(fname);
- return 0;
- }
- free(fname);
- return st.st_size;
- }
- uint32_t AP_Logger_File::_get_log_time(const uint16_t log_num)
- {
- char *fname = _log_file_name(log_num);
- if (fname == nullptr) {
- return 0;
- }
- if (_write_fd != -1 && write_fd_semaphore.take_nonblocking()) {
- if (_write_filename != nullptr && strcmp(_write_filename, fname) == 0) {
- // it is the file we are currently writing
- free(fname);
- write_fd_semaphore.give();
- uint64_t utc_usec;
- if (!AP::rtc().get_utc_usec(utc_usec)) {
- return 0;
- }
- return utc_usec / 1000000U;
- }
- write_fd_semaphore.give();
- }
- struct stat st;
- EXPECT_DELAY_MS(3000);
- if (AP::FS().stat(fname, &st) != 0) {
- free(fname);
- return 0;
- }
- free(fname);
- return st.st_mtime;
- }
- /*
- convert a list entry number back into a log number (which can then
- be converted into a filename). A "list entry number" is a sequence
- where the oldest log has a number of 1, the second-from-oldest 2,
- and so on. Thus the highest list entry number is equal to the
- number of logs.
- */
- uint16_t AP_Logger_File::_log_num_from_list_entry(const uint16_t list_entry)
- {
- uint16_t oldest_log = find_oldest_log();
- if (oldest_log == 0) {
- // We don't have any logs...
- return 0;
- }
- uint32_t log_num = oldest_log + list_entry - 1;
- if (log_num > MAX_LOG_FILES) {
- log_num -= MAX_LOG_FILES;
- }
- return (uint16_t)log_num;
- }
- /*
- find the number of pages in a log
- */
- void AP_Logger_File::get_log_boundaries(const uint16_t list_entry, uint32_t & start_page, uint32_t & end_page)
- {
- const uint16_t log_num = _log_num_from_list_entry(list_entry);
- if (log_num == 0) {
- // that failed - probably no logs
- start_page = 0;
- end_page = 0;
- return;
- }
- start_page = 0;
- end_page = _get_log_size(log_num) / LOGGER_PAGE_SIZE;
- }
- /*
- retrieve data from a log file
- */
- 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)
- {
- if (!_initialised || _open_error) {
- return -1;
- }
- const uint16_t log_num = _log_num_from_list_entry(list_entry);
- if (log_num == 0) {
- // that failed - probably no logs
- return -1;
- }
- if (_read_fd != -1 && log_num != _read_fd_log_num) {
- AP::FS().close(_read_fd);
- _read_fd = -1;
- }
- if (_read_fd == -1) {
- char *fname = _log_file_name(log_num);
- if (fname == nullptr) {
- return -1;
- }
- stop_logging();
- EXPECT_DELAY_MS(3000);
- _read_fd = AP::FS().open(fname, O_RDONLY);
- if (_read_fd == -1) {
- _open_error = true;
- int saved_errno = errno;
- ::printf("Log read open fail for %s - %s\n",
- fname, strerror(saved_errno));
- hal.console->printf("Log read open fail for %s - %s\n",
- fname, strerror(saved_errno));
- free(fname);
- return -1;
- }
- free(fname);
- _read_offset = 0;
- _read_fd_log_num = log_num;
- }
- uint32_t ofs = page * (uint32_t)LOGGER_PAGE_SIZE + offset;
- /*
- this rather strange bit of code is here to work around a bug
- in file offsets in NuttX. Every few hundred blocks of reads
- (starting at around 350k into a file) NuttX will get the
- wrong offset for sequential reads. The offset it gets is
- typically 128k earlier than it should be. It turns out that
- calling lseek() with 0 offset and SEEK_CUR works around the
- bug. We can remove this once we find the real bug.
- */
- if (ofs / 4096 != (ofs+len) / 4096) {
- off_t seek_current = AP::FS().lseek(_read_fd, 0, SEEK_CUR);
- if (seek_current == (off_t)-1) {
- AP::FS().close(_read_fd);
- _read_fd = -1;
- return -1;
- }
- if (seek_current != (off_t)_read_offset) {
- if (AP::FS().lseek(_read_fd, _read_offset, SEEK_SET) == (off_t)-1) {
- AP::FS().close(_read_fd);
- _read_fd = -1;
- return -1;
- }
- }
- }
- if (ofs != _read_offset) {
- if (AP::FS().lseek(_read_fd, ofs, SEEK_SET) == (off_t)-1) {
- AP::FS().close(_read_fd);
- _read_fd = -1;
- return -1;
- }
- _read_offset = ofs;
- }
- int16_t ret = (int16_t)AP::FS().read(_read_fd, data, len);
- if (ret > 0) {
- _read_offset += ret;
- }
- return ret;
- }
- /*
- find size and date of a log
- */
- void AP_Logger_File::get_log_info(const uint16_t list_entry, uint32_t &size, uint32_t &time_utc)
- {
- uint16_t log_num = _log_num_from_list_entry(list_entry);
- if (log_num == 0) {
- // that failed - probably no logs
- size = 0;
- time_utc = 0;
- return;
- }
- size = _get_log_size(log_num);
- time_utc = _get_log_time(log_num);
- }
- /*
- get the number of logs - note that the log numbers must be consecutive
- */
- uint16_t AP_Logger_File::get_num_logs()
- {
- uint16_t ret = 0;
- uint16_t high = find_last_log();
- uint16_t i;
- for (i=high; i>0; i--) {
- if (! log_exists(i)) {
- break;
- }
- ret++;
- }
- if (i == 0) {
- for (i=MAX_LOG_FILES; i>high; i--) {
- if (! log_exists(i)) {
- break;
- }
- ret++;
- }
- }
- return ret;
- }
- /*
- stop logging
- */
- void AP_Logger_File::stop_logging(void)
- {
- // best-case effort to avoid annoying the IO thread
- const bool have_sem = write_fd_semaphore.take(hal.util->get_soft_armed()?1:20);
- if (_write_fd != -1) {
- int fd = _write_fd;
- _write_fd = -1;
- AP::FS().close(fd);
- }
- if (have_sem) {
- write_fd_semaphore.give();
- }
- }
- void AP_Logger_File::PrepForArming()
- {
- if (logging_started()) {
- return;
- }
- start_new_log();
- }
- /*
- start writing to a new log file
- */
- uint16_t AP_Logger_File::start_new_log(void)
- {
- stop_logging();
- start_new_log_reset_variables();
- if (_open_error) {
- // we have previously failed to open a file - don't try again
- // to prevent us trying to open files while in flight
- return 0xFFFF;
- }
- if (_read_fd != -1) {
- AP::FS().close(_read_fd);
- _read_fd = -1;
- }
- if (disk_space_avail() < _free_space_min_avail && disk_space() > 0) {
- hal.console->printf("Out of space for logging\n");
- _open_error = true;
- return 0xffff;
- }
- uint16_t log_num = find_last_log();
- // re-use empty logs if possible
- if (_get_log_size(log_num) > 0 || log_num == 0) {
- log_num++;
- }
- if (log_num > MAX_LOG_FILES) {
- log_num = 1;
- }
- if (!write_fd_semaphore.take(1)) {
- _open_error = true;
- return 0xFFFF;
- }
- if (_write_filename) {
- free(_write_filename);
- _write_filename = nullptr;
- }
- _write_filename = _log_file_name(log_num);
- if (_write_filename == nullptr) {
- _open_error = true;
- write_fd_semaphore.give();
- return 0xFFFF;
- }
- #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
- // remember if we had utc time when we opened the file
- uint64_t utc_usec;
- _need_rtc_update = !AP::rtc().get_utc_usec(utc_usec);
- #endif
- EXPECT_DELAY_MS(3000);
- _write_fd = AP::FS().open(_write_filename, O_WRONLY|O_CREAT|O_TRUNC);
- _cached_oldest_log = 0;
- if (_write_fd == -1) {
- _initialised = false;
- _open_error = true;
- write_fd_semaphore.give();
- int saved_errno = errno;
- ::printf("Log open fail for %s - %s\n",
- _write_filename, strerror(saved_errno));
- hal.console->printf("Log open fail for %s - %s\n",
- _write_filename, strerror(saved_errno));
- return 0xFFFF;
- }
- _last_write_ms = AP_HAL::millis();
- _write_offset = 0;
- _writebuf.clear();
- write_fd_semaphore.give();
- // now update lastlog.txt with the new log number
- char *fname = _lastlog_file_name();
- // we avoid fopen()/fprintf() here as it is not available on as many
- // systems as open/write
- EXPECT_DELAY_MS(3000);
- int fd = AP::FS().open(fname, O_WRONLY|O_CREAT);
- free(fname);
- if (fd == -1) {
- _open_error = true;
- return 0xFFFF;
- }
- char buf[30];
- snprintf(buf, sizeof(buf), "%u\r\n", (unsigned)log_num);
- const ssize_t to_write = strlen(buf);
- const ssize_t written = AP::FS().write(fd, buf, to_write);
- AP::FS().close(fd);
- if (written < to_write) {
- _open_error = true;
- return 0xFFFF;
- }
- return log_num;
- }
- #if CONFIG_HAL_BOARD == HAL_BOARD_SITL || CONFIG_HAL_BOARD == HAL_BOARD_LINUX
- void AP_Logger_File::flush(void)
- #if APM_BUILD_TYPE(APM_BUILD_Replay) || APM_BUILD_TYPE(APM_BUILD_UNKNOWN)
- {
- uint32_t tnow = AP_HAL::millis();
- while (_write_fd != -1 && _initialised && !_open_error && _writebuf.available()) {
- // convince the IO timer that it really is OK to write out
- // less than _writebuf_chunk bytes:
- if (tnow > 2001) { // avoid resetting _last_write_time to 0
- _last_write_time = tnow - 2001;
- }
- _io_timer();
- }
- if (write_fd_semaphore.take(1)) {
- if (_write_fd != -1) {
- ::fsync(_write_fd);
- }
- write_fd_semaphore.give();
- } else {
- AP::internalerror().error(AP_InternalError::error_t::logger_flushing_without_sem);
- }
- }
- #else
- {
- // flush is for replay and examples only
- }
- #endif // APM_BUILD_TYPE(APM_BUILD_Replay) || APM_BUILD_TYPE(APM_BUILD_UNKNOWN)
- #endif
- void AP_Logger_File::_io_timer(void)
- {
- uint32_t tnow = AP_HAL::millis();
- _io_timer_heartbeat = tnow;
- if (_write_fd == -1 || !_initialised || _open_error) {
- return;
- }
- uint32_t nbytes = _writebuf.available();
- if (nbytes == 0) {
- return;
- }
- if (nbytes < _writebuf_chunk &&
- tnow - _last_write_time < 2000UL) {
- // write in _writebuf_chunk-sized chunks, but always write at
- // least once per 2 seconds if data is available
- return;
- }
- if (tnow - _free_space_last_check_time > _free_space_check_interval) {
- _free_space_last_check_time = tnow;
- last_io_operation = "disk_space_avail";
- if (disk_space_avail() < _free_space_min_avail && disk_space() > 0) {
- hal.console->printf("Out of space for logging\n");
- stop_logging();
- _open_error = true; // prevent logging starting again
- last_io_operation = "";
- return;
- }
- last_io_operation = "";
- }
- hal.util->perf_begin(_perf_write);
- _last_write_time = tnow;
- if (nbytes > _writebuf_chunk) {
- // be kind to the filesystem layer
- nbytes = _writebuf_chunk;
- }
- uint32_t size;
- const uint8_t *head = _writebuf.readptr(size);
- nbytes = MIN(nbytes, size);
- // try to align writes on a 512 byte boundary to avoid filesystem reads
- if ((nbytes + _write_offset) % 512 != 0) {
- uint32_t ofs = (nbytes + _write_offset) % 512;
- if (ofs < nbytes) {
- nbytes -= ofs;
- }
- }
- last_io_operation = "write";
- if (!write_fd_semaphore.take(1)) {
- return;
- }
- if (_write_fd == -1) {
- write_fd_semaphore.give();
- return;
- }
- ssize_t nwritten = AP::FS().write(_write_fd, head, nbytes);
- last_io_operation = "";
- if (nwritten <= 0) {
- if ((tnow - _last_write_ms)/1000U > unsigned(_front._params.file_timeout)) {
- // if we can't write for LOG_FILE_TIMEOUT seconds we give up and close
- // the file. This allows us to cope with temporary write
- // failures caused by directory listing
- hal.util->perf_count(_perf_errors);
- last_io_operation = "close";
- AP::FS().close(_write_fd);
- last_io_operation = "";
- _write_fd = -1;
- _initialised = false;
- printf("Failed to write to File: %s\n", strerror(errno));
- }
- _last_write_failed = true;
- } else {
- _last_write_failed = false;
- _last_write_ms = tnow;
- _write_offset += nwritten;
- _writebuf.advance(nwritten);
- /*
- the best strategy for minimizing corruption on microSD cards
- seems to be to write in 4k chunks and fsync the file on each
- chunk, ensuring the directory entry is updated after each
- write.
- */
- #if CONFIG_HAL_BOARD != HAL_BOARD_SITL && CONFIG_HAL_BOARD_SUBTYPE != HAL_BOARD_SUBTYPE_LINUX_NONE
- last_io_operation = "fsync";
- AP::FS().fsync(_write_fd);
- last_io_operation = "";
- #endif
- #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
- // ChibiOS does not update mtime on writes, so if we opened
- // without knowing the time we should update it later
- if (_need_rtc_update) {
- uint64_t utc_usec;
- if (AP::rtc().get_utc_usec(utc_usec)) {
- AP::FS().set_mtime(_write_filename, utc_usec/(1000U*1000U));
- _need_rtc_update = false;
- }
- }
- #endif
- }
- write_fd_semaphore.give();
- hal.util->perf_end(_perf_write);
- }
- // this sensor is enabled if we should be logging at the moment
- bool AP_Logger_File::logging_enabled() const
- {
- if (hal.util->get_soft_armed() ||
- _front.log_while_disarmed()) {
- return true;
- }
- return false;
- }
- bool AP_Logger_File::io_thread_alive() const
- {
- // if the io thread hasn't had a heartbeat in a full seconds then it is dead
- // this is enough time for a sdcard remount
- return (AP_HAL::millis() - _io_timer_heartbeat) < 3000U;
- }
- bool AP_Logger_File::logging_failed() const
- {
- if (!_initialised) {
- return true;
- }
- if (_open_error) {
- return true;
- }
- if (!io_thread_alive()) {
- // No heartbeat in a second. IO thread is dead?! Very Not
- // Good.
- return true;
- }
- if (_last_write_failed) {
- return true;
- }
- return false;
- }
- void AP_Logger_File::vehicle_was_disarmed()
- {
- if (_front._params.file_disarm_rot) {
- // rotate our log. Closing the current one and letting the
- // logging restart naturally based on log_disarmed should do
- // the trick:
- _rotate_pending = true;
- }
- }
- void AP_Logger_File::Write_AP_Logger_Stats_File(const struct df_stats &_stats)
- {
- struct log_DSF pkt = {
- LOG_PACKET_HEADER_INIT(LOG_DF_FILE_STATS),
- time_us : AP_HAL::micros64(),
- dropped : _dropped,
- blocks : _stats.blocks,
- bytes : _stats.bytes,
- buf_space_min : _stats.buf_space_min,
- buf_space_max : _stats.buf_space_max,
- buf_space_avg : (_stats.blocks) ? (_stats.buf_space_sigma / _stats.blocks) : 0,
- };
- WriteBlock(&pkt, sizeof(pkt));
- }
- void AP_Logger_File::df_stats_gather(const uint16_t bytes_written) {
- const uint32_t space_remaining = _writebuf.space();
- if (space_remaining < stats.buf_space_min) {
- stats.buf_space_min = space_remaining;
- }
- if (space_remaining > stats.buf_space_max) {
- stats.buf_space_max = space_remaining;
- }
- stats.buf_space_sigma += space_remaining;
- stats.bytes += bytes_written;
- stats.blocks++;
- }
- void AP_Logger_File::df_stats_clear() {
- memset(&stats, '\0', sizeof(stats));
- stats.buf_space_min = -1;
- }
- void AP_Logger_File::df_stats_log() {
- Write_AP_Logger_Stats_File(stats);
- df_stats_clear();
- }
- #endif // HAVE_FILESYSTEM_SUPPORT
|