xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/json/json_trace_tokenizer.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_processor/importers/json/json_trace_tokenizer.h"
18 
19 #include <cctype>
20 #include <cstddef>
21 #include <cstdint>
22 #include <memory>
23 #include <optional>
24 #include <string>
25 
26 #include "perfetto/base/logging.h"
27 #include "perfetto/base/status.h"
28 #include "perfetto/ext/base/string_utils.h"
29 #include "perfetto/ext/base/string_view.h"
30 #include "perfetto/public/compiler.h"
31 #include "perfetto/trace_processor/trace_blob_view.h"
32 #include "src/trace_processor/importers/common/legacy_v8_cpu_profile_tracker.h"
33 #include "src/trace_processor/importers/json/json_utils.h"
34 #include "src/trace_processor/sorter/trace_sorter.h"  // IWYU pragma: keep
35 #include "src/trace_processor/storage/stats.h"
36 #include "src/trace_processor/util/status_macros.h"
37 
38 namespace perfetto::trace_processor {
39 namespace {
40 
FormatErrorContext(const char * s,const char * e)41 std::string FormatErrorContext(const char* s, const char* e) {
42   return {s, static_cast<size_t>(e - s)};
43 }
44 
AppendUnescapedCharacter(char c,bool is_escaping,std::string * key)45 base::Status AppendUnescapedCharacter(char c,
46                                       bool is_escaping,
47                                       std::string* key) {
48   if (is_escaping) {
49     switch (c) {
50       case '"':
51       case '\\':
52       case '/':
53         key->push_back(c);
54         break;
55       case 'b':
56         key->push_back('\b');
57         break;
58       case 'f':
59         key->push_back('\f');
60         break;
61       case 'n':
62         key->push_back('\n');
63         break;
64       case 'r':
65         key->push_back('\r');
66         break;
67       case 't':
68         key->push_back('\t');
69         break;
70       case 'u':
71         // Just pass through \uxxxx escape sequences which JSON supports but is
72         // not worth the effort to parse as we never use them here.
73         key->append("\\u");
74         break;
75       default:
76         return base::ErrStatus("Illegal character in JSON %c", c);
77     }
78   } else if (c != '\\') {
79     key->push_back(c);
80   }
81   return base::OkStatus();
82 }
83 
84 enum class ReadStringRes {
85   kEndOfString,
86   kNeedsMoreData,
87   kFatalError,
88 };
ReadOneJsonString(const char * start,const char * end,std::string * key,const char ** next)89 ReadStringRes ReadOneJsonString(const char* start,
90                                 const char* end,
91                                 std::string* key,
92                                 const char** next) {
93   if (start == end) {
94     return ReadStringRes::kNeedsMoreData;
95   }
96   if (*start != '"') {
97     return ReadStringRes::kFatalError;
98   }
99 
100   bool is_escaping = false;
101   for (const char* s = start + 1; s < end; s++) {
102     // Control characters are not allowed in JSON strings.
103     if (iscntrl(*s))
104       return ReadStringRes::kFatalError;
105 
106     // If we get a quote character end of the string.
107     if (*s == '"' && !is_escaping) {
108       *next = s + 1;
109       return ReadStringRes::kEndOfString;
110     }
111 
112     base::Status status = AppendUnescapedCharacter(*s, is_escaping, key);
113     if (!status.ok())
114       return ReadStringRes::kFatalError;
115 
116     // If we're in a string and we see a backslash and the last character was
117     // not a backslash the next character is escaped:
118     is_escaping = *s == '\\' && !is_escaping;
119   }
120   return ReadStringRes::kNeedsMoreData;
121 }
122 
123 enum class SkipValueRes {
124   kEndOfValue,
125   kNeedsMoreData,
126   kFatalError,
127 };
SkipOneJsonValue(const char * start,const char * end,const char ** next)128 SkipValueRes SkipOneJsonValue(const char* start,
129                               const char* end,
130                               const char** next) {
131   uint32_t brace_count = 0;
132   uint32_t bracket_count = 0;
133   for (const char* s = start; s < end; s++) {
134     if (*s == '"') {
135       // Because strings can contain {}[] characters, handle them separately
136       // before anything else.
137       std::string ignored;
138       const char* str_next = nullptr;
139       switch (ReadOneJsonString(s, end, &ignored, &str_next)) {
140         case ReadStringRes::kFatalError:
141           return SkipValueRes::kFatalError;
142         case ReadStringRes::kNeedsMoreData:
143           return SkipValueRes::kNeedsMoreData;
144         case ReadStringRes::kEndOfString:
145           // -1 as the loop body will +1 getting to the correct place.
146           s = str_next - 1;
147           break;
148       }
149       continue;
150     }
151     if (brace_count == 0 && bracket_count == 0 && (*s == ',' || *s == '}')) {
152       // Regardless of a comma or brace, this will be skipped by the caller so
153       // just set it to this character.
154       *next = s;
155       return SkipValueRes::kEndOfValue;
156     }
157     if (*s == '[') {
158       ++bracket_count;
159       continue;
160     }
161     if (*s == ']') {
162       if (bracket_count == 0) {
163         return SkipValueRes::kFatalError;
164       }
165       --bracket_count;
166       continue;
167     }
168     if (*s == '{') {
169       ++brace_count;
170       continue;
171     }
172     if (*s == '}') {
173       if (brace_count == 0) {
174         return SkipValueRes::kFatalError;
175       }
176       --brace_count;
177       continue;
178     }
179   }
180   return SkipValueRes::kNeedsMoreData;
181 }
182 
SetOutAndReturn(const char * ptr,const char ** out)183 base::Status SetOutAndReturn(const char* ptr, const char** out) {
184   *out = ptr;
185   return base::OkStatus();
186 }
187 
188 }  // namespace
189 
ReadOneJsonDict(const char * start,const char * end,base::StringView * value,const char ** next)190 ReadDictRes ReadOneJsonDict(const char* start,
191                             const char* end,
192                             base::StringView* value,
193                             const char** next) {
194   int braces = 0;
195   int square_brackets = 0;
196   const char* dict_begin = nullptr;
197   bool in_string = false;
198   bool is_escaping = false;
199   for (const char* s = start; s < end; s++) {
200     if (isspace(*s) || *s == ',')
201       continue;
202     if (*s == '"' && !is_escaping) {
203       in_string = !in_string;
204       continue;
205     }
206     if (in_string) {
207       // If we're in a string and we see a backslash and the last character was
208       // not a backslash the next character is escaped:
209       is_escaping = *s == '\\' && !is_escaping;
210       // If we're currently parsing a string we should ignore otherwise special
211       // characters:
212       continue;
213     }
214     if (*s == '{') {
215       if (braces == 0)
216         dict_begin = s;
217       braces++;
218       continue;
219     }
220     if (*s == '}') {
221       if (braces <= 0)
222         return ReadDictRes::kEndOfTrace;
223       if (--braces > 0)
224         continue;
225       auto len = static_cast<size_t>((s + 1) - dict_begin);
226       *value = base::StringView(dict_begin, len);
227       *next = s + 1;
228       return ReadDictRes::kFoundDict;
229     }
230     if (*s == '[') {
231       square_brackets++;
232       continue;
233     }
234     if (*s == ']') {
235       if (square_brackets == 0) {
236         // We've reached the end of [traceEvents] array.
237         // There might be other top level keys in the json (e.g. metadata)
238         // after.
239         *next = s + 1;
240         return ReadDictRes::kEndOfArray;
241       }
242       square_brackets--;
243     }
244   }
245   return ReadDictRes::kNeedsMoreData;
246 }
247 
ReadOneJsonKey(const char * start,const char * end,std::string * key,const char ** next)248 ReadKeyRes ReadOneJsonKey(const char* start,
249                           const char* end,
250                           std::string* key,
251                           const char** next) {
252   enum class NextToken {
253     kStringOrEndOfDict,
254     kColon,
255     kValue,
256   };
257 
258   NextToken next_token = NextToken::kStringOrEndOfDict;
259   for (const char* s = start; s < end; s++) {
260     // Whitespace characters anywhere can be skipped.
261     if (isspace(*s))
262       continue;
263 
264     switch (next_token) {
265       case NextToken::kStringOrEndOfDict: {
266         // If we see a closing brace, that means we've reached the end of the
267         // wrapping dictionary.
268         if (*s == '}') {
269           *next = s + 1;
270           return ReadKeyRes::kEndOfDictionary;
271         }
272 
273         // If we see a comma separator, just ignore it.
274         if (*s == ',')
275           continue;
276 
277         auto res = ReadOneJsonString(s, end, key, &s);
278         if (res == ReadStringRes::kFatalError)
279           return ReadKeyRes::kFatalError;
280         if (res == ReadStringRes::kNeedsMoreData)
281           return ReadKeyRes::kNeedsMoreData;
282 
283         // We need to decrement from the pointer as the loop will increment
284         // it back up.
285         s--;
286         next_token = NextToken::kColon;
287         break;
288       }
289       case NextToken::kColon:
290         if (*s != ':')
291           return ReadKeyRes::kFatalError;
292         next_token = NextToken::kValue;
293         break;
294       case NextToken::kValue:
295         // Allowed value starting chars: [ { digit - "
296         // Also allowed: true, false, null. For simplicities sake, we only check
297         // against the first character as we're not trying to be super accurate.
298         if (*s == '[' || *s == '{' || isdigit(*s) || *s == '-' || *s == '"' ||
299             *s == 't' || *s == 'f' || *s == 'n') {
300           *next = s;
301           return ReadKeyRes::kFoundKey;
302         }
303         return ReadKeyRes::kFatalError;
304     }
305   }
306   return ReadKeyRes::kNeedsMoreData;
307 }
308 
ExtractValueForJsonKey(base::StringView dict,const std::string & key,std::optional<std::string> * value)309 base::Status ExtractValueForJsonKey(base::StringView dict,
310                                     const std::string& key,
311                                     std::optional<std::string>* value) {
312   PERFETTO_DCHECK(dict.size() >= 2);
313 
314   const char* start = dict.data();
315   const char* end = dict.data() + dict.size();
316 
317   enum ExtractValueState {
318     kBeforeDict,
319     kInsideDict,
320     kAfterDict,
321   };
322 
323   ExtractValueState state = kBeforeDict;
324   for (const char* s = start; s < end;) {
325     if (isspace(*s)) {
326       ++s;
327       continue;
328     }
329 
330     if (state == kBeforeDict) {
331       if (*s == '{') {
332         ++s;
333         state = kInsideDict;
334         continue;
335       }
336       return base::ErrStatus("Unexpected character before JSON dict: '%c'", *s);
337     }
338 
339     if (state == kAfterDict) {
340       return base::ErrStatus("Unexpected character after JSON dict: '%c'", *s);
341     }
342 
343     PERFETTO_DCHECK(state == kInsideDict);
344     PERFETTO_DCHECK(s < end);
345 
346     if (*s == '}') {
347       ++s;
348       state = kAfterDict;
349       continue;
350     }
351 
352     std::string current_key;
353     auto res = ReadOneJsonKey(s, end, &current_key, &s);
354     if (res == ReadKeyRes::kEndOfDictionary)
355       break;
356 
357     if (res == ReadKeyRes::kFatalError) {
358       return base::ErrStatus(
359           "Failure parsing JSON: encountered fatal error while parsing key for "
360           "value: '%s'",
361           FormatErrorContext(s, end).c_str());
362     }
363 
364     if (res == ReadKeyRes::kNeedsMoreData) {
365       return base::ErrStatus(
366           "Failure parsing JSON: partial JSON dictionary: '%s'",
367           FormatErrorContext(s, end).c_str());
368     }
369 
370     PERFETTO_DCHECK(res == ReadKeyRes::kFoundKey);
371 
372     if (*s == '[') {
373       return base::ErrStatus(
374           "Failure parsing JSON: unsupported JSON dictionary with array: '%s'",
375           FormatErrorContext(s, end).c_str());
376     }
377 
378     std::string value_str;
379     if (*s == '{') {
380       base::StringView dict_str;
381       ReadDictRes dict_res = ReadOneJsonDict(s, end, &dict_str, &s);
382       if (dict_res == ReadDictRes::kNeedsMoreData ||
383           dict_res == ReadDictRes::kEndOfArray ||
384           dict_res == ReadDictRes::kEndOfTrace) {
385         return base::ErrStatus(
386             "Failure parsing JSON: unable to parse dictionary: '%s'",
387             FormatErrorContext(s, end).c_str());
388       }
389       value_str = dict_str.ToStdString();
390     } else if (*s == '"') {
391       auto str_res = ReadOneJsonString(s, end, &value_str, &s);
392       if (str_res == ReadStringRes::kNeedsMoreData ||
393           str_res == ReadStringRes::kFatalError) {
394         return base::ErrStatus(
395             "Failure parsing JSON: unable to parse string: '%s",
396             FormatErrorContext(s, end).c_str());
397       }
398     } else {
399       const char* value_start = s;
400       const char* value_end = end;
401       for (; s < end; ++s) {
402         if (*s == ',' || isspace(*s) || *s == '}') {
403           value_end = s;
404           break;
405         }
406       }
407       value_str = std::string(value_start, value_end);
408     }
409 
410     if (key == current_key) {
411       *value = value_str;
412       return base::OkStatus();
413     }
414   }
415 
416   if (state != kAfterDict) {
417     return base::ErrStatus("Failure parsing JSON: malformed dictionary: '%s'",
418                            FormatErrorContext(start, end).c_str());
419   }
420 
421   *value = std::nullopt;
422   return base::OkStatus();
423 }
424 
ReadOneSystemTraceLine(const char * start,const char * end,std::string * line,const char ** next)425 ReadSystemLineRes ReadOneSystemTraceLine(const char* start,
426                                          const char* end,
427                                          std::string* line,
428                                          const char** next) {
429   bool is_escaping = false;
430   for (const char* s = start; s < end; s++) {
431     // If we get a quote character and we're not escaping, we are done with the
432     // system trace string.
433     if (*s == '"' && !is_escaping) {
434       *next = s + 1;
435       return ReadSystemLineRes::kEndOfSystemTrace;
436     }
437 
438     // If we are escaping n, that means this is a new line which is a delimiter
439     // for a system trace line.
440     if (*s == 'n' && is_escaping) {
441       *next = s + 1;
442       return ReadSystemLineRes::kFoundLine;
443     }
444 
445     base::Status status = AppendUnescapedCharacter(*s, is_escaping, line);
446     if (!status.ok())
447       return ReadSystemLineRes::kFatalError;
448 
449     // If we're in a string and we see a backslash and the last character was
450     // not a backslash the next character is escaped:
451     is_escaping = *s == '\\' && !is_escaping;
452   }
453   return ReadSystemLineRes::kNeedsMoreData;
454 }
455 
JsonTraceTokenizer(TraceProcessorContext * ctx)456 JsonTraceTokenizer::JsonTraceTokenizer(TraceProcessorContext* ctx)
457     : context_(ctx) {}
458 JsonTraceTokenizer::~JsonTraceTokenizer() = default;
459 
Parse(TraceBlobView blob)460 base::Status JsonTraceTokenizer::Parse(TraceBlobView blob) {
461   PERFETTO_DCHECK(json::IsJsonSupported());
462 
463   buffer_.insert(buffer_.end(), blob.data(), blob.data() + blob.size());
464   const char* buf = buffer_.data();
465   const char* next = buf;
466   const char* end = buf + buffer_.size();
467 
468   if (offset_ == 0) {
469     // Strip leading whitespace.
470     while (next != end && isspace(*next)) {
471       next++;
472     }
473     if (next == end) {
474       return base::ErrStatus(
475           "Failure parsing JSON: first chunk has only whitespace");
476     }
477 
478     // Trace could begin in any of these ways:
479     // {"traceEvents":[{
480     // { "traceEvents": [{
481     // [{
482     if (*next != '{' && *next != '[') {
483       return base::ErrStatus(
484           "Failure parsing JSON: first non-whitespace character is not [ or {");
485     }
486 
487     // Figure out the format of the JSON file based on the first non-whitespace
488     // character.
489     format_ = *next == '{' ? TraceFormat::kOuterDictionary
490                            : TraceFormat::kOnlyTraceEvents;
491 
492     // Skip the '[' or '{' character.
493     next++;
494 
495     // Set our current position based on the format of the trace.
496     position_ = format_ == TraceFormat::kOuterDictionary
497                     ? TracePosition::kDictionaryKey
498                     : TracePosition::kInsideTraceEventsArray;
499   }
500   RETURN_IF_ERROR(ParseInternal(next, end, &next));
501 
502   offset_ += static_cast<uint64_t>(next - buf);
503   buffer_.erase(buffer_.begin(), buffer_.begin() + (next - buf));
504   return base::OkStatus();
505 }
506 
ParseInternal(const char * start,const char * end,const char ** out)507 base::Status JsonTraceTokenizer::ParseInternal(const char* start,
508                                                const char* end,
509                                                const char** out) {
510   PERFETTO_DCHECK(json::IsJsonSupported());
511 
512   switch (position_) {
513     case TracePosition::kDictionaryKey:
514       return HandleDictionaryKey(start, end, out);
515     case TracePosition::kInsideSystemTraceEventsString:
516       return HandleSystemTraceEvent(start, end, out);
517     case TracePosition::kInsideTraceEventsArray:
518       return HandleTraceEvent(start, end, out);
519     case TracePosition::kEof: {
520       return start == end
521                  ? base::OkStatus()
522                  : base::ErrStatus(
523                        "Failure parsing JSON: tried to parse data after EOF");
524     }
525   }
526   PERFETTO_FATAL("For GCC");
527 }
528 
HandleTraceEvent(const char * start,const char * end,const char ** out)529 base::Status JsonTraceTokenizer::HandleTraceEvent(const char* start,
530                                                   const char* end,
531                                                   const char** out) {
532   const char* next = start;
533   while (next < end) {
534     base::StringView unparsed;
535     switch (ReadOneJsonDict(next, end, &unparsed, &next)) {
536       case ReadDictRes::kEndOfArray: {
537         if (format_ == TraceFormat::kOnlyTraceEvents) {
538           position_ = TracePosition::kEof;
539           return SetOutAndReturn(next, out);
540         }
541 
542         position_ = TracePosition::kDictionaryKey;
543         return ParseInternal(next, end, out);
544       }
545       case ReadDictRes::kEndOfTrace:
546         position_ = TracePosition::kEof;
547         return SetOutAndReturn(next, out);
548       case ReadDictRes::kNeedsMoreData:
549         return SetOutAndReturn(next, out);
550       case ReadDictRes::kFoundDict:
551         break;
552     }
553 
554     // Metadata events may omit ts. In all other cases error:
555     std::optional<std::string> opt_raw_ph;
556     RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "ph", &opt_raw_ph));
557     if (PERFETTO_UNLIKELY(opt_raw_ph == "P")) {
558       RETURN_IF_ERROR(ParseV8SampleEvent(unparsed));
559       continue;
560     }
561 
562     std::optional<std::string> opt_raw_ts;
563     RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "ts", &opt_raw_ts));
564     std::optional<int64_t> opt_ts =
565         opt_raw_ts ? json::CoerceToTs(*opt_raw_ts) : std::nullopt;
566     std::optional<std::string> opt_raw_dur;
567     RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "dur", &opt_raw_dur));
568     std::optional<int64_t> opt_dur =
569         opt_raw_dur ? json::CoerceToTs(*opt_raw_dur) : std::nullopt;
570     int64_t ts = 0;
571     if (opt_ts.has_value()) {
572       ts = opt_ts.value();
573     } else {
574       if (!opt_raw_ph || *opt_raw_ph != "M") {
575         context_->storage->IncrementStats(stats::json_tokenizer_failure);
576         continue;
577       }
578     }
579     context_->sorter->PushJsonValue(ts, unparsed.ToStdString(), opt_dur);
580   }
581   return SetOutAndReturn(next, out);
582 }
583 
ParseV8SampleEvent(base::StringView unparsed)584 base::Status JsonTraceTokenizer::ParseV8SampleEvent(base::StringView unparsed) {
585   auto opt_evt = json::ParseJsonString(unparsed);
586   if (!opt_evt) {
587     return base::OkStatus();
588   }
589   const auto& evt = *opt_evt;
590   std::optional<uint32_t> id = base::StringToUInt32(evt["id"].asString(), 16);
591   if (!id) {
592     return base::OkStatus();
593   }
594   uint32_t pid = evt["pid"].asUInt();
595   uint32_t tid = evt["tid"].asUInt();
596   const auto& val = evt["args"]["data"];
597   if (val.isMember("startTime")) {
598     context_->legacy_v8_cpu_profile_tracker->SetStartTsForSessionAndPid(
599         *id, pid, val["startTime"].asInt64() * 1000);
600     return base::OkStatus();
601   }
602   const auto& profile = val["cpuProfile"];
603   for (const auto& n : profile["nodes"]) {
604     uint32_t node_id = n["id"].asUInt();
605     std::optional<uint32_t> parent_node_id =
606         n.isMember("parent") ? std::make_optional(n["parent"].asUInt())
607                              : std::nullopt;
608     const auto& frame = n["callFrame"];
609     base::StringView url =
610         frame.isMember("url") ? frame["url"].asCString() : base::StringView();
611     base::StringView function_name = frame["functionName"].asCString();
612     base::Status status = context_->legacy_v8_cpu_profile_tracker->AddCallsite(
613         *id, pid, node_id, parent_node_id, url, function_name);
614     if (!status.ok()) {
615       context_->storage->IncrementStats(
616           stats::legacy_v8_cpu_profile_invalid_callsite);
617       continue;
618     }
619   }
620   const auto& samples = profile["samples"];
621   const auto& deltas = val["timeDeltas"];
622   if (samples.size() != deltas.size()) {
623     return base::ErrStatus(
624         "v8 legacy profile: samples and timestamps do not have same size");
625   }
626   for (uint32_t i = 0; i < samples.size(); ++i) {
627     ASSIGN_OR_RETURN(int64_t ts,
628                      context_->legacy_v8_cpu_profile_tracker->AddDeltaAndGetTs(
629                          *id, pid, deltas[i].asInt64() * 1000));
630     context_->sorter->PushLegacyV8CpuProfileEvent(ts, *id, pid, tid,
631                                                   samples[i].asUInt());
632   }
633   return base::OkStatus();
634 }
635 
HandleDictionaryKey(const char * start,const char * end,const char ** out)636 base::Status JsonTraceTokenizer::HandleDictionaryKey(const char* start,
637                                                      const char* end,
638                                                      const char** out) {
639   if (format_ != TraceFormat::kOuterDictionary) {
640     return base::ErrStatus(
641         "Failure parsing JSON: illegal format when parsing dictionary key");
642   }
643 
644   const char* next = start;
645   std::string key;
646   switch (ReadOneJsonKey(start, end, &key, &next)) {
647     case ReadKeyRes::kFatalError:
648       return base::ErrStatus(
649           "Failure parsing JSON: encountered fatal error while parsing key");
650     case ReadKeyRes::kEndOfDictionary:
651       position_ = TracePosition::kEof;
652       return SetOutAndReturn(next, out);
653     case ReadKeyRes::kNeedsMoreData:
654       // If we didn't manage to read the key we need to set |out| to |start|
655       // (*not* |next|) to keep the state machine happy.
656       return SetOutAndReturn(start, out);
657     case ReadKeyRes::kFoundKey:
658       break;
659   }
660 
661   // ReadOneJsonKey should ensure that the first character of the value is
662   // available.
663   PERFETTO_CHECK(next < end);
664 
665   if (key == "traceEvents") {
666     // Skip the [ character opening the array.
667     if (*next != '[') {
668       return base::ErrStatus(
669           "Failure parsing JSON: traceEvents is not an array.");
670     }
671     next++;
672 
673     position_ = TracePosition::kInsideTraceEventsArray;
674     return ParseInternal(next, end, out);
675   }
676 
677   if (key == "systemTraceEvents") {
678     // Skip the " character opening the string.
679     if (*next != '"') {
680       return base::ErrStatus(
681           "Failure parsing JSON: systemTraceEvents is not an string.");
682     }
683     next++;
684 
685     position_ = TracePosition::kInsideSystemTraceEventsString;
686     return ParseInternal(next, end, out);
687   }
688 
689   if (key == "displayTimeUnit") {
690     std::string time_unit;
691     auto result = ReadOneJsonString(next, end, &time_unit, &next);
692     if (result == ReadStringRes::kFatalError)
693       return base::ErrStatus("Could not parse displayTimeUnit");
694     context_->storage->IncrementStats(stats::json_display_time_unit);
695     return ParseInternal(next, end, out);
696   }
697 
698   // If we don't know the key for this JSON value just skip it.
699   switch (SkipOneJsonValue(next, end, &next)) {
700     case SkipValueRes::kFatalError:
701       return base::ErrStatus(
702           "Failure parsing JSON: error while parsing value for key %s",
703           key.c_str());
704     case SkipValueRes::kNeedsMoreData:
705       // If we didn't manage to read the key *and* the value, we need to set
706       // |out| to |start| (*not* |next|) to keep the state machine happy (as
707       // we expect to always see a key before the value).
708       return SetOutAndReturn(start, out);
709     case SkipValueRes::kEndOfValue:
710       return ParseInternal(next, end, out);
711   }
712   PERFETTO_FATAL("For GCC");
713 }
714 
HandleSystemTraceEvent(const char * start,const char * end,const char ** out)715 base::Status JsonTraceTokenizer::HandleSystemTraceEvent(const char* start,
716                                                         const char* end,
717                                                         const char** out) {
718   if (format_ != TraceFormat::kOuterDictionary) {
719     return base::ErrStatus(
720         "Failure parsing JSON: illegal format when parsing system events");
721   }
722 
723   const char* next = start;
724   while (next < end) {
725     std::string raw_line;
726     switch (ReadOneSystemTraceLine(next, end, &raw_line, &next)) {
727       case ReadSystemLineRes::kFatalError:
728         return base::ErrStatus(
729             "Failure parsing JSON: encountered fatal error while parsing "
730             "event inside trace event string");
731       case ReadSystemLineRes::kNeedsMoreData:
732         return SetOutAndReturn(next, out);
733       case ReadSystemLineRes::kEndOfSystemTrace:
734         position_ = TracePosition::kDictionaryKey;
735         return ParseInternal(next, end, out);
736       case ReadSystemLineRes::kFoundLine:
737         break;
738     }
739 
740     if (base::StartsWith(raw_line, "#") || raw_line.empty())
741       continue;
742 
743     SystraceLine line;
744     RETURN_IF_ERROR(systrace_line_tokenizer_.Tokenize(raw_line, &line));
745     context_->sorter->PushSystraceLine(std::move(line));
746   }
747   return SetOutAndReturn(next, out);
748 }
749 
NotifyEndOfFile()750 base::Status JsonTraceTokenizer::NotifyEndOfFile() {
751   return position_ == TracePosition::kEof ||
752                  (position_ == TracePosition::kInsideTraceEventsArray &&
753                   format_ == TraceFormat::kOnlyTraceEvents)
754              ? base::OkStatus()
755              : base::ErrStatus("JSON trace file is incomplete");
756 }
757 
758 }  // namespace perfetto::trace_processor
759