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/json_trace_tokenizer.h"
18
19 #include <json/reader.h>
20 #include <json/value.h>
21
22 #include "src/trace_processor/json_trace_utils.h"
23 #include "src/trace_processor/stats.h"
24 #include "src/trace_processor/trace_blob_view.h"
25 #include "src/trace_processor/trace_sorter.h"
26
27 namespace perfetto {
28 namespace trace_processor {
29
30 // Parses at most one JSON dictionary and returns a pointer to the end of it,
31 // or nullptr if no dict could be detected.
32 // This is to avoid decoding the full trace in memory and reduce heap traffic.
33 // E.g. input: { a:1 b:{ c:2, d:{ e:3 } } } , { a:4, ... },
34 // output: [ only this is parsed ] ^return value points here.
ReadOneJsonDict(const char * start,const char * end,Json::Value * value,const char ** next)35 ReadDictRes ReadOneJsonDict(const char* start,
36 const char* end,
37 Json::Value* value,
38 const char** next) {
39 int braces = 0;
40 int square_brackets = 0;
41 const char* dict_begin = nullptr;
42 bool in_string = false;
43 bool is_escaping = false;
44 for (const char* s = start; s < end; s++) {
45 if (isspace(*s) || *s == ',')
46 continue;
47 if (*s == '"' && !is_escaping) {
48 in_string = !in_string;
49 continue;
50 }
51 if (in_string) {
52 // If we're in a string and we see a backslash and the last character was
53 // not a backslash the next character is escaped:
54 is_escaping = *s == '\\' && !is_escaping;
55 // If we're currently parsing a string we should ignore otherwise special
56 // characters:
57 continue;
58 }
59 if (*s == '{') {
60 if (braces == 0)
61 dict_begin = s;
62 braces++;
63 continue;
64 }
65 if (*s == '}') {
66 if (braces <= 0)
67 return kEndOfTrace;
68 if (--braces > 0)
69 continue;
70 Json::Reader reader;
71 if (!reader.parse(dict_begin, s + 1, *value, /*collectComments=*/false)) {
72 PERFETTO_ELOG("JSON error: %s",
73 reader.getFormattedErrorMessages().c_str());
74 return kFatalError;
75 }
76 *next = s + 1;
77 return kFoundDict;
78 }
79 if (*s == '[') {
80 square_brackets++;
81 continue;
82 }
83 if (*s == ']') {
84 if (square_brackets == 0) {
85 // We've reached the end of [traceEvents] array.
86 // There might be other top level keys in the json (e.g. metadata)
87 // after.
88 // TODO(dproy): Handle trace metadata importing.
89 return kEndOfTrace;
90 }
91 square_brackets--;
92 }
93 }
94 return kNeedsMoreData;
95 }
96
JsonTraceTokenizer(TraceProcessorContext * ctx)97 JsonTraceTokenizer::JsonTraceTokenizer(TraceProcessorContext* ctx)
98 : context_(ctx) {}
99 JsonTraceTokenizer::~JsonTraceTokenizer() = default;
100
Parse(std::unique_ptr<uint8_t[]> data,size_t size)101 bool JsonTraceTokenizer::Parse(std::unique_ptr<uint8_t[]> data, size_t size) {
102 buffer_.insert(buffer_.end(), data.get(), data.get() + size);
103 const char* buf = buffer_.data();
104 const char* next = buf;
105 const char* end = buf + buffer_.size();
106
107 if (offset_ == 0) {
108 // Trace could begin in any of these ways:
109 // {"traceEvents":[{
110 // { "traceEvents": [{
111 // [{
112 // Skip up to the first '['
113 while (next != end && *next != '[') {
114 next++;
115 }
116 if (next == end) {
117 PERFETTO_ELOG("Failed to parse: first chunk missing opening [");
118 return false;
119 }
120 next++;
121 }
122
123 auto* trace_sorter = context_->sorter.get();
124
125 while (next < end) {
126 std::unique_ptr<Json::Value> value(new Json::Value());
127 const auto res = ReadOneJsonDict(next, end, value.get(), &next);
128 if (res == kFatalError)
129 return false;
130 if (res == kEndOfTrace || res == kNeedsMoreData)
131 break;
132
133 base::Optional<int64_t> opt_ts =
134 json_trace_utils::CoerceToNs((*value)["ts"]);
135 if (!opt_ts.has_value()) {
136 context_->storage->IncrementStats(stats::json_tokenizer_failure);
137 continue;
138 }
139 int64_t ts = opt_ts.value();
140
141 trace_sorter->PushJsonValue(ts, std::move(value));
142 }
143
144 offset_ += static_cast<uint64_t>(next - buf);
145 buffer_.erase(buffer_.begin(), buffer_.begin() + (next - buf));
146 return true;
147 }
148
149 } // namespace trace_processor
150 } // namespace perfetto
151