copying branch over from other 2013 repo
diff --git a/aos/atom_code/atom_code.gyp b/aos/atom_code/atom_code.gyp
new file mode 100644
index 0000000..8253588
--- /dev/null
+++ b/aos/atom_code/atom_code.gyp
@@ -0,0 +1,16 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'init',
+ 'type': 'static_library',
+ 'sources': [
+ '<(AOS)/atom_code/init.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+ '<(AOS)/common/common.gyp:die',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ },
+ ],
+}
diff --git a/aos/atom_code/camera/Buffers.cpp b/aos/atom_code/camera/Buffers.cpp
index 22b7337..f580f89 100644
--- a/aos/atom_code/camera/Buffers.cpp
+++ b/aos/atom_code/camera/Buffers.cpp
@@ -1,8 +1,10 @@
-#include "Buffers.h"
-#include "V4L2.h"
+#include "aos/atom_code/camera/Buffers.h"
#include <sys/mman.h>
+#include "aos/atom_code/camera/V4L2.h"
+#include "aos/common/logging/logging.h"
+
namespace aos {
namespace camera {
diff --git a/aos/atom_code/camera/Buffers.h b/aos/atom_code/camera/Buffers.h
index 7f1206d..b72cd3e 100644
--- a/aos/atom_code/camera/Buffers.h
+++ b/aos/atom_code/camera/Buffers.h
@@ -6,7 +6,7 @@
#include <string>
-#include "aos/aos_core.h"
+#include "aos/atom_code/ipc_lib/queue.h"
#include "aos/common/type_traits.h"
namespace aos {
diff --git a/aos/atom_code/camera/camera.gyp b/aos/atom_code/camera/camera.gyp
index e94a6ac..b8e8dd3 100644
--- a/aos/atom_code/camera/camera.gyp
+++ b/aos/atom_code/camera/camera.gyp
@@ -43,13 +43,28 @@
'hard_dependency': 1,
},
{
+ 'target_name': 'buffers',
+ 'type': 'static_library',
+ 'sources': [
+ 'Buffers.cpp',
+ ],
+ 'dependencies': [
+ '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+ ],
+ },
+ {
'target_name': 'CameraHTTPStreamer',
'type': 'executable',
'sources': [
'HTTPStreamer.cpp',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
+ 'buffers',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
{
@@ -59,7 +74,8 @@
'Reader.cpp',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
+ 'buffers',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
],
diff --git a/aos/atom_code/camera/jni.cpp b/aos/atom_code/camera/jni.cpp
index 7b7eafa..ad5177a 100644
--- a/aos/atom_code/camera/jni.cpp
+++ b/aos/atom_code/camera/jni.cpp
@@ -3,6 +3,8 @@
#include "jni/aos_Natives.h"
#include "aos/atom_code/camera/Buffers.h"
#include "aos/externals/libjpeg/include/jpeglib.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/atom_code/init.h"
using aos::camera::Buffers;
@@ -248,8 +250,8 @@
} else {
level = DEBUG;
}
- // can't use Get/ReleaseStringCritical because log_do might block waiting to
- // put its message into the queue
+ // Can't use Get/ReleaseStringCritical because log_do might block waiting to
+ // put its message into the queue.
const char *const message_chars = env->GetStringUTFChars(message, NULL);
if (message_chars == NULL) return;
log_do(level, "%s\n", message_chars);
diff --git a/aos/atom_code/core/BinaryLogReader.cpp b/aos/atom_code/core/BinaryLogReader.cpp
index b673c26..7027d21 100644
--- a/aos/atom_code/core/BinaryLogReader.cpp
+++ b/aos/atom_code/core/BinaryLogReader.cpp
@@ -11,16 +11,20 @@
#include <map>
-#include "aos/aos_core.h"
+#include "aos/atom_code/logging/atom_logging.h"
#include "aos/atom_code/core/LogFileCommon.h"
#include "aos/common/Configuration.h"
+#include "aos/atom_code/init.h"
-static const char *const kCRIOName = "CRIO";
+namespace aos {
+namespace logging {
+namespace atom {
+namespace {
-int main() {
- aos::InitNRT();
+int binary_log_reader_main() {
+ InitNRT();
- const char *folder = aos::configuration::GetLoggingDirectory();
+ 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);
}
@@ -28,19 +32,24 @@
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)."
+ 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)."
+ 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));
+ 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"
@@ -52,11 +61,12 @@
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)."
+ fprintf(stderr,
+ "BinaryLogReader: couldn't open file '%s' because of %d (%s)."
" exiting\n", tmp, errno, strerror(errno));
return EXIT_FAILURE;
}
- aos::LogFileAccessor writer(fd, true);
+ LogFileAccessor writer(fd, true);
struct timespec timespec;
time_t last_sec = 0;
@@ -68,47 +78,39 @@
writer.Sync();
}
- const log_message *const msg = log_read_next();
+ const LogMessage *const msg = ReadNext();
if (msg == NULL) continue;
- const log_crio_message *const crio_msg = reinterpret_cast<const log_crio_message *>(
- msg);
- size_t name_size, message_size;
- if (msg->source == -1) {
- name_size = strlen(kCRIOName);
- message_size = strlen(crio_msg->message);
- } else {
- name_size = strlen(msg->name);
- message_size = strlen(msg->message);
- }
- // add on size for terminating '\0'
- name_size += 1;
- message_size += 1;
+ // add 1 for terminating '\0'
+ size_t name_size = strlen(msg->name) + 1;
+ size_t message_size = strlen(msg->message) + 1;
- aos::LogFileMessageHeader *const output = writer.GetWritePosition(
- sizeof(aos::LogFileMessageHeader) + name_size + message_size);
+ 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;
- if (msg->source == -1) {
- output->level = crio_msg->level;
- // TODO(brians) figure out what time to put in
- output->sequence = crio_msg->sequence;
- memcpy(output_strings, kCRIOName, name_size);
- memcpy(output_strings + name_size, crio_msg->message, message_size);
- } else {
- output->level = msg->level;
- output->time_sec = msg->time.tv_sec;
- output->time_nsec = msg->time.tv_nsec;
- output->sequence = msg->sequence;
- memcpy(output_strings, msg->name, name_size);
- memcpy(output_strings + name_size, msg->message, message_size);
- }
+ 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);
condition_set(&output->marker);
- log_free_message(msg);
+ logging::atom::Free(msg);
}
- aos::Cleanup();
+ Cleanup();
+ return 0;
+}
+
+} // namespace
+} // namespace atom
+} // namespace logging
+} // namespace aos
+
+int main() {
+ return aos::logging::atom::binary_log_reader_main();
}
diff --git a/aos/atom_code/core/CRIOLogReader.cpp b/aos/atom_code/core/CRIOLogReader.cpp
index f32e21d..2aa216c 100644
--- a/aos/atom_code/core/CRIOLogReader.cpp
+++ b/aos/atom_code/core/CRIOLogReader.cpp
@@ -8,44 +8,55 @@
#include <fcntl.h>
#include <arpa/inet.h>
#include <errno.h>
+#include <string.h>
#include <map>
-#include "aos/aos_core.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/atom_code/logging/atom_logging.h"
#include "aos/common/Configuration.h"
#include "aos/common/byteorder.h"
+#include "aos/atom_code/init.h"
+
+namespace aos {
+namespace logging {
+namespace atom {
+namespace {
struct log_buffer {
- log_crio_message msg;
+ LogMessage *msg;
size_t used;
- log_buffer() {
+ log_buffer() : msg(NULL) {
Clear();
}
void Clear() {
+ logging::atom::Free(msg);
+ msg = logging::atom::Get();
used = 0;
}
- // returns whether msg is now complete
+
+ // Returns whether msg is now complete.
bool ReceiveFrom(int sock) {
- const ssize_t ret = recv(sock, reinterpret_cast<uint8_t *>(&msg) + used,
- sizeof(msg) - used, 0);
+ const ssize_t ret = recv(sock, reinterpret_cast<uint8_t *>(msg) + used,
+ sizeof(*msg) - used, 0);
if (ret == -1) {
LOG(ERROR, "recv(%d, %p, %d) failed because of %d: %s\n",
- sock, reinterpret_cast<uint8_t *>(&msg) + used, sizeof(msg) - used,
+ sock, reinterpret_cast<uint8_t *>(msg) + used, sizeof(*msg) - used,
errno, strerror(errno));
return false;
} else {
used += ret;
- if (used > sizeof(msg)) {
- LOG(WARNING, "used(%zd) is > sizeof(msg)(%zd)\n", used, sizeof(msg));
+ if (used > sizeof(*msg)) {
+ LOG(WARNING, "used(%zd) is > sizeof(*msg)(%zd)\n", used, sizeof(*msg));
}
- return used >= sizeof(msg);
+ return used >= sizeof(*msg);
}
}
};
-int main() {
- aos::InitNRT();
+int crio_log_reader_main() {
+ InitNRT();
const int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
@@ -59,11 +70,11 @@
} bind_sockaddr;
memset(&bind_sockaddr, 0, sizeof(bind_sockaddr));
bind_sockaddr.in.sin_family = AF_INET;
- bind_sockaddr.in.sin_port = htons(static_cast<uint16_t>(aos::NetworkPort::kLogs));
+ bind_sockaddr.in.sin_port = htons(static_cast<uint16_t>(NetworkPort::kLogs));
bind_sockaddr.in.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, &bind_sockaddr.addr, sizeof(bind_sockaddr.addr)) == -1) {
- LOG(ERROR, "bind(%d, %p) failed because of %d: %s\n", sock, &bind_sockaddr.addr,
- errno, strerror(errno));
+ LOG(ERROR, "bind(%d, %p) failed because of %d: %s\n", sock,
+ &bind_sockaddr.addr, errno, strerror(errno));
return EXIT_FAILURE;
}
if (listen(sock, 10) == -1) {
@@ -109,21 +120,35 @@
"because of %d: %s\n",
sock, SOCK_NONBLOCK, errno, strerror(errno));
} else {
- socks[new_sock]; // creates using value's default constructor
+ socks[new_sock]; // creates using value's default constructor
}
}
for (auto it = socks.begin(); it != socks.end(); ++it) {
if (FD_ISSET(it->first, &read_fds)) {
if (it->second.ReceiveFrom(it->first)) {
- it->second.msg.identifier = -1;
- it->second.msg.time = aos::ntoh(it->second.msg.time);
- log_crio_message_send(it->second.msg);
+ it->second.msg->source = ntoh(it->second.msg->source);
+ it->second.msg->sequence = ntoh(it->second.msg->sequence);
+ it->second.msg->level = ntoh(it->second.msg->level);
+ it->second.msg->seconds = ntoh(it->second.msg->seconds);
+ it->second.msg->nseconds = ntoh(it->second.msg->nseconds);
+
+ logging::atom::Write(it->second.msg);
+ it->second.msg = NULL;
it->second.Clear();
}
}
}
}
- aos::Cleanup();
+ Cleanup();
+}
+
+} // namespace
+} // namespace atom
+} // namespace logging
+} // namespace aos
+
+int main() {
+ return aos::logging::atom::crio_log_reader_main();
}
diff --git a/aos/atom_code/core/LogDisplayer.cpp b/aos/atom_code/core/LogDisplayer.cpp
index 9dee08f..d8d9f94 100644
--- a/aos/atom_code/core/LogDisplayer.cpp
+++ b/aos/atom_code/core/LogDisplayer.cpp
@@ -9,6 +9,7 @@
#include "aos/atom_code/core/LogFileCommon.h"
#include "aos/aos_core.h"
+#include "aos/common/logging/logging_impl.h"
namespace {
@@ -81,7 +82,7 @@
filter_name = optarg;
break;
case 'l':
- filter_level = str_log(optarg);
+ filter_level = aos::logging::str_log(optarg);
if (filter_level == LOG_UNKNOWN) {
fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
exit(EXIT_FAILURE);
@@ -121,7 +122,7 @@
}
fprintf(stderr, "displaying down to level %s from file '%s'\n",
- log_str(filter_level), filename);
+ aos::logging::log_str(filter_level), filename);
if (optind < argc) {
fprintf(stderr, "non-option ARGV-elements: ");
while (optind < argc) {
@@ -135,23 +136,33 @@
filename, strerror(errno));
exit(EXIT_FAILURE);
}
- aos::LogFileAccessor accessor(fd, false);
+ aos::logging::LogFileAccessor accessor(fd, false);
if (!start_at_beginning) {
accessor.MoveToEnd();
}
- const aos::LogFileMessageHeader *msg;
+ const aos::logging::LogFileMessageHeader *msg;
+ aos::logging::LogMessage log_message;
do {
msg = accessor.ReadNextMessage(follow);
if (msg == NULL) continue;
- if (log_gt_important(filter_level, msg->level)) 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;
}
- printf("%s(%"PRId32")(%03"PRId8"): %s at %010"PRIu64"s%09"PRIu64"ns: %s",
- reinterpret_cast<const char *>(msg) + sizeof(*msg), msg->source,
- msg->sequence, log_str(msg->level), msg->time_sec, msg->time_nsec,
- reinterpret_cast<const char *>(msg) + sizeof(*msg) + msg->name_size);
+
+ 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/atom_code/core/LogFileCommon.h b/aos/atom_code/core/LogFileCommon.h
index 235c55b..afb86b0 100644
--- a/aos/atom_code/core/LogFileCommon.h
+++ b/aos/atom_code/core/LogFileCommon.h
@@ -11,18 +11,22 @@
#include <algorithm>
-#include "aos/aos_core.h"
+#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 *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 log_message even on
+// 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 condition_set once this one has been written
// for readers keeping up with a live writer
@@ -36,18 +40,17 @@
log_level level;
static_assert(sizeof(level) == 1, "log_level changed size!");
- uint64_t time_sec;
- static_assert(sizeof(time_sec) >= sizeof(log_message::time.tv_sec), "tv_sec won't fit");
- uint64_t time_nsec;
- static_assert(sizeof(time_nsec) >= sizeof(log_message::time.tv_nsec),
+ 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; // pid or -1 for crio
- static_assert(sizeof(source) >= sizeof(log_message::source), "PIDs won't fit");
- uint8_t sequence;
- static_assert(sizeof(sequence) == sizeof(log_crio_message::sequence),
- "something changed");
- static_assert(sizeof(sequence) == sizeof(log_message::sequence),
+ 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'
@@ -129,7 +132,7 @@
sizeof(mutex) > kPageSize) {
char *const temp = current_;
MapNextPage();
- if (condition_set_value(reinterpret_cast<mutex *>(&temp[position_]), 2) != 0) {
+ if (condition_set_value(reinterpret_cast<mutex *>(&temp[position_]), 2) == -1) {
fprintf(stderr, "LogFileCommon: condition_set_value(%p, 2) failed with %d: %s."
" readers will hang\n", &temp[position_], errno, strerror(errno));
}
@@ -186,7 +189,7 @@
}
};
-};
+} // namespace logging
+} // namespace aos
#endif
-
diff --git a/aos/atom_code/core/LogStreamer.cpp b/aos/atom_code/core/LogStreamer.cpp
index ceec18d..ffecac8 100644
--- a/aos/atom_code/core/LogStreamer.cpp
+++ b/aos/atom_code/core/LogStreamer.cpp
@@ -10,35 +10,43 @@
#include <fcntl.h>
#include <inttypes.h>
-#include "aos/aos_core.h"
+#include "aos/atom_code/logging/atom_logging.h"
#include "aos/atom_code/core/LogFileCommon.h"
+#include "aos/atom_code/init.h"
+#include "aos/atom_code/ipc_lib/queue.h"
+#include "aos/common/logging/logging_impl.h"
-static const char *const kCRIOName = "CRIO";
+namespace aos {
+namespace logging {
+namespace atom {
+namespace {
-int main() {
- aos::InitNRT();
+int log_streamer_main() {
+ InitNRT();
const time_t t = time(NULL);
- printf("starting at %jd----------------------------------\n", static_cast<uintmax_t>(t));
+ printf("starting at %jd----------------------------------\n",
+ static_cast<uintmax_t>(t));
int index = 0;
while (true) {
- const log_message *const msg = log_read_next2(BLOCK, &index);
+ const LogMessage *const msg = ReadNext(BLOCK, &index);
if (msg == NULL) continue;
- const log_crio_message *const crio_msg = reinterpret_cast<const log_crio_message *>(
- msg);
- if (msg->source == -1) {
- printf("CRIO(%03"PRId8"): %s at %f: %s", crio_msg->sequence,
- log_str(crio_msg->level), crio_msg->time, crio_msg->message);
- } else {
- printf("%s(%"PRId32")(%03"PRId8"): %s at %010jus%09luns: %s",
- msg->name, msg->source, msg->sequence, log_str(msg->level),
- static_cast<uintmax_t>(msg->time.tv_sec), msg->time.tv_nsec, msg->message);
- }
+ internal::PrintMessage(stdout, *msg);
- log_free_message(msg);
+ logging::atom::Free(msg);
}
- aos::Cleanup();
+ Cleanup();
+ return 0;
+}
+
+} // namespace
+} // namespace atom
+} // namespace logging
+} // namespace aos
+
+int main() {
+ return aos::logging::atom::log_streamer_main();
}
diff --git a/aos/atom_code/core/core.cc b/aos/atom_code/core/core.cc
index b34169b..4179a0a 100644
--- a/aos/atom_code/core/core.cc
+++ b/aos/atom_code/core/core.cc
@@ -3,10 +3,10 @@
// Purposes: All shared memory gets allocated here.
//
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include "aos/aos_core.h"
+#include <sys/select.h>
+#include <stdlib.h>
+
+#include "aos/atom_code/init.h"
int main() {
aos::InitCreate();
diff --git a/aos/atom_code/core/core.gyp b/aos/atom_code/core/core.gyp
index 04d3cc8..4e13e51 100644
--- a/aos/atom_code/core/core.gyp
+++ b/aos/atom_code/core/core.gyp
@@ -7,7 +7,7 @@
'core.cc',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
{
@@ -17,7 +17,9 @@
'BinaryLogReader.cpp',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/atom_code/atom_code.gyp:init',
+ '<(AOS)/common/common.gyp:common',
],
},
{
@@ -27,7 +29,8 @@
'LogStreamer.cpp',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
{
@@ -37,7 +40,8 @@
'LogDisplayer.cpp',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
{
@@ -47,7 +51,8 @@
'CRIOLogReader.cpp',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
],
diff --git a/aos/atom_code/init.cc b/aos/atom_code/init.cc
index 4776261..7eecde3 100644
--- a/aos/atom_code/init.cc
+++ b/aos/atom_code/init.cc
@@ -12,8 +12,9 @@
#include <stdlib.h>
#include <stdint.h>
-#include "aos/aos_core.h"
#include "aos/common/die.h"
+#include "aos/atom_code/logging/atom_logging.h"
+#include "aos/atom_code/ipc_lib/shared_mem.h"
namespace aos {
@@ -69,10 +70,7 @@
Die("%s-init: creating shared memory reference failed\n",
program_invocation_short_name);
}
- if (log_init(program_invocation_short_name) != 0) {
- Die("%s-init: initializing logging failed\n",
- program_invocation_short_name);
- }
+ logging::atom::Register();
}
const char *const kNoRealtimeEnvironmentVariable = "AOS_NO_REALTIME";
diff --git a/aos/atom_code/ipc_lib/mutex.cpp b/aos/atom_code/ipc_lib/mutex.cpp
index d1f0ef2..4753e78 100644
--- a/aos/atom_code/ipc_lib/mutex.cpp
+++ b/aos/atom_code/ipc_lib/mutex.cpp
@@ -2,6 +2,8 @@
#include <inttypes.h>
#include <errno.h>
+#include <stdio.h>
+#include <string.h>
#include "aos/aos_core.h"
#include "aos/common/type_traits.h"
diff --git a/aos/atom_code/ipc_lib/queue.c b/aos/atom_code/ipc_lib/queue.c
index 5cfd2ac..2e45326 100644
--- a/aos/atom_code/ipc_lib/queue.c
+++ b/aos/atom_code/ipc_lib/queue.c
@@ -6,6 +6,8 @@
#include <errno.h>
#include <assert.h>
+#include "aos/common/logging/logging.h"
+
#define READ_DEBUG 0
#define WRITE_DEBUG 0
#define REF_DEBUG 0
@@ -36,7 +38,7 @@
abort();
}
#if REF_DEBUG
- printf("ref_free_count: %p\n", msg);
+ printf("ref free: %p\n", msg);
#endif
--pool->used;
@@ -484,10 +486,7 @@
msg = pool->pool[pool->used];
} else {
if (pool->length >= pool->mem_length) {
- //TODO(brians) log this if it isn't the log queue
- fprintf(stderr, "queue: overused_pool\n");
- msg = NULL;
- goto exit;
+ LOG(FATAL, "overused pool %p\n", pool);
}
msg = pool->pool[pool->length] = aos_alloc_msg(pool);
++pool->length;
@@ -500,7 +499,6 @@
#endif
header->index = pool->used;
++pool->used;
-exit:
mutex_unlock(&pool->pool_lock);
return msg;
}
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',
- ],
- },
],
}
diff --git a/aos/atom_code/output/HTTPServer.cpp b/aos/atom_code/output/HTTPServer.cpp
index 88e3c6f..b0d6b93 100644
--- a/aos/atom_code/output/HTTPServer.cpp
+++ b/aos/atom_code/output/HTTPServer.cpp
@@ -4,6 +4,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <string.h>
#include <memory>
diff --git a/aos/atom_code/starter/starter.cpp b/aos/atom_code/starter/starter.cpp
index 0cfddd5..e47e213 100644
--- a/aos/atom_code/starter/starter.cpp
+++ b/aos/atom_code/starter/starter.cpp
@@ -8,54 +8,56 @@
#include <sys/inotify.h>
#include <sys/stat.h>
-#include "aos/aos_core.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/atom_code/init.h"
void niceexit(int status);
pid_t start(const char *cmd, uint8_t times) {
char *which_cmd, *which_cmd_stm;
if (asprintf(&which_cmd, "which %s", cmd) == -1) {
- LOG_IFINIT(ERROR, "creating \"which %s\" failed with %d: %s\n",
- cmd, errno, strerror(errno));
+ LOG(ERROR, "creating \"which %s\" failed with %d: %s\n",
+ cmd, errno, strerror(errno));
niceexit(EXIT_FAILURE);
}
if (asprintf(&which_cmd_stm, "which %s.stm", cmd) == -1) {
- LOG_IFINIT(ERROR, "creating \"which %s.stm\" failed with %d: %s\n",
- cmd, errno, strerror(errno));
+ LOG(ERROR, "creating \"which %s.stm\" failed with %d: %s\n",
+ cmd, errno, strerror(errno));
niceexit(EXIT_FAILURE);
}
FILE *which = popen(which_cmd, "r");
char exe[CMDLEN + 5], orig_exe[CMDLEN];
size_t ret;
if ((ret = fread(orig_exe, 1, sizeof(orig_exe), which)) == CMDLEN) {
- LOG_IFINIT(ERROR, "`which %s` was too long. not starting '%s'\n", cmd, cmd);
+ LOG(ERROR, "`which %s` was too long. not starting '%s'\n", cmd, cmd);
return 0;
}
orig_exe[ret] = '\0';
if (pclose(which) == -1) {
- LOG_IFINIT(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
+ LOG(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
}
free(which_cmd);
if (strlen(orig_exe) == 0) { // which returned nothing; check if stm exists
- LOG_IFINIT(INFO, "%s didn't exist. trying %s.stm\n", cmd, cmd);
+ LOG(INFO, "%s didn't exist. trying %s.stm\n", cmd, cmd);
FILE *which_stm = popen(which_cmd_stm, "r");
if ((ret = fread(orig_exe, 1, sizeof(orig_exe), which_stm)) == CMDLEN) {
- LOG_IFINIT(ERROR, "`which %s.stm` was too long. not starting %s\n", cmd, cmd);
+ LOG(ERROR, "`which %s.stm` was too long. not starting %s\n", cmd, cmd);
return 0;
}
orig_exe[ret] = '\0';
if (pclose(which) == -1) {
- LOG_IFINIT(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
+ LOG(WARNING, "pclose failed with %d: %s\n", errno, strerror(errno));
}
}
if (strlen(orig_exe) == 0) {
- LOG_IFINIT(WARNING, "couldn't find file '%s[.stm]'. not going to start it\n",
- cmd);
+ LOG(WARNING, "couldn't find file '%s[.stm]'. not going to start it\n",
+ cmd);
return 0;
}
if (orig_exe[strlen(orig_exe) - 1] != '\n') {
- LOG_IFINIT(WARNING, "no \\n on the end of `which %s[.stm]` output ('%s')\n",
- cmd, orig_exe);
+ LOG(WARNING, "no \\n on the end of `which %s[.stm]` output ('%s')\n",
+ cmd, orig_exe);
} else {
orig_exe[strlen(orig_exe) - 1] = '\0'; // get rid of the \n
}
@@ -65,11 +67,11 @@
struct stat st;
errno = 0;
if (stat(orig_exe, &st) != 0 && errno != ENOENT) {
- LOG_IFINIT(ERROR, "killing everything because stat('%s') failed with %d: %s\n",
- orig_exe, errno, strerror(errno));
+ LOG(ERROR, "killing everything because stat('%s') failed with %d: %s\n",
+ orig_exe, errno, strerror(errno));
niceexit(EXIT_FAILURE);
} else if (errno == ENOENT) {
- LOG_IFINIT(WARNING, "binary '%s' doesn't exist. not starting it\n", orig_exe);
+ LOG(WARNING, "binary '%s' doesn't exist. not starting it\n", orig_exe);
return 0;
}
struct stat st2;
@@ -78,38 +80,38 @@
if (!orig_zero) {
// if it failed and it wasn't because it was missing
if (unlink(exe) != 0 && (errno != EROFS && errno != ENOENT)) {
- LOG_IFINIT(ERROR,
- "killing everything because unlink('%s') failed with %d: %s\n",
- exe, errno, strerror(errno));
+ LOG(ERROR,
+ "killing everything because unlink('%s') failed with %d: %s\n",
+ exe, errno, strerror(errno));
niceexit(EXIT_FAILURE);
}
if (link(orig_exe, exe) != 0) {
- LOG_IFINIT(ERROR,
- "killing everything because link('%s', '%s') failed with %d: %s\n",
- orig_exe, exe, errno, strerror(errno));
+ LOG(ERROR,
+ "killing everything because link('%s', '%s') failed with %d: %s\n",
+ orig_exe, exe, errno, strerror(errno));
niceexit(EXIT_FAILURE);
}
}
if (errno == EEXIST) {
- LOG_IFINIT(INFO, "exe ('%s') already existed\n", exe);
+ LOG(INFO, "exe ('%s') already existed\n", exe);
}
pid_t child;
if ((child = fork()) == 0) {
execlp(exe, orig_exe, static_cast<char *>(NULL));
- LOG_IFINIT(ERROR,
- "killing everything because execlp('%s', '%s', NULL) "
- "failed with %d: %s\n",
- exe, cmd, errno, strerror(errno));
+ LOG(ERROR,
+ "killing everything because execlp('%s', '%s', NULL) "
+ "failed with %d: %s\n",
+ exe, cmd, errno, strerror(errno));
_exit(EXIT_FAILURE); // don't niceexit or anything because this is the child!!
}
if (child == -1) {
- LOG_IFINIT(WARNING, "fork on '%s' failed with %d: %s",
- cmd, errno, strerror(errno));
+ LOG(WARNING, "fork on '%s' failed with %d: %s",
+ cmd, errno, strerror(errno));
if (times < 100) {
return start(cmd, times + 1);
} else {
- LOG_IFINIT(ERROR, "tried to start '%s' too many times. giving up\n", cmd);
+ LOG(ERROR, "tried to start '%s' too many times. giving up\n", cmd);
return 0;
}
} else {
@@ -117,9 +119,9 @@
files[child] = orig_exe;
int ret = inotify_add_watch(notifyfd, orig_exe, IN_ATTRIB | IN_MODIFY);
if (ret < 0) {
- LOG_IFINIT(WARNING, "inotify_add_watch('%s') failed: "
- "not going to watch for changes to it because of %d: %s\n",
- orig_exe, errno, strerror(errno));
+ LOG(WARNING, "inotify_add_watch('%s') failed: "
+ "not going to watch for changes to it because of %d: %s\n",
+ orig_exe, errno, strerror(errno));
} else {
watches[ret] = child;
mtimes[ret] = st2.st_mtime;
@@ -164,6 +166,8 @@
atexit(exit_handler);
+ aos::logging::Init();
+
notifyfd = inotify_init1(IN_NONBLOCK);
pid_t core = start("core", 0);
@@ -330,18 +334,6 @@
niceexit(EXIT_FAILURE);
}
- /*// remove all of the watches assigned to the pid that just died
- for (auto it = watches.begin(); it != watches.end(); ++it) {
- if (it->second == infop.si_pid) {
- watches_to_ignore.insert(it->first);
- }
- }
- for (auto it = watches_to_ignore.begin();
- it != watches_to_ignore.end(); ++it) {
- LOG(DEBUG, "watch id %d was on PID %d\n", *it, infop.si_pid);
- watches.erase(*it);
- }*/
-
start(children[infop.si_pid], 0);
LOG(DEBUG, "erasing %d from children\n", infop.si_pid);
children.erase(infop.si_pid);
diff --git a/aos/atom_code/starter/starter.gyp b/aos/atom_code/starter/starter.gyp
index 44e4b08..cff6d07 100644
--- a/aos/atom_code/starter/starter.gyp
+++ b/aos/atom_code/starter/starter.gyp
@@ -7,7 +7,7 @@
'starter.cpp',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
'copies': [
{
diff --git a/aos/atom_code/thread_local.h b/aos/atom_code/thread_local.h
new file mode 100644
index 0000000..9563a87
--- /dev/null
+++ b/aos/atom_code/thread_local.h
@@ -0,0 +1,13 @@
+#ifndef AOS_ATOM_CODE_THREAD_LOCAL_H_
+#define AOS_ATOM_CODE_THREAD_LOCAL_H_
+
+// The storage class to use when declaring thread-local variables. This provides
+// a single place to change it if/when we want to switch to something standard.
+//
+// Example: AOS_THREAD_LOCAL void *bla; // at namespace (aka global) scope
+//
+// C++11 has thread_local, but it's not clear whether Clang supports that as of
+// 12/18/12.
+#define AOS_THREAD_LOCAL __thread
+
+#endif // AOS_ATOM_CODE_THREAD_LOCAL_H_
diff --git a/aos/build/aos.gyp b/aos/build/aos.gyp
index b6bd3ee..0c1506c 100644
--- a/aos/build/aos.gyp
+++ b/aos/build/aos.gyp
@@ -8,17 +8,12 @@
'conditions': [
['OS=="crio"', {
'libaos_source_files': [
- '<!@(find <(AOS)/crio/controls <(AOS)/crio/messages <(AOS)/crio/motor_server <(AOS)/crio/shared_libs -name *.c -or -name *.cpp -or -name *.cc)',
+ '<!@(find <(AOS)/crio/controls <(AOS)/crio/messages <(AOS)/crio/motor_server -name *.c -or -name *.cpp -or -name *.cc)',
'<(AOS)/crio/Talon.cpp',
- '<(AOS)/common/die.cc',
],
}, {
'libaos_source_files': [
- '<(AOS)/atom_code/camera/Buffers.cpp',
'<(AOS)/atom_code/async_action/AsyncAction_real.cpp',
- '<(AOS)/atom_code/init.cc',
- '<(AOS)/atom_code/ipc_lib/mutex.cpp',
- '<(AOS)/common/die.cc',
],
}
],
@@ -28,17 +23,20 @@
{
'target_name': 'logging',
'type': 'static_library',
+ 'sources': [
+ '<(AOS)/common/logging/logging_impl.cc',
+ ],
'conditions': [
['OS=="crio"', {
'sources': [
- '<(AOS)/crio/logging/crio_logging.cpp',
+ '<(AOS)/crio/logging/crio_logging.cc',
],
'dependencies': [
'<(EXTERNALS):WPILib',
]
}, {
'sources': [
- '<(AOS)/atom_code/logging/atom_logging.cpp'
+ '<(AOS)/atom_code/logging/atom_logging.cc',
],
'dependencies': [
'<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
@@ -50,6 +48,9 @@
],
'dependencies': [
'<(AOS)/common/common.gyp:time',
+ '<(AOS)/common/common.gyp:once',
+ '<(AOS)/common/common.gyp:mutex',
+ '<(AOS)/common/common.gyp:die',
],
},
{
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 68439e3..d1f446a 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -19,7 +19,6 @@
'../crio/crio.gyp:unsafe_queue_test',
'../common/common.gyp:queue_test',
#'../common/messages/messages.gyp:*', # TODO(brians) did this test ever exist?
- '../atom_code/logging/logging.gyp:*',
'../common/common.gyp:die_test',
'../common/util/util.gyp:trapezoid_profile_test',
'Common',
@@ -44,6 +43,7 @@
'<(AOS)/common/common.gyp:time_test',
'<(AOS)/common/common.gyp:mutex_test',
'<(AOS)/common/common.gyp:once_test',
+ '<(AOS)/common/logging/logging.gyp:logging_impl_test',
],
},
],
diff --git a/aos/common/byteorder.h b/aos/common/byteorder.h
index 3444f98..87ef39a 100644
--- a/aos/common/byteorder.h
+++ b/aos/common/byteorder.h
@@ -4,6 +4,7 @@
#ifndef __VXWORKS__
#include <endian.h> // endian(3)
#endif
+#include <string.h>
// Contains functions for converting between host and network byte order for
// things other than 16/32 bit integers (those are provided by byteorder(3)).
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 967eb51..81e2da7 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -132,12 +132,19 @@
{
'target_name': 'controls',
'type': 'static_library',
- 'sources': [],
+ 'sources': [
+ # 'control_loop/ControlLoop-tmpl.h',
+ ],
'dependencies': [
'<(AOS)/common/messages/messages.gyp:aos_queues',
'<(AOS)/build/aos.gyp:logging',
'timing',
],
+ 'export_dependent_settings': [
+ '<(AOS)/common/messages/messages.gyp:aos_queues',
+ '<(AOS)/build/aos.gyp:logging',
+ 'timing',
+ ],
},
{
'target_name': 'queue_test',
@@ -208,6 +215,34 @@
],
},
{
+ 'target_name': 'die',
+ 'type': 'static_library',
+ 'sources': [
+ 'die.cc',
+ ],
+ },
+ {
+ 'target_name': 'mutex',
+ 'type': 'static_library',
+ 'conditions': [
+ ['OS=="crio"', {
+ 'sources': [
+ '<(AOS)/crio/shared_libs/mutex.cpp',
+ ],
+ }, {
+ 'sources': [
+ '<(AOS)/atom_code/ipc_lib/mutex.cpp',
+ ],
+ 'dependencies': [
+ '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/atom_code/ipc_lib/ipc_lib.gyp:ipc_lib',
+ ],
+ }],
+ ],
+ },
+ {
'target_name': 'mutex_test',
'type': '<(aos_target)',
'sources': [
diff --git a/aos/common/control_loop/ControlLoop.h b/aos/common/control_loop/ControlLoop.h
index b69cf01..e866bb7 100644
--- a/aos/common/control_loop/ControlLoop.h
+++ b/aos/common/control_loop/ControlLoop.h
@@ -5,8 +5,8 @@
#include "aos/aos_core.h"
#include "aos/common/control_loop/Timing.h"
-#include "aos/common/messages/RobotState.q.h"
#include "aos/common/type_traits.h"
+#include "aos/common/queue.h"
namespace aos {
namespace control_loops {
diff --git a/aos/common/control_loop/Timing.cpp b/aos/common/control_loop/Timing.cpp
index 63fda44..6db179c 100644
--- a/aos/common/control_loop/Timing.cpp
+++ b/aos/common/control_loop/Timing.cpp
@@ -1,6 +1,8 @@
-#include "aos/common/logging/logging.h"
#include "aos/common/control_loop/Timing.h"
+#include <string.h>
+
+#include "aos/common/logging/logging.h"
#include "aos/common/time.h"
namespace aos {
diff --git a/aos/common/inttypes.h b/aos/common/inttypes.h
index e10a33b..d243860 100644
--- a/aos/common/inttypes.h
+++ b/aos/common/inttypes.h
@@ -8,12 +8,15 @@
#ifndef __VXWORKS__
#include <inttypes.h>
#else
+#define PRId64 "lld"
+
// It warns about just "d", but not this, which is kind of weird because
// sizeof(int) == sizeof(long) == sizeof(int32_t) == 4, but oh well.
#define PRId32 "ld"
#define PRIx32 "lx"
-#define PRId64 "lld"
-#define PRIu16 "u"
+#define PRIu32 "lu"
+
+#define PRIu16 "hu"
#endif
#endif // AOS_COMMON_INTTYPES_H_
diff --git a/aos/common/logging/logging.gyp b/aos/common/logging/logging.gyp
new file mode 100644
index 0000000..52d2527
--- /dev/null
+++ b/aos/common/logging/logging.gyp
@@ -0,0 +1,15 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'logging_impl_test',
+ 'type': '<(aos_target)',
+ 'sources': [
+ 'logging_impl_test.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):gtest',
+ '<(AOS)/build/aos.gyp:libaos',
+ ],
+ },
+ ],
+}
diff --git a/aos/common/logging/logging.h b/aos/common/logging/logging.h
index e3ff839..392fc5b 100644
--- a/aos/common/logging/logging.h
+++ b/aos/common/logging/logging.h
@@ -1,102 +1,114 @@
#ifndef AOS_COMMON_LOGGING_LOGGING_H_
-// must be kept in sync with crio/logging/crio_logging.h and atom_code/logging/atom_logging.h
#define AOS_COMMON_LOGGING_LOGGING_H_
+// This file contains the logging client interface. It works with both C and C++
+// code.
+
+#include <stdio.h>
#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef uint8_t log_level;
+
#define DECL_LEVELS \
DECL_LEVEL(DEBUG, 0); /* stuff that gets printed out every cycle */ \
DECL_LEVEL(INFO, 1); /* things like PosEdge/NegEdge */ \
-/* things that might still work if they happen occasionally but should be watched */ \
+/* things that might still work if they happen occasionally */ \
DECL_LEVEL(WARNING, 2); \
/*-1 so that vxworks macro of same name will have same effect if used*/ \
DECL_LEVEL(ERROR, -1); /* errors */ \
-DECL_LEVEL(FATAL, 4); /* serious errors. the logging code will terminate the process/task */ \
+/* serious errors. the logging code will terminate the process/task */ \
+DECL_LEVEL(FATAL, 4); \
DECL_LEVEL(LOG_UNKNOWN, 5); /* unknown logging level */
-#define DECL_LEVEL(name, value) extern const log_level name;
+#define DECL_LEVEL(name, value) static const log_level name = value;
#undef ERROR
-DECL_LEVELS
+DECL_LEVELS;
#undef DECL_LEVEL
#define STRINGIFY(x) TO_STRING(x)
#define TO_STRING(x) #x
//not static const size_t for c code
-#define LOG_MESSAGE_LEN 300
+#define LOG_MESSAGE_LEN 500
+
+#ifdef __VXWORKS__
+// We're using ancient glibc, so sticking to just what the syscall can handle is
+// probably safer.
+#define LOG_PRINTF_FORMAT_TYPE printf
+#else
+#define LOG_PRINTF_FORMAT_TYPE gnu_printf
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Actually implements the basic logging call.
+// Does not check that level is valid.
+void log_do(log_level level, const char *format, ...)
+ __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
+
+void log_cork(int line, const char *function, const char *format, ...)
+ __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 3, 4)));
+// Implements the uncork logging call.
+void log_uncork(int line, const char *function, log_level level,
+ const char *file, const char *format, ...)
+ __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 5, 6)));
+#ifdef __cplusplus
+}
+#endif
+
+// A magical static const char[] or string literal that communicates the name
+// of the enclosing function.
+// It's currently using __PRETTY_FUNCTION__ because both GCC and Clang support
+// that and it gives nicer results in C++ than the standard __func__ (which
+// would also work).
+#define LOG_CURRENT_FUNCTION __PRETTY_FUNCTION__
+
+// The basic logging call.
+#define LOG(level, format, args...) do {\
+ log_do(level, LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": %s: " format, \
+ LOG_CURRENT_FUNCTION, ##args); \
+ /* so that GCC knows that it won't return */ \
+ if (level == FATAL) { \
+ fprintf(stderr, "log_do(FATAL) fell through!!!!!\n"); \
+ printf("see stderr\n"); \
+ abort(); \
+ } \
+} while (0)
// Allows format to not be a string constant.
-#define LOG_DYNAMIC(level, format, args...) do{ \
+#define LOG_DYNAMIC(level, format, args...) do { \
static char log_buf[LOG_MESSAGE_LEN]; \
int ret = snprintf(log_buf, sizeof(log_buf), format, ##args); \
- if(ret < 0 || (uintmax_t)ret >= LOG_MESSAGE_LEN){ \
- LOG(WARNING, "next message was too long so not subbing in args\n"); \
+ if (ret < 0 || (uintmax_t)ret >= LOG_MESSAGE_LEN) { \
+ LOG(ERROR, "next message was too long so not subbing in args\n"); \
LOG(level, "%s", format); \
}else{ \
LOG(level, "%s", log_buf); \
} \
-}while(0)
+} while (0)
-// The struct that the crio-side code uses for making logging calls.
-// Packed so it's the same on the atom and the crio.
-typedef struct {
- // pid_t here at the front like in log_message
- pid_t identifier; // must ALWAYS be -1 to identify that this is a crio log message
- log_level level;
- // still has to fit in LOG_MESSAGE_LEN on the atom side
- char message[LOG_MESSAGE_LEN - 50];
- double time;
- uint8_t sequence;
-} __attribute__((packed)) log_crio_message;
+// Allows "bottling up" multiple log fragments which can then all be logged in
+// one message with LOG_UNCORK.
+// Calls from a given thread/task will be grouped together.
+#define LOG_CORK(format, args...) do { \
+ log_cork(__LINE__, LOG_CURRENT_FUNCTION, 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__, LOG_CURRENT_FUNCTION, level, LOG_SOURCENAME, \
+ format, ##args); \
+} while (0)
-#ifdef __cplusplus
-// Just sticks the message into the shared memory queue.
-int log_crio_message_send(log_crio_message &to_send);
-// Returns left > right. LOG_UNKNOWN is most important.
-static inline bool log_gt_important(log_level left, log_level right) {
- log_level l = left, r = right;
- if (l == ERROR) l = 3;
- if (r == ERROR) r = 3;
- return left > right;
-}
-#endif
-
-// Returns a string representing level or "unknown".
-static inline const char *log_str(log_level level) {
- // c doesn't really have const variables so they don't work in case statements
- if (level == DEBUG) return "DEBUG";
- if (level == INFO) return "INFO";
- if (level == WARNING) return "WARNING";
- if (level == ERROR) return "ERROR";
- if (level == FATAL) return "FATAL";
- return "unknown";
-}
-// Returns the log level represented by str or LOG_UNKNOWN.
-static inline log_level str_log(const char *str) {
- if (!strcmp(str, "DEBUG")) return DEBUG;
- if (!strcmp(str, "INFO")) return INFO;
- if (!strcmp(str, "WARNING")) return WARNING;
- if (!strcmp(str, "ERROR")) return ERROR;
- if (!strcmp(str, "FATAL")) return FATAL;
- return LOG_UNKNOWN;
-}
+// TODO(brians) add CHECK macros like glog
+// (<http://google-glog.googlecode.com/svn/trunk/doc/glog.html>)
+// and replace assert with one
#ifdef __cplusplus
}
#endif
-#ifdef __unix
-#include "aos/atom_code/logging/atom_logging.h" // IWYU pragma: export
-#else
-#include "aos/crio/logging/crio_logging.h" // IWYU pragma: export
#endif
-
-#endif
-
diff --git a/aos/common/logging/logging_impl.cc b/aos/common/logging/logging_impl.cc
new file mode 100644
index 0000000..66e64d8
--- /dev/null
+++ b/aos/common/logging/logging_impl.cc
@@ -0,0 +1,230 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <assert.h>
+
+#include "aos/common/die.h"
+#include "aos/common/time.h"
+#include "aos/common/inttypes.h"
+#include "aos/common/once.h"
+
+namespace aos {
+namespace logging {
+namespace {
+
+using internal::Context;
+
+LogImplementation *global_top_implementation(NULL);
+// Just going to use a mutex instead of getting fancy because speed doesn't
+// really matter when accessing global_top_implementation.
+Mutex global_top_implementation_mutex;
+LogImplementation *get_global_top_implementation() {
+ MutexLocker locker(&global_top_implementation_mutex);
+ return global_top_implementation;
+}
+
+// The root LogImplementation. It only logs to stderr/stdout.
+// Some of the things specified in the LogImplementation documentation doesn't
+// apply here (mostly the parts about being able to use LOG) because this is the
+// root one.
+class RootLogImplementation : public LogImplementation {
+ virtual void set_next(LogImplementation *) {
+ LOG(FATAL, "can't have a next logger from here\n");
+ }
+
+ virtual void DoLog(log_level level, const char *format, va_list ap) {
+ LogMessage message;
+ internal::FillInMessage(level, format, ap, &message);
+ internal::PrintMessage(stderr, message);
+ fputs("root logger got used, see stderr for message\n", stdout);
+ }
+};
+
+void SetGlobalImplementation(LogImplementation *implementation) {
+ Context *context = Context::Get();
+
+ context->implementation = implementation;
+ {
+ MutexLocker locker(&global_top_implementation_mutex);
+ global_top_implementation = implementation;
+ }
+}
+
+// Prints format (with ap) into output and correctly deals with the result
+// being too long etc.
+void ExecuteFormat(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) {
+ LOG(FATAL, "vsnprintf(%p, %zd, %s, %p) failed with %d (%s)\n",
+ output, size, format, ap, errno, strerror(errno));
+ } else if (static_cast<uintmax_t>(ret) >= static_cast<uintmax_t>(size)) {
+ // Overwrite the '\0' at the end of the existing data and
+ // copy in the one on the end of continued.
+ memcpy(&output[size - 1], continued, strlen(continued) + 1);
+ }
+}
+
+void *DoInit() {
+ SetGlobalImplementation(new RootLogImplementation());
+ return NULL;
+}
+
+} // namespace
+namespace internal {
+
+Context::Context()
+ : implementation(get_global_top_implementation()),
+ sequence(0) {
+ cork_data.Reset();
+}
+
+void FillInMessage(log_level level, const char *format, va_list ap,
+ LogMessage *message) {
+ Context *context = Context::Get();
+
+ ExecuteFormat(message->message, sizeof(message->message), format, ap);
+
+ message->level = level;
+ message->source = context->source;
+ memcpy(message->name, context->name, context->name_size);
+
+ time::Time now = time::Time::Now();
+ message->seconds = now.sec();
+ message->nseconds = now.nsec();
+
+ message->sequence = context->sequence++;
+}
+
+void PrintMessage(FILE *output, const LogMessage &message) {
+ fprintf(output, "%s(%"PRId32")(%05"PRIu16"): %s at"
+ " %010"PRId32".%09"PRId32"s: %s",
+ message.name, message.source, message.sequence,
+ log_str(message.level), message.seconds, message.nseconds,
+ message.message);
+}
+
+} // namespace internal
+
+void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
+ int levels) {
+ Context *context = Context::Get();
+
+ LogImplementation *top_implementation = context->implementation;
+ LogImplementation *new_implementation = top_implementation;
+ LogImplementation *implementation = NULL;
+ assert(levels >= 1);
+ for (int i = 0; i < levels; ++i) {
+ implementation = new_implementation;
+ if (new_implementation == NULL) {
+ Die("no logging implementation to use\n");
+ }
+ new_implementation = new_implementation->next();
+ }
+ context->implementation = new_implementation;
+ implementation->DoLog(level, format, ap);
+ context->implementation = top_implementation;
+
+ if (level == FATAL) {
+ VDie(format, ap);
+ }
+}
+
+void VLog(log_level level, const char *format, va_list ap) {
+ LogImplementation::DoVLog(level, format, ap, 1);
+}
+
+void VCork(int line, const char *function, const char *format, va_list ap) {
+ Context *context = Context::Get();
+
+ const size_t message_length = strlen(context->cork_data.message);
+ if (line > context->cork_data.line_max) context->cork_data.line_max = line;
+ if (line < context->cork_data.line_min) context->cork_data.line_min = line;
+
+ if (context->cork_data.function == NULL) {
+ context->cork_data.function = function;
+ } else {
+ if (strcmp(context->cork_data.function, function) != 0) {
+ LOG(FATAL, "started corking data in function %s but then moved to %s\n",
+ context->cork_data.function, function);
+ }
+ }
+
+ ExecuteFormat(context->cork_data.message + message_length,
+ sizeof(context->cork_data.message) - message_length,
+ format, ap);
+}
+
+void VUnCork(int line, const char *function, log_level level, const char *file,
+ const char *format, va_list ap) {
+ Context *context = Context::Get();
+
+ VCork(line, function, format, ap);
+
+ log_do(level, "%s: %d-%d: %s: %s", file,
+ context->cork_data.line_min, context->cork_data.line_max, function,
+ context->cork_data.message);
+
+ context->cork_data.Reset();
+}
+
+void LogNext(log_level level, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ LogImplementation::DoVLog(level, format, ap, 2);
+ va_end(ap);
+}
+
+void AddImplementation(LogImplementation *implementation) {
+ Context *context = Context::Get();
+
+ if (implementation->next() != NULL) {
+ LOG(FATAL, "%p already has a next implementation, but it's not"
+ " being used yet\n", implementation);
+ }
+
+ LogImplementation *old = context->implementation;
+ if (old != NULL) {
+ implementation->set_next(old);
+ }
+ SetGlobalImplementation(implementation);
+}
+
+void Init() {
+ static Once<void> once(DoInit);
+ once.Get();
+}
+
+void Load() {
+ Context::Get();
+}
+
+void Cleanup() {
+ Context::Delete();
+}
+
+} // namespace logging
+} // namespace aos
+
+void log_do(log_level level, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ aos::logging::VLog(level, format, ap);
+ va_end(ap);
+}
+
+void log_cork(int line, const char *function, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ aos::logging::VCork(line, function, format, ap);
+ va_end(ap);
+}
+
+void log_uncork(int line, const char *function, log_level level,
+ const char *file, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ aos::logging::VUnCork(line, function, level, file, format, ap);
+ va_end(ap);
+}
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
new file mode 100644
index 0000000..0b1e256
--- /dev/null
+++ b/aos/common/logging/logging_impl.h
@@ -0,0 +1,205 @@
+#ifndef AOS_COMMON_LOGGING_LOGGING_IMPL_H_
+#define AOS_COMMON_LOGGING_LOGGING_IMPL_H_
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <string>
+
+#include "aos/common/logging/logging.h"
+#include "aos/common/type_traits.h"
+#include "aos/common/mutex.h"
+
+// This file has all of the logging implementation. It can't be #included by C
+// code like logging.h can.
+
+namespace aos {
+namespace logging {
+
+// Unless explicitly stated otherwise, format must always be a string constant,
+// args are printf-style arguments for format, and ap is a va_list of args.
+// The validitiy of format and args together will be checked at compile time
+// using a gcc function attribute.
+
+// The struct that the code uses for making logging calls.
+// Packed so that it ends up the same under both linux and vxworks.
+struct __attribute__((packed)) LogMessage {
+#ifdef __VXWORKS__
+ static_assert(sizeof(pid_t) == sizeof(int),
+ "we use task IDs (aka ints) and pid_t interchangeably");
+#endif
+ // Actually the task ID (aka a pointer to the TCB) on the cRIO.
+ pid_t source;
+ static_assert(sizeof(source) == 4, "that's how they get printed");
+ // Per task/thread.
+ uint16_t sequence;
+ log_level level;
+ int32_t seconds, nseconds;
+ char name[100];
+ char message[LOG_MESSAGE_LEN];
+};
+static_assert(shm_ok<LogMessage>::value, "it's going in a queue");
+
+// Returns left > right. LOG_UNKNOWN is most important.
+static inline bool log_gt_important(log_level left, log_level right) {
+ if (left == ERROR) left = 3;
+ if (right == ERROR) right = 3;
+ return left > right;
+}
+
+// Returns a string representing level or "unknown".
+static inline const char *log_str(log_level level) {
+#define DECL_LEVEL(name, value) if (level == name) return #name;
+ DECL_LEVELS;
+#undef DECL_LEVEL
+ return "unknown";
+}
+// Returns the log level represented by str or LOG_UNKNOWN.
+static inline log_level str_log(const char *str) {
+#define DECL_LEVEL(name, value) if (!strcmp(str, #name)) return name;
+ DECL_LEVELS;
+#undef DECL_LEVEL
+ return LOG_UNKNOWN;
+}
+
+// Takes a message and logs it. It will set everything up and then call DoLog
+// for the current LogImplementation.
+void VLog(log_level level, const char *format, va_list ap);
+// Adds to the saved up message.
+void VCork(int line, const char *format, va_list ap);
+// Actually logs the saved up message.
+void VUnCork(int line, log_level level, const char *file,
+ const char *format, va_list ap);
+
+// Will call VLog with the given arguments for the next logger in the chain.
+void LogNext(log_level level, const char *format, ...)
+ __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
+
+// Represents a system that can actually take log messages and do something
+// useful with them.
+// All of the code (transitively too!) in the DoLog here can make
+// normal LOG and LOG_DYNAMIC calls but can NOT call LOG_CORK/LOG_UNCORK. These
+// calls will not result in DoLog recursing. However, implementations must be
+// safe to call from multiple threads/tasks at the same time. Also, any other
+// overriden methods may end up logging through a given implementation's DoLog.
+class LogImplementation {
+ public:
+ LogImplementation() : next_(NULL) {}
+
+ // The one that this one's implementation logs to.
+ // NULL means that there is no next one.
+ LogImplementation *next() { return next_; }
+ // Virtual in case a subclass wants to perform checks. There will be a valid
+ // logger other than this one available while this is called.
+ virtual void set_next(LogImplementation *next) { next_ = next; }
+
+ private:
+ // Actually logs the given message. Implementations should somehow create a
+ // LogMessage and then call internal::FillInMessage.
+ virtual void DoLog(log_level level, const char *format, va_list ap) = 0;
+
+ // Function of this class so that it can access DoLog.
+ // Levels is how many LogImplementations to not use off the stack.
+ static void DoVLog(log_level, const char *format, va_list ap, int levels);
+ // Friends so that they can access DoVLog.
+ friend void VLog(log_level, const char *, va_list);
+ friend void LogNext(log_level, const char *, ...);
+
+ LogImplementation *next_;
+};
+
+// Adds another implementation to the stack of implementations in this
+// task/thread.
+// Any tasks/threads created after this call will also use this implementation.
+// The cutoff is when the state in a given task/thread is created (either lazily
+// when needed or by calling Load()).
+// The logging system takes ownership of implementation. It will delete it if
+// necessary, so it must be created with new.
+void AddImplementation(LogImplementation *implementation);
+
+// Must be called at least once per process/load before anything else is
+// called. This function is safe to call multiple times from multiple
+// tasks/threads.
+void Init();
+
+// Forces all of the state that is usually lazily created when first needed to
+// be created when called. Cleanup() will delete it.
+void Load();
+// Resets all information in this task/thread to its initial state.
+// NOTE: This is not the opposite of Init(). The state that this deletes is
+// lazily created when needed. It is actually the opposite of Load().
+void Cleanup();
+
+// This is where all of the code that is only used by actual LogImplementations
+// goes.
+namespace internal {
+
+// An separate instance of this class is accessible from each task/thread.
+struct Context {
+ Context();
+
+ // Gets the Context object for this task/thread. Will create one the first
+ // time it is called.
+ //
+ // The implementation for each platform will lazily instantiate a new instance
+ // and then initialize name the first time.
+ // IMPORTANT: The implementation of this can not use logging.
+ static Context *Get();
+ // Deletes the Context object for this task/thread so that the next Get() is
+ // called it will create a new one.
+ // It is valid to call this when Get() has never been called.
+ static void Delete();
+
+ // Which one to log to right now.
+ // Will be NULL if there is no logging implementation to use right now.
+ LogImplementation *implementation;
+
+ // A string representing this task/(process and thread).
+ const char *name;
+ // The number of bytes in name (including the terminating '\0').
+ // Must be <= sizeof(LogMessage::name).
+ size_t name_size;
+
+ // What to assign LogMessage::source to in this task/thread.
+ pid_t source;
+
+ // The sequence value to send out with the next message.
+ uint16_t sequence;
+
+ // Contains all of the information related to implementing LOG_CORK and
+ // LOG_UNCORK.
+ struct {
+ char message[LOG_MESSAGE_LEN];
+ int line_min, line_max;
+ // Sets the data up to record a new series of corked logs.
+ void Reset() {
+ message[0] = '\0'; // make strlen of it 0
+ line_min = INT_MAX;
+ line_max = -1;
+ function = NULL;
+ }
+ // The function that the calls are in.
+ // REMEMBER: While the compiler/linker will probably optimize all of the
+ // identical strings to point to the same data, it might not, so using == to
+ // compare this with another value is a bad idea.
+ const char *function;
+ } cork_data;
+};
+
+// Fills in *message according to the given inputs. Used for implementing
+// LogImplementation::DoLog.
+void FillInMessage(log_level level, const char *format, va_list ap,
+ LogMessage *message);
+
+// Prints message to output.
+void PrintMessage(FILE *output, const LogMessage &message);
+
+} // namespace internal
+} // namespace logging
+} // namespace aos
+
+#endif // AOS_COMMON_LOGGING_LOGGING_IMPL_H_
diff --git a/aos/common/logging/logging_impl_test.cc b/aos/common/logging/logging_impl_test.cc
new file mode 100644
index 0000000..bc411ce
--- /dev/null
+++ b/aos/common/logging/logging_impl_test.cc
@@ -0,0 +1,176 @@
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/time.h"
+#include "aos/common/die.h"
+#include "aos/common/inttypes.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::AssertionFailure;
+
+namespace aos {
+namespace logging {
+namespace testing {
+
+class TestLogImplementation : public LogImplementation {
+ virtual void DoLog(log_level level, const char *format, va_list ap) {
+ internal::FillInMessage(level, format, ap, &message_);
+
+ used_ = true;
+ }
+
+ LogMessage message_;
+
+ public:
+ const LogMessage &message() { return message_; }
+ bool used() { return used_; }
+ void reset_used() { used_ = false; }
+
+ TestLogImplementation() : used_(false) {}
+
+ bool used_;
+};
+class LoggingTest : public ::testing::Test {
+ protected:
+ AssertionResult WasAnythingLogged() {
+ if (log_implementation->used()) {
+ return AssertionSuccess() << "read message '" <<
+ log_implementation->message().message << "'";
+ }
+ return AssertionFailure();
+ }
+ AssertionResult WasLogged(log_level level, const std::string message) {
+ if (!log_implementation->used()) {
+ return AssertionFailure() << "nothing was logged";
+ }
+ if (log_implementation->message().level != level) {
+ return AssertionFailure() << "a message with level " <<
+ log_str(log_implementation->message().level) <<
+ " was logged instead of " << log_str(level);
+ }
+ internal::Context *context = internal::Context::Get();
+ if (log_implementation->message().source != context->source) {
+ Die("got a message from %"PRIu32", but we're %"PRIu32"\n",
+ static_cast<uint32_t>(log_implementation->message().source),
+ static_cast<uint32_t>(context->source));
+ }
+ if (strcmp(log_implementation->message().name, context->name) != 0) {
+ Die("got a message from %s, but we're %s\n",
+ log_implementation->message().name, context->name);
+ }
+ if (strstr(log_implementation->message().message, message.c_str())
+ == NULL) {
+ return AssertionFailure() << "got a message of '" <<
+ log_implementation->message().message <<
+ "' but expected it to contain '" << message << "'";
+ }
+
+ return AssertionSuccess() << log_implementation->message().message;
+ }
+
+ private:
+ virtual void SetUp() {
+ static bool first = true;
+ if (first) {
+ first = false;
+
+ Init();
+ // We'll end up with several of them stacked up, but it really doesn't
+ // matter.
+ AddImplementation(log_implementation = new TestLogImplementation());
+ }
+
+ log_implementation->reset_used();
+ }
+ virtual void TearDown() {
+ Cleanup();
+ }
+
+ static TestLogImplementation *log_implementation;
+};
+TestLogImplementation *LoggingTest::log_implementation(NULL);
+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);
+ std::stringstream expected;
+ expected << "logging_impl_test.cc: ";
+ expected << (begin_line + 1);
+ expected << "-";
+ expected << (end_line + 1);
+ expected << ": ";
+ expected << __PRETTY_FUNCTION__;
+ expected << ": first part second part (=19) third part last part 5\n";
+ EXPECT_TRUE(WasLogged(WARNING, expected.str()));
+}
+
+#ifndef __VXWORKS__
+TEST_F(LoggingDeathTest, Fatal) {
+ ASSERT_EXIT(LOG(FATAL, "this should crash it\n"),
+ ::testing::KilledBySignal(SIGABRT),
+ "this should crash it");
+}
+#endif
+
+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"));
+}
+
+TEST_F(LoggingTest, Timing) {
+ // For writing only.
+ //static const long kTimingCycles = 5000000;
+ static const long kTimingCycles = 5000;
+
+ time::Time start = time::Time::Now();
+ for (long i = 0; i < kTimingCycles; ++i) {
+ LOG(INFO, "a\n");
+ }
+ time::Time end = time::Time::Now();
+ time::Time diff = end - start;
+ printf("short message took %lld nsec for %ld\n",
+ diff.ToNSec(), kTimingCycles);
+
+ start = time::Time::Now();
+ for (long i = 0; i < kTimingCycles; ++i) {
+ LOG(INFO, "something longer than just \"a\" to log to test timing\n");
+ }
+ end = time::Time::Now();
+ diff = end - start;
+ printf("long message took %lld nsec for %ld\n",
+ diff.ToNSec(), kTimingCycles);
+}
+
+} // namespace testing
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/messages/QueueHolder.h b/aos/common/messages/QueueHolder.h
index 6cf2835..2b7c0d7 100644
--- a/aos/common/messages/QueueHolder.h
+++ b/aos/common/messages/QueueHolder.h
@@ -2,6 +2,7 @@
#define __AOS_MESSAGES_QUEUE_HOLDER_H_
#include <stddef.h>
+#include <string.h>
#include <algorithm>
diff --git a/aos/common/mutex.h b/aos/common/mutex.h
index c1d6f95..035889b 100644
--- a/aos/common/mutex.h
+++ b/aos/common/mutex.h
@@ -5,9 +5,9 @@
#include <semLib.h>
#endif
-#include "aos/aos_core.h"
#include "aos/common/macros.h"
#include "aos/common/type_traits.h"
+#include "aos/atom_code/ipc_lib/aos_sync.h"
namespace aos {
diff --git a/aos/common/queue_testutils.cc b/aos/common/queue_testutils.cc
index 6cce012..d35dc45 100644
--- a/aos/common/queue_testutils.cc
+++ b/aos/common/queue_testutils.cc
@@ -1,4 +1,7 @@
#include "aos/common/queue_testutils.h"
+
+#include <string.h>
+
#include "aos/common/queue.h"
namespace aos {
diff --git a/aos/common/time.cc b/aos/common/time.cc
index 1d97b80..00c936d 100644
--- a/aos/common/time.cc
+++ b/aos/common/time.cc
@@ -3,6 +3,8 @@
#ifdef __VXWORKS__
#include <taskLib.h>
#endif
+#include <errno.h>
+#include <string.h>
#include "aos/common/logging/logging.h"
#include "aos/common/inttypes.h"
diff --git a/aos/crio/controls/ControlsManager.cpp b/aos/crio/controls/ControlsManager.cpp
index a37ec6f..c1f11cd 100644
--- a/aos/crio/controls/ControlsManager.cpp
+++ b/aos/crio/controls/ControlsManager.cpp
@@ -3,7 +3,7 @@
#include "WPILib/Compressor.h"
-#include "aos/aos_core.h"
+#include "aos/crio/logging/crio_logging.h"
#include "aos/crio/controls/ControlsManager.h"
#include "aos/common/Configuration.h"
#include "aos/crio/aos_ctdt.h"
@@ -22,7 +22,7 @@
printf("aos::ControlsManager::RobotMain\n");
(new Compressor(14, 1))->Start();
- logging::Start();
+ logging::crio::Register();
LOG(INFO, "logging started\n");
GetWatchdog().SetEnabled(false);
diff --git a/aos/crio/logging/crio_logging.cc b/aos/crio/logging/crio_logging.cc
new file mode 100644
index 0000000..af55e88
--- /dev/null
+++ b/aos/crio/logging/crio_logging.cc
@@ -0,0 +1,196 @@
+#include "aos/crio/logging/crio_logging.h"
+
+#include <string.h>
+#include <msgQLib.h>
+#include <stdint.h>
+#include <taskLib.h>
+
+#include "WPILib/Timer.h"
+#include "WPILib/Task.h"
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/network/SendSocket.h"
+#include "aos/common/Configuration.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace logging {
+namespace {
+
+using internal::Context;
+
+MSG_Q_ID queue;
+static const int kQueueLength = 150;
+
+// This gets run in a low-priority task to take the logs from the queue and send
+// them to the atom over a TCP socket.
+void DoTask() {
+ SendSocket sock;
+ LogMessage msg;
+ while (true) {
+ const int ret = msgQReceive(queue, reinterpret_cast<char *>(&msg),
+ sizeof(msg), WAIT_FOREVER);
+ if (ret == ERROR) {
+ LOG(WARNING, "receiving a message failed with %d (%s)",
+ errno, strerror(errno));
+ continue;
+ }
+ if (ret != sizeof(msg)) {
+ LOG(WARNING, "received a message of size %d instead of %d\n",
+ ret, sizeof(msg));
+ continue;
+ }
+
+ if (sock.LastStatus() != 0) {
+ int numMsgs = msgQNumMsgs(queue);
+ if (numMsgs == ERROR) {
+ LOG(FATAL, "msgQNumMsgs(%p) failed with %d: %s\n", queue,
+ errno, strerror(errno));
+ }
+ // If the queue is more than a little bit full.
+ // Otherwise, without a connection on the other end, the queue just fills
+ // up and then all the senders start failing.
+ if (numMsgs > kQueueLength * 3 / 10) {
+ // DEBUG and INFO aren't worth recording here.
+ if (log_gt_important(msg.level, INFO)) {
+ LogNext(WARNING, "log_sender: dropping %s", msg.message);
+ }
+ continue;
+ } else {
+ if (sock.Connect(NetworkPort::kLogs,
+ configuration::GetIPAddress(
+ configuration::NetworkDevice::kAtom),
+ SOCK_STREAM) != 0) {
+ LOG(WARNING, "connecting failed because of %d: %s\n",
+ errno, strerror(errno));
+ LogNext(WARNING, "log_sender: couldn't send %s", msg.message);
+ continue;
+ }
+ }
+ }
+ sock.Send(&msg, sizeof(msg));
+ if (sock.LastStatus() != 0) {
+ LOG(WARNING, "sending '%s' failed because of %d: %s\n",
+ msg.message, errno, strerror(errno));
+ }
+ }
+}
+
+// A simple linked list of Contexts.
+struct ContextHolder {
+ static ContextHolder *head;
+ // Which task this one belongs to.
+ const int task;
+ ContextHolder *next;
+ Context context;
+
+ ContextHolder() : task(taskIdSelf()) {
+ MutexLocker locker(&list_mutex);
+ next = head;
+ head = this;
+ }
+ ~ContextHolder() {
+ MutexLocker locker(&list_mutex);
+ head = next;
+ }
+
+ static ContextHolder *Get() {
+ int thisTask = taskIdSelf();
+ for (ContextHolder *c = head; c != NULL; c = c->next) {
+ if (c->task == thisTask) {
+ return c;
+ }
+ }
+ return NULL;
+ }
+
+ private:
+ static Mutex list_mutex;
+};
+ContextHolder *ContextHolder::head(NULL);
+Mutex ContextHolder::list_mutex;
+
+} // namespace
+namespace internal {
+
+Context *Context::Get() {
+ ContextHolder *holder = ContextHolder::Get();
+
+ if (holder == NULL) {
+ holder = new ContextHolder();
+
+ errno = 0; // in case taskName decides not to set it
+ // We're going to make a copy of it because vxworks might allocate the
+ // memory for it from some funky place or something.
+ const char *my_name = taskName(0);
+ if (my_name == NULL) {
+ Die("logging: taskName(0) failed with %d: %s\n",
+ errno, strerror(errno));
+ }
+ holder->context.name_size = strlen(my_name);
+ if (holder->context.name_size > sizeof(LogMessage::name)) {
+ Die("logging: somebody chose a task name ('%s') that's too long\n",
+ my_name);
+ }
+ char *name_chars = new char[holder->context.name_size];
+ memcpy(name_chars, my_name, holder->context.name_size);
+ name_chars[holder->context.name_size - 1] = '\0';
+ holder->context.name = name_chars;
+ holder->context.source = taskIdSelf();
+ }
+
+ return &holder->context;
+}
+
+void Context::Delete() {
+ delete ContextHolder::Get();
+}
+
+} // namespace internal
+
+namespace crio {
+namespace {
+
+class CrioLogImplementation : public LogImplementation {
+ virtual void DoLog(log_level level, const char *format, va_list ap) {
+ LogMessage message;
+
+ internal::FillInMessage(level, format, ap, &message);
+
+ if (msgQSend(queue, reinterpret_cast<char *>(&message), sizeof(message),
+ NO_WAIT, MSG_PRI_NORMAL) != 0) {
+ if (errno == S_objLib_OBJ_UNAVAILABLE) {
+ LOG(WARNING, "no space for sending %s", message.message);
+ } else {
+ LOG(FATAL, "msgQSend(%p, %p, %d, NO_WAIT, MSG_PRI_NORMAL) failed"
+ " with %d: %s\n", queue, &message, sizeof(message),
+ errno, strerror(errno));
+ }
+ }
+ }
+};
+
+} // namespace
+
+void Register() {
+ queue = msgQCreate(kQueueLength,
+ sizeof(LogMessage),
+ MSG_Q_PRIORITY);
+ if (queue == NULL) {
+ Die("msgQCreate(%d, %d, MSG_Q_PRIORITY) failed with %d: %s\n",
+ kQueueLength, sizeof(LogMessage), errno, strerror(errno));
+ }
+
+ Task *task = new Task("LogSender",
+ (FUNCPTR)(DoTask),
+ 150); // low priority
+ task->Start();
+
+ Init();
+
+ AddImplementation(new CrioLogImplementation());
+}
+
+} // namespace crio
+} // namespace logging
+} // namespace aos
diff --git a/aos/crio/logging/crio_logging.cpp b/aos/crio/logging/crio_logging.cpp
deleted file mode 100644
index 8ebafc0..0000000
--- a/aos/crio/logging/crio_logging.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#include <string.h>
-
-#include "WPILib/Timer.h"
-#include "WPILib/Task.h"
-
-#include "aos/aos_core.h"
-#include "aos/common/network/SendSocket.h"
-#include "aos/common/Configuration.h"
-#include "aos/common/die.h"
-
-#undef ERROR
-#define DECL_LEVEL(name, value) const log_level name = value;
-DECL_LEVELS
-#undef DECL_LEVEL
-
-//#define fprintf(...)
-
-namespace aos {
-namespace logging {
-namespace {
-
-MSG_Q_ID queue;
-uint8_t sequence = 0;
-
-// This gets run in a low-priority task to take the logs from the queue and send
-// them to the atom over a TCP socket.
-void DoTask() {
- SendSocket sock;
- log_crio_message msg;
- while (true) {
- const int ret = msgQReceive(queue, reinterpret_cast<char *>(&msg),
- sizeof(msg), WAIT_FOREVER);
- if (ret == ERROR) {
- fprintf(stderr, "logging: warning: receiving a message failed"
- " with %d (%s)", errno, strerror(errno));
- continue;
- }
- if (ret != sizeof(msg)) {
- fprintf(stderr, "logging: warning: received a message of size %d "
- "instead of %zd\n", ret, sizeof(msg));
- continue;
- }
-
- if (sock.LastStatus() != 0) {
- if (sock.Connect(NetworkPort::kLogs,
- configuration::GetIPAddress(
- configuration::NetworkDevice::kAtom),
- SOCK_STREAM) != 0) {
- fprintf(stderr, "logging: warning: connecting failed"
- " because of %d: %s\n", errno, strerror(errno));
- }
- }
- sock.Send(&msg, sizeof(msg));
- if (sock.LastStatus() != 0) {
- fprintf(stderr, "logging: warning: sending '%s' failed"
- " because of %d: %s\n", msg.message, errno, strerror(errno));
- }
- }
-}
-
-} // namespace
-
-void Start() {
- queue = msgQCreate(100, // max messages
- sizeof(log_crio_message),
- MSG_Q_PRIORITY);
- Task *task = new Task("LogSender",
- (FUNCPTR)(DoTask),
- 150); // priority
- task->Start();
-}
-
-int Do(log_level level, const char *format, ...) {
- log_crio_message msg;
- msg.time = Timer::GetFPGATimestamp();
- msg.level = level;
- msg.sequence = __sync_fetch_and_add(&sequence, 1);
-
- const char *continued = "...";
- const size_t size = sizeof(msg.message) - strlen(continued);
- va_list ap;
- va_start(ap, format);
- const int ret = vsnprintf(msg.message, size, format, ap);
- va_end(ap);
-
- if (ret < 0) {
- fprintf(stderr, "logging: error: vsnprintf failed with %d (%s)\n",
- errno, strerror(errno));
- return -1;
- } 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(&msg.message[size - 1], continued, strlen(continued) + 1);
- }
- if (msgQSend(queue, reinterpret_cast<char *>(&msg), sizeof(msg),
- NO_WAIT, MSG_PRI_NORMAL) == ERROR) {
- fprintf(stderr, "logging: warning: sending message '%s'"
- " failed with %d (%s)", msg.message, errno, strerror(errno));
- return -1;
- }
-
- if (level == FATAL) {
- aos::Die("%s", msg.message);
- }
-
- return 0;
-}
-
-} // namespace logging
-} // namespace aos
diff --git a/aos/crio/logging/crio_logging.h b/aos/crio/logging/crio_logging.h
index c3dbf2a..3fad750 100644
--- a/aos/crio/logging/crio_logging.h
+++ b/aos/crio/logging/crio_logging.h
@@ -1,35 +1,17 @@
#ifndef AOS_CRIO_CRIO_LOGGING_LOGGING_H_
#define AOS_CRIO_CRIO_LOGGING_LOGGING_H_
-#ifndef AOS_COMMON_LOGGING_LOGGING_H_
-#error This file may only be #included through common/logging/logging.h!!!
-#endif
-
-#undef extern
-#undef const
-#undef ERROR
-
-#include <msgQLib.h>
-#include <stdint.h>
-
-//#define LOG(level, fmt, args...) printf(STRINGIFY(level) ": " fmt, ##args)
-#define LOG(level, fmt, args...) \
- ::aos::logging::Do(level, \
- LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " fmt, \
- ##args)
-//#define LOG(...)
-
namespace aos {
namespace logging {
+namespace crio {
-// Initialize internal variables and start up the task that sends the logs to
-// the atom. Must be called before Do.
-void Start();
-// The function that the LOG macro actually calls. Queues up a message for the
-// task to send. Start must be called before this function is.
-int Do(log_level level, const char *format, ...)
- __attribute__((format(printf, 2, 3)));
+// Calls AddImplementation to register the usual crio logging implementation
+// which sends the messages to the atom. This function will start the task that
+// does the actual sending. It relies on a process being running on the atom to
+// receive them.
+void Register();
+} // namespace crio
} // namespace logging
} // namespace aos
diff --git a/aos/crio/shared_libs/interrupt_bridge-tmpl.h b/aos/crio/shared_libs/interrupt_bridge-tmpl.h
index cc00ab8..fbd3e98 100644
--- a/aos/crio/shared_libs/interrupt_bridge-tmpl.h
+++ b/aos/crio/shared_libs/interrupt_bridge-tmpl.h
@@ -5,6 +5,7 @@
#include "WPILib/Task.h"
#include "aos/common/logging/logging.h"
+#include "aos/crio/motor_server/MotorServer.h"
#include "aos/common/time.h"
extern "C" {
diff --git a/aos/crio/shared_libs/interrupt_bridge.h b/aos/crio/shared_libs/interrupt_bridge.h
index 85ecd0f..f8f68b4 100644
--- a/aos/crio/shared_libs/interrupt_bridge.h
+++ b/aos/crio/shared_libs/interrupt_bridge.h
@@ -8,7 +8,7 @@
#include "aos/common/scoped_ptr.h"
-#include "aos/aos_core.h"
+#include "aos/common/macros.h"
class Task;
diff --git a/aos/crio/shared_libs/interrupt_bridge_demo.cc b/aos/crio/shared_libs/interrupt_bridge_demo.cc
index d7e66be..0755c97 100644
--- a/aos/crio/shared_libs/interrupt_bridge_demo.cc
+++ b/aos/crio/shared_libs/interrupt_bridge_demo.cc
@@ -4,7 +4,6 @@
#include "WPILib/Timer.h"
-#include "aos/aos_core.h"
#include "aos/common/time.h"
using aos::time::Time;
diff --git a/aos/crio/shared_libs/mutex.cpp b/aos/crio/shared_libs/mutex.cpp
index 35ac4e3..d921492 100644
--- a/aos/crio/shared_libs/mutex.cpp
+++ b/aos/crio/shared_libs/mutex.cpp
@@ -1,6 +1,9 @@
#include "aos/common/mutex.h"
#include <semLib.h>
+#include <string.h>
+
+#include "aos/common/logging/logging.h"
namespace aos {
diff --git a/aos/crio/shared_libs/shared_libs.gyp b/aos/crio/shared_libs/shared_libs.gyp
new file mode 100644
index 0000000..90b3cf0
--- /dev/null
+++ b/aos/crio/shared_libs/shared_libs.gyp
@@ -0,0 +1,25 @@
+{
+ 'targets': [
+ {
+# This one includes interrupt_bridge.h too.
+ 'target_name': 'interrupt_notifier',
+ 'type': 'static_library',
+ 'sources': [
+ 'interrupt_bridge.cc',
+ 'interrupt_bridge_c.c',
+ 'interrupt_bridge_demo.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(EXTERNALS):WPILib',
+ '<(AOS)/common/messages/messages.gyp:aos_queues',
+ ],
+ 'export_dependent_settings': [
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(EXTERNALS):WPILib',
+ ],
+ },
+ ],
+}
diff --git a/frc971/control_loops/control_loops.gyp b/frc971/control_loops/control_loops.gyp
index a9ddb12..8bdc7a0 100644
--- a/frc971/control_loops/control_loops.gyp
+++ b/frc971/control_loops/control_loops.gyp
@@ -13,12 +13,10 @@
'header_path': 'frc971/control_loops',
},
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
'<(AOS)/common/common.gyp:control_loop_queues',
'<(AOS)/common/common.gyp:queues',
],
'export_dependent_settings': [
- '<(AOS)/build/aos.gyp:libaos',
'<(AOS)/common/common.gyp:control_loop_queues',
'<(AOS)/common/common.gyp:queues',
],
@@ -33,10 +31,10 @@
'dependencies': [
'<(AOS)/build/aos.gyp:logging',
'<(AOS)/common/common.gyp:controls',
- '<(AOS)/build/aos.gyp:libaos',
'control_loops',
'<(DEPTH)/frc971/queues/queues.gyp:queues',
'<(EXTERNALS):eigen',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
],
diff --git a/frc971/input/GyroReader.cc b/frc971/input/GyroReader.cc
index a6e1d00..9fbee44 100644
--- a/frc971/input/GyroReader.cc
+++ b/frc971/input/GyroReader.cc
@@ -3,7 +3,7 @@
#include <sys/stat.h>
#include <fcntl.h>
-#include "aos/aos_core.h"
+#include "aos/common/logging/logging.h"
#include "frc971/queues/GyroAngle.q.h"
diff --git a/frc971/input/input.gyp b/frc971/input/input.gyp
index 43f591a..c62365b 100644
--- a/frc971/input/input.gyp
+++ b/frc971/input/input.gyp
@@ -26,6 +26,7 @@
'actions',
'<(DEPTH)/frc971/control_loops/control_loops.gyp:control_loops',
'<(DEPTH)/frc971/queues/queues.gyp:queues',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
{
@@ -40,6 +41,13 @@
'<(DEPTH)/frc971/queues/queues.gyp:queues',
'<(AOS)/common/network/network.gyp:socket',
],
+ 'conditions': [
+ ['OS!="crio"', {
+ 'dependencies': [
+ '<(AOS)/atom_code/atom_code.gyp:init',
+ ],
+ }],
+ ],
},
{
'target_name': 'SensorWriter',
@@ -50,6 +58,7 @@
'dependencies': [
'<(AOS)/build/aos.gyp:libaos',
'<(DEPTH)/frc971/control_loops/control_loops.gyp:control_loops',
+ '<(AOS)/crio/shared_libs/shared_libs.gyp:interrupt_notifier',
],
},
{
@@ -59,8 +68,9 @@
'GyroReader.cc',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:libaos',
'<(DEPTH)/frc971/queues/queues.gyp:queues',
+ '<(AOS)/atom_code/atom_code.gyp:init',
+ '<(AOS)/build/aos.gyp:logging',
],
},
{
@@ -74,6 +84,8 @@
'<(DEPTH)/frc971/control_loops/control_loops.gyp:control_loops',
'<(DEPTH)/frc971/queues/queues.gyp:queues',
'actions',
+# TODO(brians) this shouldn't need to be here
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
},
],
diff --git a/frc971/output/output.gyp b/frc971/output/output.gyp
index 6cb28d1..780eadf 100644
--- a/frc971/output/output.gyp
+++ b/frc971/output/output.gyp
@@ -10,6 +10,7 @@
'<(AOS)/build/aos.gyp:libaos',
'<(AOS)/atom_code/output/output.gyp:http_server',
'../frc971.gyp:common',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
'copies': [
{
@@ -30,6 +31,7 @@
'../frc971.gyp:common',
'<(AOS)/atom_code/output/output.gyp:motor_output',
'<(AOS)/atom_code/messages/messages.gyp:messages',
+ '<(AOS)/atom_code/atom_code.gyp:init',
],
}, {
'sources': ['CRIOMotorWriter.cc'],