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, ¤t_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