logger: add mission file to LogWriterFile backend

Not used yet, it should not affect anything, except for slight RAM
increase.
This commit is contained in:
Beat Küng
2018-10-26 08:02:42 +02:00
parent b86c7d2e8f
commit d6e820fe67
4 changed files with 273 additions and 166 deletions
+194 -125
View File
@@ -51,17 +51,21 @@ namespace logger
{
constexpr size_t LogWriterFile::_min_write_chunk;
LogWriterFile::LogWriterFile(size_t buffer_size) :
LogWriterFile::LogWriterFile(size_t buffer_size)
: _buffers{
//We always write larger chunks (orb messages) to the buffer, so the buffer
//needs to be larger than the minimum write chunk (300 is somewhat arbitrary)
_buffer_size(math::max(buffer_size, _min_write_chunk + 300))
{
math::max(buffer_size, _min_write_chunk + 300),
perf_alloc(PC_ELAPSED, "logger_sd_write"), perf_alloc(PC_ELAPSED, "logger_sd_fsync")},
{
300, // buffer size for the mission log (can be kept fairly small)
perf_alloc(PC_ELAPSED, "logger_sd_write_mission"), perf_alloc(PC_ELAPSED, "logger_sd_fsync_mission")}
}
{
pthread_mutex_init(&_mtx, nullptr);
pthread_cond_init(&_cv, nullptr);
/* allocate write performance counters */
_perf_write = perf_alloc(PC_ELAPSED, "logger_sd_write");
_perf_fsync = perf_alloc(PC_ELAPSED, "logger_sd_fsync");
}
bool LogWriterFile::init()
@@ -73,59 +77,26 @@ LogWriterFile::~LogWriterFile()
{
pthread_mutex_destroy(&_mtx);
pthread_cond_destroy(&_cv);
perf_free(_perf_write);
perf_free(_perf_fsync);
if (_fd >= 0) {
::close(_fd);
}
if (_buffer) {
delete[] _buffer;
}
}
void LogWriterFile::start_log(const char *filename)
void LogWriterFile::start_log(LogType type, const char *filename)
{
// register the current file with the hardfault handler: if the system crashes,
// the hardfault handler will append the crash log to that file on the next reboot.
// Note that we don't deregister it when closing the log, so that crashes after disarming
// are appended as well (the same holds for crashes before arming, which can be a bit misleading)
int ret = hardfault_store_filename(filename);
if (type == LogType::Full) {
// register the current file with the hardfault handler: if the system crashes,
// the hardfault handler will append the crash log to that file on the next reboot.
// Note that we don't deregister it when closing the log, so that crashes after disarming
// are appended as well (the same holds for crashes before arming, which can be a bit misleading)
int ret = hardfault_store_filename(filename);
if (ret) {
PX4_ERR("Failed to register ULog file to the hardfault handler (%i)", ret);
}
_fd = ::open(filename, O_CREAT | O_WRONLY, PX4_O_MODE_666);
if (_fd < 0) {
PX4_ERR("Can't open log file %s, errno: %d", filename, errno);
_should_run = false;
return;
}
if (_buffer == nullptr) {
_buffer = new uint8_t[_buffer_size];
if (_buffer == nullptr) {
PX4_ERR("Can't create log buffer");
::close(_fd);
_fd = -1;
_should_run = false;
return;
if (ret) {
PX4_ERR("Failed to register ULog file to the hardfault handler (%i)", ret);
}
}
PX4_INFO("Opened log file: %s", filename);
_should_run = true;
_running = true;
// Clear buffer and counters
_head = 0;
_count = 0;
_total_written = 0;
notify();
if (_buffers[(int)type].start_log(filename)) {
PX4_INFO("Opened %s log file: %s", log_type_str(type), filename);
notify();
}
}
int LogWriterFile::hardfault_store_filename(const char *log_file)
@@ -160,9 +131,9 @@ int LogWriterFile::hardfault_store_filename(const char *log_file)
return 0;
}
void LogWriterFile::stop_log()
void LogWriterFile::stop_log(LogType type)
{
_should_run = false;
_buffers[(int)type]._should_run = false;
notify();
}
@@ -188,7 +159,7 @@ void LogWriterFile::thread_stop()
{
// this will terminate the main loop of the writer thread
_exit_thread = true;
_should_run = false;
_buffers[0]._should_run = _buffers[1]._should_run = false;
notify();
@@ -218,7 +189,7 @@ void LogWriterFile::run()
bool start = false;
pthread_mutex_lock(&_mtx);
pthread_cond_wait(&_cv, &_mtx);
start = _should_run;
start = _buffers[0]._should_run || _buffers[1]._should_run;
pthread_mutex_unlock(&_mtx);
if (start) {
@@ -234,95 +205,95 @@ void LogWriterFile::run()
int written = 0;
hrt_abstime last_fsync = hrt_absolute_time();
pthread_mutex_lock(&_mtx);
while (true) {
size_t available = 0;
void *read_ptr = nullptr;
bool is_part = false;
/* lock _buffer
* wait for sufficient data, cycle on notify()
*/
pthread_mutex_lock(&_mtx);
const hrt_abstime now = hrt_absolute_time();
while (true) {
available = get_read_ptr(&read_ptr, &is_part);
/* call fsync periodically to minimize potential loss of data */
const bool call_fsync = ++poll_count >= 100 || now - last_fsync > 1_s;
/* if sufficient data available or partial read or terminating, exit this wait loop */
if ((available >= _min_write_chunk) || is_part || !_should_run) {
/* GOTO end of block */
break;
}
/* wait for a call to notify()
* this call unlocks the mutex while waiting, and returns with the mutex locked
*/
pthread_cond_wait(&_cv, &_mtx);
if (call_fsync) {
last_fsync = now;
poll_count = 0;
}
pthread_mutex_unlock(&_mtx);
written = 0;
constexpr size_t min_available[(int)LogType::Count] = {
_min_write_chunk,
1 // For the mission log, write as soon as there is data available
};
if (available > 0) {
perf_begin(_perf_write);
written = ::write(_fd, read_ptr, available);
perf_end(_perf_write);
/* Check all buffers for available data. Mission log is first to avoid drops */
int i = (int)LogType::Count - 1;
const hrt_abstime now = hrt_absolute_time();
while (i >= 0) {
void *read_ptr;
bool is_part;
LogFileBuffer &buffer = _buffers[i];
size_t available = buffer.get_read_ptr(&read_ptr, &is_part);
/* call fsync periodically to minimize potential loss of data */
if (++poll_count >= 100 || now - last_fsync > 1_s) {
perf_begin(_perf_fsync);
::fsync(_fd);
perf_end(_perf_fsync);
poll_count = 0;
last_fsync = now;
}
/* if sufficient data available or partial read or terminating, write data */
if (available >= min_available[i] || is_part || (!buffer._should_run && available > 0)) {
pthread_mutex_unlock(&_mtx);
if (written < 0) {
PX4_WARN("error writing log file");
_should_run = false;
/* GOTO end of block */
break;
}
written = buffer.write_to_file(read_ptr, available, call_fsync);
pthread_mutex_lock(&_mtx);
/* subtract bytes written from number in _buffer (_count -= written) */
mark_read(written);
pthread_mutex_unlock(&_mtx);
/* buffer.mark_read() requires _mtx to be locked */
pthread_mutex_lock(&_mtx);
_total_written += written;
}
if (written >= 0) {
/* subtract bytes written from number in buffer (count -= written) */
buffer.mark_read(written);
if (!_should_run && written == static_cast<int>(available) && !is_part) {
// Stop only when all data written
_running = false;
_head = 0;
_count = 0;
if (_fd >= 0) {
int res = ::close(_fd);
_fd = -1;
if (res) {
PX4_WARN("error closing log file");
if (!buffer._should_run && written == static_cast<int>(available) && !is_part) {
/* Stop only when all data written */
buffer.close_file();
}
} else {
PX4_INFO("closed logfile, bytes written: %zu", _total_written);
PX4_ERR("write failed (%i)", errno);
buffer._should_run = false;
buffer.close_file();
}
} else if (call_fsync) {
buffer.fsync();
} else if (available == 0 && !buffer._should_run) {
buffer.close_file();
}
/* if split into 2 parts, write the second part immediately as well */
if (!is_part) {
--i;
}
}
if (_buffers[0].fd() < 0 && _buffers[1].fd() < 0) {
// stop when both files are closed
break;
}
/* Wait for a call to notify(), which indicates new data is available.
* Note that at this point there could already be new data available (because of a longer write),
* and calling pthread_cond_wait() will still wait for the next notify(). But this is generally
* not an issue because notify() is called regularly. */
pthread_cond_wait(&_cv, &_mtx);
}
// go back to idle
pthread_mutex_unlock(&_mtx);
}
}
int LogWriterFile::write_message(void *ptr, size_t size, uint64_t dropout_start)
int LogWriterFile::write_message(LogType type, void *ptr, size_t size, uint64_t dropout_start)
{
if (_need_reliable_transfer) {
int ret;
while ((ret = write(ptr, size, dropout_start)) == -1) {
while ((ret = write(type, ptr, size, dropout_start)) == -1) {
unlock();
notify();
usleep(3000);
@@ -332,17 +303,17 @@ int LogWriterFile::write_message(void *ptr, size_t size, uint64_t dropout_start)
return ret;
}
return write(ptr, size, dropout_start);
return write(type, ptr, size, dropout_start);
}
int LogWriterFile::write(void *ptr, size_t size, uint64_t dropout_start)
int LogWriterFile::write(LogType type, void *ptr, size_t size, uint64_t dropout_start)
{
if (!is_started()) {
if (!is_started(type)) {
return 0;
}
// Bytes available to write
size_t available = _buffer_size - _count;
size_t available = _buffers[(int)type].available();
size_t dropout_size = 0;
if (dropout_start) {
@@ -358,14 +329,44 @@ int LogWriterFile::write(void *ptr, size_t size, uint64_t dropout_start)
//write dropout msg
ulog_message_dropout_s dropout_msg;
dropout_msg.duration = (uint16_t)(hrt_elapsed_time(&dropout_start) / 1000);
write_no_check(&dropout_msg, sizeof(dropout_msg));
_buffers[(int)type].write_no_check(&dropout_msg, sizeof(dropout_msg));
}
write_no_check(ptr, size);
_buffers[(int)type].write_no_check(ptr, size);
return 0;
}
void LogWriterFile::write_no_check(void *ptr, size_t size)
const char *log_type_str(LogType type)
{
switch (type) {
case LogType::Full: return "full";
case LogType::Mission: return "mission";
case LogType::Count: break;
}
return "unknown";
}
LogWriterFile::LogFileBuffer::LogFileBuffer(size_t buffer_size, perf_counter_t perf_write, perf_counter_t perf_fsync)
: _buffer_size(buffer_size), _perf_write(perf_write), _perf_fsync(perf_fsync)
{
}
LogWriterFile::LogFileBuffer::~LogFileBuffer()
{
if (_fd >= 0) {
close(_fd);
}
delete[] _buffer;
perf_free(_perf_write);
perf_free(_perf_fsync);
}
void LogWriterFile::LogFileBuffer::write_no_check(void *ptr, size_t size)
{
size_t n = _buffer_size - _head; // bytes to end of the buffer
@@ -387,7 +388,7 @@ void LogWriterFile::write_no_check(void *ptr, size_t size)
_count += size;
}
size_t LogWriterFile::get_read_ptr(void **ptr, bool *is_part)
size_t LogWriterFile::LogFileBuffer::get_read_ptr(void **ptr, bool *is_part)
{
// bytes available to read
int read_ptr = _head - _count;
@@ -405,5 +406,73 @@ size_t LogWriterFile::get_read_ptr(void **ptr, bool *is_part)
}
}
bool LogWriterFile::LogFileBuffer::start_log(const char *filename)
{
_fd = ::open(filename, O_CREAT | O_WRONLY, PX4_O_MODE_666);
if (_fd < 0) {
PX4_ERR("Can't open log file %s, errno: %d", filename, errno);
return false;
}
if (_buffer == nullptr) {
_buffer = new uint8_t[_buffer_size];
if (_buffer == nullptr) {
PX4_ERR("Can't create log buffer");
::close(_fd);
_fd = -1;
return false;
}
}
// Clear buffer and counters
_head = 0;
_count = 0;
_total_written = 0;
_should_run = true;
return true;
}
void LogWriterFile::LogFileBuffer::fsync() const
{
perf_begin(_perf_fsync);
::fsync(_fd);
perf_end(_perf_fsync);
}
ssize_t LogWriterFile::LogFileBuffer::write_to_file(const void *buffer, size_t size, bool call_fsync) const
{
perf_begin(_perf_write);
ssize_t ret = ::write(_fd, buffer, size);
perf_end(_perf_write);
if (call_fsync) {
fsync();
}
return ret;
}
void LogWriterFile::LogFileBuffer::close_file()
{
_head = 0;
_count = 0;
if (_fd >= 0) {
int res = close(_fd);
_fd = -1;
if (res) {
PX4_WARN("closing log file failed (%i)", errno);
} else {
PX4_INFO("closed logfile, bytes written: %zu", _total_written);
}
}
}
}
}