got matrix logging stuff to compile
diff --git a/aos/build/queues/output/message_dec.rb b/aos/build/queues/output/message_dec.rb
index 9ef2a68..59e2f35 100644
--- a/aos/build/queues/output/message_dec.rb
+++ b/aos/build/queues/output/message_dec.rb
@@ -284,9 +284,9 @@
 		f_call.args.dont_wrap = true
 	end
 	def getTypeID()
-		Digest::SHA1.hexdigest(@type)[0..7].to_i(16) |
+		'0x' + ((Digest::SHA1.hexdigest(@type)[0..3].to_i(16) << 16) |
                 0x2000 | # marks it as primitive
-                size
+                size).to_s(16)
 	end
 	def simpleStr()
 		"#{@type} #{@name}"
diff --git a/aos/build/queues/queue_primitives.rb b/aos/build/queues/queue_primitives.rb
index bd91191..2c9594e 100644
--- a/aos/build/queues/queue_primitives.rb
+++ b/aos/build/queues/queue_primitives.rb
@@ -15,17 +15,16 @@
 #include <stdint.h>
 
 namespace aos {
-
-enum class QueuePrimitiveTypes : uint32_t {
+namespace queue_primitive_types {
 #{TypeNames.collect do |name|
   message_element = Target::MessageElement.new(name, 'value')
   statement = MessageElementStmt.new(name, 'value')
   message_element.size = statement.size
   next <<END2
-  #{name}_p = #{message_element.getTypeID()},
+  static const uint32_t #{name}_p = #{message_element.getTypeID()};
 END2
 end.join('')}
-};
+}  // namespace queue_primitive_types
 
 // A class for mapping an actual type to a type ID.
 // There are specializations for all of the actual primitive types.
diff --git a/aos/common/logging/logging_impl.cc b/aos/common/logging/logging_impl.cc
index 681d202..f8312a7 100644
--- a/aos/common/logging/logging_impl.cc
+++ b/aos/common/logging/logging_impl.cc
@@ -122,7 +122,7 @@
       fprintf(output, BASE_FORMAT "%.*s", BASE_ARGS,
               static_cast<int>(message.message_length), message.message);
       break;
-    case LogMessage::Type::kStruct:
+    case LogMessage::Type::kStruct: {
       char buffer[1024];
       size_t output_length = sizeof(buffer);
       size_t input_length = message.message_length;
@@ -144,7 +144,30 @@
               static_cast<int>(message.structure.string_length),
               message.structure.serialized,
               static_cast<int>(sizeof(buffer) - output_length), buffer);
-      break;
+    } break;
+    case LogMessage::Type::kMatrix: {
+      char buffer[1024];
+      size_t output_length = sizeof(buffer);
+      if (message.message_length !=
+          static_cast<size_t>(message.matrix.rows * message.matrix.cols *
+                              MessageType::Sizeof(message.matrix.type))) {
+        LOG(FATAL, "expected %d bytes of matrix data but have %zu\n",
+            message.matrix.rows * message.matrix.cols *
+                MessageType::Sizeof(message.matrix.type),
+            message.message_length);
+      }
+      if (!PrintMatrix(buffer, &output_length,
+                       message.matrix.data + message.matrix.string_length,
+                       message.matrix.type, message.matrix.rows,
+                       message.matrix.cols)) {
+        LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n",
+            message.matrix.rows, message.matrix.cols, message.matrix.type);
+      }
+      fprintf(output, BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
+              static_cast<int>(message.matrix.string_length),
+              message.structure.serialized,
+              static_cast<int>(sizeof(buffer) - output_length), buffer);
+    } break;
   }
 #undef NSECONDS_DIGITS
 #undef BASE_FORMAT
@@ -174,6 +197,28 @@
                 static_cast<int>(sizeof(printed) - printed_bytes), printed);
 }
 
