copying branch over from other 2013 repo
diff --git a/aos/atom_code/logging/atom_logging.cc b/aos/atom_code/logging/atom_logging.cc
new file mode 100644
index 0000000..c9b1195
--- /dev/null
+++ b/aos/atom_code/logging/atom_logging.cc
@@ -0,0 +1,145 @@
+#include "aos/atom_code/logging/atom_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 <sys/prctl.h>
+
+#include <algorithm>
+
+#include "aos/common/die.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/atom_code/thread_local.h"
+#include "aos/atom_code/ipc_lib/queue.h"
+
+namespace aos {
+namespace logging {
+namespace {
+
+using internal::Context;
+
+AOS_THREAD_LOCAL Context *my_context(NULL);
+
+std::string GetMyName() {
+ // The maximum number of characters that can make up a thread name.
+ // The docs are unclear if it can be 16 characters with no '\0', so we'll be
+ // safe by adding our own where necessary.
+ static const size_t kThreadNameLength = 16;
+
+ std::string process_name(program_invocation_short_name);
+
+ char thread_name_array[kThreadNameLength + 1];
+ if (prctl(PR_GET_NAME, thread_name_array) != 0) {
+ Die("prctl(PR_GET_NAME, %p) failed with %d: %s\n",
+ thread_name_array, errno, strerror(errno));
+ }
+ thread_name_array[sizeof(thread_name_array) - 1] = '\0';
+ std::string thread_name(thread_name_array);
+
+ // If the first bunch of characters are the same.
+ // We cut off comparing at the shorter of the 2 strings because one or the
+ // other often ends up cut off.
+ if (strncmp(thread_name.c_str(), process_name.c_str(),
+ std::min(thread_name.length(), process_name.length())) == 0) {
+ // This thread doesn't have an actual name.
+ return process_name;
+ }
+
+ return process_name + '.' + thread_name;
+}
+
+static const aos_type_sig message_sig = {sizeof(LogMessage), 1234, 1500};
+static aos_queue *queue;
+
+} // namespace
+namespace internal {
+
+Context *Context::Get() {
+ if (my_context == NULL) {
+ my_context = new Context();
+ std::string name = GetMyName();
+ char *name_chars = new char[name.size() + 1];
+ my_context->name_size = std::min(name.size() + 1, sizeof(LogMessage::name));
+ memcpy(name_chars, name.c_str(), my_context->name_size);
+ name_chars[my_context->name_size - 1] = '\0';
+ my_context->name = name_chars;
+ my_context->source = getpid();
+ }
+ return my_context;
+}
+
+void Context::Delete() {
+ delete my_context;
+ my_context = NULL;
+}
+
+} // namespace internal
+namespace atom {
+namespace {
+
+class AtomLogImplementation : public LogImplementation {
+ virtual void DoLog(log_level level, const char *format, va_list ap) {
+ LogMessage *message = static_cast<LogMessage *>(aos_queue_get_msg(queue));
+ if (message == NULL) {
+ LOG(FATAL, "queue get message failed\n");
+ }
+
+ internal::FillInMessage(level, format, ap, message);
+
+ Write(message);
+ }
+};
+
+} // namespace
+
+void Register() {
+ Init();
+
+ queue = aos_fetch_queue("LoggingQueue", &message_sig);
+ if (queue == NULL) {
+ Die("logging: couldn't fetch queue\n");
+ }
+
+ AddImplementation(new AtomLogImplementation());
+}
+
+const LogMessage *ReadNext(int flags, int *index) {
+ return static_cast<const LogMessage *>(
+ aos_queue_read_msg_index(queue, flags, index));
+}
+
+const LogMessage *ReadNext() {
+ return ReadNext(BLOCK);
+}
+
+const LogMessage *ReadNext(int flags) {
+ const LogMessage *r = NULL;
+ do {
+ r = static_cast<const LogMessage *>(aos_queue_read_msg(queue, flags));
+ // not blocking means return a NULL if that's what it gets
+ } while ((flags & BLOCK) && r == NULL);
+ return r;
+}
+
+LogMessage *Get() {
+ return static_cast<LogMessage *>(aos_queue_get_msg(queue));
+}
+
+void Free(const LogMessage *msg) {
+ aos_queue_free_msg(queue, msg);
+}
+
+void Write(LogMessage *msg) {
+ if (aos_queue_write_msg_free(queue, msg, OVERRIDE) < 0) {
+ LOG(FATAL, "writing failed");
+ }
+}
+
+} // namespace atom
+} // namespace logging
+} // namespace aos
diff --git a/aos/atom_code/logging/atom_logging.cpp b/aos/atom_code/logging/atom_logging.cpp
deleted file mode 100644
index e98078b..0000000
--- a/aos/atom_code/logging/atom_logging.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-#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 <algorithm>
-
-#include "aos/aos_core.h"
-#include "aos/common/die.h"
-
-#define DECL_LEVEL(name, value) const log_level name = value;
-DECL_LEVELS
-#undef DECL_LEVEL
-
-log_level log_min = 0;
-
-static const aos_type_sig message_sig = {sizeof(log_queue_message), 1234, 1500};
-static const char *name;
-static size_t name_size;
-static aos_queue *queue;
-static log_message corked_message;
-static int cork_line_min, cork_line_max;
-bool log_initted = false;
-
-static inline void cork_init() {
- corked_message.message[0] = '\0'; // make strlen of it 0
- cork_line_min = INT_MAX;
- cork_line_max = -1;
-}
-int log_init(const char *name_in){
- if (log_initted) {
- return 1;
- }
-
- const size_t name_in_len = strlen(name_in);
- const char *last_slash = static_cast<const char *>(memrchr(name_in,
- '/', name_in_len));
- if (last_slash == NULL) {
- name_size = name_in_len;
- last_slash = name_in - 1;
- } else {
- name_size = name_in + name_in_len - last_slash;
- }
- if (name_size >= sizeof(log_message::name)) {
- fprintf(stderr, "logging: error: name '%s' (going to use %zu bytes) is too long\n",
- name_in, name_size);
- return -1;
- }
- char *const tmp = static_cast<char *>(malloc(name_size + 1));
- if (tmp == NULL) {
- fprintf(stderr, "logging: error: couldn't malloc(%zd)\n", name_size + 1);
- return -1;
- }
- name = tmp;
- memcpy(tmp, last_slash + 1, name_size);
- tmp[name_size] = 0;
- queue = aos_fetch_queue("LoggingQueue", &message_sig);
- if (queue == NULL) {
- fprintf(stderr, "logging: error: couldn't fetch queue\n");
- return -1;
- }
-
- cork_init();
-
- log_initted = true;
- return 0;
-}
-void log_uninit() {
- free(const_cast<char *>(name));
- name = NULL;
- name_size = 0;
- queue = NULL;
- log_initted = false;
-}
-
-static inline void check_init() {
- if (!log_initted) {
- fprintf(stderr, "logging: warning: not initialized in %jd."
- " initializing using \"<null>\" as name\n", static_cast<intmax_t>(getpid()));
- log_init("<null>");
- }
-}
-
-const log_message *log_read_next2(int flags, int *index) {
- check_init();
- return static_cast<const log_message *>(aos_queue_read_msg_index(queue, flags, index));
-}
-const log_message *log_read_next1(int flags) {
- check_init();
- const log_message *r = NULL;
- do {
- r = static_cast<const log_message *>(aos_queue_read_msg(queue, flags));
- } while ((flags & BLOCK) && r == NULL); // not blocking means return a NULL if that's what it gets
- return r;
-}
-void log_free_message(const log_message *msg) {
- check_init();
- aos_queue_free_msg(queue, msg);
-}
-
-int log_crio_message_send(log_crio_message &to_send) {
- check_init();
-
- log_crio_message *msg = static_cast<log_crio_message *>(aos_queue_get_msg(queue));
- if (msg == NULL) {
- fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n",
- to_send.message);
- return -1;
- }
- //*msg = to_send;
- static_assert(sizeof(to_send) == sizeof(*msg), "something is very wrong here");
- memcpy(msg, &to_send, sizeof(to_send));
- if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) {
- fprintf(stderr, "logging: error: writing crio message '%s' failed\n", msg->message);
- aos_queue_free_msg(queue, msg);
- return -1;
- }
-
- return 0;
-}
-
-// Prints format (with ap) into output and correctly deals with the message
-// being too long etc.
-// Returns whether it succeeded or not.
-static inline bool vsprintf_in(char *output, size_t output_size,
- const char *format, va_list ap) {
- static const char *continued = "...\n";
- const size_t size = output_size - strlen(continued);
- const int ret = vsnprintf(output, size, format, ap);
- if (ret < 0) {
- fprintf(stderr, "logging: error: vsnprintf failed with %d (%s)\n",
- errno, strerror(errno));
- return false;
- } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
- // overwrite the NULL at the end of the existing one and
- // copy in the one on the end of continued
- memcpy(&output[size - 1], continued, strlen(continued) + 1);
- }
- return true;
-}
-static inline bool write_message(log_message *msg, log_level level) {
- msg->level = level;
- msg->source = getpid();
- memcpy(msg->name, name, name_size + 1);
- if (clock_gettime(CLOCK_REALTIME, &msg->time) == -1) {
- fprintf(stderr, "logging: warning: couldn't get the current time "
- "because of %d (%s)\n", errno, strerror(errno));
- msg->time.tv_sec = 0;
- msg->time.tv_nsec = 0;
- }
-
- static uint8_t local_sequence = -1;
- msg->sequence = ++local_sequence;
-
- if (aos_queue_write_msg(queue, msg, OVERRIDE) < 0) {
- fprintf(stderr, "logging: error: writing message '%s' failed\n", msg->message);
- aos_queue_free_msg(queue, msg);
- return false;
- }
- return true;
-}
-static inline int vlog_do(log_level level, const char *format, va_list ap) {
- log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue));
- if (msg == NULL) {
- fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format);
- return -1;
- }
-
- if (!vsprintf_in(msg->message, sizeof(msg->message), format, ap)) {
- return -1;
- }
-
- if (!write_message(msg, level)) {
- return -1;
- }
-
- if (level == FATAL) {
- aos::Die("%s", msg->message);
- }
-
- return 0;
-}
-int log_do(log_level level, const char *format, ...) {
- check_init();
- va_list ap;
- va_start(ap, format);
- const int ret = vlog_do(level, format, ap);
- va_end(ap);
- return ret;
-}
-
-static inline int vlog_cork(int line, const char *format, va_list ap) {
- const size_t message_length = strlen(corked_message.message);
- if (line > cork_line_max) cork_line_max = line;
- if (line < cork_line_min) cork_line_min = line;
- return vsprintf_in(corked_message.message + message_length,
- sizeof(corked_message.message) - message_length, format, ap) ? 0 : -1;
-}
-int log_cork(int line, const char *format, ...) {
- check_init();
- va_list ap;
- va_start(ap, format);
- const int ret = vlog_cork(line, format, ap);
- va_end(ap);
- return ret;
-}
-static inline bool log_uncork_helper(char *output, size_t output_size,
- const char *format, ...) {
- check_init();
- va_list ap;
- va_start(ap, format);
- const bool ret = vsprintf_in(output, output_size, format, ap);
- va_end(ap);
- return ret;
-}
-int log_uncork(int line, log_level level, const char *begin_format,
- const char *format, ...) {
- check_init();
- va_list ap;
- va_start(ap, format);
- const int ret = vlog_cork(line, format, ap);
- va_end(ap);
- if (ret != 0) {
- return ret;
- }
-
- log_message *msg = static_cast<log_message *>(aos_queue_get_msg(queue));
- if (msg == NULL) {
- fprintf(stderr, "logging: error: couldn't get a message to send '%s'\n", format);
- cork_init();
- return -1;
- }
-
- static char new_format[LOG_MESSAGE_LEN];
- if (!log_uncork_helper(new_format, sizeof(new_format), begin_format,
- cork_line_min, cork_line_max)) {
- cork_init();
- return -1;
- }
- const size_t new_length = strlen(new_format);
- memcpy(msg->message, new_format, new_length);
- memcpy(msg->message + new_length, corked_message.message,
- std::min(strlen(corked_message.message) + 1,
- sizeof(msg->message) - new_length));
- // in case corked_message.message was too long, it'll still be NULL-terminated
- msg->message[sizeof(msg->message) - 1] = '\0';
- cork_init();
-
- if (!write_message(msg, level)) {
- return -1;
- }
-
- return 0;
-}
-
diff --git a/aos/atom_code/logging/atom_logging.h b/aos/atom_code/logging/atom_logging.h
index 6211e22..45f2a07 100644
--- a/aos/atom_code/logging/atom_logging.h
+++ b/aos/atom_code/logging/atom_logging.h
@@ -1,109 +1,30 @@
#ifndef AOS_ATOM_CODE_LOGGING_LOGGING_H_
#define AOS_ATOM_CODE_LOGGING_LOGGING_H_
-// IWYU pragma: private, include "aos/common/logging/logging.h"
+#include "aos/common/logging/logging_impl.h"
-#ifndef AOS_COMMON_LOGGING_LOGGING_H_
-#error This file may only be #included through common/logging/logging.h!!!
-#endif
+namespace aos {
+namespace logging {
+namespace atom {
-#include "aos/aos_core.h"
+// Calls AddImplementation to register the usual atom logging implementation
+// which sends the messages through a queue. This implementation relies on
+// another process(es) to read the log messages that it puts into the queue.
+// It gets called by aos::Init*.
+void Register();
-#ifdef __cplusplus
-extern "C" {
-#endif
+// Fairly simple wrappers around the raw queue calls.
-int log_init(const char *name);
-// WARNING: THIS LEAKS MEMORY AND SHARED MEMORY
-void log_uninit(void);
+// This one never returns NULL if flags contains BLOCK.
+const LogMessage *ReadNext(int flags);
+const LogMessage *ReadNext(int flags, int *index);
+const LogMessage *ReadNext();
+LogMessage *Get();
+void Free(const LogMessage *msg);
+void Write(LogMessage *msg);
-extern log_level log_min;
-
-// The basic structure that goes into the shared memory queue.
-// It is packed so the pid_t at the front is at the same location as
-// the one in log_crio_message.
-typedef struct log_message_t_ {
- pid_t source;
- log_level level;
- char message[LOG_MESSAGE_LEN];
- char name[40];
- struct timespec time;
- uint8_t sequence; // per process
-} __attribute__((packed)) log_message;
-
-#ifdef __cplusplus
-#define LOG_BOOL bool
-#else
-#define LOG_BOOL uint8_t
-#endif
-extern LOG_BOOL log_initted;
-#undef LOG_BOOL
-
-// Unless explicitly stated otherwise, format must always be a string constant
-// and args are printf-style arguments for format.
-// The validitiy of format and args together will be checked at compile time
-// using a gcc function attribute.
-
-// Logs the specified thing.
-#define LOG(level, format, args...) do { \
- if (level >= log_min) { \
- log_do(level, LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " format, ##args); \
- } \
-} while (0)
-// Allows "bottling up" multiple log fragments which can then all be logged in
-// one message with LOG_UNCORK.
-// format does not have to be a constant
-#define LOG_CORK(format, args...) do { \
- log_cork(__LINE__, format, ##args); \
-} while (0)
-// Actually logs all of the saved up log fragments (including format and args on
-// the end).
-#define LOG_UNCORK(level, format, args...) do { \
- log_uncork(__LINE__, level, LOG_SOURCENAME ": %d-%d: ", format, ##args); \
-} while (0)
-// Makes a normal logging call if possible or just prints it out on stderr.
-#define LOG_IFINIT(level, format, args...) do{ \
- if(log_initted) { \
- LOG(level, format, args); \
- } else { \
- fprintf(stderr, "%s-noinit: " format, log_str(level), ##args); \
- } \
-}while(0)
-
-// All functions return 0 for success and - for error.
-
-// Actually implements the basic logging call.
-// Does not check that level is valid.
-// TODO(brians): Fix this so it works with clang.
-int log_do(log_level level, const char *format, ...)
- __attribute__((format(gnu_printf, 2, 3)));
-
-// TODO(brians): Fix this so it works with clang.
-int log_cork(int line, const char *format, ...)
- __attribute__((format(gnu_printf, 2, 3)));
-// Implements the uncork logging call.
-// IMPORTANT: begin_format must have 2 %d formats as its only 2 format specifiers
-// which will get passed the minimum and maximum line numbers that have been
-// corked into this call.
-// TODO(brians): Fix this so it works with clang.
-int log_uncork(int line, log_level level, const char *begin_format,
- const char *format, ...)
- __attribute__((format(gnu_printf, 4, 5)));
-
-const log_message *log_read_next1(int flags);
-const log_message *log_read_next2(int flags, int *index);
-inline const log_message *log_read_next(void) { return log_read_next1(BLOCK); }
-void log_free_message(const log_message *msg);
-
-// The structure that is actually in the shared memory queue.
-union log_queue_message {
- log_message atom;
- log_crio_message crio;
-};
-
-#ifdef __cplusplus
-}
-#endif
+} // namespace atom
+} // namespace logging
+} // namespace aos
#endif
-
diff --git a/aos/atom_code/logging/atom_logging_test.cpp b/aos/atom_code/logging/atom_logging_test.cpp
deleted file mode 100644
index a97d1e9..0000000
--- a/aos/atom_code/logging/atom_logging_test.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include <string>
-
-#include "gtest/gtest.h"
-
-#include "aos/aos_core.h"
-#include "aos/atom_code/ipc_lib/sharedmem_test_setup.h"
-#include "aos/common/control_loop/Timing.h"
-#include "aos/common/inttypes.h"
-#include "aos/common/time.h"
-
-using ::aos::time::Time;
-using ::testing::AssertionResult;
-using ::testing::AssertionSuccess;
-using ::testing::AssertionFailure;
-
-namespace aos {
-namespace testing {
-
-static const std::string kLoggingName = "LoggingTestName";
-
-class LoggingTest : public SharedMemTestSetup {
- virtual void SetUp() {
- SharedMemTestSetup::SetUp();
- ASSERT_EQ(0, log_init(kLoggingName.c_str()));
- }
- virtual void TearDown() {
- log_uninit();
- SharedMemTestSetup::TearDown();
- }
-
- public:
- AssertionResult WasAnythingLogged() {
- const log_message *msg = log_read_next1(NON_BLOCK);
- if (msg != NULL) {
- char bad_msg[LOG_MESSAGE_LEN];
- memcpy(bad_msg, msg->message, sizeof(bad_msg));
- log_free_message(msg);
- return AssertionSuccess() << "read message '" << bad_msg << "'";
- }
- return AssertionFailure();
- }
- AssertionResult WasLogged(log_level level, const std::string value) {
- const log_message *msg = NULL;
- char bad_msg[LOG_MESSAGE_LEN];
- bad_msg[0] = '\0';
- const pid_t owner = getpid();
- while (true) {
- if (msg != NULL) {
- static_assert(sizeof(bad_msg) == sizeof(msg->message),
- "something is wrong");
- if (bad_msg[0] != '\0') {
- printf("read bad message: %s", bad_msg);
- }
- memcpy(bad_msg, msg->message, sizeof(bad_msg));
- log_free_message(msg);
- msg = NULL;
- }
- msg = log_read_next1(NON_BLOCK);
- if (msg == NULL) {
- return AssertionFailure() << "last message read was '" << bad_msg << "'"
- " instead of '" << value << "'";
- }
- if (msg->source != owner) continue;
- if (msg->level != level) continue;
- if (strcmp(msg->name, kLoggingName.c_str()) != 0) continue;
- if (strcmp(msg->message + strlen(msg->message) - value.length(),
- value.c_str()) != 0) continue;
-
- // if it's gotten this far, then the message is correct
- log_free_message(msg);
- return AssertionSuccess();
- }
- }
-};
-typedef LoggingTest LoggingDeathTest;
-
-// Tests both basic logging functionality and that the test setup works
-// correctly.
-TEST_F(LoggingTest, Basic) {
- EXPECT_FALSE(WasAnythingLogged());
- LOG(INFO, "test log 1\n");
- EXPECT_TRUE(WasLogged(INFO, "test log 1\n"));
- LOG(INFO, "test log 1.5\n");
- // there's a subtle typo on purpose...
- EXPECT_FALSE(WasLogged(INFO, "test log 15\n"));
- LOG(ERROR, "test log 2=%d\n", 55);
- EXPECT_TRUE(WasLogged(ERROR, "test log 2=55\n"));
- LOG(ERROR, "test log 3\n");
- EXPECT_FALSE(WasLogged(WARNING, "test log 3\n"));
- LOG(WARNING, "test log 4\n");
- EXPECT_TRUE(WasAnythingLogged());
-}
-TEST_F(LoggingTest, Cork) {
- static const int begin_line = __LINE__;
- LOG_CORK("first part ");
- LOG_CORK("second part (=%d) ", 19);
- EXPECT_FALSE(WasAnythingLogged());
- LOG_CORK("third part ");
- static const int end_line = __LINE__;
- LOG_UNCORK(WARNING, "last part %d\n", 5);
- char *expected;
- ASSERT_GT(asprintf(&expected, "atom_logging_test.cpp: %d-%d: "
- "first part second part (=19) third part last part 5\n",
- begin_line + 1, end_line + 1), 0);
- EXPECT_TRUE(WasLogged(WARNING, std::string(expected)));
-}
-
-TEST_F(LoggingDeathTest, Fatal) {
- ASSERT_EXIT(LOG(FATAL, "this should crash it\n"),
- ::testing::KilledBySignal(SIGABRT),
- "this should crash it");
-}
-
-TEST_F(LoggingTest, PrintfDirectives) {
- LOG(INFO, "test log %%1 %%d\n");
- EXPECT_TRUE(WasLogged(INFO, "test log %1 %d\n"));
- LOG_DYNAMIC(WARNING, "test log %%2 %%f\n");
- EXPECT_TRUE(WasLogged(WARNING, "test log %2 %f\n"));
- LOG_CORK("log 3 part %%1 %%d ");
- LOG_UNCORK(DEBUG, "log 3 part %%2 %%f\n");
- EXPECT_TRUE(WasLogged(DEBUG, "log 3 part %1 %d log 3 part %2 %f\n"));
-}
-
-// For writing only.
-TEST_F(LoggingTest, Timing) {
- static const long kTimingCycles = 5000;
- Time start = Time::Now();
- for (long i = 0; i < kTimingCycles; ++i) {
- LOG(INFO, "a\n");
- }
- Time elapsed = Time::Now() - start;
-
- printf("short message took %"PRId64" nsec for %ld\n", elapsed.ToNSec(),
- kTimingCycles);
-
- start = Time::Now();
- for (long i = 0; i < kTimingCycles; ++i) {
- LOG(INFO, "something longer than just \"a\" to log to test timing\n");
- }
- elapsed = Time::Now() - start;
- printf("long message took %"PRId64" nsec for %ld\n", elapsed.ToNSec(),
- kTimingCycles);
-}
-
-} // namespace testing
-} // namespace aos
diff --git a/aos/atom_code/logging/logging.gyp b/aos/atom_code/logging/logging.gyp
index 5d29da8..dfb189c 100644
--- a/aos/atom_code/logging/logging.gyp
+++ b/aos/atom_code/logging/logging.gyp
@@ -1,15 +1,4 @@
{
'targets': [
- {
- 'target_name': 'atom_logging_test',
- 'type': 'executable',
- 'sources': [
- 'atom_logging_test.cpp',
- ],
- 'dependencies': [
- '<(EXTERNALS):gtest',
- '<(AOS)/build/aos.gyp:libaos',
- ],
- },
],
}