Merge "split out the simpler parts of ConditionTest"
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 19eb0c3..2f6be93 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -21,6 +21,7 @@
         '<(AOS)/common/common.gyp:queue_test',
         '<(AOS)/common/common.gyp:die_test',
         '<(AOS)/common/common.gyp:queue_types_test',
+        '<(AOS)/common/util/util.gyp:string_to_num_test',
         '<(AOS)/common/util/util.gyp:trapezoid_profile_test',
         '<(AOS)/common/util/util.gyp:wrapping_counter_test',
         '<(AOS)/common/libc/libc.gyp:dirname_test',
@@ -29,6 +30,7 @@
         '<(AOS)/common/util/util.gyp:run_command_test',
         '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:cows_test',
         '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:packet_finder_test',
+        '<(AOS)/linux_code/linux_code.gyp:complex_thread_local_test',
         'Common',
       ],
     },
diff --git a/aos/common/util/string_to_num.h b/aos/common/util/string_to_num.h
new file mode 100644
index 0000000..c6dcb30
--- /dev/null
+++ b/aos/common/util/string_to_num.h
@@ -0,0 +1,29 @@
+#ifndef AOS_COMMON_UTIL_STRING_TO_NUM_H_
+#define AOS_COMMON_UTIL_STRING_TO_NUM_H_
+
+#include <sstream>
+#include <string>
+
+namespace aos {
+namespace util {
+
+// Converts a string into a specified integral type. If it can't be converted
+// completely or at all, or if the converted number would overflow the
+// specified integral type, it returns false.
+template<typename T>
+inline bool StringToInteger(const ::std::string &input, T *out_num) {
+  ::std::istringstream stream(input);
+  stream >> *out_num;
+
+  if (stream.fail() || !stream.eof()) {
+    return false;
+  }
+
+  return true;
+}
+
+
+}  // util
+}  // aos
+
+#endif
diff --git a/aos/common/util/string_to_num_test.cc b/aos/common/util/string_to_num_test.cc
new file mode 100644
index 0000000..e1ee1cb
--- /dev/null
+++ b/aos/common/util/string_to_num_test.cc
@@ -0,0 +1,43 @@
+#include <stdint.h>
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/util/string_to_num.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+TEST(StringToNumTest, CorrectNumber) {
+  int result;
+  ASSERT_TRUE(StringToInteger<int>(::std::string("42"), &result));
+  EXPECT_EQ(result, 42);
+}
+
+TEST(StringToNumTest, NegativeTest) {
+  int result;
+  ASSERT_TRUE(StringToInteger<int>(::std::string("-42"), &result));
+  EXPECT_EQ(result, -42);
+}
+
+TEST(StringToNumTest, NonNumber) {
+  int result;
+  ASSERT_FALSE(StringToInteger<int>(::std::string("Daniel"), &result));
+}
+
+TEST(StringToNumTest, NumberWithText) {
+  int result;
+  ASSERT_FALSE(StringToInteger<int>(::std::string("42Daniel"), &result));
+}
+
+TEST(StringToNumTest, OverflowTest) {
+  uint32_t result;
+  // 2 << 32 should overflow.
+  ASSERT_FALSE(StringToInteger<uint32_t>(::std::string("4294967296"), &result));
+}
+
+}  // testing
+}  // util
+}  // aos
diff --git a/aos/common/util/thread.h b/aos/common/util/thread.h
index bf90ce4..993e343 100644
--- a/aos/common/util/thread.h
+++ b/aos/common/util/thread.h
@@ -69,6 +69,13 @@
   FunctionThread(::std::function<void(FunctionThread *)> function)
       : function_(function) {}
 
