trace_processor: JSON importer coerces numbers

Some json traces look like:
{"name": "build.ninja", "tid": "0", "dur": "42", "pid": "0"...}

Instead of:
{"name": "build.ninja", "tid": 0, "dur": 42, "pid": 0...}

Attempt to coerce the relevant fields to numbers.

Change-Id: Ifd9c09b204f6da6a7f007367daa0b32f46aff700
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 7f5fbca..132565b 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -176,6 +176,10 @@
     "../../protos/perfetto/trace:lite",
     "../base",
   ]
+  if (perfetto_build_standalone) {
+    sources += [ "json_trace_parser_unittest.cc" ]
+    deps += [ "../../gn:jsoncpp_deps" ]
+  }
 }
 
 source_set("integrationtests") {
diff --git a/src/trace_processor/json_trace_parser.cc b/src/trace_processor/json_trace_parser.cc
index 245348c..5bb3b28 100644
--- a/src/trace_processor/json_trace_parser.cc
+++ b/src/trace_processor/json_trace_parser.cc
@@ -16,6 +16,7 @@
 
 #include "src/trace_processor/json_trace_parser.h"
 
+#include <inttypes.h>
 #include <json/reader.h>
 #include <json/value.h>
 
@@ -36,7 +37,6 @@
 
 namespace perfetto {
 namespace trace_processor {
-
 namespace {
 
 enum ReadDictRes { kFoundDict, kNeedsMoreData, kEndOfTrace, kFatalError };
@@ -82,6 +82,31 @@
 
 }  // namespace
 
+bool CoerceToUint64(const Json::Value& value, uint64_t* integer_ptr) {
+  switch (static_cast<size_t>(value.type())) {
+    case Json::uintValue:
+    case Json::intValue:
+      *integer_ptr = value.asUInt64();
+      return true;
+    case Json::stringValue: {
+      std::string s = value.asString();
+      char* end;
+      *integer_ptr = strtoull(s.c_str(), &end, 10);
+      return end == s.data() + s.size();
+    }
+    default:
+      return false;
+  }
+}
+
+bool CoerceToUint32(const Json::Value& value, uint32_t* integer_ptr) {
+  uint64_t n = 0;
+  if (!CoerceToUint64(value, &n))
+    return false;
+  *integer_ptr = static_cast<uint32_t>(n);
+  return true;
+}
+
 JsonTraceParser::JsonTraceParser(TraceProcessorContext* context)
     : context_(context) {}
 
@@ -124,9 +149,13 @@
     if (!ph.isString())
       continue;
     char phase = *ph.asCString();
-    uint32_t tid = value["tid"].asUInt();
-    uint32_t pid = value["pid"].asUInt();
-    uint64_t ts = value["ts"].asLargestUInt() * 1000;
+    uint32_t tid = 0;
+    PERFETTO_CHECK(CoerceToUint32(value["tid"], &tid));
+    uint32_t pid = 0;
+    PERFETTO_CHECK(CoerceToUint32(value["pid"], &pid));
+    uint64_t ts = 0;
+    PERFETTO_CHECK(CoerceToUint64(value["ts"], &ts));
+    ts *= 1000;
     const char* cat = value.isMember("cat") ? value["cat"].asCString() : "";
     const char* name = value.isMember("name") ? value["name"].asCString() : "";
     StringId cat_id = storage->InternString(cat);
@@ -143,7 +172,9 @@
         break;
       }
       case 'X': {  // TRACE_EVENT (scoped event).
-        uint64_t duration = value["dur"].asUInt() * 1000;
+        uint64_t duration = 0;
+        PERFETTO_CHECK(CoerceToUint64(value["ts"], &duration));
+        duration *= 1000;
         slice_tracker->Scoped(ts, utid, cat_id, name_id, duration);
         break;
       }
diff --git a/src/trace_processor/json_trace_parser.h b/src/trace_processor/json_trace_parser.h
index bdd9378..36f001d 100644
--- a/src/trace_processor/json_trace_parser.h
+++ b/src/trace_processor/json_trace_parser.h
@@ -26,11 +26,19 @@
 #include "src/trace_processor/chunked_trace_reader.h"
 #include "src/trace_processor/trace_storage.h"
 
+namespace Json {
+class Value;
+}
+
 namespace perfetto {
 namespace trace_processor {
 
 class TraceProcessorContext;
 
+// TODO(hjd): Make optional.
+bool CoerceToUint64(const Json::Value& value, uint64_t* integer_ptr);
+bool CoerceToUint32(const Json::Value& value, uint32_t* integer_ptr);
+
 // Parses legacy chrome JSON traces. The support for now is extremely rough
 // and supports only explicit TRACE_EVENT_BEGIN/END events.
 class JsonTraceParser : public ChunkedTraceReader {
diff --git a/src/trace_processor/json_trace_parser_unittest.cc b/src/trace_processor/json_trace_parser_unittest.cc
new file mode 100644
index 0000000..be23d7a
--- /dev/null
+++ b/src/trace_processor/json_trace_parser_unittest.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/json_trace_parser.h"
+
+#include <json/value.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(JsonTraceParserTest, CoerceToUint32) {
+  uint32_t n = 0;
+
+  ASSERT_TRUE(CoerceToUint32(Json::Value(42), &n));
+  EXPECT_EQ(n, 42);
+
+  ASSERT_TRUE(CoerceToUint32(Json::Value("42"), &n));
+  EXPECT_EQ(n, 42);
+}
+
+TEST(JsonTraceParserTest, CoerceToUint64) {
+  uint64_t n = 0;
+
+  ASSERT_TRUE(CoerceToUint64(Json::Value(42), &n));
+  EXPECT_EQ(n, 42);
+
+  ASSERT_TRUE(CoerceToUint64(Json::Value("42"), &n));
+  EXPECT_EQ(n, 42);
+
+  ASSERT_FALSE(CoerceToUint64(Json::Value("foo"), &n));
+  ASSERT_FALSE(CoerceToUint64(Json::Value("1234!"), &n));
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/trace_processor_impl_unittest.cc b/src/trace_processor/trace_processor_impl_unittest.cc
index a901de6..9200d4e 100644
--- a/src/trace_processor/trace_processor_impl_unittest.cc
+++ b/src/trace_processor/trace_processor_impl_unittest.cc
@@ -23,27 +23,27 @@
 namespace trace_processor {
 namespace {
 
-TEST(TraceProcessorImpl, GuessTraceType_Empty) {
+TEST(TraceProcessorImplTest, GuessTraceType_Empty) {
   const uint8_t prefix[] = "";
   EXPECT_EQ(kUnknownTraceType, GuessTraceType(prefix, 0));
 }
 
-TEST(TraceProcessorImpl, GuessTraceType_Json) {
+TEST(TraceProcessorImplTest, GuessTraceType_Json) {
   const uint8_t prefix[] = "{\"traceEvents\":[";
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
 
-TEST(TraceProcessorImpl, GuessTraceType_JsonWithSpaces) {
+TEST(TraceProcessorImplTest, GuessTraceType_JsonWithSpaces) {
   const uint8_t prefix[] = "\n{ \"traceEvents\": [";
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
 
-TEST(TraceProcessorImpl, GuessTraceType_JsonMissingTraceEvents) {
+TEST(TraceProcessorImplTest, GuessTraceType_JsonMissingTraceEvents) {
   const uint8_t prefix[] = "[{";
   EXPECT_EQ(kJsonTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }
 
-TEST(TraceProcessorImpl, GuessTraceType_Proto) {
+TEST(TraceProcessorImplTest, GuessTraceType_Proto) {
   const uint8_t prefix[] = {0x0a, 0x65, 0x18, 0x8f, 0x4e, 0x32, 0x60, 0x0a};
   EXPECT_EQ(kProtoTraceType, GuessTraceType(prefix, sizeof(prefix)));
 }