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