actually write matrix logs to the log file
diff --git a/aos/common/logging/logging_impl.cc b/aos/common/logging/logging_impl.cc
index f8312a7..b74b69d 100644
--- a/aos/common/logging/logging_impl.cc
+++ b/aos/common/logging/logging_impl.cc
@@ -99,6 +99,35 @@
   message->type = LogMessage::Type::kStruct;
 }
 
+void FillInMessageMatrix(log_level level,
+                         const ::std::string &message_string, uint32_t type_id,
+                         int rows, int cols, const void *data,
+                         LogMessage *message) {
+  CHECK(MessageType::IsPrimitive(type_id));
+  message->matrix.type = type_id;
+
+  const auto element_size = MessageType::Sizeof(type_id);
+
+  FillInMessageBase(level, message);
+
+  message->message_length = rows * cols * element_size;
+  if (message_string.size() + message->message_length >
+      sizeof(message->matrix.data)) {
+    LOG(FATAL, "%dx%d matrix of type %" PRIu32
+               " (size %u) and message %s is too big\n",
+        rows, cols, type_id, element_size, message_string.c_str());
+  }
+  message->matrix.string_length = message_string.size();
+  memcpy(message->matrix.data, message_string.data(),
+         message->matrix.string_length);
+
+  message->matrix.rows = rows;
+  message->matrix.cols = cols;
+  SerializeMatrix(type_id, &message->matrix.data[message->matrix.string_length],
+                  data, rows, cols);
+  message->type = LogMessage::Type::kMatrix;
+}
+
 void FillInMessage(log_level level, const char *format, va_list ap,
                    LogMessage *message) {
   FillInMessageBase(level, message);
@@ -165,7 +194,7 @@
       }
       fprintf(output, BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
               static_cast<int>(message.matrix.string_length),
-              message.structure.serialized,
+              message.matrix.data,
               static_cast<int>(sizeof(buffer) - output_length), buffer);
     } break;
   }
diff --git a/aos/common/logging/logging_impl.h b/aos/common/logging/logging_impl.h
index 312a977..440e3fe 100644
--- a/aos/common/logging/logging_impl.h
+++ b/aos/common/logging/logging_impl.h
@@ -287,6 +287,13 @@
                             const ::std::function<size_t(char *)> &serialize,
                             LogMessage *message);
 
+// Fills in all the parts of the message according to the given inputs (with
+// type kMatrix).
+void FillInMessageMatrix(log_level level,
+                         const ::std::string &message_string, uint32_t type_id,
+                         int rows, int cols, const void *data,
+                         LogMessage *message);
+
 // Fills in *message according to the given inputs (with type kString).
 // Used for implementing LogImplementation::DoLog.
 void FillInMessage(log_level level, const char *format, va_list ap,
diff --git a/aos/linux_code/logging/binary_log_file.h b/aos/linux_code/logging/binary_log_file.h
index 72aac09..f25482c 100644
--- a/aos/linux_code/logging/binary_log_file.h
+++ b/aos/linux_code/logging/binary_log_file.h
@@ -39,6 +39,7 @@
     kString,
     kStructType,
     kStruct,
+    kMatrix,
   };
 
   // Gets futex_set once this one has been written
diff --git a/aos/linux_code/logging/binary_log_writer.cc b/aos/linux_code/logging/binary_log_writer.cc
index 76b53ef..d689109 100644
--- a/aos/linux_code/logging/binary_log_writer.cc
+++ b/aos/linux_code/logging/binary_log_writer.cc
@@ -154,12 +154,17 @@
     const LogMessage *const msg = ReadNext();
     if (msg == NULL) continue;
 
-    size_t output_length =
+    const size_t raw_output_length =
         sizeof(LogFileMessageHeader) + msg->name_length + msg->message_length;
