/* * 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 #include #include #include #include "perfetto/base/build_config.h" #include "perfetto/base/status.h" #include "perfetto/protozero/field.h" #include "perfetto/public/compiler.h" #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h" #include "protos/perfetto/trace/profiling/profile_common.pbzero.h" #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h" #include "src/trace_processor/util/proto_to_args_parser.h" namespace perfetto::trace_processor::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; } constexpr 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, 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 perfetto::trace_processor::util