| /* |
| * 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. |
| */ |
| |
| #ifndef INCLUDE_PERFETTO_TRACING_TRACED_PROTO_H_ |
| #define INCLUDE_PERFETTO_TRACING_TRACED_PROTO_H_ |
| |
| #include "perfetto/base/template_util.h" |
| #include "perfetto/protozero/field_writer.h" |
| #include "perfetto/protozero/proto_utils.h" |
| #include "perfetto/tracing/event_context.h" |
| |
| namespace perfetto { |
| |
| // A wrapper around a protozero message to allow C++ classes to specify how it |
| // should be serialised into the trace: |
| // |
| // class Foo { |
| // public: |
| // void WriteIntoTrace(perfetto::TracedProto<pbzero::Foo> message) { |
| // message->set_int_field(int_field_); |
| // } |
| // }; |
| // |
| // This class also exposes EventContext, e.g. to enable data interning. |
| // |
| // NOTE: the functionality below is not ready yet. |
| // TODO(altimin): Make the interop below possible. |
| // TracedProto also provides a seamless integration with writing untyped |
| // values via TracedValue / TracedDictionary / TracedArray: |
| // |
| // - TracedValue can be converted to a TracedProto, either by calling |
| // TracedValue::WriteProto<T>() or implicitly. |
| // - If a proto message has a repeating DebugAnnotation debug_annotations |
| // field, it can be filled using the TracedDictionary obtained from |
| // TracedProto::WriteDebugAnnotations. |
| template <typename MessageType> |
| class TracedProto { |
| public: |
| static_assert(std::is_base_of<protozero::Message, MessageType>::value, |
| "TracedProto can be used only with protozero messages"); |
| |
| static TracedProto FromProto(MessageType* message, EventContext& context) { |
| return TracedProto(message, context); |
| } |
| |
| TracedProto(const TracedProto&) = delete; |
| TracedProto& operator=(const TracedProto&) = delete; |
| TracedProto& operator=(TracedProto&&) = delete; |
| TracedProto(TracedProto&&) = default; |
| ~TracedProto() = default; |
| |
| MessageType* operator->() const { return message_; } |
| |
| MessageType* message() { return message_; } |
| |
| EventContext& context() const { return context_; } |
| |
| private: |
| TracedProto(MessageType* message, EventContext& context) |
| : message_(message), context_(context) {} |
| |
| MessageType* const message_; |
| EventContext& context_; |
| }; |
| |
| namespace internal { |
| |
| // TypedProtoWriter takes the protozero message (TracedProto<MessageType>), |
| // field description (FieldMetadata) and value and writes the given value |
| // into the given field of the given protozero message. |
| // |
| // This is primarily used for inline writing of typed messages: |
| // TRACE_EVENT(..., pbzero::Message:kField, value); |
| // |
| // Ideally we would use a function here and not a struct, but passing template |
| // arguments directly to the function (e.g. foo<void>()) isn't supported until |
| // C++20, so we have to use a helper struct here. |
| template <typename FieldMetadata> |
| struct TypedProtoWriter { |
| private: |
| using ProtoSchemaType = protozero::proto_utils::ProtoSchemaType; |
| using RepetitionType = protozero::proto_utils::RepetitionType; |
| |
| static_assert(FieldMetadata::kRepetitionType != |
| RepetitionType::kRepeatedPacked, |
| "writing packed fields isn't supported yet"); |
| |
| public: |
| // Implementation note: typename Check=void is used to ensure that SFINAE |
| // kicks in and the methods which do not match FieldMetadata do not fail |
| // to compile. std::is_same<Check,void> prevents early evaluation of the |
| // first enable_if_t argument. |
| |
| // Simple non-repeated field. |
| template <typename Proto, typename ValueType, typename Check = void> |
| static typename base::enable_if_t< |
| FieldMetadata::kProtoFieldType != ProtoSchemaType::kMessage && |
| FieldMetadata::kRepetitionType == RepetitionType::kNotRepeated && |
| std::is_same<Check, void>::value> |
| Write(TracedProto<Proto> context, ValueType&& value) { |
| protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append( |
| *context.message(), FieldMetadata::kFieldId, value); |
| } |
| |
| // Simple repeated non-packed field. |
| template <typename Proto, typename ValueType, typename Check = void> |
| static typename base::enable_if_t< |
| FieldMetadata::kProtoFieldType != ProtoSchemaType::kMessage && |
| FieldMetadata::kRepetitionType == RepetitionType::kRepeatedNotPacked && |
| std::is_same<Check, void>::value> |
| Write(TracedProto<Proto> context, ValueType&& value) { |
| for (auto&& item : value) { |
| protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append( |
| *context.message(), FieldMetadata::kFieldId, item); |
| } |
| } |
| |
| // Nested non-repeated field. |
| template <typename Proto, typename ValueType, typename Check = void> |
| static typename base::enable_if_t< |
| FieldMetadata::kProtoFieldType == ProtoSchemaType::kMessage && |
| FieldMetadata::kRepetitionType == RepetitionType::kNotRepeated && |
| std::is_same<Check, void>::value> |
| Write(TracedProto<Proto> context, ValueType&& value) { |
| // TODO(altimin): support TraceFormatTraits here. |
| value.WriteIntoTrace( |
| TracedProto<typename FieldMetadata::cpp_field_type>::FromProto( |
| context->template BeginNestedMessage< |
| typename FieldMetadata::cpp_field_type>( |
| FieldMetadata::kFieldId), |
| context.context())); |
| } |
| |
| // Nested repeated non-packed field. |
| template <typename Proto, typename ValueType, typename Check = void> |
| static typename base::enable_if_t< |
| FieldMetadata::kProtoFieldType == ProtoSchemaType::kMessage && |
| FieldMetadata::kRepetitionType == RepetitionType::kRepeatedNotPacked && |
| std::is_same<Check, void>::value> |
| Write(TracedProto<Proto> context, ValueType&& value) { |
| // TODO(altimin): support TraceFormatTraits here. |
| for (auto&& item : value) { |
| item.WriteIntoTrace( |
| TracedProto<typename FieldMetadata::cpp_field_type>::FromProto( |
| context->template BeginNestedMessage< |
| typename FieldMetadata::cpp_field_type>( |
| FieldMetadata::kFieldId), |
| context.context())); |
| } |
| } |
| }; |
| |
| } // namespace internal |
| |
| template <typename MessageType, typename FieldMetadataType, typename ValueType> |
| void WriteIntoTracedProto( |
| TracedProto<MessageType> message, |
| protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadataType>, |
| ValueType&& value) { |
| static_assert( |
| std::is_base_of<protozero::proto_utils::FieldMetadataBase, |
| FieldMetadataType>::value, |
| "Field name should be a protozero::internal::FieldMetadata<...>"); |
| static_assert( |
| std::is_base_of<MessageType, |
| typename FieldMetadataType::message_type>::value, |
| "Field's parent type should match the context."); |
| |
| internal::TypedProtoWriter<FieldMetadataType>::Write( |
| std::move(message), std::forward<ValueType>(value)); |
| } |
| |
| } // namespace perfetto |
| |
| #endif // INCLUDE_PERFETTO_TRACING_TRACED_PROTO_H_ |