+    size_t output_length = raw_output_length;
     if (msg->type == LogMessage::Type::kStruct) {
       output_length += sizeof(msg->structure.type_id) + sizeof(uint32_t) +
                        msg->structure.string_length;
       CheckTypeWritten(msg->structure.type_id, writer);
+    } else if (msg->type == LogMessage::Type::kMatrix) {
+      output_length +=
+          sizeof(msg->matrix.type) + sizeof(uint32_t) + sizeof(uint16_t) +
+          sizeof(uint16_t) + msg->matrix.string_length;
     }
     LogFileMessageHeader *const output = writer.GetWritePosition(output_length);;
     char *output_strings = reinterpret_cast<char *>(output) + sizeof(*output);
@@ -209,9 +214,7 @@
         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;
+        output->message_size += sizeof(length);
 
         uint16_t rows = msg->matrix.rows, cols = msg->matrix.cols;
         memcpy(position, &rows, sizeof(rows));
@@ -222,12 +225,19 @@
         CHECK_EQ(msg->message_length,
                  MessageType::Sizeof(msg->matrix.type) * rows * cols);
 
-        memcpy(position,
-               msg->matrix.data + msg->matrix.string_length,
-               msg->message_length);
+        memcpy(position, msg->matrix.data, msg->message_length + length);
+        output->message_size += length;
+
+        output->type = LogFileMessageHeader::MessageType::kMatrix;
       } break;
     }
 
+    if (output->message_size - msg->message_length !=
+        output_length - raw_output_length) {
+      LOG(FATAL, "%zu != %zu\n", output->message_size - msg->message_length,
+          output_length - raw_output_length);
+    }
+
     futex_set(&output->marker);
 
     logging::linux_code::Free(msg);
diff --git a/aos/linux_code/logging/linux_logging.cc b/aos/linux_code/logging/linux_logging.cc
index dc322ed..aed8697 100644
--- a/aos/linux_code/logging/linux_logging.cc
+++ b/aos/linux_code/logging/linux_logging.cc
@@ -52,6 +52,15 @@
                                      serialize, message);
     Write(message);
   }
+
+  virtual void LogMatrix(log_level level, const ::std::string &message_string,
+                         uint32_t type_id, int rows, int cols, const void *data)
+      override {
+    LogMessage *message = GetMessageOrDie();
+    internal::FillInMessageMatrix(level, message_string, type_id, rows, cols,
+                                  data, message);
+    Write(message);
+  }
 };
 
 }  // namespace
diff --git a/aos/linux_code/logging/log_displayer.cc b/aos/linux_code/logging/log_displayer.cc
index 98021b5..1cc5f43 100644
--- a/aos/linux_code/logging/log_displayer.cc
+++ b/aos/linux_code/logging/log_displayer.cc
@@ -227,7 +227,39 @@
         log_message.type = ::aos::logging::LogMessage::Type::kStruct;
         break;
       }
-      case LogFileMessageHeader::MessageType::kString:
+      case LogFileMessageHeader::MessageType::kMatrix: {
+        const char *position =
+            reinterpret_cast<const char *>(msg + 1) + msg->name_size;
+        memcpy(&log_message.matrix.type, position,
+               sizeof(log_message.matrix.type));
+        position += sizeof(log_message.matrix.type);
+
+        uint32_t length;
+        memcpy(&length, position, sizeof(length));
+        log_message.matrix.string_length = length;
+        position += sizeof(length);
+
+        uint16_t rows;
+        memcpy(&rows, position, sizeof(rows));
+        log_message.matrix.rows = rows;
+        position += sizeof(rows);
+        uint16_t cols;
+        memcpy(&cols, position, sizeof(cols));
+        log_message.matrix.cols = cols;
+        position += sizeof(cols);
+
+        log_message.message_length -=
+            sizeof(log_message.matrix.type) + sizeof(uint32_t) +
+            sizeof(uint16_t) + sizeof(uint16_t) + length;
+        CHECK_EQ(
+            log_message.message_length,
+            ::aos::MessageType::Sizeof(log_message.matrix.type) * rows * cols);
+        memcpy(log_message.matrix.data, position,
+               log_message.message_length + length);
+
+        log_message.type = ::aos::logging::LogMessage::Type::kMatrix;
+        break;
+      } case LogFileMessageHeader::MessageType::kString:
         memcpy(
             log_message.message,
             reinterpret_cast<const char *>(msg) + sizeof(*msg) + msg->name_size,