+  // Runs function in a new thread and waits for it to return.
+  static void RunInOtherThread(::std::function<void()> function) {
+    FunctionThread t([&function](FunctionThread *) { function(); });
+    t.Start();
+    t.Join();
+  }
+
  private:
   virtual void Run() override {
     function_(this);
diff --git a/aos/common/util/util.gyp b/aos/common/util/util.gyp
index fa1fb7f..5008dd8 100644
--- a/aos/common/util/util.gyp
+++ b/aos/common/util/util.gyp
@@ -70,6 +70,24 @@
       ],
     },
     {
+      'target_name': 'string_to_num',
+      'type': 'static_library',
+      'sources': [
+        #'string_to_num.h',
+      ],
+    },
+    {
+      'target_name': 'string_to_num_test',
+      'type': 'executable',
+      'sources': [
+        'string_to_num_test.cc',
+      ],
+      'dependencies': [
+        ':string_to_num',
+        '<(EXTERNALS):gtest',
+      ],
+    },
+    {
       'target_name': 'thread',
       'type': 'static_library',
       'sources': [
diff --git a/aos/linux_code/complex_thread_local.cc b/aos/linux_code/complex_thread_local.cc
new file mode 100644
index 0000000..d57323e
--- /dev/null
+++ b/aos/linux_code/complex_thread_local.cc
@@ -0,0 +1,69 @@
+#include "aos/linux_code/complex_thread_local.h"
+
+#include <pthread.h>
+
+#include "aos/common/once.h"
+#include "aos/common/die.h"
+
+#define SIMPLE_CHECK(call)              \
+  do {                                  \
+    const int value = call;             \
+    if (value != 0) {                   \
+      PRDie(value, "%s failed", #call); \
+    }                                   \
+  } while (false)
+
+namespace aos {
+namespace {
+
+void ExecuteDestructorList(void *v) {
+  for (const ComplexThreadLocalDestructor *c =
+           static_cast<ComplexThreadLocalDestructor *>(v);
+       c != nullptr; c = c->next) {
+    c->function(c->param);
+  }
+}
+
+pthread_key_t *CreateKey() {
+  static pthread_key_t r;
+  SIMPLE_CHECK(pthread_key_create(&r, ExecuteDestructorList));
+  return &r;
+}
+
+::aos::Once<pthread_key_t> key_once(CreateKey);
+
+} // namespace
+
+void ComplexThreadLocalDestructor::Add() {
+  static_assert(
+      ::std::is_pod<ComplexThreadLocalDestructor>::value,
+      "ComplexThreadLocalDestructor might not be safe to pass through void*");
+  pthread_key_t *const key = key_once.Get();
+
+  next = static_cast<ComplexThreadLocalDestructor *>(pthread_getspecific(*key));
+  SIMPLE_CHECK(pthread_setspecific(*key, this));
+}
+
+void ComplexThreadLocalDestructor::Remove() {
+  pthread_key_t *const key = key_once.Get();
+
+  ComplexThreadLocalDestructor *previous = nullptr;
+  for (ComplexThreadLocalDestructor *c =
+           static_cast<ComplexThreadLocalDestructor *>(
+               pthread_getspecific(*key));
+       c != nullptr; c = c->next) {
+    if (c == this) {
+      // If it's the first one.
+      if (previous == nullptr) {
+        SIMPLE_CHECK(pthread_setspecific(*key, next));
+      } else {
+        previous->next = next;
+      }
+      return;
+    }
+    previous = c;
+  }
+  ::aos::Die("%p is not in the destructor list\n", this);
+}
+
+}  // namespace aos
diff --git a/aos/linux_code/complex_thread_local.h b/aos/linux_code/complex_thread_local.h
new file mode 100644
index 0000000..7ada875
--- /dev/null
+++ b/aos/linux_code/complex_thread_local.h
@@ -0,0 +1,131 @@
+#ifndef AOS_LINUX_CODE_COMPLEX_THREAD_LOCAL_H_
+#define AOS_LINUX_CODE_COMPLEX_THREAD_LOCAL_H_
+
+#include <assert.h>
+
+#include <type_traits>
+#include <utility>
+
+namespace aos {
+
+// Instances form a (per-thread) list of destructor functions to call when the
+// thread exits.
+// Only ComplexThreadLocal should use this.
+struct ComplexThreadLocalDestructor {
+  // Adds this to the list of destructors in this thread.
+  void Add();
+  // Removes this from the list of destructors in this thread. ::aos::Dies if it
+  // is not there.
+  void Remove();
+
+  void (*function)(void *);
+  void *param;
+
+  ComplexThreadLocalDestructor *next;
+};
+
+// Handles creating a thread-local (per type) object with non-trivial
+// constructor and/or destructor. It will be correctly destroyed on thread exit.
+//
+// Each thread using an instantiation of this class has its own independent slot
+// for storing a T. An instance of T is not actually constructed until a thread
+// calls Create, after which a pointer to it will be returned from get() etc
+// until after Clear is called.
+//
+// Example usage:
+// class Something {
+//  private:
+//   class Data {
+//    public:
+//     Data(const ::std::string &value) : value_(value) {}
+//
+//     int DoSomething() {
+//       if (cached_result_ == 0) {
+//         // Do something expensive with value_ and store it in
+//         // cached_result_.
+//       }
+//       return cached_result_;
+//     }
+//
+//    private:
+//     const ::std::string value_;
+//     int cached_result_ = 0;
+//   };
+//   ComplexThreadLocal<Data> thread_local_;
+//   ::std::string a_string_;
+//
+//   int DoSomething() {
+//     thread_local_.Create(a_string_);
+//     return thread_local_->DoSomething();
+//   }
+// };
+//
+// The current implementation is based on
+// <http://stackoverflow.com/questions/12049684/gcc-4-7-on-linux-pthreads-nontrivial-thread-local-workaround-using-thread-n>.
+// TODO(brians): Change this to just simple standard C++ thread_local once all
+// of our compilers have support.
+template <typename T>
+class ComplexThreadLocal {
+ public:
+  // Actually creates the object in this thread if there is not one there
+  // already.
+  // args are all perfectly forwarded to the constructor.
+  template <typename... Args>
+  void Create(Args &&... args) {
+    if (initialized) return;
+    new (&storage) T(::std::forward<Args>(args)...);
+    destructor.function = PlacementDelete;
+    destructor.param = &storage;
+    destructor.Add();
+    initialized = true;
+  }
+
+  // Removes the object in this thread (if any), including calling its
+  // destructor.
+  void Clear() {
+    if (!initialized) return;
+    destructor.Remove();
+    PlacementDelete(&storage);
+    initialized = false;
+  }
+
+  // Returns true if there is already an object in this thread.
+  bool created() const { return initialized; }
+
+  // Returns the object currently created in this thread or nullptr.
+  T *operator->() const {
+    return get();
+  }
+  T *get() const {
+    if (initialized) {
+      return static_cast<T *>(static_cast<void *>(&storage));
+    } else {
+      return nullptr;
+    }
+  }
+
+ private:
+  typedef typename ::std::aligned_storage<
+      sizeof(T), ::std::alignment_of<T>::value>::type Storage;
+
+  // Convenient helper for calling a destructor.
+  static void PlacementDelete(void *t) { static_cast<T *>(t)->~T(); }
+
+  // True iff this storage has been initialized.
+  static __thread bool initialized;
+  // Where we actually store the object for this thread (if any).
+  static __thread Storage storage;
+  // The linked list element representing this storage.
+  static __thread ComplexThreadLocalDestructor destructor;
+};
+
+template <typename T>
+__thread bool ComplexThreadLocal<T>::initialized;
+template <typename T>
+__thread typename ComplexThreadLocal<T>::Storage ComplexThreadLocal<T>::storage;
+template <typename T>
+__thread ComplexThreadLocalDestructor ComplexThreadLocal<T>::destructor;
+
+}  // namespace aos
+
+#endif  // AOS_LINUX_CODE_COMPLEX_THREAD_LOCAL_H_
diff --git a/aos/linux_code/complex_thread_local_test.cc b/aos/linux_code/complex_thread_local_test.cc
new file mode 100644
index 0000000..97f0568
--- /dev/null
+++ b/aos/linux_code/complex_thread_local_test.cc
@@ -0,0 +1,101 @@
+#include "aos/linux_code/complex_thread_local.h"
+
+#include <atomic>
+
+#include "gtest/gtest.h"
+
+#include "aos/common/util/thread.h"
+
+namespace aos {
+namespace testing {
+
+class ComplexThreadLocalTest : public ::testing::Test {
+ protected:
+  struct TraceableObject {
+    TraceableObject(int data = 0) : data(data) { ++constructions; }
+    ~TraceableObject() { ++destructions; }
+
+    static ::std::atomic<int> constructions, destructions;
+
+    int data;
+  };
+  ComplexThreadLocal<TraceableObject> local;
+
+ private:
+  void SetUp() override {
+    local.Clear();
+    EXPECT_EQ(TraceableObject::constructions, TraceableObject::destructions)
+        << "There should be no way to create and destroy different numbers.";
+    TraceableObject::constructions = TraceableObject::destructions = 0;
+  }
+};
+::std::atomic<int> ComplexThreadLocalTest::TraceableObject::constructions;
+::std::atomic<int> ComplexThreadLocalTest::TraceableObject::destructions;
+
+TEST_F(ComplexThreadLocalTest, Basic) {
+  EXPECT_EQ(0, TraceableObject::constructions);
+  EXPECT_EQ(0, TraceableObject::destructions);
+  EXPECT_FALSE(local.created());
+  EXPECT_EQ(nullptr, local.get());
+
+  local.Create(971);
+  EXPECT_EQ(1, TraceableObject::constructions);
+  EXPECT_EQ(0, TraceableObject::destructions);
+  EXPECT_TRUE(local.created());
+  EXPECT_EQ(971, local->data);
+
+  local.Create(254);
+  EXPECT_EQ(1, TraceableObject::constructions);
+  EXPECT_EQ(0, TraceableObject::destructions);
+  EXPECT_TRUE(local.created());
+  EXPECT_EQ(971, local->data);
+
+  local.Clear();
+  EXPECT_EQ(1, TraceableObject::constructions);
+  EXPECT_EQ(1, TraceableObject::destructions);
+  EXPECT_FALSE(local.created());
+  EXPECT_EQ(nullptr, local.get());
+
+  local.Create(973);
+  EXPECT_EQ(2, TraceableObject::constructions);
+  EXPECT_EQ(1, TraceableObject::destructions);
+  EXPECT_TRUE(local.created());
+  EXPECT_EQ(973, local->data);
+}
+
+TEST_F(ComplexThreadLocalTest, AnotherThread) {
+  EXPECT_FALSE(local.created());
+  util::FunctionThread::RunInOtherThread([this]() {
+    EXPECT_FALSE(local.created());
+    local.Create(971);
+    EXPECT_TRUE(local.created());
+    EXPECT_EQ(971, local->data);
+    EXPECT_EQ(1, TraceableObject::constructions);
+    EXPECT_EQ(0, TraceableObject::destructions);
+  });
+  EXPECT_EQ(1, TraceableObject::constructions);
+  EXPECT_EQ(1, TraceableObject::destructions);
+  EXPECT_FALSE(local.created());
+}
+
+TEST_F(ComplexThreadLocalTest, TwoThreads) {
+  util::FunctionThread thread([this](util::FunctionThread *) {
+    local.Create(971);
+    EXPECT_EQ(971, local->data);
+    EXPECT_EQ(0, TraceableObject::destructions);
+  });
+  thread.Start();
+  local.Create(973);
+  EXPECT_EQ(973, local->data);
+  thread.Join();
+  EXPECT_TRUE(local.created());
+  EXPECT_EQ(2, TraceableObject::constructions);
+  EXPECT_EQ(1, TraceableObject::destructions);
+  local.Clear();
+  EXPECT_EQ(2, TraceableObject::constructions);
+  EXPECT_EQ(2, TraceableObject::destructions);
+  EXPECT_FALSE(local.created());
+}
+
+}  // namespace testing
+}  // namespace aos
diff --git a/aos/linux_code/linux_code.gyp b/aos/linux_code/linux_code.gyp
index 74b788d..c34b42e 100644
--- a/aos/linux_code/linux_code.gyp
+++ b/aos/linux_code/linux_code.gyp
@@ -1,6 +1,30 @@
 {
   'targets': [
     {
+      'target_name': 'complex_thread_local',
+      'type': 'static_library',
+      'sources': [
+        'complex_thread_local.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/common/common.gyp:once',
+        '<(AOS)/common/common.gyp:die',
+      ],
+    },
+    {
+      'target_name': 'complex_thread_local_test',
+      'type': 'executable',
+      'sources': [
+        'complex_thread_local_test.cc',
+      ],
+      'dependencies': [
+        'complex_thread_local',
+        '<(EXTERNALS):gtest',
+        '<(AOS)/common/util/util.gyp:thread',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+    },
+    {
       'target_name': 'init',
       'type': 'static_library',
       'sources': [
diff --git a/aos/linux_code/logging/log_displayer.cc b/aos/linux_code/logging/log_displayer.cc
index 805267c..bc00ddc 100644
--- a/aos/linux_code/logging/log_displayer.cc
+++ b/aos/linux_code/logging/log_displayer.cc
@@ -7,11 +7,14 @@
 #include <inttypes.h>
 
 #include <algorithm>
+#include <memory>
+#include <string>
 
 #include "aos/linux_code/logging/binary_log_file.h"
 #include "aos/common/queue_types.h"
 #include "aos/common/logging/logging_impl.h"
 #include "aos/common/logging/logging_printf_formats.h"
+#include "aos/common/util/string_to_num.h"
 
 using ::aos::logging::linux_code::LogFileMessageHeader;
 
@@ -24,24 +27,70 @@
     "  -n, --name NAME       only display entries from processes named NAME\n"
     "  -l, --level LEVEL     "
       "only display log entries at least as important as LEVEL\n"
-    "  // -p, --pid PID        only display log entries from process PID\n"
+    "  -p, --pid PID        only display log entries from process PID\n"
     "  -f, --follow          "
       "wait when the end of the file is reached (implies --end)\n"
     "  -t, --terminate       stop when the end of file is reached (default)\n"
     "  -b, --beginning       start at the beginning of the file (default)\n"
     "  -e, --end             start at the end of the file\n"
     "  -s, --skip NUMBER     skip NUMBER matching logs\n"
-    "  // -m, --max NUMBER     only display up to NUMBER logs\n"
+    "  -m, --max NUMBER     only display up to NUMBER logs\n"
     "  // -o, --format FORMAT  use FORMAT to display log entries\n"
     "  -h, --help            display this help and exit\n"
     "\n"
     "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
     "  It defaults to INFO.\n"
     "\n"
-    "TODO(brians) implement the commented out ones and changing FILE\n";
+    "TODO(brians) implement the commented out ones.\n";
+
+const char *kExampleUsages = "To view logs from the shooter:\n"
+    "\t`log_displayer -n shooter`\n"
+    "To view debug logs from the shooter:\n"
+    "\t`log_displayer -n shooter -l DEBUG`\n"
+    "To view what the shooter is logging in realtime:\n"
+    "\t`log_displayer -f -n shooter`\n"
+    "To view shooter logs from an old log file:\n"
+    "\t`log_displayer aos_log-<number> -n shooter`\n"
+    "To view the statuses of the shooter hall effects in realtime:\n"
+    "\t`log_displayer -f -n shooter -l DEBUG | grep .Position`\n";
 
 void PrintHelpAndExit() {
   fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
+  fprintf(stderr, "\nExample usages:\n\n%s", kExampleUsages);
+
+  // Get the possible executables from start_list.txt.
+  FILE *start_list = fopen("start_list.txt", "r");
+  if (start_list == NULL) {
+    PLOG(FATAL, "Unable to open start_list.txt");
+  }
+
+  // Get file size.
+  if (fseek(start_list, 0, SEEK_END)) {
+    PLOG(FATAL, "fseek() failed while reading start_list.txt");
+  }
+  int size = ftell(start_list);
+  if (size < 0) {
+    PLOG(FATAL, "ftell() failed while reading start_list.txt");
+  }
+  rewind(start_list);
+
+  ::std::unique_ptr<char[]> contents(new char[size + 1]);
+  if (contents == NULL) {
+    LOG(FATAL, "malloc() failed while reading start_list.txt.\n");
+  }
+  size_t bytes_read = fread(contents.get(), 1, size, start_list);
+  if (bytes_read < static_cast<size_t>(size)) {
+    LOG(FATAL, "Read %zu bytes from start_list.txt, expected %d.\n",
+        bytes_read, size);
+  }
+
+  // printf doesn't like strings without the \0.
+  contents[size] = '\0';
+  fprintf(stderr, "\nPossible arguments for the -n option:\n%s", contents.get());
+
+  if (fclose(start_list)) {
+    LOG(FATAL, "fclose() failed.\n");
+  }
 
   exit(EXIT_SUCCESS);
 }
@@ -56,6 +105,8 @@
   // Whether we need to skip everything until we get to the end of the file.
   bool skip_to_end = false;
   const char *filename = "aos_log-current";
+  int display_max = 0;
+  int32_t source_pid = -1;
 
   ::aos::logging::Init();
   ::aos::logging::AddImplementation(
@@ -88,7 +139,7 @@
     }
     switch (c) {
       case 0:
-        fprintf(stderr, "LogDisplayer: got a 0 option but didn't set up any\n");
+        fputs("LogDisplayer: got a 0 option but didn't set up any\n", stderr);
         abort();
       case 'n':
         filter_name = optarg;
@@ -102,7 +153,14 @@
         }
         break;
       case 'p':
-        abort();
+        if (!::aos::util::StringToInteger(::std::string(optarg), &source_pid)) {
+          fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
+          exit(EXIT_FAILURE);
+        }
+        if (source_pid < 0) {
+          fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
+          exit(EXIT_FAILURE);
+        }
         break;
       case 'f':
         follow = true;
@@ -118,7 +176,15 @@
         skip_to_end = true;
         break;
       case 'm':
-        abort();
+        if (!::aos::util::StringToInteger(::std::string(optarg), &display_max)) {
+          fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
+          exit(EXIT_FAILURE);
+        }
+        if (display_max <= 0) {
+          fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
+              optarg);
+          exit(EXIT_FAILURE);
+        }
         break;
       case 'o':
         abort();
@@ -135,16 +201,22 @@
     }
   }
 
-  fprintf(stderr, "displaying down to level %s from file '%s'\n",
-          ::aos::logging::log_str(filter_level), filename);
   if (optind < argc) {
-    fprintf(stderr, "non-option ARGV-elements: ");
+    // We got a filename.
+    filename = argv[optind++];
+  }
+  if (optind < argc) {
+    fputs("non-option ARGV-elements: ", stderr);
     while (optind < argc) {
       fprintf(stderr, "%s\n", argv[optind++]);
     }
   }
 
+  fprintf(stderr, "displaying down to level %s from file '%s'\n",
+      ::aos::logging::log_str(filter_level), filename);
+
   int fd = open(filename, O_RDONLY);
+
   if (fd == -1) {
     PLOG(FATAL, "couldn't open file '%s' for reading", filename);
   }
@@ -155,6 +227,7 @@
   }
 
   const LogFileMessageHeader *msg;
+  int displayed = 0;
   do {
     msg = reader.ReadNextMessage(follow);
     if (msg == NULL) {
@@ -162,6 +235,11 @@
       return 0;
     }
 
+    if (source_pid >= 0 && msg->source != source_pid) {
+      // Message is from the wrong process.
+      continue;
+    }
+
     if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
       size_t bytes = msg->message_size;
       ::aos::MessageType *type = ::aos::MessageType::Deserialize(
@@ -190,6 +268,11 @@
       }
     }
 
+    if (display_max && displayed++ >= display_max) {
+      fputs("Not displaying the rest of the messages.\n", stderr);
+      return 0;
+    }
+
     const char *position =
         reinterpret_cast<const char *>(msg + 1) + msg->name_size;
 #define BASE_ARGS                                                           \