blob: 0c2792efa97768ba603417c6c4e71b5f7d1cd000 [file] [log] [blame]
/*
* Copyright (C) 2021 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/util/debug_annotation_parser.h"
#include "perfetto/base/build_config.h"
#include "src/trace_processor/util/interned_message_view.h"
#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
namespace perfetto {
namespace trace_processor {
namespace util {
namespace {
std::string SanitizeDebugAnnotationName(const std::string& raw_name) {
std::string result = raw_name;
std::replace(result.begin(), result.end(), '.', '_');
std::replace(result.begin(), result.end(), '[', '_');
std::replace(result.begin(), result.end(), ']', '_');
return result;
}
bool IsJsonSupported() {
#if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
return true;
#else
return false;
#endif
}
} // namespace
DebugAnnotationParser::DebugAnnotationParser(ProtoToArgsParser& parser)
: proto_to_args_parser_(parser) {}
base::Status DebugAnnotationParser::ParseDebugAnnotationName(
protos::pbzero::DebugAnnotation::Decoder& annotation,
ProtoToArgsParser::Delegate& delegate,
std::string& result) {
uint64_t name_iid = annotation.name_iid();
if (PERFETTO_LIKELY(name_iid)) {
auto* decoder = delegate.GetInternedMessage(
protos::pbzero::InternedData::kDebugAnnotationNames, name_iid);
if (!decoder)
return base::ErrStatus("Debug annotation with invalid name_iid");
result = SanitizeDebugAnnotationName(decoder->name().ToStdString());
} else if (annotation.has_name()) {
result = SanitizeDebugAnnotationName(annotation.name().ToStdString());
} else {
return base::ErrStatus("Debug annotation without name");
}
return base::OkStatus();
}
DebugAnnotationParser::ParseResult
DebugAnnotationParser::ParseDebugAnnotationValue(
protos::pbzero::DebugAnnotation::Decoder& annotation,
ProtoToArgsParser::Delegate& delegate,
const ProtoToArgsParser::Key& context_name) {
if (annotation.has_bool_value()) {
delegate.AddBoolean(context_name, annotation.bool_value());
} else if (annotation.has_uint_value()) {
delegate.AddUnsignedInteger(context_name, annotation.uint_value());
} else if (annotation.has_int_value()) {
delegate.AddInteger(context_name, annotation.int_value());
} else if (annotation.has_double_value()) {
delegate.AddDouble(context_name, annotation.double_value());
} else if (annotation.has_string_value()) {
delegate.AddString(context_name, annotation.string_value());
} else if (annotation.has_string_value_iid()) {
auto* decoder = delegate.GetInternedMessage(
protos::pbzero::InternedData::kDebugAnnotationStringValues,
annotation.string_value_iid());
if (!decoder) {
return {base::ErrStatus("Debug annotation with invalid string_value_iid"),
false};
}
delegate.AddString(context_name, decoder->str().ToStdString());
} else if (annotation.has_pointer_value()) {
delegate.AddPointer(context_name, reinterpret_cast<const void*>(
annotation.pointer_value()));
} else if (annotation.has_dict_entries()) {
bool added_entry = false;
for (auto it = annotation.dict_entries(); it; ++it) {
protos::pbzero::DebugAnnotation::Decoder key_value(*it);
std::string key;
base::Status key_parse_result =
ParseDebugAnnotationName(key_value, delegate, key);
if (!key_parse_result.ok())
return {key_parse_result, added_entry};
auto nested_key = proto_to_args_parser_.EnterDictionary(key);
ParseResult value_parse_result =
ParseDebugAnnotationValue(key_value, delegate, nested_key.key());
added_entry |= value_parse_result.added_entry;
if (!value_parse_result.status.ok())
return {value_parse_result.status, added_entry};
}
} else if (annotation.has_array_values()) {
size_t index = delegate.GetArrayEntryIndex(context_name.key);
bool added_entry = false;
for (auto it = annotation.array_values(); it; ++it) {
std::string array_key = context_name.key;
protos::pbzero::DebugAnnotation::Decoder value(*it);
auto nested_key = proto_to_args_parser_.EnterArray(index);
ParseResult value_parse_result =
ParseDebugAnnotationValue(value, delegate, nested_key.key());
if (value_parse_result.added_entry) {
index = delegate.IncrementArrayEntryIndex(array_key);
added_entry = true;
}
if (!value_parse_result.status.ok())
return {value_parse_result.status, added_entry};
}
} else if (annotation.has_legacy_json_value()) {
if (!IsJsonSupported())
return {base::ErrStatus("Ignoring legacy_json_value (no json support)"),
false};
bool added_entry =
delegate.AddJson(context_name, annotation.legacy_json_value());
return {base::OkStatus(), added_entry};
} else if (annotation.has_nested_value()) {
return ParseNestedValueArgs(annotation.nested_value(), context_name,
delegate);
} else if (annotation.has_proto_value()) {
std::string type_name;
if (annotation.has_proto_type_name()) {
type_name = annotation.proto_type_name().ToStdString();
} else if (annotation.has_proto_type_name_iid()) {
auto* interned_name = delegate.GetInternedMessage(
protos::pbzero::InternedData::kDebugAnnotationValueTypeNames,
annotation.proto_type_name_iid());
if (!interned_name)
return {base::ErrStatus("Interned proto type name not found"), false};
type_name = interned_name->name().ToStdString();
} else {
return {base::ErrStatus("DebugAnnotation has proto_value, but doesn't "
"have proto type name"),
false};
}
return {proto_to_args_parser_.ParseMessage(annotation.proto_value(),
type_name, nullptr, delegate),
true};
} else {
return {base::OkStatus(), /*added_entry=*/false};
}
return {base::OkStatus(), /*added_entry=*/true};
}
// static
base::Status DebugAnnotationParser::Parse(
protozero::ConstBytes data,
ProtoToArgsParser::Delegate& delegate) {
protos::pbzero::DebugAnnotation::Decoder annotation(data);
std::string name;
base::Status name_parse_result =
ParseDebugAnnotationName(annotation, delegate, name);
if (!name_parse_result.ok())
return name_parse_result;
auto context = proto_to_args_parser_.EnterDictionary(name);
return ParseDebugAnnotationValue(annotation, delegate, context.key()).status;
}
DebugAnnotationParser::ParseResult DebugAnnotationParser::ParseNestedValueArgs(
protozero::ConstBytes nested_value,
const ProtoToArgsParser::Key& context_name,
ProtoToArgsParser::Delegate& delegate) {
protos::pbzero::DebugAnnotation::NestedValue::Decoder value(nested_value);
switch (value.nested_type()) {
case protos::pbzero::DebugAnnotation::NestedValue::UNSPECIFIED: {
// Leaf value.
if (value.has_bool_value()) {
delegate.AddBoolean(context_name, value.bool_value());
return {base::OkStatus(), true};
}
if (value.has_int_value()) {
delegate.AddInteger(context_name, value.int_value());
return {base::OkStatus(), true};
}
if (value.has_double_value()) {
delegate.AddDouble(context_name, value.double_value());
return {base::OkStatus(), true};
}
if (value.has_string_value()) {
delegate.AddString(context_name, value.string_value());
return {base::OkStatus(), true};
}
return {base::OkStatus(), false};
}
case protos::pbzero::DebugAnnotation::NestedValue::DICT: {
bool added_entry = false;
auto key_it = value.dict_keys();
auto value_it = value.dict_values();
for (; key_it && value_it; ++key_it, ++value_it) {
std::string child_name =
SanitizeDebugAnnotationName((*key_it).ToStdString());
auto nested_key = proto_to_args_parser_.EnterDictionary(child_name);
ParseResult result =
ParseNestedValueArgs(*value_it, nested_key.key(), delegate);
added_entry |= result.added_entry;
if (!result.status.ok())
return {result.status, added_entry};
}
return {base::OkStatus(), true};
}
case protos::pbzero::DebugAnnotation::NestedValue::ARRAY: {
std::string array_key = context_name.key;
size_t array_index = delegate.GetArrayEntryIndex(context_name.key);
bool added_entry = false;
for (auto value_it = value.array_values(); value_it; ++value_it) {
auto nested_key = proto_to_args_parser_.EnterArray(array_index);
ParseResult result =
ParseNestedValueArgs(*value_it, nested_key.key(), delegate);
if (result.added_entry) {
++array_index;
delegate.IncrementArrayEntryIndex(array_key);
added_entry = true;
}
if (!result.status.ok())
return {result.status, added_entry};
}
return {base::OkStatus(), added_entry};
}
}
return {base::OkStatus(), false};
}
} // namespace util
} // namespace trace_processor
} // namespace perfetto