1 /*
2 * Copyright (C) 2020 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_utils.h"
18
19 #include "perfetto/base/build_config.h"
20
21 #include <limits>
22
23 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
24 #include <json/reader.h>
25 #include "perfetto/ext/base/string_utils.h"
26 #endif
27
28 namespace perfetto {
29 namespace trace_processor {
30 namespace json {
31
CoerceToTs(const Json::Value & value)32 std::optional<int64_t> CoerceToTs(const Json::Value& value) {
33 PERFETTO_DCHECK(IsJsonSupported());
34
35 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
36 switch (static_cast<size_t>(value.type())) {
37 case Json::realValue:
38 return static_cast<int64_t>(value.asDouble() * 1000.0);
39 case Json::uintValue:
40 case Json::intValue:
41 return value.asInt64() * 1000;
42 case Json::stringValue:
43 return CoerceToTs(value.asString());
44 default:
45 return std::nullopt;
46 }
47 #else
48 perfetto::base::ignore_result(value);
49 return std::nullopt;
50 #endif
51 }
52
CoerceToTs(const std::string & s)53 std::optional<int64_t> CoerceToTs(const std::string& s) {
54 PERFETTO_DCHECK(IsJsonSupported());
55
56 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
57 // 's' is formatted as a JSON Number, in microseconds
58 // goal: reformat 's' to be as an int, in nanoseconds
59 std::string s_as_ns = s;
60
61 // detect and remove scientific notation's exponents
62 int32_t exp_shift = 0;
63 if (size_t exp_start = s.find_first_of("eE");
64 exp_start != std::string::npos) {
65 const std::string exp_s = s.substr(exp_start + 1, s.size());
66 const std::optional<int32_t> exp = base::StringToInt32(exp_s);
67 if (!exp.has_value()) {
68 return std::nullopt;
69 }
70 s_as_ns.erase(exp_start);
71 exp_shift = *exp;
72 }
73
74 // detect and remove decimal separator
75 size_t int_size = s_as_ns.size();
76 if (size_t frac_start = s.find('.'); frac_start != std::string::npos) {
77 s_as_ns.erase(frac_start, 1);
78 int_size = frac_start;
79 }
80
81 // expand or shrink to the new size
82 constexpr int us_to_ns_shift = 3;
83 const size_t s_as_ns_size = size_t(
84 std::max<ptrdiff_t>(1, ptrdiff_t(int_size) + exp_shift + us_to_ns_shift));
85 s_as_ns.resize(s_as_ns_size, '0'); // pads or truncates
86
87 return base::StringToInt64(s_as_ns);
88 #else
89 perfetto::base::ignore_result(s);
90 return std::nullopt;
91 #endif
92 }
93
CoerceToInt64(const Json::Value & value)94 std::optional<int64_t> CoerceToInt64(const Json::Value& value) {
95 PERFETTO_DCHECK(IsJsonSupported());
96
97 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
98 switch (static_cast<size_t>(value.type())) {
99 case Json::realValue:
100 case Json::uintValue:
101 return static_cast<int64_t>(value.asUInt64());
102 case Json::intValue:
103 return value.asInt64();
104 case Json::stringValue: {
105 std::string s = value.asString();
106 char* end;
107 int64_t n = strtoll(s.c_str(), &end, 10);
108 if (end != s.data() + s.size())
109 return std::nullopt;
110 return n;
111 }
112 default:
113 return std::nullopt;
114 }
115 #else
116 perfetto::base::ignore_result(value);
117 return std::nullopt;
118 #endif
119 }
120
CoerceToUint32(const Json::Value & value)121 std::optional<uint32_t> CoerceToUint32(const Json::Value& value) {
122 PERFETTO_DCHECK(IsJsonSupported());
123
124 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
125 std::optional<int64_t> result = CoerceToInt64(value);
126 if (!result.has_value())
127 return std::nullopt;
128 int64_t n = result.value();
129 if (n < 0 || n > std::numeric_limits<uint32_t>::max())
130 return std::nullopt;
131 return static_cast<uint32_t>(n);
132 #else
133 perfetto::base::ignore_result(value);
134 return std::nullopt;
135 #endif
136 }
137
ParseJsonString(base::StringView raw_string)138 std::optional<Json::Value> ParseJsonString(base::StringView raw_string) {
139 PERFETTO_DCHECK(IsJsonSupported());
140
141 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
142 Json::CharReaderBuilder b;
143 auto reader = std::unique_ptr<Json::CharReader>(b.newCharReader());
144
145 Json::Value value;
146 const char* begin = raw_string.data();
147 return reader->parse(begin, begin + raw_string.size(), &value, nullptr)
148 ? std::make_optional(std::move(value))
149 : std::nullopt;
150 #else
151 perfetto::base::ignore_result(raw_string);
152 return std::nullopt;
153 #endif
154 }
155
AddJsonValueToArgs(const Json::Value & value,base::StringView flat_key,base::StringView key,TraceStorage * storage,ArgsTracker::BoundInserter * inserter)156 bool AddJsonValueToArgs(const Json::Value& value,
157 base::StringView flat_key,
158 base::StringView key,
159 TraceStorage* storage,
160 ArgsTracker::BoundInserter* inserter) {
161 PERFETTO_DCHECK(IsJsonSupported());
162
163 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
164 if (value.isObject()) {
165 auto it = value.begin();
166 bool inserted = false;
167 for (; it != value.end(); ++it) {
168 std::string child_name = it.name();
169 std::string child_flat_key = flat_key.ToStdString() + "." + child_name;
170 std::string child_key = key.ToStdString() + "." + child_name;
171 inserted |=
172 AddJsonValueToArgs(*it, base::StringView(child_flat_key),
173 base::StringView(child_key), storage, inserter);
174 }
175 return inserted;
176 }
177
178 if (value.isArray()) {
179 auto it = value.begin();
180 bool inserted_any = false;
181 std::string array_key = key.ToStdString();
182 StringId array_key_id = storage->InternString(key);
183 for (; it != value.end(); ++it) {
184 size_t array_index = inserter->GetNextArrayEntryIndex(array_key_id);
185 std::string child_key =
186 array_key + "[" + std::to_string(array_index) + "]";
187 bool inserted = AddJsonValueToArgs(
188 *it, flat_key, base::StringView(child_key), storage, inserter);
189 if (inserted)
190 inserter->IncrementArrayEntryIndex(array_key_id);
191 inserted_any |= inserted;
192 }
193 return inserted_any;
194 }
195
196 // Leaf value.
197 auto flat_key_id = storage->InternString(flat_key);
198 auto key_id = storage->InternString(key);
199
200 switch (value.type()) {
201 case Json::ValueType::nullValue:
202 break;
203 case Json::ValueType::intValue:
204 inserter->AddArg(flat_key_id, key_id, Variadic::Integer(value.asInt64()));
205 return true;
206 case Json::ValueType::uintValue:
207 inserter->AddArg(flat_key_id, key_id,
208 Variadic::UnsignedInteger(value.asUInt64()));
209 return true;
210 case Json::ValueType::realValue:
211 inserter->AddArg(flat_key_id, key_id, Variadic::Real(value.asDouble()));
212 return true;
213 case Json::ValueType::stringValue:
214 inserter->AddArg(flat_key_id, key_id,
215 Variadic::String(storage->InternString(
216 base::StringView(value.asString()))));
217 return true;
218 case Json::ValueType::booleanValue:
219 inserter->AddArg(flat_key_id, key_id, Variadic::Boolean(value.asBool()));
220 return true;
221 case Json::ValueType::objectValue:
222 case Json::ValueType::arrayValue:
223 PERFETTO_FATAL("Non-leaf types handled above");
224 break;
225 }
226 return false;
227 #else
228 perfetto::base::ignore_result(value);
229 perfetto::base::ignore_result(flat_key);
230 perfetto::base::ignore_result(key);
231 perfetto::base::ignore_result(storage);
232 perfetto::base::ignore_result(inserter);
233 return false;
234 #endif
235 }
236
237 } // namespace json
238 } // namespace trace_processor
239 } // namespace perfetto
240