tp: fix parsing of unknown values in top level of JSON trace

Skip any values we don't know with a single skip function - this
unifies our handling of any keys regardless of type.

Change-Id: I52555d71acff9fece117482f02af6260364c6229
Bug: https://github.com/google/perfetto/issues/365
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.cc b/src/trace_processor/importers/json/json_trace_tokenizer.cc
index d57ad8b..b8c486f 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.cc
@@ -110,6 +110,67 @@
   return ReadStringRes::kNeedsMoreData;
 }
 
+enum class SkipValueRes {
+  kEndOfValue,
+  kNeedsMoreData,
+  kFatalError,
+};
+SkipValueRes SkipOneJsonValue(const char* start,
+                              const char* end,
+                              const char** next) {
+  uint32_t brace_count = 0;
+  uint32_t bracket_count = 0;
+  for (const char* s = start; s < end; s++) {
+    if (*s == '"') {
+      // Because strings can contain {}[] characters, handle them separately
+      // before anything else.
+      std::string ignored;
+      const char* str_next = nullptr;
+      switch (ReadOneJsonString(s, end, &ignored, &str_next)) {
+        case ReadStringRes::kFatalError:
+          return SkipValueRes::kFatalError;
+        case ReadStringRes::kNeedsMoreData:
+          return SkipValueRes::kNeedsMoreData;
+        case ReadStringRes::kEndOfString:
+          // -1 as the loop body will +1 getting to the correct place.
+          s = str_next - 1;
+          break;
+      }
+      continue;
+    }
+    if (brace_count == 0 && bracket_count == 0 && (*s == ',' || *s == '}')) {
+      // If we're at the outermost level and we see a comma or a closing brace,
+      // we've hit the end of the value.
+      // +1 so we skip the closing comma/brace itself.
+      *next = s + 1;
+      return SkipValueRes::kEndOfValue;
+    }
+    if (*s == '[') {
+      ++bracket_count;
+      continue;
+    }
+    if (*s == ']') {
+      if (bracket_count == 0) {
+        return SkipValueRes::kFatalError;
+      }
+      --bracket_count;
+      continue;
+    }
+    if (*s == '{') {
+      ++brace_count;
+      continue;
+    }
+    if (*s == '}') {
+      if (brace_count == 0) {
+        return SkipValueRes::kFatalError;
+      }
+      --brace_count;
+      continue;
+    }
+  }
+  return SkipValueRes::kNeedsMoreData;
+}
+
 }  // namespace
 
 ReadDictRes ReadOneJsonDict(const char* start,
@@ -278,8 +339,11 @@
     if (res == ReadKeyRes::kEndOfDictionary)
       break;
 
-    if (res == ReadKeyRes::kFatalError)
-      return util::ErrStatus("Failure parsing JSON: encountered fatal error");
+    if (res == ReadKeyRes::kFatalError) {
+      return util::ErrStatus(
+          "Failure parsing JSON: encountered fatal error while parsing key for "
+          "value");
+    }
 
     if (res == ReadKeyRes::kNeedsMoreData) {
       return util::ErrStatus("Failure parsing JSON: partial JSON dictionary");
@@ -432,8 +496,10 @@
 
       std::string key;
       ReadKeyRes res = ReadOneJsonKey(start, end, &key, &next);
-      if (res == ReadKeyRes::kFatalError)
-        return util::ErrStatus("Failure parsing JSON: encountered fatal error");
+      if (res == ReadKeyRes::kFatalError) {
+        return util::ErrStatus(
+            "Failure parsing JSON: encountered fatal error while parsing key");
+      }
 
       if (res == ReadKeyRes::kEndOfDictionary ||
           res == ReadKeyRes::kNeedsMoreData) {
@@ -477,40 +543,16 @@
         return ParseInternal(next, end, out);
       }
 
-      // If we don't recognize the key, check if it could be a string or dict.
-      // If either of them, parse it and throw it away to move onto the next
-      // key.
-      if (*next == '"') {
-        std::string ignored;
-        ReadStringRes value_res = ReadOneJsonString(next, end, &ignored, &next);
-        if (value_res == ReadStringRes::kFatalError) {
+      // If we don't know the key for this JSON value just skip it.
+      switch (SkipOneJsonValue(next, end, &next)) {
+        case SkipValueRes::kFatalError:
           return base::ErrStatus(
-              "Failure parsing JSON: unable to read string for key %s",
+              "Failure parsing JSON: error while parsing value for key %s",
               key.c_str());
-        }
-        if (value_res == ReadStringRes::kNeedsMoreData) {
-          break;
-        }
-        return ParseInternal(next, end, out);
+        case SkipValueRes::kNeedsMoreData:
+        case SkipValueRes::kEndOfValue:
+          return ParseInternal(next, end, out);
       }
-      if (*next == '{') {
-        base::StringView ignored;
-        ReadDictRes value_res = ReadOneJsonDict(next, end, &ignored, &next);
-        if (value_res == ReadDictRes::kEndOfTrace ||
-            value_res == ReadDictRes::kEndOfArray) {
-          return util::ErrStatus(
-              "Failure parsing JSON: unable to read dict for key %s",
-              key.c_str());
-        }
-        if (value_res == ReadDictRes::kNeedsMoreData) {
-          break;
-        }
-        return ParseInternal(next, end, out);
-      }
-
-      // Otherwise, just jump to the end of the trace.
-      // TODO(lalitm): do something better here.
-      position_ = TracePosition::kEof;
       break;
     }
     case TracePosition::kInsideSystemTraceEventsString: {
@@ -522,9 +564,11 @@
       while (next < end) {
         std::string raw_line;
         auto res = ReadOneSystemTraceLine(next, end, &raw_line, &next);
-        if (res == ReadSystemLineRes::kFatalError)
+        if (res == ReadSystemLineRes::kFatalError) {
           return util::ErrStatus(
-              "Failure parsing JSON: encountered fatal error");
+              "Failure parsing JSON: encountered fatal error while parsing "
+              "event inside trace event string");
+        }
 
         if (res == ReadSystemLineRes::kNeedsMoreData)
           break;