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