• 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/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