+void LogImplementation::LogMatrix(
+    log_level level, const ::std::string &message, uint32_t type_id,
+    int rows, int cols, const void *data) {
+  char serialized[1024];
+  if (static_cast<size_t>(rows * cols * MessageType::Sizeof(type_id)) >
+      sizeof(serialized)) {
+    LOG(FATAL, "matrix of size %u too big to serialize\n",
+        rows * cols * MessageType::Sizeof(type_id));
+  }
+  SerializeMatrix(type_id, serialized, data, rows, cols);
+  char printed[1024];
+  size_t printed_bytes = sizeof(printed);
+  if (!PrintMatrix(printed, &printed_bytes, serialized, type_id, rows, cols)) {
+    LOG(FATAL, "PrintMatrix(%p, %p(=%zd), %p, %" PRIu32 ", %d, %d) failed\n",
+        printed, &printed_bytes, printed_bytes, serialized, type_id, rows,
+        cols);
+  }
+  DoLogVariadic(level, "%.*s: %.*s\n", static_cast<int>(message.size()),
+                message.data(),
+                static_cast<int>(sizeof(printed) - printed_bytes), printed);
+}
+
 StreamLogImplementation::StreamLogImplementation(FILE *stream)
     : stream_(stream) {}
 
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
index 11b28b0..1d68a16 100644
--- a/aos/common/logging/logging_impl.h
+++ b/aos/common/logging/logging_impl.h
@@ -46,7 +46,8 @@
   };
 
   int32_t seconds, nseconds;
-  // message_length is the length of everything in message for all types.
+  // message_length is just the length of the actual data (which member depends
+  // on the type).
   size_t message_length, name_length;
   pid_t source;
   static_assert(sizeof(source) == 4, "that's how they get printed");
@@ -66,9 +67,11 @@
     struct {
       // The type ID of the element type.
       uint32_t type;
-      int rows, columns;
+      int rows, cols;
+      size_t string_length;
+      // The message string and then the serialized matrix.
       char
-          data[LOG_MESSAGE_LEN - sizeof(type) - sizeof(rows) - sizeof(columns)];
+          data[LOG_MESSAGE_LEN - sizeof(type) - sizeof(rows) - sizeof(cols)];
     } matrix;
   };
 };
@@ -170,9 +173,10 @@
                           size_t size, const MessageType *type,
                           const ::std::function<size_t(char *)> &serialize,
                           int levels);
+  // This one is implemented in matrix_logging.cc.
   static void DoLogMatrix(log_level level, const ::std::string &message,
                           uint32_t type_id, int rows, int cols,
-                          const void *data);
+                          const void *data, int levels);
 
   // Friends so that they can access the static Do* functions.
   friend void VLog(log_level, const char *, va_list);
diff --git a/aos/common/logging/matrix_logging-tmpl.h b/aos/common/logging/matrix_logging-tmpl.h
index 8d18f67..bfc3ce5 100644
--- a/aos/common/logging/matrix_logging-tmpl.h
+++ b/aos/common/logging/matrix_logging-tmpl.h
@@ -10,8 +10,10 @@
 template <class T>
 void DoLogMatrix(log_level level, const ::std::string &message,
                  const T &matrix) {
+  static_assert(!T::IsRowMajor, "we only handle column-major storage");
   LogImplementation::DoLogMatrix(level, message, TypeID<typename T::Scalar>::id,
-                                 matrix.rows(), matrix.cols(), matrix.data());
+                                 matrix.rows(), matrix.cols(), matrix.data(),
+                                 1);
 }
 
 }  // namespace logging
diff --git a/aos/common/logging/matrix_logging.cc b/aos/common/logging/matrix_logging.cc
index cd30509..6173074 100644
--- a/aos/common/logging/matrix_logging.cc
+++ b/aos/common/logging/matrix_logging.cc
@@ -1 +1,35 @@
 #include "aos/common/logging/matrix_logging.h"
