1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef INCLUDE_PERFETTO_TRACING_TRACED_PROTO_H_
18 #define INCLUDE_PERFETTO_TRACING_TRACED_PROTO_H_
19
20 #include "perfetto/base/template_util.h"
21 #include "perfetto/protozero/field_writer.h"
22 #include "perfetto/protozero/proto_utils.h"
23
24 namespace perfetto {
25 class EventContext;
26
27 // A Wrapper around a protozero message to allow C++ classes to specify how it
28 // should be serialised into the trace:
29 //
30 // class Foo {
31 // public:
32 // void WriteIntoTrace(perfetto::TracedProto<pbzero::Foo> message) {
33 // message->set_int_field(int_field_);
34 // }
35 // };
36 //
37 // This class also exposes EventContext, e.g. to enable data interning.
38 //
39 // NOTE: the functionality below is not ready yet.
40 // TODO(altimin): Make the interop below possible.
41 // TracedProto also provides a seamless integration with writing untyped
42 // values via TracedValue / TracedDictionary / TracedArray:
43 //
44 // - TracedValue can be converted to a TracedProto, either by calling
45 // TracedValue::WriteProto<T>() or implicitly.
46 // - If a proto message has a repeating DebugAnnotation debug_annotations
47 // field, it can be filled using the TracedDictionary obtained from
48 // TracedProto::WriteDebugAnnotations.
49 template <typename MessageType>
50 class TracedProto {
51 public:
52 TracedProto(const TracedProto&) = delete;
53 TracedProto& operator=(const TracedProto&) = delete;
54 TracedProto& operator=(TracedProto&&) = delete;
55 TracedProto(TracedProto&&) = default;
56 ~TracedProto() = default;
57
58 MessageType* operator->() const { return message_; }
59
message()60 MessageType* message() { return message_; }
61
context()62 EventContext& context() const { return context_; }
63
64 private:
65 friend class EventContext;
66
TracedProto(MessageType * message,EventContext & context)67 TracedProto(MessageType* message, EventContext& context)
68 : message_(message), context_(context) {}
69
70 MessageType* const message_;
71 EventContext& context_;
72 };
73
74 namespace internal {
75
76 // TypedProtoWriter takes the protozero message (TracedProto<MessageType>),
77 // field description (FieldMetadata) and value and writes the given value
78 // into the given field of the given protozero message.
79 //
80 // This is primarily used for inline writing of typed messages:
81 // TRACE_EVENT(..., pbzero::Message:kField, value);
82 //
83 // Ideally we would use a function here and not a struct, but passing template
84 // arguments directly to the function (e.g. foo<void>()) isn't supported until
85 // C++20, so we have to use a helper struct here.
86 template <typename FieldMetadata>
87 struct TypedProtoWriter {
88 private:
89 using ProtoSchemaType = protozero::proto_utils::ProtoSchemaType;
90 using RepetitionType = protozero::proto_utils::RepetitionType;
91
92 static_assert(FieldMetadata::kRepetitionType !=
93 RepetitionType::kRepeatedPacked,
94 "writing packed fields isn't supported yet");
95
96 public:
97 // Implementation note: typename Check=void is used to ensure that SFINAE
98 // kicks in and the methods which do not match FieldMetadata do not fail
99 // to compile. std::is_same<Check,void> prevents early evaluation of the
100 // first enable_if_t argument.
101
102 // Simple non-repeated field.
103 template <typename Proto, typename ValueType, typename Check = void>
104 static typename base::enable_if_t<
105 FieldMetadata::kProtoFieldType != ProtoSchemaType::kMessage &&
106 FieldMetadata::kRepetitionType == RepetitionType::kNotRepeated &&
107 std::is_same<Check, void>::value>
WriteTypedProtoWriter108 Write(TracedProto<Proto> context, ValueType&& value) {
109 protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append(
110 *context.message(), FieldMetadata::kFieldId, value);
111 }
112
113 // Simple repeated non-packed field.
114 template <typename Proto, typename ValueType, typename Check = void>
115 static typename base::enable_if_t<
116 FieldMetadata::kProtoFieldType != ProtoSchemaType::kMessage &&
117 FieldMetadata::kRepetitionType == RepetitionType::kRepeatedNotPacked &&
118 std::is_same<Check, void>::value>
WriteTypedProtoWriter119 Write(TracedProto<Proto> context, ValueType&& value) {
120 for (auto&& item : value) {
121 protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append(
122 *context.message(), FieldMetadata::kFieldId, item);
123 }
124 }
125
126 // Nested non-repeated field.
127 template <typename Proto, typename ValueType, typename Check = void>
128 static typename base::enable_if_t<
129 FieldMetadata::kProtoFieldType == ProtoSchemaType::kMessage &&
130 FieldMetadata::kRepetitionType == RepetitionType::kNotRepeated &&
131 std::is_same<Check, void>::value>
WriteTypedProtoWriter132 Write(TracedProto<Proto> context, ValueType&& value) {
133 // TODO(altimin): support TraceFormatTraits here.
134 value.WriteIntoTrace(
135 context.context().Wrap(context.message()
136 ->template BeginNestedMessage<
137 typename FieldMetadata::cpp_field_type>(
138 FieldMetadata::kFieldId)));
139 }
140
141 // Nested repeated non-packed field.
142 template <typename Proto, typename ValueType, typename Check = void>
143 static typename base::enable_if_t<
144 FieldMetadata::kProtoFieldType == ProtoSchemaType::kMessage &&
145 FieldMetadata::kRepetitionType == RepetitionType::kRepeatedNotPacked &&
146 std::is_same<Check, void>::value>
WriteTypedProtoWriter147 Write(TracedProto<Proto> context, ValueType&& value) {
148 // TODO(altimin): support TraceFormatTraits here.
149 for (auto&& item : value) {
150 item.WriteIntoTrace(context.context().Wrap(
151 context.message()
152 ->template BeginNestedMessage<
153 typename FieldMetadata::cpp_field_type>(
154 FieldMetadata::kFieldId)));
155 }
156 }
157 };
158
159 } // namespace internal
160
161 template <typename MessageType, typename FieldMetadataType, typename ValueType>
WriteIntoTracedProto(TracedProto<MessageType> message,protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadataType>,ValueType && value)162 void WriteIntoTracedProto(
163 TracedProto<MessageType> message,
164 protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadataType>,
165 ValueType&& value) {
166 static_assert(
167 std::is_base_of<protozero::proto_utils::FieldMetadataBase,
168 FieldMetadataType>::value,
169 "Field name should be a protozero::internal::FieldMetadata<...>");
170 static_assert(
171 std::is_base_of<MessageType,
172 typename FieldMetadataType::message_type>::value,
173 "Field's parent type should match the context.");
174
175 internal::TypedProtoWriter<FieldMetadataType>::Write(
176 std::move(message), std::forward<ValueType>(value));
177 }
178
179 } // namespace perfetto
180
181 #endif // INCLUDE_PERFETTO_TRACING_TRACED_PROTO_H_
182