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