+
+#include "aos/common/queue_types.h"
+
+namespace aos {
+namespace logging {
+
+void LogImplementation::DoLogMatrix(log_level level,
+                                    const ::std::string &message,
+                                    uint32_t type_id, int rows, int cols,
+                                    const void *data, int levels) {
+  internal::RunWithCurrentImplementation(
+      levels, [&](LogImplementation * implementation) {
+    implementation->LogMatrix(level, message, type_id, rows, cols, data);
+  });
+
+  if (level == FATAL) {
+    char serialized[1024];
+    if (static_cast<size_t>(rows * cols * MessageType::Sizeof(type_id)) >
+        sizeof(serialized)) {
+      Die("LOG(FATAL) matrix too big to serialize");
+    }
+    SerializeMatrix(type_id, serialized, data, rows, cols);
+    char printed[LOG_MESSAGE_LEN];
+    size_t printed_bytes = sizeof(printed);
+    if (!PrintMatrix(printed, &printed_bytes, serialized, type_id, rows, cols)) {
+      Die("LOG(FATAL) PrintMatrix 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.cc b/aos/common/logging/queue_logging.cc
index 021da7c..ce96c29 100644
--- a/aos/common/logging/queue_logging.cc
+++ b/aos/common/logging/queue_logging.cc
@@ -13,22 +13,22 @@
   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);
-    }
   });
+
+  if (level == FATAL) {
+    char serialized[1024];
+    if (size > sizeof(serialized)) {
+      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
diff --git a/aos/common/queue_types.cc b/aos/common/queue_types.cc
index 22b6f4a..93cf135 100644
--- a/aos/common/queue_types.cc
+++ b/aos/common/queue_types.cc
@@ -164,6 +164,95 @@
   return true;
 }
 
+bool PrintMatrix(char *output, size_t *output_bytes, const void *input,
+                 uint32_t type_id, int rows, int cols) {
+  CHECK(MessageType::IsPrimitive(type_id));
+  const size_t element_size = MessageType::Sizeof(type_id);
+
+  if (*output_bytes < 1) return false;
+  *output_bytes -= 1;
+  *(output++) = '[';
+
+  bool first_row = true;
+  for (int row = 0; row < rows; ++row) {
+    if (first_row) {
+      first_row = false;
+    } else {
+      if (*output_bytes < 2) return false;
+      *output_bytes -= 2;
+      *(output++) = ',';
+      *(output++) = ' ';
+    }
+
+    if (*output_bytes < 1) return false;
+    *output_bytes -= 1;
+    *(output++) = '[';
+
+    bool first_col = true;
+    for (int col = 0; col < cols; ++col) {
+      if (first_col) {
+        first_col = false;
+      } else {
+        if (*output_bytes < 2) return false;
+        *output_bytes -= 2;
+        *(output++) = ',';
+        *(output++) = ' ';
+      }
+
+      const size_t output_bytes_before = *output_bytes;
+      size_t input_bytes = element_size;
+      if (!PrintField(output, output_bytes,
+                      static_cast<const char *>(input) +
+                          (row + col * rows) * element_size,
+                      &input_bytes, type_id)) {
+        return false;
+      }
+      CHECK_EQ(0u, input_bytes);
+      // Update the output pointer, ignoring the trailing '\0' that
+      // the subcall put on.
+      output += output_bytes_before - *output_bytes - 1;
+      *output_bytes += 1;
+    }
+
+    if (*output_bytes < 1) return false;
+    *output_bytes -= 1;
+    *(output++) = ']';
+  }
+  if (*output_bytes < 2) return false;
+  *output_bytes -= 2;
+  *(output++) = ']';
+  *(output++) = '\0';
+  return true;
+}
+
+void SerializeMatrix(int type_id, void *output_void, const void *input_void,
+                     int rows, int cols) {
+  char *const output = static_cast<char *>(output_void);
+  const char *const input = static_cast<const char *>(input_void);
+
+  CHECK(MessageType::IsPrimitive(type_id));
+  const size_t element_size = MessageType::Sizeof(type_id);
+
+  for (int i = 0; i < rows * cols; ++i) {
+    switch(element_size) {
+      case 1:
+        to_network<1>(&input[i * element_size], &output[i * element_size]);
+        break;
+      case 2:
+        to_network<2>(&input[i * element_size], &output[i * element_size]);
+        break;
+      case 4:
+        to_network<4>(&input[i * element_size], &output[i * element_size]);
+        break;
+      case 8:
+        to_network<8>(&input[i * element_size], &output[i * element_size]);
+        break;
+      default:
+        LOG(FATAL, "illegal primitive type size %zu\n", element_size);
+    }
+  }
+}
+
 namespace type_cache {
 namespace {
 
diff --git a/aos/common/queue_types.h b/aos/common/queue_types.h
index a1d9ce0..b95c946 100644
--- a/aos/common/queue_types.h
+++ b/aos/common/queue_types.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "aos/common/macros.h"
+#include "aos/common/byteorder.h"
 
 namespace aos {
 
@@ -52,6 +53,14 @@
     return (type_id & 0x2000) != 0;
   }
 
+  static unsigned int Sizeof(uint32_t type_id) {
+    if (IsPrimitive(type_id)) {
+      return (type_id & 0xFFFF) - 0x2000;
+    } else {
+      return type_id & 0xFFFF;
+    }
+  }
+
   // How many (serialized) bytes the superclass takes up.
   uint16_t super_size;
   // The type ID for this.
@@ -101,6 +110,14 @@
 bool PrintMessage(char *output, size_t *output_bytes, const void *input,
                   size_t *input_bytes, const MessageType &type)
     __attribute__((warn_unused_result));
+// Calls PrintField to print out a matrix of values.
+bool PrintMatrix(char *output, size_t *output_bytes, const void *input,
+                 uint32_t type, int rows, int cols);
+
+// "Serializes" a matrix (basically just converts to network byte order). The
+// result can be passed to PrintMatrix.
+void SerializeMatrix(int type_id, void *output_void, const void *input_void,
+                     int rows, int cols);
 
 // Implements a cache of types which generally works per-process but can (when
 // instructed) put a type in shared memory which other processes will
diff --git a/aos/common/queue_types_test.cc b/aos/common/queue_types_test.cc
index 72eae94..ac7e683 100644
--- a/aos/common/queue_types_test.cc
+++ b/aos/common/queue_types_test.cc
@@ -6,6 +6,7 @@
 
 #include "aos/common/test_queue.q.h"
 #include "aos/common/byteorder.h"
+#include "aos/queue_primitives.h"
 
 using ::aos::common::testing::Structure;
 using ::aos::common::testing::MessageWithStructure;
@@ -174,5 +175,19 @@
   EXPECT_EQ(kTestStructure1String.size() + 1, sizeof(output) - output_bytes);
 }
 
+TEST_F(PrintMessageTest, Matrix) {
+  static const uint16_t kTestMatrix[] = {971, 254, 1768, 8971, 9971, 973};
+  uint16_t test_matrix[sizeof(kTestMatrix) / sizeof(kTestMatrix[0])];
+  SerializeMatrix(queue_primitive_types::uint16_t_p, test_matrix, kTestMatrix,
+                  3, 2);
+  static const ::std::string kOutput =
+      "[[971, 8971], [254, 9971], [1768, 973]]";
+  output_bytes = sizeof(output);
+  ASSERT_TRUE(PrintMatrix(output, &output_bytes, test_matrix,
+                          queue_primitive_types::uint16_t_p, 3, 2));
+  EXPECT_EQ(kOutput, ::std::string(output));
+  EXPECT_EQ(kOutput.size() + 1, sizeof(output) - output_bytes);
+}
+
 }  // namespace testing
 }  // namespace aos
diff --git a/aos/linux_code/logging/binary_log_writer.cc b/aos/linux_code/logging/binary_log_writer.cc
index e7b1e9f..c404c39 100644
--- a/aos/linux_code/logging/binary_log_writer.cc
+++ b/aos/linux_code/logging/binary_log_writer.cc
@@ -178,10 +178,11 @@
                msg->message_length);
         output->type = LogFileMessageHeader::MessageType::kString;
         break;
-      case LogMessage::Type::kStruct:
+      case LogMessage::Type::kStruct: {
         char *position = output_strings + msg->name_length;
 
-        memcpy(position, &msg->structure.type_id, sizeof(msg->structure.type_id));
+        memcpy(position, &msg->structure.type_id,
+               sizeof(msg->structure.type_id));
         position += sizeof(msg->structure.type_id);
         output->message_size += sizeof(msg->structure.type_id);
 
@@ -197,7 +198,34 @@
                msg->message_length);
 
         output->type = LogFileMessageHeader::MessageType::kStruct;
-        break;
+      } break;
+      case LogMessage::Type::kMatrix: {
+        char *position = output_strings + msg->name_length;
+
+        memcpy(position, &msg->matrix.type, sizeof(msg->matrix.type));
+        position += sizeof(msg->matrix.type);
+        output->message_size += sizeof(msg->matrix.type);
+
+        uint32_t length = msg->matrix.string_length;
+        memcpy(position, &length, sizeof(length));
+        position += sizeof(length);
+        memcpy(position, msg->matrix.data, length);
+        position += length;
+        output->message_size += sizeof(length) + length;
+
+        uint16_t rows = msg->matrix.rows, cols = msg->matrix.cols;
+        memcpy(position, &rows, sizeof(rows));
+        position += sizeof(rows);
+        memcpy(position, &cols, sizeof(cols));
+        position += sizeof(cols);
+        output->message_size += sizeof(rows) + sizeof(cols);
+        CHECK_EQ(msg->message_length,
+                 MessageType::Sizeof(msg->matrix.type) * rows * cols);
+
+        memcpy(position,
+               msg->matrix.data + msg->matrix.string_length,
+               msg->message_length);
+      } break;
     }
 
     futex_set(&output->marker);