moved the logging-related executables into an intelligent location
diff --git a/aos/linux_code/logging/binary_log_file.h b/aos/linux_code/logging/binary_log_file.h
new file mode 100644
index 0000000..6ce20cd
--- /dev/null
+++ b/aos/linux_code/logging/binary_log_file.h
@@ -0,0 +1,199 @@
+#ifndef AOS_LINUX_CODE_LOGGING_BINARY_LOG_FILE_H_
+#define AOS_LINUX_CODE_LOGGING_BINARY_LOG_FILE_H_
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "aos/common/logging/logging_impl.h"
+
+namespace aos {
+namespace logging {
+
+// File format: {
+// LogFileMessageHeader header;
+// char *name; // of the process that wrote the message
+// char *message;
+// } not crossing kPageSize boundaries into the file.
+//
+// Field sizes designed to fit the various values from LogMessage even on
+// other machines (hopefully) because they're baked into the files.
+//
+// A lot of the fields don't have comments because they're the same as the
+// identically named fields in LogMessage.
+struct __attribute__((aligned)) LogFileMessageHeader {
+ // gets futex_set once this one has been written
+ // for readers keeping up with a live writer
+ //
+ // gets initialized to 0 by ftruncate
+ //
+ // there will be something here after the last log on a "page" set to 2
+ // (by the futex_set) to indicate that the next log is on the next page
+ mutex marker;
+ static_assert(sizeof(marker) == 4, "mutex changed size!");
+ log_level level;
+ static_assert(sizeof(level) == 1, "log_level changed size!");
+
+ uint32_t time_sec;
+ static_assert(sizeof(time_sec) >= sizeof(LogMessage::seconds),
+ "tv_sec won't fit");
+ uint32_t time_nsec;
+ static_assert(sizeof(time_nsec) >= sizeof(LogMessage::nseconds),
+ "tv_nsec won't fit");
+
+ int32_t source;
+ static_assert(sizeof(source) >= sizeof(LogMessage::source), "PIDs won't fit");
+ uint16_t sequence;
+ static_assert(sizeof(sequence) == sizeof(LogMessage::sequence),
+ "something changed");
+
+ // both including the terminating '\0'
+ uint32_t name_size;
+ uint32_t message_size;
+};
+static_assert(std::is_pod<LogFileMessageHeader>::value,
+ "LogFileMessageHeader will to get dumped to a file");
+
+// Handles the mmapping and munmapping for reading and writing log files.
+class LogFileAccessor {
+ private:
+ // The size of the chunks that get mmaped/munmapped together. Large enough so
+ // that not too much space is wasted and it's hopefully bigger than and a
+ // multiple of the system page size but small enough so that really large chunks
+ // of memory don't have to get mapped at the same time.
+ static const size_t kPageSize = 32768;
+ // What to align messages to. Necessary for futexes to work.
+ static const size_t kAlignment = 64;
+ static_assert(kAlignment >= __alignof__(mutex), "futexes will complain");
+
+ const int fd_;
+ const bool writable_;
+
+ off_t offset_; // into the file. will be aligned to kPageSize
+ char *current_;
+ size_t position_;
+
+ inline unsigned long SystemPageSize() {
+ static unsigned long r = sysconf(_SC_PAGESIZE);
+ return r;
+ }
+ void MapNextPage() {
+ if (writable_) {
+ if (ftruncate(fd_, offset_ + kPageSize) == -1) {
+ fprintf(stderr, "ftruncate(%d, %zd) failed with %d: %s. aborting\n",
+ fd_, kPageSize, errno, strerror(errno));
+ printf("see stderr\n");
+ abort();
+ }
+ }
+ current_ = static_cast<char *>(mmap(NULL, kPageSize,
+ PROT_READ | (writable_ ? PROT_WRITE : 0),
+ MAP_SHARED, fd_, offset_));
+ if (current_ == MAP_FAILED) {
+ fprintf(stderr, "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)"
+ " failed with %d: %s. aborting\n", kPageSize, fd_,
+ static_cast<intmax_t>(offset_), errno, strerror(errno));
+ printf("see stderr\n");
+ abort();
+ }
+ offset_ += kPageSize;
+ }
+ void Unmap(void *location) {
+ if (munmap(location, kPageSize) == -1) {
+ fprintf(stderr, "munmap(%p, %zd) failed with %d: %s. aborting\n",
+ location, kPageSize, errno, strerror(errno));
+ printf("see stderr\n");
+ abort();
+ }
+ }
+ public:
+ LogFileAccessor(int fd, bool writable) : fd_(fd), writable_(writable),
+ offset_(0), current_(0), position_(0) {
+ // check to make sure that mmap will allow mmaping in chunks of kPageSize
+ if (SystemPageSize() > kPageSize || (kPageSize % SystemPageSize()) != 0) {
+ fprintf(stderr, "LogFileCommon: system page size (%lu)"
+ " not compatible with kPageSize (%zd). aborting\n",
+ SystemPageSize(), kPageSize);
+ printf("see stderr\n");
+ abort();
+ }
+
+ MapNextPage();
+ }
+ // message_size should be the total number of bytes needed for the message
+ LogFileMessageHeader *GetWritePosition(size_t message_size) {
+ if (position_ + message_size + (kAlignment - (message_size % kAlignment)) +
+ sizeof(mutex) > kPageSize) {
+ char *const temp = current_;
+ MapNextPage();
+ if (futex_set_value(static_cast<mutex *>(static_cast<void *>(
+ &temp[position_])), 2) == -1) {
+ fprintf(stderr,
+ "LogFileCommon: futex_set_value(%p, 2) failed with %d: %s."
+ " readers will hang\n",
+ &temp[position_], errno, strerror(errno));
+ }
+ Unmap(temp);
+ position_ = 0;
+ }
+ LogFileMessageHeader *const r = static_cast<LogFileMessageHeader *>(
+ static_cast<void *>(¤t_[position_]));
+ position_ += message_size;
+ // keep it aligned for next time
+ position_ += kAlignment - (position_ % kAlignment);
+ return r;
+ }
+ // may only return NULL if wait is false
+ const LogFileMessageHeader *ReadNextMessage(bool wait) {
+ LogFileMessageHeader *r;
+ do {
+ r = static_cast<LogFileMessageHeader *>(
+ static_cast<void *>(¤t_[position_]));
+ if (wait) {
+ if (futex_wait(&r->marker) != 0) continue;
+ }
+ if (r->marker == 2) {
+ Unmap(current_);
+ MapNextPage();
+ position_ = 0;
+ r = static_cast<LogFileMessageHeader *>(static_cast<void *>(current_));
+ }
+ } while (wait && r->marker == 0);
+ if (r->marker == 0) {
+ return NULL;
+ }
+ position_ += sizeof(LogFileMessageHeader) + r->name_size + r->message_size;
+ // keep it aligned for next time
+ position_ += kAlignment - (position_ % kAlignment);
+ return r;
+ }
+
+ // asynchronously syncs all open mappings
+ void Sync() {
+ msync(current_, kPageSize, MS_ASYNC | MS_INVALIDATE);
+ }
+
+ void MoveToEnd() {
+ Unmap(current_);
+ struct stat info;
+ if (fstat(fd_, &info) == -1) {
+ fprintf(stderr, "LOgFileCommon: fstat(%d, %p) failed with %d: %s\n",
+ fd_, &info, errno, strerror(errno));
+ printf("see stderr\n");
+ abort();
+ }
+ offset_ = info.st_size - kPageSize;
+ MapNextPage();
+ }
+};
+
+} // namespace logging
+} // namespace aos
+
+#endif // AOS_LINUX_CODE_LOGGING_BINARY_LOG_FILE_H_
diff --git a/aos/linux_code/logging/binary_log_writer.cc b/aos/linux_code/logging/binary_log_writer.cc
new file mode 100644
index 0000000..48e9cf1
--- /dev/null
+++ b/aos/linux_code/logging/binary_log_writer.cc
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <string>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <fcntl.h>
+
+#include <map>
+
+#include "aos/linux_code/logging/linux_logging.h"
+#include "aos/linux_code/logging/binary_log_file.h"
+#include "aos/linux_code/init.h"
+#include "aos/linux_code/configuration.h"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+namespace {
+
+int BinaryLogReaderMain() {
+ InitNRT();
+
+ const char *folder = configuration::GetLoggingDirectory();
+ if (access(folder, R_OK | W_OK) == -1) {
+ LOG(FATAL, "folder '%s' does not exist. please create it\n", folder);
+ }
+ LOG(INFO, "logging to folder '%s'\n", folder);
+
+ const time_t t = time(NULL);
+ char *tmp;
+ if (asprintf(&tmp, "%s/aos_log-%jd", folder, static_cast<uintmax_t>(t)) ==
+ -1) {
+ fprintf(stderr,
+ "BinaryLogReader: couldn't create final name because of %d (%s)."
+ " exiting\n", errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ char *tmp2;
+ if (asprintf(&tmp2, "%s/aos_log-current", folder) == -1) {
+ fprintf(stderr,
+ "BinaryLogReader: couldn't create symlink name because of %d (%s)."
+ " not creating current symlink\n", errno, strerror(errno));
+ } else {
+ if (unlink(tmp2) == -1 && (errno != EROFS && errno != ENOENT)) {
+ fprintf(stderr,
+ "BinaryLogReader: warning: unlink('%s') failed"
+ " because of %d (%s)\n",
+ tmp2, errno, strerror(errno));
+ }
+ if (symlink(tmp, tmp2) == -1) {
+ fprintf(stderr, "BinaryLogReader: warning: symlink('%s', '%s') failed"
+ " because of %d (%s)\n", tmp, tmp2, errno, strerror(errno));
+ }
+ free(tmp2);
+ }
+ int fd = open(tmp, O_SYNC | O_APPEND | O_RDWR | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ free(tmp);
+ if (fd == -1) {
+ fprintf(stderr,
+ "BinaryLogReader: couldn't open file '%s' because of %d (%s)."
+ " exiting\n", tmp, errno, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ LogFileAccessor writer(fd, true);
+
+ struct timespec timespec;
+ time_t last_sec = 0;
+ while (true) {
+ clock_gettime(CLOCK_MONOTONIC, ×pec);
+ if (last_sec != timespec.tv_sec) {
+ LOG(INFO, "msyncing output\n");
+ last_sec = timespec.tv_sec;
+ writer.Sync();
+ }
+
+ const LogMessage *const msg = ReadNext();
+ if (msg == NULL) continue;
+
+ // add 1 for terminating '\0'
+ size_t name_size = strlen(msg->name) + 1;
+ size_t message_size = strlen(msg->message) + 1;
+
+ LogFileMessageHeader *const output = writer.GetWritePosition(
+ sizeof(LogFileMessageHeader) + name_size + message_size);
+ char *output_strings = reinterpret_cast<char *>(output) + sizeof(*output);
+ output->name_size = name_size;
+ output->message_size = message_size;
+ output->source = msg->source;
+ output->level = msg->level;
+ output->time_sec = msg->seconds;
+ output->time_nsec = msg->nseconds;
+ output->sequence = msg->sequence;
+ memcpy(output_strings, msg->name, name_size);
+ memcpy(output_strings + name_size, msg->message, message_size);
+ futex_set(&output->marker);
+
+ logging::linux_code::Free(msg);
+ }
+
+ Cleanup();
+ return 0;
+}
+
+} // namespace
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
+
+int main() {
+ return ::aos::logging::linux_code::BinaryLogReaderMain();
+}
diff --git a/aos/linux_code/logging/log_displayer.cc b/aos/linux_code/logging/log_displayer.cc
new file mode 100644
index 0000000..988c307
--- /dev/null
+++ b/aos/linux_code/logging/log_displayer.cc
@@ -0,0 +1,170 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "aos/linux_code/logging/binary_log_file.h"
+#include "aos/common/logging/logging_impl.h"
+
+namespace {
+
+const char *kArgsHelp = "[OPTION]... [FILE]\n"
+ "Display log file FILE (created by BinaryLogReader) to stdout.\n"
+ "FILE is \"aos_log-current\" by default.\n"
+ "\n"
+ " -n, --name NAME only display entries from processes named NAME\n"
+ " -l, --level LEVEL "
+ "only display log entries at least as important as LEVEL\n"
+ " // -p, --pid PID only display log entries from process PID\n"
+ " -f, --follow "
+ "wait when the end of the file is reached (implies --end)\n"
+ " -t, --terminate stop when the end of file is reached (default)\n"
+ " -b, --beginning start at the beginning of the file (default)\n"
+ " -e, --end start at the end of the file\n"
+ " -s, --skip NUMBER skip NUMBER matching logs\n"
+ " // -m, --max NUMBER only display up to NUMBER logs\n"
+ " // -o, --format FORMAT use FORMAT to display log entries\n"
+ " -h, --help display this help and exit\n"
+ "\n"
+ "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
+ " It defaults to INFO.\n"
+ "\n"
+ "TODO(brians) implement the commented out ones and changing FILE\n";
+
+void PrintHelpAndExit() {
+ fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
+
+ exit(EXIT_SUCCESS);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ const char *filter_name = NULL;
+ log_level filter_level = INFO;
+ bool follow = false, start_at_beginning = true;
+ const char *filename = "aos_log-current";
+
+ while (true) {
+ static struct option long_options[] = {
+ {"name", required_argument, NULL, 'n'},
+ {"level", required_argument, NULL, 'l'},
+ {"pid", required_argument, NULL, 'p'},
+
+ {"follow", no_argument, NULL, 'f'},
+ {"terminate", no_argument, NULL, 't'},
+ {"beginning", no_argument, NULL, 'b'},
+ {"end", no_argument, NULL, 'e'},
+ {"skip", required_argument, NULL, 's'},
+ {"max", required_argument, NULL, 'm'},
+
+ {"format", required_argument, NULL, 'o'},
+
+ {"help", no_argument, NULL, 'h'},
+ {0, 0, 0, 0}
+ };
+ int option_index = 0;
+
+ const int c = getopt_long(argc, argv, "n:l:p:fts:m:o:h",
+ long_options, &option_index);
+ if (c == -1) { // if we're at the end
+ break;
+ }
+ switch (c) {
+ case 0:
+ fprintf(stderr, "LogDisplayer: got a 0 option but didn't set up any\n");
+ abort();
+ case 'n':
+ filter_name = optarg;
+ break;
+ case 'l':
+ filter_level = ::aos::logging::str_log(optarg);
+ if (filter_level == LOG_UNKNOWN) {
+ fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'p':
+ abort();
+ break;
+ case 'f':
+ follow = true;
+ start_at_beginning = false;
+ break;
+ case 't':
+ follow = false;
+ break;
+ case 'b':
+ start_at_beginning = true;
+ break;
+ case 'e':
+ start_at_beginning = false;
+ break;
+ case 'm':
+ abort();
+ break;
+ case 'o':
+ abort();
+ break;
+ case 'h':
+ PrintHelpAndExit();
+ break;
+ case '?':
+ break;
+ default:
+ fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
+ __FILE__, __LINE__);
+ abort();
+ }
+ }
+
+ fprintf(stderr, "displaying down to level %s from file '%s'\n",
+ ::aos::logging::log_str(filter_level), filename);
+ if (optind < argc) {
+ fprintf(stderr, "non-option ARGV-elements: ");
+ while (optind < argc) {
+ fprintf(stderr, "%s\n", argv[optind++]);
+ }
+ }
+
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "error: couldn't open file '%s' for reading because of %s\n",
+ filename, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ ::aos::logging::LogFileAccessor accessor(fd, false);
+ if (!start_at_beginning) {
+ accessor.MoveToEnd();
+ }
+ const ::aos::logging::LogFileMessageHeader *msg;
+ ::aos::logging::LogMessage log_message;
+ do {
+ msg = accessor.ReadNextMessage(follow);
+ if (msg == NULL) continue;
+ if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
+ if (filter_name != NULL &&
+ strcmp(filter_name,
+ reinterpret_cast<const char *>(msg) + sizeof(*msg)) != 0) {
+ continue;
+ }
+
+ log_message.source = msg->source;
+ log_message.sequence = msg->sequence;
+ log_message.level = msg->level;
+ log_message.seconds = msg->time_sec;
+ log_message.nseconds = msg->time_nsec;
+ strncpy(log_message.name,
+ reinterpret_cast<const char *>(msg) + sizeof(*msg),
+ sizeof(log_message.name));
+ strncpy(log_message.message,
+ reinterpret_cast<const char *>(msg) + sizeof(*msg) +
+ msg->name_size,
+ sizeof(log_message.message));
+ ::aos::logging::internal::PrintMessage(stdout, log_message);
+ } while (msg != NULL);
+}
diff --git a/aos/linux_code/logging/log_streamer.cc b/aos/linux_code/logging/log_streamer.cc
new file mode 100644
index 0000000..ad351e7
--- /dev/null
+++ b/aos/linux_code/logging/log_streamer.cc
@@ -0,0 +1,53 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <string>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include "aos/linux_code/logging/linux_logging.h"
+#include "aos/linux_code/logging/binary_log_file.h"
+#include "aos/linux_code/init.h"
+#include "aos/linux_code/ipc_lib/queue.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/time.h"
+
+namespace aos {
+namespace logging {
+namespace linux_code {
+namespace {
+
+int LogStreamerMain() {
+ InitNRT();
+
+ const time::Time now = time::Time::Now();
+ printf("starting at %" PRId32 "s%" PRId32 "ns-----------------------------\n",
+ now.sec(), now.nsec());
+
+ int index = 0;
+ while (true) {
+ const LogMessage *const msg = ReadNext(RawQueue::kBlock, &index);
+ if (msg == NULL) continue;
+
+ internal::PrintMessage(stdout, *msg);
+
+ logging::linux_code::Free(msg);
+ }
+
+ Cleanup();
+ return 0;
+}
+
+} // namespace
+} // namespace linux_code
+} // namespace logging
+} // namespace aos
+
+int main() {
+ return ::aos::logging::linux_code::LogStreamerMain();
+}
diff --git a/aos/linux_code/logging/logging.gyp b/aos/linux_code/logging/logging.gyp
index dfb189c..96c197f 100644
--- a/aos/linux_code/logging/logging.gyp
+++ b/aos/linux_code/logging/logging.gyp
@@ -1,4 +1,40 @@
{
'targets': [
+ {
+ 'target_name': 'binary_log_writer',
+ 'type': 'executable',
+ 'sources': [
+ 'binary_log_writer.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/linux_code/linux_code.gyp:configuration',
+ ],
+ },
+ {
+ 'target_name': 'log_streamer',
+ 'type': 'executable',
+ 'sources': [
+ 'log_streamer.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:queue',
+ ],
+ },
+ {
+ 'target_name': 'log_displayer',
+ 'type': 'executable',
+ 'sources': [
+ 'log_displayer.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/linux_code/linux_code.gyp:init',
+ ],
+ },
],
}