got logging structs working (theoretically) and used it
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 7c38c53..7259c32 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -182,6 +182,7 @@
'timing',
'time',
'control_loop_queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
],
'export_dependent_settings': [
'<(AOS)/common/messages/messages.gyp:aos_queues',
@@ -189,6 +190,7 @@
'timing',
'time',
'control_loop_queues',
+ '<(AOS)/common/logging/logging.gyp:queue_logging',
],
},
{
diff --git a/aos/common/control_loop/ControlLoop-tmpl.h b/aos/common/control_loop/ControlLoop-tmpl.h
index 69c3121..3173d2c 100644
--- a/aos/common/control_loop/ControlLoop-tmpl.h
+++ b/aos/common/control_loop/ControlLoop-tmpl.h
@@ -3,6 +3,7 @@
#include "aos/common/logging/logging.h"
#include "aos/common/control_loop/Timing.h"
#include "aos/common/messages/RobotState.q.h"
+#include "aos/common/logging/queue_logging.h"
namespace aos {
namespace control_loops {
@@ -19,9 +20,6 @@
template <class T, bool has_position, bool fail_no_position>
void ControlLoop<T, has_position, fail_no_position>::Iterate() {
- // Temporary storage for printing out inputs and outputs.
- char state[1024];
-
// Fetch the latest control loop goal and position. If there is no new
// goal, we will just reuse the old one.
// If there is no goal, we haven't started up fully. It isn't worth
@@ -35,8 +33,7 @@
ZeroOutputs();
return;
}
- goal->Print(state, sizeof(state));
- LOG(DEBUG, "goal={%s}\n", state);
+ LOG_STRUCT(DEBUG, "goal", *goal);
// Only pass in a position if we got one this cycle.
const PositionType *position = NULL;
@@ -67,8 +64,7 @@
}
}
if (position) {
- position->Print(state, sizeof(state));
- LOG(DEBUG, "position={%s}\n", state);
+ LOG_STRUCT(DEBUG, "position", *position);
}
}
@@ -100,8 +96,7 @@
control_loop_->output.MakeMessage();
RunIteration(goal, position, output.get(), status.get());
- output->Print(state, sizeof(state));
- LOG(DEBUG, "output={%s}\n", state);
+ LOG_STRUCT(DEBUG, "output", *output);
output.Send();
} else {
// The outputs are disabled, so pass NULL in for the output.
@@ -109,8 +104,7 @@
ZeroOutputs();
}
- status->Print(state, sizeof(state));
- LOG(DEBUG, "status={%s}\n", state);
+ LOG_STRUCT(DEBUG, "status", *status);
status.Send();
}
diff --git a/aos/common/logging/logging.gyp b/aos/common/logging/logging.gyp
index ad20166..e230e47 100644
--- a/aos/common/logging/logging.gyp
+++ b/aos/common/logging/logging.gyp
@@ -18,8 +18,13 @@
'queue_logging.cc',
],
'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:die',
'<(AOS)/common/common.gyp:queue_types',
],
+ 'export_dependent_settings': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
},
],
}
diff --git a/aos/common/logging/logging.h b/aos/common/logging/logging.h
index c71ee46..673cc9a 100644
--- a/aos/common/logging/logging.h
+++ b/aos/common/logging/logging.h
@@ -71,41 +71,45 @@
#define LOG_SOURCENAME __FILE__
// 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)
+#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 { \
- 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(ERROR, "next message was too long so not subbing in args\n"); \
- LOG(level, "%s", format); \
- }else{ \
- LOG(level, "%s", log_buf); \
- } \
-} while (0)
+#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(ERROR, "next message was too long so not subbing in args\n"); \
+ LOG(level, "%s", format); \
+ } else { \
+ LOG(level, "%s", log_buf); \
+ } \
+ } while (0)
// 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)
+#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)
+#define LOG_UNCORK(level, format, args...) \
+ do { \
+ log_uncork(__LINE__, LOG_CURRENT_FUNCTION, level, LOG_SOURCENAME, format, \
+ ##args); \
+ } while (0)
#ifdef __cplusplus
}
@@ -154,8 +158,8 @@
// controlled by NDEBUG, so the check will be executed regardless of
// compilation mode. Therefore, it is safe to do things like:
// CHECK(fp->Write(x) == 4)
-#define CHECK(condition) \
- if (__builtin_expect(!(condition), 0)) { \
+#define CHECK(condition) \
+ if (__builtin_expect(!(condition), 0)) { \
LOG(FATAL, "CHECK(%s) failed\n", #condition); \
}
@@ -163,16 +167,16 @@
// The (int, int) specialization works around the issue that the compiler
// will not instantiate the template version of the function on values of
// unnamed enum type.
-#define DEFINE_CHECK_OP_IMPL(name, op) \
- template <typename T1, typename T2> \
- inline void LogImpl##name(const T1& v1, const T2& v2, \
- const char* exprtext) { \
- if (!__builtin_expect(v1 op v2, 1)) { \
- LOG(FATAL, "CHECK(%s) failed\n", exprtext); \
- } \
- } \
- inline void LogImpl##name(int v1, int v2, const char* exprtext) { \
- ::aos::LogImpl##name<int, int>(v1, v2, exprtext); \
+#define DEFINE_CHECK_OP_IMPL(name, op) \
+ template <typename T1, typename T2> \
+ inline void LogImpl##name(const T1 &v1, const T2 &v2, \
+ const char *exprtext) { \
+ if (!__builtin_expect(v1 op v2, 1)) { \
+ LOG(FATAL, "CHECK(%s) failed\n", exprtext); \
+ } \
+ } \
+ inline void LogImpl##name(int v1, int v2, const char *exprtext) { \
+ ::aos::LogImpl##name<int, int>(v1, v2, exprtext); \
}
// We use the full name Check_EQ, Check_NE, etc. in case the file including
@@ -186,7 +190,7 @@
DEFINE_CHECK_OP_IMPL(Check_GE, >=)
DEFINE_CHECK_OP_IMPL(Check_GT, > )
-#define CHECK_OP(name, op, val1, val2) \
+#define CHECK_OP(name, op, val1, val2) \
::aos::LogImplCheck##name(val1, val2, \
STRINGIFY(val1) STRINGIFY(op) STRINGIFY(val2))
@@ -208,8 +212,7 @@
// Check that the input is non NULL. This very useful in constructor
// initializer lists.
-#define CHECK_NOTNULL(val) \
- ::aos::CheckNotNull(STRINGIFY(val), val)
+#define CHECK_NOTNULL(val) ::aos::CheckNotNull(STRINGIFY(val), val)
} // namespace aos
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
index ca19dbc..bdbe585 100644
--- a/aos/common/logging/logging_impl.h
+++ b/aos/common/logging/logging_impl.h
@@ -9,11 +9,18 @@
#include <stdio.h>
#include <string>
+#include <functional>
#include "aos/common/logging/logging.h"
#include "aos/common/type_traits.h"
#include "aos/common/mutex.h"
+namespace aos {
+
+class MessageType;
+
+} // namespace aos
+
// This file has all of the logging implementation. It can't be #included by C
// code like logging.h can.
// It is useful for the rest of the logging implementation and other C++ code
@@ -85,6 +92,10 @@
void LogNext(log_level level, const char *format, ...)
__attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
+// Will take a structure and log it.
+template <class T>
+void DoLogStruct(log_level, const ::std::string &, const T &);
+
// 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
@@ -108,12 +119,31 @@
// 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.
+ // Logs the contents of an auto-generated structure. The implementation here
+ // just converts it to a string with PrintMessage and then calls DoLog with
+ // that, however some implementations can be a lot more efficient than that.
+ // size and type are the result of calling Size() and Type() on the type of
+ // the message.
+ // serialize will call Serialize on the message.
+ virtual void LogStruct(log_level level, const ::std::string &message,
+ size_t size, const MessageType *type,
+ const ::std::function<size_t(char *)> &serialize);
+
+ // These functions call similar methods on the "current" LogImplementation or
+ // Die if they can't find one.
+ // 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.
+ // This one is implemented in queue_logging.cc.
+ static void DoLogStruct(log_level level, const ::std::string &message,
+ size_t size, const MessageType *type,
+ const ::std::function<size_t(char *)> &serialize,
+ int levels);
+
+ // Friends so that they can access the static Do* functions.
friend void VLog(log_level, const char *, va_list);
friend void LogNext(log_level, const char *, ...);
+ template <class T>
+ friend void DoLogStruct(log_level, const ::std::string &, const T &);
LogImplementation *next_;
};
@@ -221,6 +251,11 @@
void ExecuteFormat(char *output, size_t output_size, const char *format,
va_list ap);
+// Runs the given function with the current LogImplementation (handles switching
+// it out while running function etc).
+void RunWithCurrentImplementation(
+ int levels, ::std::function<void(LogImplementation *)> function);
+
} // namespace internal
} // namespace logging
} // namespace aos
diff --git a/aos/common/logging/logging_interface.cc b/aos/common/logging/logging_interface.cc
index 01fb708..9f1168f 100644
--- a/aos/common/logging/logging_interface.cc
+++ b/aos/common/logging/logging_interface.cc
@@ -36,15 +36,11 @@
}
}
-} // namespace internal
-
-using internal::Context;
-
-void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
- int levels) {
+void RunWithCurrentImplementation(
+ int levels, ::std::function<void(LogImplementation *)> function) {
Context *context = Context::Get();
- LogImplementation *top_implementation = context->implementation;
+ LogImplementation *const top_implementation = context->implementation;
LogImplementation *new_implementation = top_implementation;
LogImplementation *implementation = NULL;
assert(levels >= 1);
@@ -56,12 +52,34 @@
new_implementation = new_implementation->next();
}
context->implementation = new_implementation;
- implementation->DoLog(level, format, ap);
+ function(implementation);
context->implementation = top_implementation;
+}
- if (level == FATAL) {
- VDie(format, ap);
- }
+} // namespace internal
+
+using internal::Context;
+
+void LogImplementation::LogStruct(
+ log_level level, const ::std::string &message, size_t size,
+ const MessageType *type, const ::std::function<size_t(char *)> &serialize) {
+ (void)level;
+ (void)message;
+ (void)size;
+ (void)type;
+ (void)serialize;
+}
+
+void LogImplementation::DoVLog(log_level level, const char *format, va_list ap,
+ int levels) {
+ internal::RunWithCurrentImplementation(
+ levels, [&](LogImplementation * implementation) {
+ implementation->DoLog(level, format, ap);
+
+ if (level == FATAL) {
+ VDie(format, ap);
+ }
+ });
}
void VLog(log_level level, const char *format, va_list ap) {
diff --git a/aos/common/logging/queue_logging-tmpl.h b/aos/common/logging/queue_logging-tmpl.h
new file mode 100644
index 0000000..e0a89e4
--- /dev/null
+++ b/aos/common/logging/queue_logging-tmpl.h
@@ -0,0 +1,19 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include <functional>
+
+namespace aos {
+namespace logging {
+
+template <class T>
+void DoLogStruct(log_level level, const ::std::string &message,
+ const T &structure) {
+ LogImplementation::DoLogStruct(level, message, T::Size(), T::GetType(),
+ [&structure](char * buffer)->size_t{
+ return structure.Serialize(buffer);
+ },
+ 1);
+}
+
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/logging/queue_logging.cc b/aos/common/logging/queue_logging.cc
new file mode 100644
index 0000000..eefb4ac
--- /dev/null
+++ b/aos/common/logging/queue_logging.cc
@@ -0,0 +1,35 @@
+#include "aos/common/logging/logging_impl.h"
+
+#include "aos/common/die.h"
+#include "aos/common/queue_types.h"
+
+namespace aos {
+namespace logging {
+
+void LogImplementation::DoLogStruct(
+ log_level level, const ::std::string &message, size_t size,
+ const MessageType *type, const ::std::function<size_t(char *)> &serialize,
+ int levels) {
+ internal::RunWithCurrentImplementation(
+ levels, [&](LogImplementation * implementation) {
+ implementation->LogStruct(level, message, size, type, serialize);
+
+ if (level == FATAL) {
+ char serialized[1024];
+ if (size > sizeof(serialize)) {
+ Die("LOG(FATAL) structure too big to serialize");
+ }
+ size_t used = serialize(serialized);
+ char printed[LOG_MESSAGE_LEN];
+ size_t printed_bytes = sizeof(printed);
+ if (!PrintMessage(printed, &printed_bytes, serialized, &used, *type)) {
+ Die("LOG(FATAL) PrintMessage call failed");
+ }
+ Die("%.*s: %.*s\n", static_cast<int>(message.size()), message.data(),
+ static_cast<int>(printed_bytes), printed);
+ }
+ });
+}
+
+} // namespace logging
+} // namespace aos
diff --git a/aos/common/logging/queue_logging.h b/aos/common/logging/queue_logging.h
new file mode 100644
index 0000000..191d4c7
--- /dev/null
+++ b/aos/common/logging/queue_logging.h
@@ -0,0 +1,36 @@
+#ifndef AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
+#define AOS_COMMON_LOGGING_QUEUE_LOGGING_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace logging {
+
+#define LOG_STRUCT(level, message, structure) \
+ do { \
+ static const ::std::string kAosLoggingMessage( \
+ LOG_SOURCENAME ": " STRINGIFY(__LINE__) ": " message); \
+ ::aos::logging::DoLogStruct(level, kAosLoggingMessage, structure); \
+ /* so that GCC knows that it won't return */ \
+ if (level == FATAL) { \
+ fprintf(stderr, "DoLogStruct(FATAL) fell through!!!!!\n"); \
+ printf("see stderr\n"); \
+ abort(); \
+ } \
+ } while (false)
+
+template <class T>
+void DoLogStruct(log_level level, const ::std::string &message,
+ const T &structure);
+
+} // namespace logging
+} // namespace aos
+
+#include "aos/common/logging/queue_logging-tmpl.h"
+
+#endif // AOS_COMMON_LOGGING_QUEUE_LOGGING_H_