Add log_displayer support for -m and -p.

These options work as advertised in the help message. Also add
a new function in util for converting strings to numbers.

Change-Id: I33ab9add3f55c9139557b30ffe4de633782933e5
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 19eb0c3..0e6370b 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',
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/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/logging/log_displayer.cc b/aos/linux_code/logging/log_displayer.cc
index 8141cd4..5eb6f8a 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,14 +27,14 @@
     "  -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"
@@ -56,6 +59,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(
@@ -102,7 +107,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 +130,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();
@@ -161,6 +181,7 @@
   }
 
   const LogFileMessageHeader *msg;
+  int displayed = 0;
   do {
     msg = reader.ReadNextMessage(follow);
     if (msg == NULL) {
@@ -168,6 +189,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(
@@ -196,6 +222,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                                                           \