blob: 14ce72b74406d90ca14d64160ae7435f4a132ba8 [file] [log] [blame]
#include "aos/common/logging/linux_logging.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <inttypes.h>
#include <algorithm>
#include "aos/common/die.h"
#include "aos/common/logging/logging_interface.h"
#include "aos/linux_code/ipc_lib/queue.h"
#include "aos/common/time.h"
namespace aos {
namespace logging {
namespace linux_code {
namespace {
RawQueue *queue = NULL;
int dropped_messages = 0;
::aos::time::Time dropped_start, backoff_start;
// Wait this long after dropping a message before even trying to write any more.
constexpr ::aos::time::Time kDropBackoff = ::aos::time::Time::InSeconds(0.1);
LogMessage *GetMessageOrDie() {
LogMessage *message = static_cast<LogMessage *>(queue->GetMessage());
if (message == NULL) {
LOG(FATAL, "%p->GetMessage() failed\n", queue);
} else {
return message;
}
}
class LinuxQueueLogImplementation : public LogImplementation {
__attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)))
virtual void DoLog(log_level level, const char *format, va_list ap) override {
LogMessage *message = GetMessageOrDie();
internal::FillInMessage(level, format, ap, message);
Write(message);
}
virtual void LogStruct(log_level level, const ::std::string &message_string,
size_t size, const MessageType *type,
const ::std::function<size_t(char *)> &serialize)
override {
LogMessage *message = GetMessageOrDie();
internal::FillInMessageStructure(level, message_string, size, type,
serialize, message);
Write(message);
}
virtual void LogMatrix(log_level level, const ::std::string &message_string,
uint32_t type_id, int rows, int cols, const void *data)
override {
LogMessage *message = GetMessageOrDie();
internal::FillInMessageMatrix(level, message_string, type_id, rows, cols,
data, message);
Write(message);
}
};
} // namespace
void Register() {
Init();
queue = RawQueue::Fetch("LoggingQueue", sizeof(LogMessage), 1323, 40000);
if (queue == NULL) {
Die("logging: couldn't fetch queue\n");
}
AddImplementation(new LinuxQueueLogImplementation());
}
const LogMessage *ReadNext(Options<RawQueue> flags, int *index) {
return static_cast<const LogMessage *>(queue->ReadMessageIndex(flags, index));
}
const LogMessage *ReadNext() {
return ReadNext(RawQueue::kBlock);
}
const LogMessage *ReadNext(Options<RawQueue> flags) {
const LogMessage *r = NULL;
do {
r = static_cast<const LogMessage *>(queue->ReadMessage(flags));
// not blocking means return a NULL if that's what it gets
} while ((flags & RawQueue::kBlock) && r == NULL);
return r;
}
LogMessage *Get() {
return static_cast<LogMessage *>(queue->GetMessage());
}
void Free(const LogMessage *msg) {
queue->FreeMessage(msg);
}
void Write(LogMessage *msg) {
if (__builtin_expect(dropped_messages > 0, false)) {
::aos::time::Time message_time =
::aos::time::Time(msg->seconds, msg->nseconds);
if (message_time - backoff_start < kDropBackoff) {
++dropped_messages;
queue->FreeMessage(msg);
return;
}
LogMessage *dropped_message = GetMessageOrDie();
internal::FillInMessageVarargs(
ERROR, dropped_message,
"%d logs starting at %" PRId32 ".%" PRId32 " dropped\n",
dropped_messages, dropped_start.sec(), dropped_start.nsec());
if (queue->WriteMessage(dropped_message, RawQueue::kNonBlock)) {
dropped_messages = 0;
} else {
// Don't even bother trying to write this message because it's not likely
// to work and it would be confusing to have one log in the middle of a
// string of failures get through.
++dropped_messages;
backoff_start = message_time;
queue->FreeMessage(msg);
return;
}
}
if (!queue->WriteMessage(msg, RawQueue::kNonBlock)) {
if (dropped_messages == 0) {
dropped_start = backoff_start =
::aos::time::Time(msg->seconds, msg->nseconds);
}
++dropped_messages;
}
}
} // namespace linux_code
} // namespace logging
} // namespace aos