blob: be9492b6173ff80f6337f3d7d7b994455ba95bb9 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/importers/json/json_utils.h"
#include "perfetto/base/build_config.h"
#include <limits>
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
#include <json/reader.h>
#include "perfetto/ext/base/string_utils.h"
#endif
namespace perfetto {
namespace trace_processor {
namespace json {
bool IsJsonSupported() {
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
return true;
#else
return false;
#endif
}
std::optional<int64_t> CoerceToTs(const Json::Value& value) {
PERFETTO_DCHECK(IsJsonSupported());
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
switch (static_cast<size_t>(value.type())) {
case Json::realValue:
return static_cast<int64_t>(value.asDouble() * 1000.0);
case Json::uintValue:
case Json::intValue:
return value.asInt64() * 1000;
case Json::stringValue:
return CoerceToTs(value.asString());
default:
return std::nullopt;
}
#else
perfetto::base::ignore_result(value);
return std::nullopt;
#endif
}
std::optional<int64_t> CoerceToTs(const std::string& s) {
PERFETTO_DCHECK(IsJsonSupported());
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
size_t lhs_end = std::min<size_t>(s.find('.'), s.size());
size_t rhs_start = std::min<size_t>(lhs_end + 1, s.size());
std::optional<int64_t> lhs = base::StringToInt64(s.substr(0, lhs_end));
std::optional<double> rhs =
base::StringToDouble("0." + s.substr(rhs_start, std::string::npos));
if ((!lhs.has_value() && lhs_end > 0) ||
(!rhs.has_value() && rhs_start < s.size())) {
return std::nullopt;
}
return lhs.value_or(0) * 1000 +
static_cast<int64_t>(rhs.value_or(0) * 1000.0);
#else
perfetto::base::ignore_result(s);
return std::nullopt;
#endif
}
std::optional<int64_t> CoerceToInt64(const Json::Value& value) {
PERFETTO_DCHECK(IsJsonSupported());
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
switch (static_cast<size_t>(value.type())) {
case Json::realValue:
case Json::uintValue:
return static_cast<int64_t>(value.asUInt64());
case Json::intValue:
return value.asInt64();
case Json::stringValue: {
std::string s = value.asString();
char* end;
int64_t n = strtoll(s.c_str(), &end, 10);
if (end != s.data() + s.size())
return std::nullopt;
return n;
}
default:
return std::nullopt;
}
#else
perfetto::base::ignore_result(value);
return std::nullopt;
#endif
}
std::optional<uint32_t> CoerceToUint32(const Json::Value& value) {
PERFETTO_DCHECK(IsJsonSupported());
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
std::optional<int64_t> result = CoerceToInt64(value);
if (!result.has_value())
return std::nullopt;
int64_t n = result.value();
if (n < 0 || n > std::numeric_limits<uint32_t>::max())
return std::nullopt;
return static_cast<uint32_t>(n);
#else
perfetto::base::ignore_result(value);
return std::nullopt;
#endif
}
std::optional<Json::Value> ParseJsonString(base::StringView raw_string) {
PERFETTO_DCHECK(IsJsonSupported());
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
Json::CharReaderBuilder b;
auto reader = std::unique_ptr<Json::CharReader>(b.newCharReader());
Json::Value value;
const char* begin = raw_string.data();
return reader->parse(begin, begin + raw_string.size(), &value, nullptr)
? std::make_optional(std::move(value))
: std::nullopt;
#else
perfetto::base::ignore_result(raw_string);
return std::nullopt;
#endif
}
bool AddJsonValueToArgs(const Json::Value& value,
base::StringView flat_key,
base::StringView key,
TraceStorage* storage,
ArgsTracker::BoundInserter* inserter) {
PERFETTO_DCHECK(IsJsonSupported());
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
if (value.isObject()) {
auto it = value.begin();
bool inserted = false;
for (; it != value.end(); ++it) {
std::string child_name = it.name();
std::string child_flat_key = flat_key.ToStdString() + "." + child_name;
std::string child_key = key.ToStdString() + "." + child_name;
inserted |=
AddJsonValueToArgs(*it, base::StringView(child_flat_key),
base::StringView(child_key), storage, inserter);
}
return inserted;
}
if (value.isArray()) {
auto it = value.begin();
bool inserted_any = false;
std::string array_key = key.ToStdString();
StringId array_key_id = storage->InternString(key);
for (; it != value.end(); ++it) {
size_t array_index = inserter->GetNextArrayEntryIndex(array_key_id);
std::string child_key =
array_key + "[" + std::to_string(array_index) + "]";
bool inserted = AddJsonValueToArgs(
*it, flat_key, base::StringView(child_key), storage, inserter);
if (inserted)
inserter->IncrementArrayEntryIndex(array_key_id);
inserted_any |= inserted;
}
return inserted_any;
}
// Leaf value.
auto flat_key_id = storage->InternString(flat_key);
auto key_id = storage->InternString(key);
switch (value.type()) {
case Json::ValueType::nullValue:
break;
case Json::ValueType::intValue:
inserter->AddArg(flat_key_id, key_id, Variadic::Integer(value.asInt64()));
return true;
case Json::ValueType::uintValue:
inserter->AddArg(flat_key_id, key_id,
Variadic::UnsignedInteger(value.asUInt64()));
return true;
case Json::ValueType::realValue:
inserter->AddArg(flat_key_id, key_id, Variadic::Real(value.asDouble()));
return true;
case Json::ValueType::stringValue:
inserter->AddArg(flat_key_id, key_id,
Variadic::String(storage->InternString(
base::StringView(value.asString()))));
return true;
case Json::ValueType::booleanValue:
inserter->AddArg(flat_key_id, key_id, Variadic::Boolean(value.asBool()));
return true;
case Json::ValueType::objectValue:
case Json::ValueType::arrayValue:
PERFETTO_FATAL("Non-leaf types handled above");
break;
}
return false;
#else
perfetto::base::ignore_result(value);
perfetto::base::ignore_result(flat_key);
perfetto::base::ignore_result(key);
perfetto::base::ignore_result(storage);
perfetto::base::ignore_result(inserter);
return false;
#endif
}
} // namespace json
} // namespace trace_processor
} // namespace perfetto